142 lines
6.2 KiB
Python
142 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Render a dynamic Neko docker-compose file from the current environment."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def env(name: str, default: str) -> str:
|
|
value = os.environ.get(name)
|
|
return value if value not in {None, ""} else default
|
|
|
|
|
|
def env_enabled(name: str, default: bool = False) -> bool:
|
|
raw = os.environ.get(name)
|
|
if raw in {None, ""}:
|
|
return default
|
|
return raw.strip().lower() in {"1", "true", "yes", "on", "enable", "enabled"}
|
|
|
|
|
|
def q(value: str) -> str:
|
|
return json.dumps(str(value), ensure_ascii=False)
|
|
|
|
|
|
def instance_value(prefix: str, idx: int, default: str) -> str:
|
|
return env(f"{prefix}_{idx}", default)
|
|
|
|
|
|
def default_udp_range(idx: int) -> str:
|
|
if idx == 1:
|
|
return "52000-52099"
|
|
if idx == 2:
|
|
return "52101-52199"
|
|
if idx == 3:
|
|
return "52201-52299"
|
|
start = 52400 + ((idx - 4) * 100)
|
|
return f"{start}-{start + 99}"
|
|
|
|
|
|
def render(count: int) -> str:
|
|
image = env("NEKO_IMAGE", "ghcr.io/m1k1o/neko/chromium:3.0.0")
|
|
shm = env("NEKO_SHM_SIZE", "512mb")
|
|
screen = env("NEKO_DESKTOP_SCREEN", "1280x720@15")
|
|
user_pass = env("NEKO_USER_PASS", "12345678")
|
|
admin_pass = env("NEKO_ADMIN_PASS", "12345678")
|
|
icelite = env("NEKO_WEBRTC_ICELITE", "0")
|
|
nat1to1 = env("NEKO_WEBRTC_NAT1TO1", "")
|
|
max_fps = env("NEKO_MAX_FPS", "15")
|
|
video_bitrate = env("NEKO_VIDEO_BITRATE", "1200")
|
|
plugins_enabled = env("NEKO_PLUGINS_ENABLED", "true")
|
|
start_url = env("NEKO_START_URL", "https://accounts.google.com/ServiceLogin?continue=https://www.youtube.com/")
|
|
render_mode = env("NEKO_BROWSER_RENDER_MODE", "auto")
|
|
supervisor_conf = env("NEKO_BROWSER_SUPERVISOR_CONF", "chromium.conf")
|
|
base_profile = env("NEKO_PROFILE_HOST_DIR", str(Path.cwd() / "data" / "chromium-profile-v2"))
|
|
http_base = int(env("NEKO_HTTP_PORT", "9200"))
|
|
mux_base = int(env("NEKO_WEBRTC_TCPMUX", "52300"))
|
|
managed_policies = env_enabled("NEKO_MANAGED_POLICIES", True)
|
|
external_extensions = env_enabled("NEKO_EXTERNAL_EXTENSIONS", False)
|
|
policy_dir = "./policies" if managed_policies else "./no-managed-policies"
|
|
|
|
lines = [
|
|
"# Generated by tools/render_neko_compose.py - do not hand-edit",
|
|
"services:",
|
|
]
|
|
for idx in range(1, count + 1):
|
|
http_port = instance_value("NEKO_HTTP_PORT", idx, str(http_base + idx - 1))
|
|
tcp_mux = instance_value("NEKO_WEBRTC_TCPMUX", idx, str(mux_base + idx - 1))
|
|
udp_mux = instance_value("NEKO_WEBRTC_UDPMUX", idx, str(mux_base + idx - 1))
|
|
udp_range = instance_value("NEKO_WEBRTC_UDP_RANGE", idx, default_udp_range(idx))
|
|
epr = instance_value("NEKO_WEBRTC_EPR", idx, udp_range)
|
|
profile = instance_value("NEKO_PROFILE_HOST_DIR", idx, base_profile if idx == 1 else f"{base_profile}-{idx}")
|
|
lines.extend(
|
|
[
|
|
f" neko{idx}:",
|
|
f" image: {q(image)}",
|
|
" restart: unless-stopped",
|
|
f" shm_size: {q(shm)}",
|
|
" cap_add:",
|
|
" - SYS_ADMIN",
|
|
f" container_name: live-neko-{idx}",
|
|
" environment:",
|
|
' DISPLAY: ":99.0"',
|
|
f" NEKO_DESKTOP_SCREEN: {q(screen)}",
|
|
' NEKO_SERVER_BIND: ":8080"',
|
|
' NEKO_MEMBER_PROVIDER: "multiuser"',
|
|
f" NEKO_MEMBER_MULTIUSER_USER_PASSWORD: {q(user_pass)}",
|
|
f" NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: {q(admin_pass)}",
|
|
f" NEKO_WEBRTC_ICELITE: {q(icelite)}",
|
|
f" NEKO_WEBRTC_NAT1TO1: {q(nat1to1)}",
|
|
f" NEKO_MAX_FPS: {q(max_fps)}",
|
|
f" NEKO_VIDEO_BITRATE: {q(video_bitrate)}",
|
|
f" NEKO_PLUGINS_ENABLED: {q(plugins_enabled)}",
|
|
' NEKO_PLUGINS_DIR: "/etc/neko/plugins/"',
|
|
f" NEKO_START_URL: {q(start_url)}",
|
|
f" NEKO_BROWSER_RENDER_MODE: {q(render_mode)}",
|
|
f" NEKO_MANAGED_POLICIES: {q('1' if managed_policies else '0')}",
|
|
f" NEKO_WEBRTC_EPR: {q(epr)}",
|
|
f" NEKO_WEBRTC_TCPMUX: {q(tcp_mux)}",
|
|
f" NEKO_WEBRTC_UDPMUX: {q(udp_mux)}",
|
|
" ports:",
|
|
f" - {q(f'{http_port}:8080')}",
|
|
f" - {q(f'{tcp_mux}:{tcp_mux}/tcp')}",
|
|
f" - {q(f'{udp_mux}:{udp_mux}/udp')}",
|
|
f" - {q(f'{udp_range}:{udp_range}/udp')}",
|
|
" volumes:",
|
|
f" - {q(f'{profile}:/home/neko/.config/chromium')}",
|
|
f" - {q(f'{profile}:/home/neko/.config/google-chrome')}",
|
|
f" - {q(f'{policy_dir}:/etc/chromium/policies/managed:ro')}",
|
|
f" - {q(f'{policy_dir}:/etc/chromium-browser/policies/managed:ro')}",
|
|
f" - {q(f'{policy_dir}:/etc/opt/chrome/policies/managed:ro')}",
|
|
' - "./tampermonkey-seed:/etc/neko/tampermonkey-seed:ro"',
|
|
' - "./provisioning:/var/www/provisioning:ro"',
|
|
' - "./browser-launch.sh:/etc/neko/browser-launch.sh:ro"',
|
|
f" - {q(f'./{supervisor_conf}:/etc/neko/supervisord/{supervisor_conf}:ro')}",
|
|
]
|
|
)
|
|
if external_extensions:
|
|
lines[-3:-3] = [
|
|
' - "./external-extensions:/opt/google/chrome/extensions:ro"',
|
|
' - "./external-extensions:/usr/share/chromium/extensions:ro"',
|
|
' - "./external-extensions:/usr/share/chromium-browser/extensions:ro"',
|
|
]
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Render Neko docker compose")
|
|
parser.add_argument("--count", type=int, required=True)
|
|
parser.add_argument("--output", required=True)
|
|
args = parser.parse_args()
|
|
count = max(2, min(64, args.count))
|
|
output = Path(args.output)
|
|
output.parent.mkdir(parents=True, exist_ok=True)
|
|
output.write_text(render(count), encoding="utf-8")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|