update 2025-05-14 04:25:50

This commit is contained in:
kenzok8 2025-05-14 04:25:50 +08:00
parent 310e9dd234
commit 445f0b7a67
11 changed files with 257 additions and 192 deletions

View File

@ -9,8 +9,8 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-netspeedtest PKG_NAME:=luci-app-netspeedtest
PKG_VERSION:=5.0.1 PKG_VERSION:=5.0.2
PKG_RELEASE:=20250512 PKG_RELEASE:=20250513
LUCI_TITLE:=LuCI Support for netspeedtest LUCI_TITLE:=LuCI Support for netspeedtest
LUCI_DEPENDS:=+speedtest-cli +homebox +iperf3-ssl LUCI_DEPENDS:=+speedtest-cli +homebox +iperf3-ssl

View File

@ -30,7 +30,7 @@ return view.extend({
var iframe = E('iframe', { var iframe = E('iframe', {
src: window.location.origin + ':' + state.port, src: window.location.origin + ':' + state.port,
style: 'width: 100%; min-height: 80vh; border: none; border-radius: 3px;' style: 'border:none;width: 100%; min-height: 80vh; border: none; border-radius: 3px;overflow:hidden !important;'
}); });
function checkProcess() { function checkProcess() {
@ -42,7 +42,7 @@ return view.extend({
function controlService(action) { function controlService(action) {
var command = action === 'start' var command = action === 'start'
? 'nohup /usr/bin/homebox > /tmp/homebox.log 2>&1 &' ? 'nohup /usr/bin/homebox > /tmp/netspeedtest.log 2>&1 &'
: '/usr/bin/killall homebox'; : '/usr/bin/killall homebox';
return fs.exec('/bin/sh', ['-c', command]); return fs.exec('/bin/sh', ['-c', command]);
} }

View File

@ -12,7 +12,7 @@ var state = {
port: null port: null
}; };
const logPath = '/var/log/iperf3.log'; const logPath = '/tmp/netspeedtest.log';
function checkProcess() { function checkProcess() {
return fs.exec('/bin/pidof', ['iperf3']).then(res => ({ return fs.exec('/bin/pidof', ['iperf3']).then(res => ({
@ -172,7 +172,7 @@ const statusSection = E('div', { 'class': 'cbi-section' }, [
return E('div', [ return E('div', [
statusSection, statusSection,
E('div', { 'class': 'cbi-section' }, [ E('div', { 'class': 'cbi-section' }, [
E('h3', {}, _('Iperf3 Run Log')), E('h3', {}, _('Run Log')),
logTextarea, logTextarea,
E('div', { 'style': 'text-align: right; font-size: small; margin-top: 5px;' }, E('div', { 'style': 'text-align: right; font-size: small; margin-top: 5px;' },
_('Refresh every 5 seconds.') _('Refresh every 5 seconds.')

View File

@ -1,3 +1,4 @@
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest */
'use strict'; 'use strict';
'require dom'; 'require dom';
'require fs'; 'require fs';
@ -11,63 +12,74 @@ var logTextarea;
var log_path; var log_path;
uci.load('netspeedtest').then(function() { uci.load('netspeedtest').then(function() {
log_path = '/var/log/netspeedtest.log'; log_path = '/tmp/netspeedtest.log';
}); });
function pollLog() { function pollLog() {
return Promise.all([ return Promise.all([
fs.read_direct(log_path, 'text').then(function (res) { fs.read_direct(log_path, 'text').then(function(res) {
return res.trim().split(/\n/).join('\n').replace(/\u001b\[33mWARN\u001b\[0m/g, '').replace(/\u001b\[36mINFO\u001b\[0m/g, '').replace(/\u001b\[31mERRO\u001b\[0m/g, ''); return res.trim()
}), .split(/\n/).join('\n')
]).then(function (data) { .replace(/\u001b\[33mWARN\u001b\[0m/g, '')
logTextarea.value = data[0] || _('No log data.'); .replace(/\u001b\[36mINFO\u001b\[0m/g, '')
.replace(/\u001b\[31mERRO\u001b\[0m/g, '');
}),
]).then(function(data) {
logTextarea.value = data[0] || _('No log data.');
if (!userScrolled) { if (!userScrolled) {
logTextarea.scrollTop = logTextarea.scrollHeight; logTextarea.scrollTop = logTextarea.scrollHeight;
} else { } else {
logTextarea.scrollTop = scrollPosition; logTextarea.scrollTop = scrollPosition;
} }
}); });
}; }
return view.extend({ return view.extend({
handleCleanLogs: function () { handleCleanLogs: function() {
return fs.write(log_path, '') return fs.write(log_path, '')
.catch(function (e) { ui.addNotification(null, E('p', e.message)) }); .catch(function(e) {
}, ui.addNotification(null, E('p', e.message))
});
},
render: function () { render: function() {
logTextarea = E('textarea', { logTextarea = E('textarea', {
'class': 'cbi-input-textarea', 'class': 'cbi-input-textarea',
'wrap': 'off', 'wrap': 'off',
'readonly': 'readonly', 'readonly': 'readonly',
'style': 'width: calc(100% - 20px);height: 535px;margin: 10px;overflow-y: scroll;', 'style': 'width: calc(100% - 20px); height: 535px; margin: 10px; overflow-y: scroll;'
}); });
logTextarea.addEventListener('scroll', function () { logTextarea.addEventListener('scroll', function() {
userScrolled = true; userScrolled = true;
scrollPosition = logTextarea.scrollTop; scrollPosition = logTextarea.scrollTop;
}); });
var log_textarea_wrapper = E('div', { 'id': 'log_textarea' }, logTextarea); var log_textarea_wrapper = E('div', { 'id': 'log_textarea' }, logTextarea);
setTimeout(function () { setTimeout(function() {
poll.add(pollLog); poll.add(pollLog);
}, 100); }, 100);
var clear_logs_button = E('input', { 'class': 'btn cbi-button-action', 'type': 'button', 'style': 'margin-left: 10px; margin-top: 10px;', 'value': _('Clear logs') }); var clear_logs_button = E('input', {
clear_logs_button.addEventListener('click', this.handleCleanLogs.bind(this)); 'class': 'btn cbi-button-action',
'type': 'button',
'style': 'margin-left: 20px; margin-top: 10px;',
'value': _('Clear logs')
});
clear_logs_button.addEventListener('click', this.handleCleanLogs.bind(this));
return E([ return E('div', { 'class': 'cbi-map' }, [
E('div', { 'class': 'cbi-map' }, [ E('div', { 'class': 'cbi-section' }, [
E('div', { 'class': 'cbi-section' }, [ clear_logs_button,
clear_logs_button, log_textarea_wrapper,
log_textarea_wrapper, E('div', { 'style': 'text-align: right' }, [
E('div', { 'style': 'text-align:right' }, E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval))
E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval)), ]),
E('div', { 'class': 'cbi-section-actions cbi-section-actions-right' }) E('div', { 'class': 'cbi-section-actions cbi-section-actions-right' })
]), ]),
E('div', { 'style': 'text-align: right; font-style: italic;' }, [ E('div', { 'style': 'text-align: right; font-style: italic; margin-top: 10px;' }, [
E('span', {}, [ E('span', {}, [
_('© github '), _('© github '),
E('a', { E('a', {
@ -75,12 +87,12 @@ return view.extend({
'target': '_blank', 'target': '_blank',
'style': 'text-decoration: none;' 'style': 'text-decoration: none;'
}, 'by sirpdboy') }, 'by sirpdboy')
]) ])
]) ])
]); ]);
} }
// handleSaveApply: null, // handleSaveApply: null,
// handleSave: null, // handleSave: null,
// handleReset: null // handleReset: null
}); });

View File

@ -1,3 +1,4 @@
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest */
'use strict'; 'use strict';
'require view'; 'require view';
'require uci'; 'require uci';
@ -22,7 +23,7 @@ return view.extend({
s.render = function (section_id) { s.render = function (section_id) {
return E('iframe', { return E('iframe', {
src: '//openspeedtest.com/speedtest', src: '//openspeedtest.com/speedtest',
style: 'border:none;width:100%;height:100%;min-height:360px;border:none;overflow:hidden !important;' style: 'width:100%;height:100%;min-height:360px;border:none;overflow:hidden !important;'
}); });
}; };

View File

@ -1,3 +1,4 @@
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest */
'use strict'; 'use strict';
'require view'; 'require view';
'require poll'; 'require poll';
@ -17,7 +18,7 @@ return view.extend({
// handleSaveApply: null, // handleSaveApply: null,
// handleSave: null, // handleSave: null,
// handleReset: null, // handleReset: null,
load: function () { load() {
return Promise.all([ return Promise.all([
L.resolveDefault(fs.stat(SpeedtestCli), {}), L.resolveDefault(fs.stat(SpeedtestCli), {}),
L.resolveDefault(fs.read(ResultFile), null), L.resolveDefault(fs.read(ResultFile), null),
@ -26,76 +27,94 @@ return view.extend({
]); ]);
}, },
poll_status: function (nodes, res) { poll_status(nodes, res) {
var has_ookla = res[0].path, var has_ookla = res[0].path,
result_content = res[1] ? res[1].trim().split("\n") : []; result_content = res[1] ? res[1].trim().split("\n") : [];
var ookla_stat = nodes.querySelector('#ookla_status'), var ookla_stat = nodes.querySelector('#ookla_status'),
result_stat = nodes.querySelector('#speedtest_result'); result_stat = nodes.querySelector('#speedtest_result');
// Update status indicators // 获取版本号(新增部分)
ookla_stat.style.color = has_ookla ? 'green' : 'red'; var version_info = '';
dom.content(ookla_stat, [_(has_ookla ? 'Installed' : 'Not Installed')]); if (has_ookla) {
fs.exec_direct('/usr/bin/speedtest', ['--version'])
// Update result display .then(function(res) {
if (result_content.length) { if (res.stdout) {
if (result_content[0] == 'Testing') { var version_match = res.stdout.match(/Speedtest (\d+\.\d+\.\d+)/);
result_stat.innerHTML = "<span style='color:green;font-weight:bold'>" + if (version_match) {
"<img src='/luci-static/resources/icons/loading.gif' height='17' style='vertical-align:middle'/> " + version_info = ' ver:' + version_match[1];
_('Testing in progress...') + }
"</span>"; }
} else if (result_content[0].match(/https?:\S+/)) { // 更新状态显示(包含版本号)
result_stat.innerHTML = "<div style='max-width:500px'><a href='" + ookla_stat.style.color = 'green';
result_content[0] + "' target='_blank'><img src='" + dom.content(ookla_stat, [_(has_ookla ? 'Installed' + version_info : 'Not Installed')]);
result_content[0] + '.png' + "' style='max-width:100%'></a></div>"; })
} else if (result_content[0] == 'Test failed') { .catch(function() {
result_stat.innerHTML = "<span style='color:red;font-weight:bold'>" + // 如果获取版本失败,仍显示基本状态
_('Test failed.') + "</span>"; ookla_stat.style.color = has_ookla ? 'green' : 'red';
} dom.content(ookla_stat, [_(has_ookla ? 'Installed' : 'Not Installed')]);
} else { });
result_stat.innerHTML = "<span style='color:gray'>" + } else {
_('No test results yet.') + "</span>"; // 未安装时的显示保持不变
ookla_stat.style.color = 'red';
dom.content(ookla_stat, [_('Not Installed')]);
}
if (result_content.length) {
if (result_content[0] == 'Testing') {
result_stat.innerHTML = "<span style='color:green;font-weight:bold'>" +
"<img src='/luci-static/resources/icons/loading.gif' height='17' style='vertical-align:middle ;margin-left:20px'/> " +
_('SpeedTesting in progress...') +
"</span>";
} else if (result_content[0].match(/https?:\S+/)) {
result_stat.innerHTML = "<div style='max-width:500px'><a href='" +
result_content[0] + "' target='_blank'><img src='" +
result_content[0] + '.png' + "' style='max-width:100%;margin-left:20px'></a></div>";
} else if (result_content[0] == 'Test failed') {
result_stat.innerHTML = "<span style='color:red;font-weight:bold;margin-left:20px'>" +
_('Test failed.') + "</span>";
} }
}, } else {
result_stat.innerHTML = "<span style='color:gray;margin-left:20px'>" +
_('No test results yet.') + "</span>";
}
},
render: function (res) { render(res) {
var has_ookla = res[0].path, var has_ookla = res[0].path,
result_content = res[1] ? res[1].trim().split("\n") : [], result_content = res[1] ? res[1].trim().split("\n") : [],
result_mtime = res[2] ? res[2].mtime * 1000 : 0, result_mtime = res[2] ? res[2].mtime * 1000 : 0,
date = new Date(); date = new Date();
var m, s, o; var m, s, o;
m = new form.Map('netspeedtest', _('WAN Ookla SpeedTest')); m = new form.Map('netspeedtest', _('Wan Ookla SpeedTest'));
// Result display section // Result display section
s = m.section(form.TypedSection, '_result'); s = m.section(form.TypedSection, '_result');
s.anonymous = true; s.anonymous = true;
s.render = function () { s.render = function (section_id) {
var content; if (result_content.length) {
if (result_content.length) { if (result_content[0] == 'Testing') {
if (result_content[0] == 'Testing') { return E('div', { 'id': 'speedtest_result' }, [ E('span', { 'style': 'color:yellow;font-weight:bold' }, [
content = E('span', { style: 'color:green;font-weight:bold' }, [ E('img', { 'src': L.resource(['icons/loading.gif']), 'height': '20', 'style': 'vertical-align:middle' }, []),
E('img', { src: '/luci-static/resources/icons/loading.gif', height: '20' }), _('Testing in progress...')
' ', _('Testing in progress...') ]) ])
]); };
} else if (result_content[0].match(/https?:\S+/)) { if (result_content[0].match(/https?:\S+/)) {
content = E('div', { style: 'max-width:500px' }, [ return E('div', { 'id': 'speedtest_result' }, [ E('div', { 'style': 'max-width:500px' }, [
E('a', { href: result_content[0], target: '_blank' }, [ E('a', { 'href': result_content[0], 'target': '_blank' }, [
E('img', { src: result_content[0] + '.png', style: 'max-width:100%' }) E('img', { 'src': result_content[0] + '.png', 'style': 'max-width:100%;max-height:100%;vertical-align:middle' }, [])
]) ]) ]) ])
]); };
} else { if (result_content[0] == 'Test failed') {
content = E('span', { style: 'color:red;font-weight:bold' }, return E('div', { 'id': 'speedtest_result' }, [ E('span', { 'style': 'color:red;font-weight:bold' }, [ _('Test failed.') ]) ])
_('Test failed.')); }
} } else {
} else { return E('div', { 'id': 'speedtest_result' }, [ E('span', { 'style': 'color:red;font-weight:bold;display:none' }, [ _('No result.') ]) ])
content = E('span', { style: 'color:gray' }, }
_('No test results yet.')); };
}
return E('div', { id: 'speedtest_result' }, content);
};
// Configuration section // Configuration section
s = m.section(form.NamedSection, 'config', 'netspeedtest'); s = m.section(form.NamedSection, 'config', 'netspeedtest');
s.anonymous = true;
// Start test button // Start test button
o = s.option(form.Button, '_start', _('Start Ookla SpeedTest')); o = s.option(form.Button, '_start', _('Start Ookla SpeedTest'));

View File

@ -17,7 +17,7 @@ msgid "NetSpeedtest"
msgstr "网速测试" msgstr "网速测试"
msgid "A tool for testing network speed in multiple aspects" msgid "A tool for testing network speed in multiple aspects"
msgstr "一个用于从多方面测试本地和宽带网络速度的工具" msgstr "一个用于从多方面测试本地和宽带网络速度的测速工具"
msgid "Lan Speedtest Iperf3" msgid "Lan Speedtest Iperf3"
msgstr "本地iperf3吞吐测速" msgstr "本地iperf3吞吐测速"
@ -26,7 +26,7 @@ msgid "Lan Speedtest Homebox"
msgstr "本地homebox网页测速" msgstr "本地homebox网页测速"
msgid "Wan Ookla SpeedTest" msgid "Wan Ookla SpeedTest"
msgstr "宽带Ookla网速测" msgstr "宽带Ookla网速测"
msgid "Wan OpenSpeedTest" msgid "Wan OpenSpeedTest"
msgstr "宽带OpenSpeedTest测速" msgstr "宽带OpenSpeedTest测速"
@ -79,8 +79,8 @@ msgstr "开启日志显示"
msgid "Download iperf3 client" msgid "Download iperf3 client"
msgstr "下载iperf3客户端" msgstr "下载iperf3客户端"
msgid "Iperf3 Run Log" msgid "Run Log"
msgstr "Iperf3运行日志" msgstr "运行日志"
msgid "Refresh Log" msgid "Refresh Log"
msgstr "刷新日志" msgstr "刷新日志"
@ -109,8 +109,8 @@ msgstr "开始宽带测速"
msgid "Click to execute" msgid "Click to execute"
msgstr "点击执行" msgstr "点击执行"
msgid "Testing in progress..." msgid "SpeedTesting in progress..."
msgstr "测试正在进行中..." msgstr "测速中,请稍候..."
msgid "Test failed." msgid "Test failed."
msgstr "测速失败" msgstr "测速失败"

View File

@ -6,67 +6,72 @@
START=99 START=99
USE_PROCD=1 USE_PROCD=1
EXTRA_COMMANDS="download_ookla ookla_verify" EXTRA_COMMANDS="download verify"
EXTRA_HELP=\ EXTRA_HELP=\
" download_ookla Download Ookla Speedtest-CLI " download Download Ookla Speedtest-CLI
ookla_verify Verify Ookla Speedtest-CLI integrity" verify Verify Ookla Speedtest-CLI integrity"
#
OOKLA_SPEEDTEST='/usr/bin/speedtest' OOKLA_SPEEDTEST='/usr/bin/speedtest'
# uci
CONFIG='netspeedtest' CONFIG='netspeedtest'
NAMEDDSECTION='config' log='/tmp/netspeedtest.log'
get_config() { download() {
config_load netspeedtest . /etc/openwrt_release
config_get "proxy_enabled" "config" "proxy_enabled" "0" local url arch=$1
local proxy=$2
[ -z "$arch" ] && arch=$DISTRIB_ARCH
[ -z "$proxy" ] && proxy='0'
[ "$proxy" == "1" ] && export ALL_PROXY="http://192.168.10.8:1080"
UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
url=$( \
curl --connect-timeout 10 --retry 3 -sSL 'https://www.speedtest.net/apps/cli' \
--user-agent "$UA" \
| grep "Download for Linux" \
| sed 's|<|\n<|g' \
| sed -n '/Download for Linux/,/<\/div>/p' \
| sed -En "s|.*<a href=\"([^\"]+)\">${arch}|\1|p" \
)
[ -z "$url" ] && { echo "Failed to get download URL" >> "$log"; return 1; }
# Backup existing file
[ -f "$OOKLA_SPEEDTEST" ] && mv "$OOKLA_SPEEDTEST" "${OOKLA_SPEEDTEST}.bak"
if curl -sSL "$url" --user-agent "$UA" | tar -xvz -C /tmp; then
mkdir -p "${OOKLA_SPEEDTEST%/*}" 2>/dev/null
cp -f /tmp/speedtest "$OOKLA_SPEEDTEST"
rm -rf /tmp/speedtest
chmod 755 "$OOKLA_SPEEDTEST"
if verify; then
echo "Download successful: $($OOKLA_SPEEDTEST --version | awk '/Speedtest/{print $1" ver:"$4}')" >> "$log"
else
echo "Download failed: binary verification failed" >> "$log"
[ -f "${OOKLA_SPEEDTEST}.bak" ] && mv "${OOKLA_SPEEDTEST}.bak" "$OOKLA_SPEEDTEST"
return 1
fi
else
echo "Download failed: could not retrieve package" >> "$log"
[ -f "${OOKLA_SPEEDTEST}.bak" ] && mv "${OOKLA_SPEEDTEST}.bak" "$OOKLA_SPEEDTEST"
return 1
fi
unset ALL_PROXY
} }
verify() {
download_ookla() { [ -x "$OOKLA_SPEEDTEST" ] && "$OOKLA_SPEEDTEST" --version >/dev/null 2>&1
local url arch=$1
[ -z "$arch" ] && return 1
[ "$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_enabled)" == "1" ] && \
export ALL_PROXY=$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_protocol)://$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_server)
UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
url=$( \
curl --connect-timeout 10 --retry 3 -sSL 'https://www.speedtest.net/apps/cli' \
--user-agent "$UA" \
| grep "Download for Linux" \
| sed 's|<|\n<|g' \
| sed -n '/Download for Linux/,/<\/div>/p' \
| sed -En "s|.*<a href=\"([^\"]+)\">${arch}|\1|p" \
)
[ -z "$url" ] && return 1
[ -n "$url" ] && curl -sSL $url --user-agent "$UA" | tar -xvz -C /tmp
mkdir -p ${OOKLA_SPEEDTEST%/*} 2>/dev/null
cp -f /tmp/speedtest $OOKLA_SPEEDTEST
chmod 755 $OOKLA_SPEEDTEST
ookla_verify || rm -f $OOKLA_SPEEDTEST
unset ALL_PROXY
} }
ookla_verify() { start() {
if [ -x "$OOKLA_SPEEDTEST" ]; then if ! verify; then
return 0 download || {
else echo "Critical: Failed to install Speedtest CLI" >> "$log"
return 1 exit 1
fi }
fi
} }
restart() {
start_service() { start
ookla_verify
}
service_triggers() {
procd_add_reload_trigger "netspeedtest"
}
reload_service() {
stop
sleep 1
start
} }

View File

@ -3,35 +3,54 @@ mkdir -p /etc/speedtest
export HOME='/etc/speedtest' export HOME='/etc/speedtest'
SPEEDTEST_CLI='/usr/bin/speedtest' SPEEDTEST_CLI='/usr/bin/speedtest'
SPEEDTEST_RESULT='/tmp/speedtest_result' SPEEDTEST_RESULT='/tmp/speedtest_result'
LOG='/tmp/netspeedtest.log'
# 记录开始时间
echo "=== Speedtest started at $(date) ===" >> "$LOG"
[ -n "$(pgrep -f "$SPEEDTEST_CLI")" ] && exit 1 [ -n "$(pgrep -f "$SPEEDTEST_CLI")" ] && exit 1
echo "Start Testing" > "$SPEEDTEST_RESULT"
LOCAL_IP=$(curl -s -4 --connect-timeout 3 http://ip.3322.net) LOCAL_IP=$(curl -s -4 --connect-timeout 3 http://ip.3322.net)
echo "Local IP: $LOCAL_IP" >> "$LOG"
BAIDU_SK="LHHGlmhcb4ENvIXpR9QQ2tBYa6ooUowX hYCENCEx1nXO0Nt46ldexfG9oI49xBGh 0kKZnWWhXEPfzIkklmzAa3dZ" BAIDU_SK="LHHGlmhcb4ENvIXpR9QQ2tBYa6ooUowX hYCENCEx1nXO0Nt46ldexfG9oI49xBGh 0kKZnWWhXEPfzIkklmzAa3dZ"
if [ -n "$LOCAL_IP" ]; then if [ -n "$LOCAL_IP" ]; then
for SK in $BAIDU_SK for SK in $BAIDU_SK
do do
INFO=$(curl -sk --connect-timeout 3 "https://api.map.baidu.com/location/ip?ip="$LOCAL_IP"&coor=bd09ll&ak=$SK") INFO=$(curl -sk --connect-timeout 3 "https://api.map.baidu.com/location/ip?ip="$LOCAL_IP"&coor=bd09ll&ak=$SK")
if [ "$(echo $INFO | jsonfilter -e "@['status']")" = 0 ]; then if [ "$(echo $INFO | jsonfilter -e "@['status']")" = 0 ]; then
status=0 status=0
break break
fi fi
done done
if [ "$status" = 0 ]; then if [ "$status" = 0 ]; then
lon=$(echo $INFO | jsonfilter -e "@['content']['point']['x']") lon=$(echo $INFO | jsonfilter -e "@['content']['point']['x']")
lat=$(echo $INFO | jsonfilter -e "@['content']['point']['y']") lat=$(echo $INFO | jsonfilter -e "@['content']['point']['y']")
server_id=$(curl -sk --connect-timeout 3 "https://www.speedtest.net/api/ios-config.php?lon=$lon&lat=$lat" | grep "server url" | head -n1 | sed 's/.*id="//;s/".*//') server_id=$(curl -sk --connect-timeout 3 "https://www.speedtest.net/api/ios-config.php?lon=$lon&lat=$lat" | grep "server url" | head -n1 | sed 's/.*id="//;s/".*//')
[ -n "$server_id" ] && ARG="-s $server_id" [ -n "$server_id" ] && ARG="-s $server_id"
fi echo "Selected server ID: $server_id" >> "$LOG"
fi
fi fi
echo "Testing" > "$SPEEDTEST_RESULT"
RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no $ARG 2>&1) RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no $ARG 2>&1)
if [ $(echo $RUNTEST | grep -c "No servers defined") -ge 1 ] || [ $(echo $RUNTEST | grep -c "error") -ge 1 ]; then if [ $(echo $RUNTEST | grep -c "No servers defined") -ge 1 ] || [ $(echo $RUNTEST | grep -c "error") -ge 1 ]; then
RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no 2>&1) echo "Fallback to default server selection" >> "$LOG"
RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no 2>&1)
fi fi
# 将完整测试结果记录到日志
echo "$RUNTEST" >> "$LOG"
RESULT=$(echo "$RUNTEST" | grep "Result URL" | awk '{print $NF}') RESULT=$(echo "$RUNTEST" | grep "Result URL" | awk '{print $NF}')
[ -n "$RESULT" ] && echo "$RESULT" > "$SPEEDTEST_RESULT" || echo "Test failed" > "$SPEEDTEST_RESULT" if [ -n "$RESULT" ]; then
echo "$RESULT" > "$SPEEDTEST_RESULT"
else
echo "Test failed" > "$SPEEDTEST_RESULT"
echo "Test failed" >> "$LOG"
fi
# 记录结束时间
echo "=== Speedtest completed at $(date) ===" >> "$LOG"
echo "" >> "$LOG" # 添加空行分隔不同测试记录

View File

@ -41,5 +41,13 @@
"type": "view", "type": "view",
"path": "netspeedtest/openspeedtest" "path": "netspeedtest/openspeedtest"
} }
},
"admin/network/netspeedtest/logs": {
"title": "Log",
"order": 6,
"action": {
"type": "view",
"path": "netspeedtest/logs"
}
} }
} }

View File

@ -5,15 +5,16 @@
"file": { "file": {
"/etc/init.d/netspeedtest": [ "exec" ], "/etc/init.d/netspeedtest": [ "exec" ],
"/usr/lib/netspeedtest/speedtest": [ "exec" ], "/usr/lib/netspeedtest/speedtest": [ "exec" ],
"/tmp/speedtest_result": [ "read" ] "/tmp/speedtest_result": [ "read" ],
"/tmp/netspeedtest.log": [ "read" ]
}, },
"ubus": { "ubus": {
"service": [ "list" ] "service": [ "list" ]
}, },
"uci": [ "netspeedtest" ] "uci": [ "netspeedtest" ,"netspeedtest"]
}, },
"write": { "write": {
"uci": [ "netspeedtest" ] "uci": [ "netspeedtest","netspeedtest" ]
} }
} }
} }