Красная кнопка: ролик над журналом на время подбора и журнал без долгих пауз
- Пока идёт подбор, над журналом играет ролик; после окончания плеер убирается и ничего не качает. Звук выключается отдельной кнопкой рядом с плеером, выбор запоминается. Если браузер не даёт играть со звуком, ролик идёт без звука. - Журнал пишет по ходу: каждый сервис сразу после проверки, каждую стратегию тестера и сколько она открыла, подбор для YouTube (раньше шёл молча, минутами), каждую пробуемую точку WARP. На стенде самая длинная пауза без новых строк — 15 секунд. - Снятие настроек помечается своей фазой, и открытая в это время страница не включает ролик.
This commit is contained in:
1 parent
fb4fffd38a
commit
1730e16a6c
1 file changed
+104
-21
+104
-21
@@ -4870,25 +4870,40 @@ _rb_check_service() { # ID РЕЖИМ -> код 0, если сервис раб
|
||||
}
|
||||
|
||||
# Проверить все сервисы (кроме Telegram), печатает «id ok|fail» по строке.
|
||||
_rb_check_all() { # РЕЖИМ
|
||||
# С ПОДПИСЬЮ — ещё и печатает каждый сервис в журнал сразу, как он проверен: проверка всех идёт
|
||||
# около минуты, и журнал, молчащий минуту, выглядит зависшим.
|
||||
_rb_check_all() { # РЕЖИМ [ПОДПИСЬ]
|
||||
local id
|
||||
for id in $(_rb_svc_ids); do
|
||||
[ "$id" = telegram ] && continue
|
||||
if _rb_check_service "$id" "$1"; then echo "$id ok"; else echo "$id fail"; fi
|
||||
if _rb_check_service "$id" "$1"; then
|
||||
echo "$id ok"
|
||||
[ -n "${2:-}" ] && echo "[ OK ] $(_rb_svc_field "$id" 2) — $2" >&2
|
||||
else
|
||||
echo "$id fail"
|
||||
[ -n "${2:-}" ] && echo "[FAIL] $(_rb_svc_field "$id" 2)" >&2
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Ход тестера менеджера — в журнал по строке: какую стратегию меряем и сколько она открыла.
|
||||
_rb_test_progress() {
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
'==> ['*) echo " ${line#==> }" ;;
|
||||
'==> Результат: '*) echo " открылось ${line#==> Результат: }" | sed 's#/# из #' ;;
|
||||
'==> Контрольный тест'*) echo " Проверяем, что открывается без Zapret" ;;
|
||||
'==> Собираем стратегии'*) echo " Собираем стратегии" ;;
|
||||
'==> Собираем список доменов'*) echo " Собираем адреса для проверки" ;;
|
||||
'==> Найдено стратегий: '*) echo " Стратегий в подборе: ${line#==> Найдено стратегий: }" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
_rb_count_ok() { printf '%s\n' "$1" | grep -c ' ok$'; }
|
||||
_rb_failed() { printf '%s\n' "$1" | awk '$2=="fail"{print $1}'; }
|
||||
|
||||
_rb_print_checks() { # ТАБЛИЦА ПОДПИСЬ_OK
|
||||
local id st
|
||||
printf '%s\n' "$1" | while read -r id st; do
|
||||
[ -n "$id" ] || continue
|
||||
if [ "$st" = ok ]; then echo "[ OK ] $(_rb_svc_field "$id" 2) — $2"
|
||||
else echo "[FAIL] $(_rb_svc_field "$id" 2)"; fi
|
||||
done
|
||||
}
|
||||
|
||||
# ── пакеты ──
|
||||
|
||||
@@ -5132,11 +5147,14 @@ _rb_warp_up() {
|
||||
fi
|
||||
_rb_say "Подбираем точку входа WARP"
|
||||
cand="$(_rb_warp_candidates)"
|
||||
_rb_say "Кандидатов: $(echo "$cand" | grep -c .)"
|
||||
# Порты 4500 и 2408 выколоты из игрового фильтра zapret — туннель не попадёт под обработку.
|
||||
for port in 4500 2408; do
|
||||
for host in $cand; do
|
||||
_rb_stopped && return 1
|
||||
colo="$(_rb_warp_try "$host" "$port")" || continue
|
||||
echo " Пробуем $host:$port"
|
||||
colo="$(_rb_warp_try "$host" "$port")" || { echo " не отвечает"; continue; }
|
||||
echo " работает, колония $colo"
|
||||
if _rb_warp_is_ru "$colo"; then
|
||||
[ -n "$spare" ] || spare="$host $port $colo"
|
||||
continue
|
||||
@@ -5298,7 +5316,7 @@ _rb_result_num() { printf '%s\n' "$1" | sed -n 's/.* → \([0-9]*\)\/.*/\1/p'; }
|
||||
# Прогнать тестер. Печатает имя лучшей стратегии, если она лучше, чем без zapret.
|
||||
_rb_zapret_pick() { # РЕЖИМ_ТЕСТА
|
||||
local mode="$1" res best name ctrl bok
|
||||
do_test_run "$mode" | grep '^==> \[' | sed 's/^==> / /' >&2
|
||||
do_test_run "$mode" | _rb_test_progress >&2
|
||||
res="$(_test_results_file "$mode")"
|
||||
[ -s "$res" ] || return 1
|
||||
best=$(grep -v '^Контрольный тест' "$res" | head -n1)
|
||||
@@ -5313,7 +5331,7 @@ _rb_zapret_pick() { # РЕЖИМ_ТЕСТА
|
||||
|
||||
_rb_youtube_pick() {
|
||||
local res best name
|
||||
do_test_run youtube >/dev/null 2>&1
|
||||
do_test_run youtube | _rb_test_progress >&2
|
||||
res="$(_test_results_file youtube)"
|
||||
best=$(grep -v '^Контрольный тест' "$res" 2>/dev/null | head -n1)
|
||||
[ -n "$best" ] || return 1
|
||||
@@ -5357,8 +5375,7 @@ do_redbtn_run() {
|
||||
|
||||
_rb_phase check
|
||||
_rb_say "Проверяем сервисы при текущей настройке"
|
||||
before="$(_rb_check_all as_is)"
|
||||
_rb_print_checks "$before" "открывается"
|
||||
before="$(_rb_check_all as_is открывается)"
|
||||
_rb_stopped && { _rb_say "Остановлено"; return 0; }
|
||||
|
||||
# Подбор стратегии — только если что-то не открывается: рабочую настройку не трогаем.
|
||||
@@ -5373,7 +5390,8 @@ do_redbtn_run() {
|
||||
name="${pick% *}"
|
||||
_rb_say "Лучшая стратегия: $name (${pick#* } целей)"
|
||||
if _rb_zapret_apply "$name"; then
|
||||
after="$(_rb_check_all as_is)"
|
||||
_rb_say "Проверяем сервисы с новой стратегией"
|
||||
after="$(_rb_check_all as_is открывается)"
|
||||
# Одна проба шумит: сервис, ответивший медленно, выглядит закрытым. Прежде чем
|
||||
# откатывать, перемериваем ещё раз.
|
||||
[ "$(_rb_count_ok "$after")" -lt "$(_rb_count_ok "$before")" ] && after="$(_rb_check_all as_is)"
|
||||
@@ -5414,6 +5432,7 @@ do_redbtn_run() {
|
||||
# Что открылось — напрямую или благодаря zapret.
|
||||
_rb_phase check
|
||||
local direct
|
||||
_rb_say "Проверяем, что открывается без обхода"
|
||||
direct="$(_rb_check_all direct)"
|
||||
for id in $(printf '%s\n' "$before" | awk '$2=="ok"{print $1}'); do
|
||||
# «Через Zapret» — только если и повторная проба без обхода не прошла.
|
||||
@@ -5482,6 +5501,7 @@ do_redbtn_run() {
|
||||
}
|
||||
|
||||
do_redbtn_remove() {
|
||||
_rb_phase remove
|
||||
_rb_say "Снимаем настройки красной кнопки"
|
||||
_rb_spec_clear
|
||||
if _rb_owns "net $RB_WARP_IF"; then
|
||||
@@ -6612,7 +6632,8 @@ var PHASES = {
|
||||
zapret: 'Подбираем стратегию Zapret',
|
||||
warp: 'Поднимаем туннель WARP',
|
||||
steer: 'Применяем правила',
|
||||
telegram: 'Проверяем Telegram'
|
||||
telegram: 'Проверяем Telegram',
|
||||
remove: 'Снимаем настройки'
|
||||
};
|
||||
|
||||
var BLOCKERS = {
|
||||
@@ -6621,6 +6642,14 @@ var BLOCKERS = {
|
||||
steer: 'Обход на роутере уже настроен вручную — кнопка его не перезаписывает.'
|
||||
};
|
||||
|
||||
// Ролик на время подбора. Лежит в репозитории форка; jsDelivr первым, потому что он отдаёт
|
||||
// правильный тип файла и чаще открывается там, где raw.githubusercontent закрыт.
|
||||
var WAIT_VIDEO = [
|
||||
'https://cdn.jsdelivr.net/gh/xyzmean/Zapret-Manager@main/files/RedButton/wait.mp4',
|
||||
'https://raw.githubusercontent.com/xyzmean/Zapret-Manager/main/files/RedButton/wait.mp4'
|
||||
];
|
||||
var WAIT_POSTER = 'https://cdn.jsdelivr.net/gh/xyzmean/Zapret-Manager@main/files/RedButton/poster.jpg';
|
||||
|
||||
function stateBadge(st) {
|
||||
var s = STATES[st];
|
||||
return E('span', { 'class': 'zm-badge ' + (s ? s.cls : 'zm-off') }, [
|
||||
@@ -6651,6 +6680,54 @@ return view.extend({
|
||||
var svcCard = E('div', { 'class': 'zm-card' });
|
||||
var warpCard = E('div', { 'class': 'zm-card' });
|
||||
var dnsCard = E('div', {});
|
||||
var videoEl = null, videoBox = null, soundBtn = null;
|
||||
|
||||
// Выбор «без звука» запоминается в браузере: кто выключил звук один раз, не хочет его
|
||||
// снова на следующем подборе.
|
||||
function soundPref() { try { return localStorage.getItem('zm-redbtn-mute') !== '1'; } catch (e) { return true; } }
|
||||
function soundSave(on) { try { localStorage.setItem('zm-redbtn-mute', on ? '0' : '1'); } catch (e) {} }
|
||||
function soundLabel() { if (soundBtn && videoEl) soundBtn.textContent = videoEl.muted ? 'Включить звук' : 'Выключить звук'; }
|
||||
|
||||
// Видео создаётся заново на каждый подбор и удаляется после: пока подбора нет, оно не
|
||||
// должно ни качаться, ни играть. Со звуком — если браузер разрешит (подбор начат
|
||||
// нажатием), иначе без звука, и включить его можно в самом плеере.
|
||||
function videoShow(withSound) {
|
||||
if (videoEl) return videoEl;
|
||||
videoEl = E('video', {
|
||||
'class': 'zm-redbtn-video', 'controls': '', 'loop': '', 'playsinline': '',
|
||||
'preload': 'auto', 'poster': WAIT_POSTER
|
||||
}, WAIT_VIDEO.map(function(u) { return E('source', { 'src': u, 'type': 'video/mp4' }); }));
|
||||
videoEl.muted = !(withSound && soundPref());
|
||||
videoEl.addEventListener('volumechange', function() { soundSave(!videoEl.muted); soundLabel(); });
|
||||
soundBtn = E('button', {
|
||||
'class': 'cbi-button',
|
||||
'click': function() { if (videoEl) videoEl.muted = !videoEl.muted; }
|
||||
}, '');
|
||||
videoBox = E('div', { 'class': 'zm-redbtn-player' }, [
|
||||
videoEl, E('div', { 'class': 'zm-actions' }, [ soundBtn ])
|
||||
]);
|
||||
soundLabel();
|
||||
setTimeout(function() {
|
||||
if (!videoEl) return;
|
||||
var p = videoEl.play();
|
||||
if (p && p.catch) p.catch(function() {
|
||||
if (!videoEl) return;
|
||||
videoEl.muted = true;
|
||||
videoEl.play().catch(function() {});
|
||||
});
|
||||
}, 0);
|
||||
return videoEl;
|
||||
}
|
||||
|
||||
function videoHide() {
|
||||
if (!videoEl) return;
|
||||
videoEl.pause();
|
||||
videoEl.removeAttribute('src');
|
||||
while (videoEl.firstChild) videoEl.removeChild(videoEl.firstChild);
|
||||
videoEl.load();
|
||||
if (videoBox && videoBox.parentNode) videoBox.parentNode.removeChild(videoBox);
|
||||
videoEl = videoBox = soundBtn = null;
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
return zm.redbtnStatus().then(function(res) {
|
||||
@@ -6659,12 +6736,14 @@ return view.extend({
|
||||
});
|
||||
}
|
||||
|
||||
function follow() {
|
||||
function follow(withVideo, withSound) {
|
||||
var ticks = 0;
|
||||
busy = true;
|
||||
if (withVideo) videoShow(withSound);
|
||||
renderMain();
|
||||
zm.pollJob('redbtn', logEl, function(ok) {
|
||||
busy = false;
|
||||
videoHide();
|
||||
zm.toast(ok ? 'Готово' : 'Операция завершилась с ошибкой', ok ? 'info' : 'error');
|
||||
refresh();
|
||||
}, function() {
|
||||
@@ -6685,7 +6764,7 @@ return view.extend({
|
||||
if (res.error) { zm.toast(res.error, 'error'); return; }
|
||||
zm.toast('Подбор начат', 'warning');
|
||||
data.running = true;
|
||||
follow();
|
||||
follow(true, true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6701,7 +6780,7 @@ return view.extend({
|
||||
if (!confirm('Снять туннель WARP и правила красной кнопки? Стратегия Zapret останется.')) return;
|
||||
zm.redbtnAction('remove', '').then(function(res) {
|
||||
if (res.error) { zm.toast(res.error, 'error'); return; }
|
||||
follow();
|
||||
follow(false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6752,6 +6831,7 @@ return view.extend({
|
||||
E('span', { 'class': 'zm-label' }, 'Последний подбор'),
|
||||
E('span', {}, fmtTime(data.last))
|
||||
]));
|
||||
if (busy && videoBox) mainCard.appendChild(videoBox);
|
||||
mainCard.appendChild(logEl);
|
||||
}
|
||||
|
||||
@@ -6818,7 +6898,7 @@ return view.extend({
|
||||
wrap.appendChild(svcCard);
|
||||
wrap.appendChild(warpCard);
|
||||
|
||||
if (data.running) follow();
|
||||
if (data.running) follow(data.phase !== 'remove', false);
|
||||
return wrap;
|
||||
}
|
||||
});
|
||||
@@ -10020,6 +10100,9 @@ html.zm-theme-dark .zm-config-editor { border-color: rgba(255,255,255,.14); }
|
||||
font-size: 1.1em; font-weight: 600; padding: .7em 2.2em; border-radius: 999px;
|
||||
}
|
||||
.zm-redbtn:hover { background: #b71c1c !important; }
|
||||
.zm-redbtn-player { margin: 12px 0 0; }
|
||||
.zm-redbtn-player video { display: block; width: 100%; max-width: 560px; max-height: 360px; border-radius: 10px; background: #000; }
|
||||
.zm-redbtn-player .zm-actions { margin-top: 8px; }
|
||||
ZM_INSTALLER_EOF
|
||||
chmod 0644 '/www/luci-static/resources/view/zapret-manager/style.css'
|
||||
|
||||
|
||||
Reference in new issue
Block a user