s
This commit is contained in:
@@ -66,13 +66,17 @@ NEKO_HTTP_PORT_3=9202
|
||||
NEKO_START_URL=https://accounts.google.com/ServiceLogin?continue=https://www.youtube.com/
|
||||
NEKO_IMAGE_X86=ghcr.io/m1k1o/neko/google-chrome:3.0.0
|
||||
NEKO_IMAGE_ARM64=ghcr.io/m1k1o/neko/chromium:3.0.0
|
||||
NEKO_INSTANCE_COUNT=2
|
||||
# Neko 浏览器登录:显示昵称可填 live / admin;密码与全站策略一致(默认 12345678)
|
||||
NEKO_USER_PASS=12345678
|
||||
NEKO_ADMIN_PASS=12345678
|
||||
# 持久化 Chromium 配置(油猴、YouTube 登录、脚本、开发者数据);可从参考机 rsync 整个目录覆盖以迁移
|
||||
NEKO_PROFILE_HOST_DIR=
|
||||
NEKO_SHM_SIZE=2gb
|
||||
NEKO_DESKTOP_SCREEN=1920x1080@30
|
||||
NEKO_SHM_SIZE=512mb
|
||||
NEKO_DESKTOP_SCREEN=1280x720@15
|
||||
NEKO_MAX_FPS=15
|
||||
NEKO_VIDEO_BITRATE=1200
|
||||
NEKO_BROWSER_RENDER_MODE=auto
|
||||
NEKO_WEBRTC_EPR=52000-52099
|
||||
NEKO_WEBRTC_EPR_1=52000-52099
|
||||
NEKO_WEBRTC_EPR_2=52101-52199
|
||||
|
||||
@@ -126,11 +126,11 @@ def _resolve_youtube_push_config_file(config_file: str | None = None, *, base_di
|
||||
def _load_youtube_push_settings(config_file: str) -> dict[str, str | bool]:
|
||||
settings: dict[str, str | bool] = {
|
||||
"use_rtmps": True,
|
||||
"b_v": "4000k",
|
||||
"maxrate": "4500k",
|
||||
"bufsize": "8000k",
|
||||
"b_v": "2800k",
|
||||
"maxrate": "3200k",
|
||||
"bufsize": "5600k",
|
||||
"stream_key": "",
|
||||
"fast_audio": False,
|
||||
"fast_audio": True,
|
||||
}
|
||||
if not os.path.exists(config_file):
|
||||
return settings
|
||||
@@ -183,9 +183,9 @@ def start_douyin_youtube_ffplay(
|
||||
rtmp_base = "rtmp://a.rtmp.youtube.com/live2/"
|
||||
use_rtmps = True
|
||||
# 720p 竖屏 YouTube 建议 4–6 Mbps,偏低易触发「数据不足/卡顿」提示
|
||||
b_v, maxrate, bufsize = "4000k", "4500k", "8000k"
|
||||
b_v, maxrate, bufsize = "2800k", "3200k", "5600k"
|
||||
stream_key = None
|
||||
fast_audio = False # config: fast_audio=1 可跳过 arnndn+长 EQ,显著减轻 CPU
|
||||
fast_audio = True # 默认轻量音频链,必要时可在配置里关闭
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config = configparser.ConfigParser()
|
||||
@@ -343,7 +343,7 @@ def start_douyin_youtube_ffplay(
|
||||
print(f"[WARN] NVENC 检测失败,回退软件编码: {e}")
|
||||
|
||||
_cpu_n = os.cpu_count() or 4
|
||||
_filter_threads = str(min(4, max(1, _cpu_n // 2)))
|
||||
_filter_threads = str(min(2, max(1, _cpu_n // 2)))
|
||||
|
||||
# ====================== 开源音频处理:背景音乐弱化 + 防版权 ======================
|
||||
_script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -372,7 +372,7 @@ def start_douyin_youtube_ffplay(
|
||||
print("[INFO] 未找到 arnndn 模型,使用 afftdn 降噪(可克隆 richardpl/arnndn-models 到 arnndn-models/)")
|
||||
_af_denoise = "afftdn=nf=-26:tn=1,"
|
||||
|
||||
# 强化版:收紧带通 + 频率偏移 + 音高微调 + 多段EQ + 相位扰动(纯 FFmpeg 理论上限 ~70%)
|
||||
# 仅在明确需要时才启用重音频链,默认优先保 CPU
|
||||
_af_chain = (
|
||||
_af_denoise
|
||||
+ "highpass=f=200,lowpass=f=3800,"
|
||||
@@ -389,14 +389,14 @@ def start_douyin_youtube_ffplay(
|
||||
+ "aresample=async=1:first_pts=0"
|
||||
)
|
||||
|
||||
# 720p 竖屏,符合 YouTube 推荐:4–6 Mbps、2s 关键帧、AAC 128k
|
||||
_video_mid = ["-vsync", "cfr", "-g", "60", "-keyint_min", "60", "-r", "30",
|
||||
# 720p 竖屏低耗档:更低帧率和码率,优先保证 2 路并发可用
|
||||
_video_mid = ["-vsync", "cfr", "-g", "30", "-keyint_min", "30", "-r", "15",
|
||||
"-bf", "0", "-sc_threshold", "0"]
|
||||
if codec_v == "libx264":
|
||||
_video_mid += ["-x264-params", "force-cfr=1:scenecut=0:open_gop=0:sync-lookahead=0"]
|
||||
# -re:按源时间戳节奏拉流,避免突发过快;不用 realtime/arealtime(输入抖动时易让 YouTube 判为数据不足)
|
||||
_vf_pad = (
|
||||
"fps=30,scale=720:1280:force_original_aspect_ratio=decrease:flags=bilinear,"
|
||||
"fps=15,scale=720:1280:force_original_aspect_ratio=decrease:flags=bilinear,"
|
||||
"pad=720:1280:(ow-iw)/2:(oh-ih)/2:black"
|
||||
)
|
||||
_ffmpeg_pre_input = []
|
||||
|
||||
@@ -6,11 +6,17 @@
|
||||
"ports": { "http": 9200 },
|
||||
"env": {
|
||||
"ENABLE_NEKO": "1",
|
||||
"NEKO_INSTANCE_COUNT": "2",
|
||||
"NEKO_HTTP_PORT": "9200",
|
||||
"NEKO_HTTP_PORT_1": "9200",
|
||||
"NEKO_HTTP_PORT_2": "9201",
|
||||
"NEKO_HTTP_PORT_3": "9202",
|
||||
"NEKO_PROFILE_HOST_DIR": "/opt/live-modules/neko-profile",
|
||||
"NEKO_SHM_SIZE": "512mb",
|
||||
"NEKO_DESKTOP_SCREEN": "1280x720@15",
|
||||
"NEKO_MAX_FPS": "15",
|
||||
"NEKO_VIDEO_BITRATE": "1200",
|
||||
"NEKO_BROWSER_RENDER_MODE": "auto",
|
||||
"NEKO_WEBRTC_UDP_RANGE": "52000-52099",
|
||||
"NEKO_WEBRTC_UDP_RANGE_1": "52000-52099",
|
||||
"NEKO_WEBRTC_UDP_RANGE_2": "52101-52199",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -36,7 +36,7 @@ case "$render_mode" in
|
||||
auto)
|
||||
case "$arch:$family" in
|
||||
x86_64:google-chrome|amd64:google-chrome)
|
||||
render_flags="--disable-gpu --disable-software-rasterizer"
|
||||
render_flags="--disable-gpu --disable-software-rasterizer --disable-features=UseSkiaRenderer"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@@ -44,10 +44,10 @@ case "$render_mode" in
|
||||
render_flags=""
|
||||
;;
|
||||
legacy|x11|disable-gpu)
|
||||
render_flags="--disable-gpu --disable-software-rasterizer"
|
||||
render_flags="--disable-gpu --disable-software-rasterizer --disable-features=UseSkiaRenderer"
|
||||
;;
|
||||
software|swiftshader)
|
||||
render_flags="--disable-gpu --use-gl=swiftshader --enable-unsafe-swiftshader"
|
||||
render_flags="--disable-gpu --use-gl=swiftshader --enable-unsafe-swiftshader --disable-features=UseSkiaRenderer"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -75,8 +75,17 @@ exec "$browser_bin" \
|
||||
--user-data-dir="$profile_dir" \
|
||||
--password-store=basic \
|
||||
--no-first-run \
|
||||
--start-maximized \
|
||||
--start-minimized \
|
||||
--force-dark-mode \
|
||||
--disable-file-system \
|
||||
--disable-dev-shm-usage \
|
||||
--disable-background-networking \
|
||||
--disable-breakpad \
|
||||
--disable-default-apps \
|
||||
--disable-extensions \
|
||||
--disable-sync \
|
||||
--disable-translate \
|
||||
--metrics-recording-only \
|
||||
--no-default-browser-check \
|
||||
--no-first-run \
|
||||
"$start_url"
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
[program:chromium]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/chromium
|
||||
--window-position=0,0
|
||||
--display=%(ENV_DISPLAY)s
|
||||
--user-data-dir=/home/neko/.config/chromium
|
||||
--no-first-run
|
||||
--start-maximized
|
||||
--force-dark-mode
|
||||
--disable-file-system
|
||||
--disable-gpu
|
||||
--disable-software-rasterizer
|
||||
--disable-dev-shm-usage
|
||||
command=/etc/neko/browser-launch.sh chromium
|
||||
stopsignal=INT
|
||||
autorestart=true
|
||||
priority=800
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
x-neko-common: &neko-common
|
||||
image: "${NEKO_IMAGE:-ghcr.io/m1k1o/neko/chromium:3.0.0}"
|
||||
restart: unless-stopped
|
||||
shm_size: "${NEKO_SHM_SIZE:-2gb}"
|
||||
shm_size: "${NEKO_SHM_SIZE:-512mb}"
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
|
||||
@@ -14,9 +14,12 @@ x-neko-env: &neko-env
|
||||
NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "${NEKO_ADMIN_PASS}"
|
||||
NEKO_WEBRTC_ICELITE: "${NEKO_WEBRTC_ICELITE}"
|
||||
NEKO_WEBRTC_NAT1TO1: "${NEKO_WEBRTC_NAT1TO1}"
|
||||
NEKO_MAX_FPS: "${NEKO_MAX_FPS}"
|
||||
NEKO_VIDEO_BITRATE: "${NEKO_VIDEO_BITRATE}"
|
||||
NEKO_PLUGINS_ENABLED: "${NEKO_PLUGINS_ENABLED}"
|
||||
NEKO_PLUGINS_DIR: "/etc/neko/plugins/"
|
||||
NEKO_START_URL: "${NEKO_START_URL}"
|
||||
NEKO_BROWSER_RENDER_MODE: "${NEKO_BROWSER_RENDER_MODE}"
|
||||
|
||||
services:
|
||||
neko1:
|
||||
@@ -37,6 +40,7 @@ services:
|
||||
- "${NEKO_PROFILE_HOST_DIR_1}:/home/neko/.config/google-chrome"
|
||||
- "./policies:/etc/chromium/policies/managed:ro"
|
||||
- "./policies:/etc/opt/chrome/policies/managed:ro"
|
||||
- "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"
|
||||
- "./${NEKO_BROWSER_SUPERVISOR_CONF}:/etc/neko/supervisord/browser.conf:ro"
|
||||
|
||||
neko2:
|
||||
@@ -57,6 +61,7 @@ services:
|
||||
- "${NEKO_PROFILE_HOST_DIR_2}:/home/neko/.config/google-chrome"
|
||||
- "./policies:/etc/chromium/policies/managed:ro"
|
||||
- "./policies:/etc/opt/chrome/policies/managed:ro"
|
||||
- "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"
|
||||
- "./${NEKO_BROWSER_SUPERVISOR_CONF}:/etc/neko/supervisord/browser.conf:ro"
|
||||
|
||||
neko3:
|
||||
@@ -77,4 +82,5 @@ services:
|
||||
- "${NEKO_PROFILE_HOST_DIR_3}:/home/neko/.config/google-chrome"
|
||||
- "./policies:/etc/chromium/policies/managed:ro"
|
||||
- "./policies:/etc/opt/chrome/policies/managed:ro"
|
||||
- "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"
|
||||
- "./${NEKO_BROWSER_SUPERVISOR_CONF}:/etc/neko/supervisord/browser.conf:ro"
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
[program:google-chrome]
|
||||
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
|
||||
command=/usr/bin/google-chrome
|
||||
--window-position=0,0
|
||||
--display=%(ENV_DISPLAY)s
|
||||
--user-data-dir=/home/neko/.config/google-chrome
|
||||
--no-first-run
|
||||
--start-maximized
|
||||
--force-dark-mode
|
||||
--disable-file-system
|
||||
--disable-gpu
|
||||
--disable-software-rasterizer
|
||||
--disable-dev-shm-usage
|
||||
command=/etc/neko/browser-launch.sh google-chrome
|
||||
stopsignal=INT
|
||||
autorestart=true
|
||||
priority=800
|
||||
|
||||
@@ -497,11 +497,12 @@ def _env_int(env: dict[str, str], key: str, default: int) -> int:
|
||||
|
||||
|
||||
def _neko_http_ports(env: dict[str, str]) -> list[int]:
|
||||
count = max(1, min(3, _env_int(env, "NEKO_INSTANCE_COUNT", 2)))
|
||||
return [
|
||||
_env_int(env, "NEKO_HTTP_PORT_1", _env_int(env, "NEKO_HTTP_PORT", 9200)),
|
||||
_env_int(env, "NEKO_HTTP_PORT_2", 9201),
|
||||
_env_int(env, "NEKO_HTTP_PORT_3", 9202),
|
||||
]
|
||||
][:count]
|
||||
|
||||
|
||||
def _neko_instances(mdns_host: str, env: dict[str, str]) -> list[dict[str, Any]]:
|
||||
@@ -877,13 +878,14 @@ async def stack_stream_probes() -> dict[str, Any]:
|
||||
http = _probe_http_get(f"http://127.0.0.1:{port}/", timeout=2.5)
|
||||
instances.append({"id": idx, "port": port, "tcp": tcp, "http": http})
|
||||
primary = instances[0] if instances else {"port": 0, "tcp": {}, "http": {}}
|
||||
required_count = max(1, min(3, _env_int(env, "NEKO_INSTANCE_COUNT", 2)))
|
||||
return {
|
||||
"port": primary["port"],
|
||||
"tcp": primary["tcp"],
|
||||
"http": primary["http"],
|
||||
"instances": instances,
|
||||
"running_count": sum(1 for item in instances if item["tcp"].get("ok")),
|
||||
"required_count": 3,
|
||||
"required_count": required_count,
|
||||
}
|
||||
|
||||
srs_d, neko_d = await asyncio.gather(
|
||||
|
||||
@@ -15,6 +15,8 @@ class NekoConfigTests(unittest.TestCase):
|
||||
self.assertIn("--disable-software-rasterizer", script)
|
||||
self.assertIn("--use-gl=swiftshader", script)
|
||||
self.assertIn("NEKO_BROWSER_RENDER_MODE", script)
|
||||
self.assertIn("--start-minimized", script)
|
||||
self.assertIn("--disable-background-networking", script)
|
||||
self.assertIn('prune_path "$profile_dir/BrowserMetrics"', script)
|
||||
self.assertIn('prune_path "$profile_dir/Crash Reports"', script)
|
||||
self.assertIn('prune_path "$profile_dir/SingletonLock"', script)
|
||||
@@ -30,28 +32,29 @@ class NekoConfigTests(unittest.TestCase):
|
||||
|
||||
def test_google_chrome_supervisor_uses_direct_stable_command(self) -> None:
|
||||
conf = (ROOT / "services" / "neko" / "google-chrome.conf").read_text(encoding="utf-8")
|
||||
self.assertIn("command=/usr/bin/google-chrome", conf)
|
||||
self.assertNotIn("browser-launch.sh", conf)
|
||||
self.assertIn("--user-data-dir=/home/neko/.config/google-chrome", conf)
|
||||
self.assertIn("command=/etc/neko/browser-launch.sh google-chrome", conf)
|
||||
self.assertNotIn("--bwsi", conf)
|
||||
self.assertIn("--disable-software-rasterizer", conf)
|
||||
self.assertIn("autorestart=true", conf)
|
||||
self.assertIn("[program:openbox]", conf)
|
||||
self.assertIn("/usr/bin/openbox --config-file /etc/neko/openbox.xml", conf)
|
||||
script = (ROOT / "services" / "neko" / "browser-launch.sh").read_text(encoding="utf-8")
|
||||
self.assertIn('profile_dir="/home/neko/.config/google-chrome"', script)
|
||||
self.assertIn('--user-data-dir="$profile_dir"', script)
|
||||
|
||||
def test_chromium_supervisor_uses_direct_stable_command(self) -> None:
|
||||
conf = (ROOT / "services" / "neko" / "chromium.conf").read_text(encoding="utf-8")
|
||||
self.assertIn("command=/usr/bin/chromium", conf)
|
||||
self.assertNotIn("browser-launch.sh", conf)
|
||||
self.assertIn("--user-data-dir=/home/neko/.config/chromium", conf)
|
||||
self.assertIn("command=/etc/neko/browser-launch.sh chromium", conf)
|
||||
self.assertNotIn("--bwsi", conf)
|
||||
self.assertIn("--disable-software-rasterizer", conf)
|
||||
self.assertIn("autorestart=true", conf)
|
||||
self.assertIn("[program:openbox]", conf)
|
||||
script = (ROOT / "services" / "neko" / "browser-launch.sh").read_text(encoding="utf-8")
|
||||
self.assertIn('profile_dir="/home/neko/.config/chromium"', script)
|
||||
self.assertIn('--user-data-dir="$profile_dir"', script)
|
||||
|
||||
def test_neko_compose_mounts_only_selected_supervisor_conf(self) -> None:
|
||||
compose = (ROOT / "services" / "neko" / "docker-compose.yml").read_text(encoding="utf-8")
|
||||
self.assertIn("./${NEKO_BROWSER_SUPERVISOR_CONF}:/etc/neko/supervisord/browser.conf:ro", compose)
|
||||
self.assertIn("./browser-launch.sh:/etc/neko/browser-launch.sh:ro", compose)
|
||||
self.assertIn("container_name: live-neko-1", compose)
|
||||
self.assertIn("container_name: live-neko-2", compose)
|
||||
self.assertIn("container_name: live-neko-3", compose)
|
||||
@@ -60,7 +63,14 @@ class NekoConfigTests(unittest.TestCase):
|
||||
self.assertIn("${NEKO_HTTP_PORT_3}:8080", compose)
|
||||
self.assertNotIn("./chromium.conf:/etc/neko/supervisord/chromium.conf:ro", compose)
|
||||
self.assertNotIn("./google-chrome.conf:/etc/neko/supervisord/google-chrome.conf:ro", compose)
|
||||
self.assertNotIn("./browser-launch.sh:/etc/neko/browser-launch.sh:ro", compose)
|
||||
|
||||
def test_supervisor_uses_browser_launch_script(self) -> None:
|
||||
chrome = (ROOT / "services" / "neko" / "google-chrome.conf").read_text(encoding="utf-8")
|
||||
chromium = (ROOT / "services" / "neko" / "chromium.conf").read_text(encoding="utf-8")
|
||||
self.assertIn("command=/etc/neko/browser-launch.sh google-chrome", chrome)
|
||||
self.assertIn("command=/etc/neko/browser-launch.sh chromium", chromium)
|
||||
self.assertNotIn("--start-maximized", chrome)
|
||||
self.assertNotIn("--start-maximized", chromium)
|
||||
|
||||
def test_neko_scripts_use_family_specific_profile_dirs(self) -> None:
|
||||
service_ctl = (ROOT / "scripts" / "linux" / "service_ctl.sh").read_text(encoding="utf-8")
|
||||
|
||||
@@ -18,6 +18,18 @@ class YoutubeRuntimeTests(unittest.TestCase):
|
||||
self.assertEqual(normalize_youtube_video_save_type("ts", "tiktok"), "TS")
|
||||
self.assertEqual(normalize_youtube_video_save_type("unknown", "tiktok"), "TS")
|
||||
|
||||
def test_push_defaults_use_lighter_profile(self) -> None:
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from douyin_youtube_ffplay import _load_youtube_push_settings
|
||||
|
||||
with TemporaryDirectory() as tmp:
|
||||
cfg = Path(tmp) / "youtube.ini"
|
||||
cfg.write_text("[youtube]\nkey = abc\n", encoding="utf-8")
|
||||
settings = _load_youtube_push_settings(str(cfg))
|
||||
self.assertEqual(settings["b_v"], "2800k")
|
||||
self.assertTrue(settings["fast_audio"])
|
||||
|
||||
def test_arm_falls_back_from_vaapi_to_v4l2m2m(self) -> None:
|
||||
enc, note = resolve_live_video_encoder(
|
||||
"h264_vaapi",
|
||||
|
||||
Reference in New Issue
Block a user