This commit is contained in:
root
2026-05-18 03:02:15 +00:00
parent 00953ad831
commit ab45c6c329
12 changed files with 233 additions and 56 deletions

View File

@@ -242,6 +242,7 @@ load_env() {
NEKO_HTTP_PORT_1="${NEKO_HTTP_PORT_1:-$NEKO_HTTP_PORT}"
NEKO_HTTP_PORT_2="${NEKO_HTTP_PORT_2:-9201}"
NEKO_HTTP_PORT_3="${NEKO_HTTP_PORT_3:-9202}"
NEKO_INSTANCE_COUNT="${NEKO_INSTANCE_COUNT:-2}"
NEKO_WEBRTC_TCPMUX="${NEKO_WEBRTC_TCPMUX:-52300}"
NEKO_WEBRTC_UDPMUX="${NEKO_WEBRTC_UDPMUX:-52300}"
NEKO_WEBRTC_TCPMUX_1="${NEKO_WEBRTC_TCPMUX_1:-$NEKO_WEBRTC_TCPMUX}"
@@ -266,6 +267,11 @@ load_env() {
case "$NEKO_IMAGE_ARM64" in
ghcr.io/m1k1o/neko/chromium:latest) NEKO_IMAGE_ARM64="ghcr.io/m1k1o/neko/chromium:3.0.0" ;;
esac
NEKO_SHM_SIZE="${NEKO_SHM_SIZE:-512mb}"
NEKO_DESKTOP_SCREEN="${NEKO_DESKTOP_SCREEN:-1280x720@15}"
NEKO_MAX_FPS="${NEKO_MAX_FPS:-15}"
NEKO_VIDEO_BITRATE="${NEKO_VIDEO_BITRATE:-1200}"
NEKO_BROWSER_RENDER_MODE="${NEKO_BROWSER_RENDER_MODE:-auto}"
ENABLE_REDROID="${ENABLE_REDROID:-1}"
REDROID_DEFAULT_PORT="${REDROID_DEFAULT_PORT:-5555}"
REDROID_INSTANCE_PREFIX="${REDROID_INSTANCE_PREFIX:-redroid13}"
@@ -364,6 +370,54 @@ resolve_neko_profile_dir_for_instance() {
esac
}
resolve_neko_instance_count() {
local requested="${NEKO_INSTANCE_COUNT:-2}"
local cpu count
cpu="$(nproc 2>/dev/null || echo 4)"
case "$requested" in
1|2|3) count="$requested" ;;
auto|"")
if [ "$cpu" -le 4 ]; then
count=2
else
count=3
fi
;;
*)
count=2
;;
esac
if [ "$count" -lt 1 ]; then
count=1
fi
if [ "$count" -gt 3 ]; then
count=3
fi
printf '%s' "$count"
}
resolve_neko_service_names() {
local count name
count="$(resolve_neko_instance_count)"
for name in 1 2 3; do
[ "$name" -le "$count" ] || break
printf 'neko%s\n' "$name"
done
}
resolve_neko_instances_csv() {
resolve_neko_service_names | paste -sd, -
}
resolve_neko_http_port_for_instance() {
case "${1:-1}" in
1) printf '%s' "${NEKO_HTTP_PORT_1:-${NEKO_HTTP_PORT:-9200}}" ;;
2) printf '%s' "${NEKO_HTTP_PORT_2:-9201}" ;;
3) printf '%s' "${NEKO_HTTP_PORT_3:-9202}" ;;
*) printf '%s' "${NEKO_HTTP_PORT_1:-${NEKO_HTTP_PORT:-9200}}" ;;
esac
}
remove_legacy_neko_container() {
if have docker && docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx 'live-neko-chromium'; then
docker stop live-neko-chromium >/dev/null 2>&1 || true

View File

@@ -49,6 +49,7 @@ NEKO_HTTP_PORT="${NEKO_HTTP_PORT:-9200}"
NEKO_HTTP_PORT_1="${NEKO_HTTP_PORT_1:-$NEKO_HTTP_PORT}"
NEKO_HTTP_PORT_2="${NEKO_HTTP_PORT_2:-9201}"
NEKO_HTTP_PORT_3="${NEKO_HTTP_PORT_3:-9202}"
NEKO_INSTANCE_COUNT="${NEKO_INSTANCE_COUNT:-2}"
NEKO_WEBRTC_TCPMUX="${NEKO_WEBRTC_TCPMUX:-52300}"
NEKO_WEBRTC_UDPMUX="${NEKO_WEBRTC_UDPMUX:-52300}"
NEKO_WEBRTC_TCPMUX_1="${NEKO_WEBRTC_TCPMUX_1:-$NEKO_WEBRTC_TCPMUX}"
@@ -74,6 +75,11 @@ esac
case "$NEKO_IMAGE_ARM64" in
ghcr.io/m1k1o/neko/chromium:latest) NEKO_IMAGE_ARM64="ghcr.io/m1k1o/neko/chromium:3.0.0" ;;
esac
NEKO_SHM_SIZE="${NEKO_SHM_SIZE:-512mb}"
NEKO_DESKTOP_SCREEN="${NEKO_DESKTOP_SCREEN:-1280x720@15}"
NEKO_MAX_FPS="${NEKO_MAX_FPS:-15}"
NEKO_VIDEO_BITRATE="${NEKO_VIDEO_BITRATE:-1200}"
NEKO_BROWSER_RENDER_MODE="${NEKO_BROWSER_RENDER_MODE:-auto}"
ENABLE_NEKO="${ENABLE_NEKO:-1}"
ENABLE_MITMPROXY="${ENABLE_MITMPROXY:-1}"
MITMPROXY_WEB_PORT="${MITMPROXY_WEB_PORT:-8086}"
@@ -292,6 +298,32 @@ resolve_neko_profile_dir_for_instance() {
esac
}
resolve_neko_instance_count() {
local requested="${NEKO_INSTANCE_COUNT:-2}"
local cpu count
cpu="$(nproc 2>/dev/null || echo 4)"
case "$requested" in
1|2|3) count="$requested" ;;
auto|"")
if [ "$cpu" -le 4 ]; then
count=2
else
count=3
fi
;;
*)
count=2
;;
esac
if [ "$count" -lt 1 ]; then
count=1
fi
if [ "$count" -gt 3 ]; then
count=3
fi
printf '%s' "$count"
}
remove_legacy_neko_container() {
if have docker && docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx 'live-neko-chromium'; then
docker stop live-neko-chromium >/dev/null 2>&1 || true
@@ -810,8 +842,15 @@ service_neko_status() {
fi
kv available 1
local state
local running_count=0 total_count=3 states="" name
local running_count=0 total_count=0 states="" name instance_count
instance_count="$(resolve_neko_instance_count)"
total_count="$instance_count"
for name in live-neko-1 live-neko-2 live-neko-3; do
case "$name" in
live-neko-1) [ "$instance_count" -ge 1 ] || continue ;;
live-neko-2) [ "$instance_count" -ge 2 ] || continue ;;
live-neko-3) [ "$instance_count" -ge 3 ] || continue ;;
esac
state="$(docker_container_status "$name" || true)"
if echo "$state" | grep -qi running; then
running_count=$((running_count + 1))
@@ -834,7 +873,7 @@ service_neko_status() {
if ! neko_nat1to1_is_usable "${NEKO_WEBRTC_NAT1TO1:-}"; then
NEKO_WEBRTC_NAT1TO1="$(detect_preferred_lan_ip || true)"
fi
kv detail "image=$(resolve_neko_image); supervisor_conf=$(resolve_neko_supervisor_conf); start_url=${NEKO_START_URL}; nat1to1=${NEKO_WEBRTC_NAT1TO1:-unset}; instances=${running_count}/${total_count}; ports=${NEKO_HTTP_PORT_1},${NEKO_HTTP_PORT_2},${NEKO_HTTP_PORT_3}; profiles=$(resolve_neko_profile_dir_for_instance 1),$(resolve_neko_profile_dir_for_instance 2),$(resolve_neko_profile_dir_for_instance 3); udp_ranges=${NEKO_WEBRTC_UDP_RANGE_1},${NEKO_WEBRTC_UDP_RANGE_2},${NEKO_WEBRTC_UDP_RANGE_3}; tcpmux=${NEKO_WEBRTC_TCPMUX_1},${NEKO_WEBRTC_TCPMUX_2},${NEKO_WEBRTC_TCPMUX_3}; udpmux=${NEKO_WEBRTC_UDPMUX_1},${NEKO_WEBRTC_UDPMUX_2},${NEKO_WEBRTC_UDPMUX_3}; containers=${states}"
kv detail "image=$(resolve_neko_image); supervisor_conf=$(resolve_neko_supervisor_conf); start_url=${NEKO_START_URL}; nat1to1=${NEKO_WEBRTC_NAT1TO1:-unset}; instances=${running_count}/${total_count}; ports=${NEKO_HTTP_PORT_1},${NEKO_HTTP_PORT_2},${NEKO_HTTP_PORT_3}; profiles=$(resolve_neko_profile_dir_for_instance 1),$(resolve_neko_profile_dir_for_instance 2),$(resolve_neko_profile_dir_for_instance 3); screen=${NEKO_DESKTOP_SCREEN}; shm=${NEKO_SHM_SIZE}; fps=${NEKO_MAX_FPS}; bitrate=${NEKO_VIDEO_BITRATE}; render=${NEKO_BROWSER_RENDER_MODE}; udp_ranges=${NEKO_WEBRTC_UDP_RANGE_1},${NEKO_WEBRTC_UDP_RANGE_2},${NEKO_WEBRTC_UDP_RANGE_3}; tcpmux=${NEKO_WEBRTC_TCPMUX_1},${NEKO_WEBRTC_TCPMUX_2},${NEKO_WEBRTC_TCPMUX_3}; udpmux=${NEKO_WEBRTC_UDPMUX_1},${NEKO_WEBRTC_UDPMUX_2},${NEKO_WEBRTC_UDPMUX_3}; containers=${states}"
}
service_neko_start() {
@@ -847,23 +886,73 @@ service_neko_start() {
return 0
fi
local prof
local instance_count
local service_names="" extra_service_names="" idx_name
instance_count="$(resolve_neko_instance_count)"
for idx_name in 1 2 3; do
case "$idx_name" in
1)
if [ "$instance_count" -ge 1 ]; then
service_names="${service_names} neko1"
else
extra_service_names="${extra_service_names} neko1"
fi
;;
2)
if [ "$instance_count" -ge 2 ]; then
service_names="${service_names} neko2"
else
extra_service_names="${extra_service_names} neko2"
fi
;;
3)
if [ "$instance_count" -ge 3 ]; then
service_names="${service_names} neko3"
else
extra_service_names="${extra_service_names} neko3"
fi
;;
esac
done
for idx in 1 2 3; do
[ "$idx" -le "$instance_count" ] || break
prof="$(resolve_neko_profile_dir_for_instance "$idx")"
prune_neko_profile "$prof"
done
export_neko_env
for port in "$NEKO_HTTP_PORT_1" "$NEKO_HTTP_PORT_2" "$NEKO_HTTP_PORT_3" "$NEKO_WEBRTC_TCPMUX_1" "$NEKO_WEBRTC_TCPMUX_2" "$NEKO_WEBRTC_TCPMUX_3"; do
case "$port" in
"$NEKO_HTTP_PORT_1"|"$NEKO_WEBRTC_TCPMUX_1") [ "$instance_count" -ge 1 ] || continue ;;
"$NEKO_HTTP_PORT_2"|"$NEKO_WEBRTC_TCPMUX_2") [ "$instance_count" -ge 2 ] || continue ;;
"$NEKO_HTTP_PORT_3"|"$NEKO_WEBRTC_TCPMUX_3") [ "$instance_count" -ge 3 ] || continue ;;
esac
allow_tcp_port_if_firewall_active "$port"
done
for port in "$NEKO_WEBRTC_UDPMUX_1" "$NEKO_WEBRTC_UDPMUX_2" "$NEKO_WEBRTC_UDPMUX_3"; do
case "$port" in
"$NEKO_WEBRTC_UDPMUX_1") [ "$instance_count" -ge 1 ] || continue ;;
"$NEKO_WEBRTC_UDPMUX_2") [ "$instance_count" -ge 2 ] || continue ;;
"$NEKO_WEBRTC_UDPMUX_3") [ "$instance_count" -ge 3 ] || continue ;;
esac
allow_udp_port_if_firewall_active "$port"
done
for port_range in "$NEKO_WEBRTC_UDP_RANGE_1" "$NEKO_WEBRTC_UDP_RANGE_2" "$NEKO_WEBRTC_UDP_RANGE_3"; do
case "$port_range" in
"$NEKO_WEBRTC_UDP_RANGE_1") [ "$instance_count" -ge 1 ] || continue ;;
"$NEKO_WEBRTC_UDP_RANGE_2") [ "$instance_count" -ge 2 ] || continue ;;
"$NEKO_WEBRTC_UDP_RANGE_3") [ "$instance_count" -ge 3 ] || continue ;;
esac
allow_udp_range_if_firewall_active "$port_range"
done
remove_legacy_neko_container
compose_in_service_dir "$NEKO_COMPOSE" up -d
kv message "Neko stack started (3 browser instances)"
# 只启动实际需要的实例,避免多余的 Chrome/Xorg 进程继续吃 CPU
# shellcheck disable=SC2086
compose_in_service_dir "$NEKO_COMPOSE" up -d $service_names
if [ -n "${extra_service_names# }" ]; then
# shellcheck disable=SC2086
compose_in_service_dir "$NEKO_COMPOSE" stop $extra_service_names >/dev/null 2>&1 || true
fi
kv message "Neko stack started (${instance_count} browser instances)"
}
export_neko_env() {
@@ -871,6 +960,7 @@ export_neko_env() {
export NEKO_HTTP_PORT_1="${NEKO_HTTP_PORT_1:-$NEKO_HTTP_PORT}"
export NEKO_HTTP_PORT_2="${NEKO_HTTP_PORT_2:-9201}"
export NEKO_HTTP_PORT_3="${NEKO_HTTP_PORT_3:-9202}"
export NEKO_INSTANCE_COUNT="${NEKO_INSTANCE_COUNT:-2}"
export NEKO_USER_PASS="${NEKO_USER_PASS:-12345678}"
export NEKO_ADMIN_PASS="${NEKO_ADMIN_PASS:-12345678}"
export NEKO_PROFILE_HOST_DIR="${NEKO_PROFILE_HOST_DIR:-$(resolve_neko_profile_dir)}"
@@ -880,7 +970,11 @@ export_neko_env() {
export NEKO_START_URL="${NEKO_START_URL:-https://accounts.google.com/ServiceLogin?continue=https://www.youtube.com/}"
export NEKO_IMAGE="$(resolve_neko_image)"
export NEKO_BROWSER_SUPERVISOR_CONF="$(resolve_neko_supervisor_conf)"
export NEKO_DESKTOP_SCREEN="${NEKO_DESKTOP_SCREEN:-1920x1080@30}"
export NEKO_SHM_SIZE="${NEKO_SHM_SIZE:-512mb}"
export NEKO_DESKTOP_SCREEN="${NEKO_DESKTOP_SCREEN:-1280x720@15}"
export NEKO_MAX_FPS="${NEKO_MAX_FPS:-15}"
export NEKO_VIDEO_BITRATE="${NEKO_VIDEO_BITRATE:-1200}"
export NEKO_BROWSER_RENDER_MODE="${NEKO_BROWSER_RENDER_MODE:-auto}"
export NEKO_WEBRTC_UDP_RANGE="${NEKO_WEBRTC_UDP_RANGE:-52000-52099}"
export NEKO_WEBRTC_UDP_RANGE_1="${NEKO_WEBRTC_UDP_RANGE_1:-$NEKO_WEBRTC_UDP_RANGE}"
export NEKO_WEBRTC_UDP_RANGE_2="${NEKO_WEBRTC_UDP_RANGE_2:-52101-52199}"