Files
gitlab-instance-0a899031_sh/d2ypp2/services/neko/browser-launch.sh
eric b617555360 s
2026-05-19 09:19:52 +00:00

280 lines
7.7 KiB
Bash
Executable File

#!/bin/sh
set -eu
family="${1:-chromium}"
start_url="${NEKO_START_URL:-about:blank}"
arch="$(uname -m 2>/dev/null || echo unknown)"
prune_path() {
target="${1:-}"
[ -n "$target" ] || return 0
[ -e "$target" ] || return 0
rm -rf "$target"
}
ensure_profile_preferences() {
if ! command -v python3 >/dev/null 2>&1; then
return 0
fi
mkdir -p "$profile_dir/Default"
PROFILE_DIR="$profile_dir" python3 - <<'PY'
import json
import os
from pathlib import Path
profile_dir = Path(os.environ["PROFILE_DIR"])
def load_json(path: Path) -> dict:
if path.exists():
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
return {}
def save_json(path: Path, data: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
json.dumps(data, ensure_ascii=False, separators=(",", ":")),
encoding="utf-8",
)
for relative_path in ("Default/Preferences", "Local State"):
path = profile_dir / relative_path
data = load_json(path)
extensions = data.setdefault("extensions", {})
ui = extensions.setdefault("ui", {})
ui["developer_mode"] = True
profile = data.setdefault("profile", {})
profile["exit_type"] = "Normal"
save_json(path, data)
PY
}
managed_policies_enabled() {
case "${NEKO_MANAGED_POLICIES:-0}" in
1|true|TRUE|yes|YES|on|ON|enable|enabled)
return 0
;;
*)
return 1
;;
esac
}
copy_seed_dir_if_missing() {
src="${1:-}"
dst="${2:-}"
marker="${3:-}"
[ -d "$src" ] || return 0
if [ -e "$dst" ]; then
if [ -z "$marker" ] || grep -R -a -q "$marker" "$dst" 2>/dev/null; then
return 0
fi
rm -rf "$dst" 2>/dev/null || true
fi
mkdir -p "$(dirname "$dst")"
cp -a "$src" "$dst" 2>/dev/null || true
rm -f "$dst/LOCK" 2>/dev/null || true
}
sync_tampermonkey_preferences() {
seed_preferences="${seed_dir}/Default/Preferences"
[ -f "$seed_preferences" ] || return 0
command -v python3 >/dev/null 2>&1 || return 0
SEED_PREFERENCES="$seed_preferences" PROFILE_DIR="$profile_dir" python3 - <<'PY'
import json
import os
from pathlib import Path
ext_id = "dhdgffkkebhmkfjojejmpbldmpobfkfo"
seed_preferences = Path(os.environ["SEED_PREFERENCES"])
target_preferences = Path(os.environ["PROFILE_DIR"]) / "Default" / "Preferences"
def load(path: Path) -> dict:
if not path.exists():
return {}
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
seed = load(seed_preferences)
source = seed.get("extensions", {}).get("settings", {}).get(ext_id)
if not isinstance(source, dict):
raise SystemExit(0)
target = load(target_preferences)
settings = target.setdefault("extensions", {}).setdefault("settings", {})
current = settings.get(ext_id)
def should_replace_current(value: object) -> bool:
if not isinstance(value, dict):
return True
if value.get("state") != 1:
return True
if value.get("from_webstore") is not True:
return True
path = str(value.get("path") or "")
if path.startswith("/"):
return True
if value.get("location") == 8:
return True
return False
if should_replace_current(current):
settings[ext_id] = source
target_preferences.parent.mkdir(parents=True, exist_ok=True)
target_preferences.write_text(json.dumps(target, ensure_ascii=False, separators=(",", ":")), encoding="utf-8")
PY
}
ensure_tampermonkey_seed() {
seed_dir="${NEKO_TAMPERMONKEY_SEED_DIR:-/etc/neko/tampermonkey-seed}"
ext_id="dhdgffkkebhmkfjojejmpbldmpobfkfo"
[ -d "$seed_dir/Default" ] || return 0
mkdir -p "$profile_dir/Default"
copy_seed_dir_if_missing \
"$seed_dir/Default/Extensions/$ext_id" \
"$profile_dir/Default/Extensions/$ext_id" \
"Tampermonkey"
copy_seed_dir_if_missing \
"$seed_dir/Default/Local Extension Settings/$ext_id" \
"$profile_dir/Default/Local Extension Settings/$ext_id" \
"YouTube Studio Auto Dismiss"
if managed_policies_enabled; then
copy_seed_dir_if_missing \
"$seed_dir/Default/Managed Extension Settings/$ext_id" \
"$profile_dir/Default/Managed Extension Settings/$ext_id" \
"YouTube Studio Auto Dismiss"
else
rm -rf "$profile_dir/Default/Managed Extension Settings/$ext_id" 2>/dev/null || true
fi
sync_tampermonkey_preferences
}
find_bin() {
if [ "$family" = "google-chrome" ]; then
command -v google-chrome 2>/dev/null || command -v google-chrome-stable 2>/dev/null || true
else
command -v chromium 2>/dev/null || command -v chromium-browser 2>/dev/null || true
fi
}
browser_bin="$(find_bin)"
if [ -z "$browser_bin" ]; then
exit 0
fi
profile_dir="/home/neko/.config/chromium"
if [ "$family" = "google-chrome" ]; then
profile_dir="/home/neko/.config/google-chrome"
fi
render_mode="${NEKO_BROWSER_RENDER_MODE:-auto}"
render_flags="--disable-gpu --use-gl=swiftshader --enable-unsafe-swiftshader"
case "$render_mode" in
auto)
case "$arch:$family" in
x86_64:google-chrome|amd64:google-chrome)
render_flags="--disable-gpu --disable-software-rasterizer --disable-features=UseSkiaRenderer"
;;
esac
;;
gpu)
render_flags=""
;;
legacy|x11|disable-gpu)
render_flags="--disable-gpu --disable-software-rasterizer --disable-features=UseSkiaRenderer"
;;
software|swiftshader)
render_flags="--disable-gpu --use-gl=swiftshader --enable-unsafe-swiftshader --disable-features=UseSkiaRenderer"
;;
esac
mkdir -p "$profile_dir"
prune_path "$profile_dir/SingletonCookie"
prune_path "$profile_dir/SingletonLock"
prune_path "$profile_dir/SingletonSocket"
prune_path "$profile_dir/SingletonStartupLock"
prune_path "$profile_dir/BrowserMetrics"
prune_path "$profile_dir/Crash Reports"
prune_path "$profile_dir/Crashpad"
prune_path "$profile_dir/ShaderCache"
prune_path "$profile_dir/GrShaderCache"
prune_path "$profile_dir/GraphiteDawnCache"
prune_path "$profile_dir/Default/Cache"
prune_path "$profile_dir/Default/Code Cache"
prune_path "$profile_dir/Default/GPUCache"
prune_path "$profile_dir/Default/DawnGraphiteCache"
prune_path "$profile_dir/Default/DawnWebGPUCache"
ensure_tampermonkey_seed
ensure_profile_preferences
browser_class="google-chrome"
if [ "$family" != "google-chrome" ]; then
browser_class="chromium"
fi
launch_browser() {
"$browser_bin" \
${render_flags} \
--window-position=0,0 \
--display="${DISPLAY:?DISPLAY is required}" \
--remote-debugging-address=127.0.0.1 \
--remote-debugging-port=9222 \
--user-data-dir="$profile_dir" \
--password-store=basic \
--no-first-run \
--new-window \
--start-maximized \
--force-dark-mode \
--disable-background-mode \
--disable-dev-shm-usage \
--disable-breakpad \
--disable-default-apps \
--disable-translate \
--metrics-recording-only \
--no-default-browser-check \
--no-first-run \
"$start_url" &
browser_pid=$!
}
visible_window_count() {
if ! command -v xdotool >/dev/null 2>&1; then
return 1
fi
DISPLAY="${DISPLAY:?DISPLAY is required}" xdotool search --onlyvisible --class "$browser_class" 2>/dev/null | wc -l | tr -d ' '
}
launch_browser
seen_window=0
startup_misses=0
closed_misses=0
while kill -0 "$browser_pid" 2>/dev/null; do
window_count="$(visible_window_count || true)"
if [ -n "${window_count:-}" ] && [ "$window_count" -gt 0 ] 2>/dev/null; then
seen_window=1
startup_misses=0
closed_misses=0
else
if [ "$seen_window" -eq 0 ]; then
startup_misses=$((startup_misses + 1))
if [ "$startup_misses" -ge 15 ]; then
kill -TERM "$browser_pid" 2>/dev/null || true
break
fi
else
closed_misses=$((closed_misses + 1))
if [ "$closed_misses" -ge 3 ]; then
kill -TERM "$browser_pid" 2>/dev/null || true
break
fi
fi
fi
sleep 2
done
wait "$browser_pid"