From 3c1c6318950171fd97433167742ebd3125ba0400 Mon Sep 17 00:00:00 2001 From: hacklab Date: Fri, 22 May 2026 14:39:34 +0200 Subject: [PATCH] Modo portatil: ejecutar FOSFENO en un portatil Linux Anade 'bash install.sh --laptop' y el lanzador './fosfeno' para correr FOSFENO en portatiles Debian/Ubuntu/Mint sin Raspberry Pi: puerto 8080, sin arranque automatico ni cambios en el sistema. El servidor admite las variables FOSFENO_PORT y FOSFENO_NO_KIOSK. Nueva documentacion en docs/portatil.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 6 +++ backend/server.py | 15 ++++-- config.json | 1 - docs/README.md | 3 ++ docs/portatil.md | 71 +++++++++++++++++++++++++ fosfeno | 77 +++++++++++++++++++++++++++ install.sh | 130 +++++++++++++++++++++++++++++----------------- 7 files changed, 250 insertions(+), 53 deletions(-) create mode 100644 docs/portatil.md create mode 100755 fosfeno diff --git a/README.md b/README.md index e6b3cb8..220ef57 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,12 @@ bash install.sh --no-projectm # omite la compilación de projectM bash install.sh --check # solo comprueba el sistema, no instala nada ``` +### En un portátil Linux + +FOSFENO también corre en un portátil con Debian, Ubuntu o Mint, sin Raspberry +Pi. Se instala con `bash install.sh --laptop` y se arranca cuando quieras con +`./fosfeno`. Los detalles están en [FOSFENO en un portátil](docs/portatil.md). + Después: ```bash diff --git a/backend/server.py b/backend/server.py index 71d9b69..b8f7007 100644 --- a/backend/server.py +++ b/backend/server.py @@ -13,6 +13,7 @@ Motores: projectm (nativo), butterchurn, hydra, shaders (GLSL) y mixer """ import json +import os import shutil import socket import subprocess @@ -44,7 +45,10 @@ def load_json(path, default): CFG = load_json(BASE / "config.json", {}) HOST = CFG.get("server", {}).get("host", "0.0.0.0") -PORT = int(CFG.get("server", {}).get("port", 80)) +# El puerto y el modo kiosko se pueden forzar por variable de entorno; asi el +# modo portatil (script 'fosfeno') usa el puerto 8080 sin tocar config.json. +PORT = int(os.environ.get("FOSFENO_PORT") or CFG.get("server", {}).get("port", 80)) +NO_KIOSK = bool(os.environ.get("FOSFENO_NO_KIOSK")) app = Flask(__name__, static_folder=None) socketio = SocketIO(app, async_mode="threading", cors_allowed_origins="*") @@ -180,7 +184,7 @@ def start_chromium(): notify("error", "No se encontro Chromium. Las visuales web no pueden " "mostrarse. Ejecuta install.sh para instalarlo.") return - url = kiosk.get("url", f"http://localhost:{PORT}/stage") + url = f"http://localhost:{PORT}/stage" procs["chromium"] = subprocess.Popen([ browser, "--kiosk", f"--app={url}", @@ -442,7 +446,8 @@ def watchdog(): time.sleep(10) if refresh_network(): broadcast() - if state["engine"] != "projectm" and not is_running(procs["chromium"]): + if not NO_KIOSK and state["engine"] != "projectm" \ + and not is_running(procs["chromium"]): notify("warn", "El navegador de las visuales se cerro. " "Reabriendolo automaticamente.") start_chromium() @@ -471,7 +476,9 @@ def main(): check_install() setup_audio() refresh_network() - threading.Timer(3.0, start_chromium).start() + # En modo portatil el script 'fosfeno' abre el navegador; el servidor no. + if not NO_KIOSK: + threading.Timer(3.0, start_chromium).start() threading.Thread(target=watchdog, daemon=True).start() net = state["network"] diff --git a/config.json b/config.json index 5d45f03..5071c9c 100644 --- a/config.json +++ b/config.json @@ -4,7 +4,6 @@ "port": 80 }, "kiosk": { - "url": "http://localhost/stage", "browser": "chromium-browser" }, "audio": { diff --git a/docs/README.md b/docs/README.md index d22d43f..efd62df 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,9 @@ para consultarse a saltos después. Cómo encuentra el usuario el panel de control: el código QR del proyector, la dirección `fosfeno.local` y qué hacer con el router. +- [FOSFENO en un portátil Linux](portatil.md) + Cómo correr FOSFENO en un portátil Debian, Ubuntu o Mint, sin Raspberry Pi. + - [Uso del panel](uso.md) Cómo se maneja desde el móvil, qué hace cada motor de visuales y cómo se configura cada opción. diff --git a/docs/portatil.md b/docs/portatil.md new file mode 100644 index 0000000..5d920c4 --- /dev/null +++ b/docs/portatil.md @@ -0,0 +1,71 @@ +# FOSFENO en un portátil Linux + +FOSFENO nació para una Raspberry Pi, pero está hecho con tecnología web, así +que funciona igual de bien en un portátil con Linux. De hecho un portátil va +más sobrado, ya trae micrófono y cámara, y lo conectas al proyector por HDMI +como cualquier otra cosa. + +Este modo está pensado para Debian, Ubuntu o Linux Mint, que son las +distribuciones que usan `apt`. + +## Instalar + +Una sola vez, desde la carpeta del proyecto: + +``` +bash install.sh --laptop +``` + +Hace lo mismo que en la Raspberry (entorno de Python, librerías de visuales, +y projectM si quieres), pero sin las cosas propias de un aparato dedicado: no +toca el arranque del sistema, no cambia el nombre de red y no necesita +permisos especiales de puertos. + +## Arrancar + +Cuando quieras usarlo, desde la carpeta del proyecto: + +``` +./fosfeno +``` + +Eso levanta FOSFENO y abre dos cosas: + +- El **panel de control** en tu navegador, en `http://localhost:8080/`. +- Una **ventana con las visuales**, aparte. + +Para cerrarlo todo, pulsa `Ctrl+C` en la terminal donde lanzaste `./fosfeno`. + +## Llevar las visuales al proyector + +La ventana de las visuales es una ventana normal. Conecta el proyector por +HDMI, arrastra esa ventana a la pantalla del proyector y pulsa `F11` para +ponerla a pantalla completa. El panel de control lo manejas desde el portátil, +o desde el móvil si prefieres. + +## Micrófono y cámara + +El portátil ya trae micrófono y cámara integrados. No hace falta nada por USB, +aunque puedes usarlo si quieres mejor sonido o una webcam mejor. + +La primera vez, elige el micrófono en el apartado Audio del panel, y la cámara +en el apartado del Mezclador. Si los conectas con FOSFENO ya abierto, usa el +botón de buscar dispositivos de nuevo. + +## Controlarlo desde el móvil + +Si quieres manejar el panel desde el móvil mientras el portátil hace de VJ, +conecta el móvil a la misma red WiFi y entra en `http://nombre-del-portatil.local:8080/`. +El nombre del portátil lo ves con el comando `hostname`. La dirección exacta +también aparece en la terminal al arrancar `./fosfeno`. + +## En qué se diferencia de la Raspberry + +- No arranca solo: lo lanzas tú con `./fosfeno` cuando lo necesitas. +- Usa el puerto 8080 en lugar del 80. +- No cambia el nombre de red del equipo. +- Los botones de reiniciar y apagar del panel no harán nada (es tu portátil, + no un aparato dedicado, así que es lo correcto). + +Todo lo demás (los cinco motores, el editor de código, la detección de BPM, +el mezclador) funciona exactamente igual. diff --git a/fosfeno b/fosfeno new file mode 100755 index 0000000..ecadeff --- /dev/null +++ b/fosfeno @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# =========================================================================== +# FOSFENO :: arranque en modo portatil (Linux de escritorio) +# +# Lanza el servidor, abre el panel y las visuales, y lo cierra todo con Ctrl+C. +# Antes hay que instalar una vez con: bash install.sh --laptop +# =========================================================================== +set -u + +DIR="$(cd "$(dirname "$0")" && pwd)" +PORT=8080 +export FOSFENO_PORT="$PORT" +export FOSFENO_NO_KIOSK=1 + +if [ ! -x "$DIR/.venv/bin/python3" ]; then + echo "FOSFENO no esta instalado todavia. Ejecuta primero:" + echo " bash install.sh --laptop" + exit 1 +fi + +SERVER_PID="" +STAGE_PID="" +cleanup() { + echo + echo "Cerrando FOSFENO..." + [ -n "$STAGE_PID" ] && kill "$STAGE_PID" 2>/dev/null + [ -n "$SERVER_PID" ] && kill "$SERVER_PID" 2>/dev/null + exit 0 +} +trap cleanup INT TERM + +echo "Arrancando FOSFENO..." +"$DIR/.venv/bin/python3" "$DIR/backend/server.py" & +SERVER_PID=$! + +# Espera (hasta 20 s) a que el servidor responda +for _ in $(seq 1 40); do + curl -s "http://localhost:$PORT/" >/dev/null 2>&1 && break + sleep 0.5 +done + +# Abre las visuales en una ventana propia de Chromium. Es una ventana normal: +# arrastrala al proyector y pulsa F11 para ponerla a pantalla completa. +BROWSER="$(command -v chromium-browser || command -v chromium \ + || command -v google-chrome || true)" +if [ -n "$BROWSER" ]; then + "$BROWSER" --app="http://localhost:$PORT/stage" \ + --user-data-dir="/tmp/fosfeno-stage" \ + --use-fake-ui-for-media-stream \ + --autoplay-policy=no-user-gesture-required \ + >/dev/null 2>&1 & + STAGE_PID=$! +else + echo "Aviso: no se encontro Chromium. Abre las visuales a mano en:" + echo " http://localhost:$PORT/stage" +fi + +# Abre el panel de control en el navegador por defecto +xdg-open "http://localhost:$PORT/" >/dev/null 2>&1 || true + +cat < "$HOME/.config/autostart/fosfeno.desktop"; then - log_ok "Arranque automatico configurado (~/.config/autostart/fosfeno.desktop)" -fi +log_step "[8/9] Arranque automatico y permisos" +chmod +x "$DIR/scripts/"*.sh "$DIR/install.sh" "$DIR/uninstall.sh" 2>/dev/null +[ -f "$DIR/fosfeno" ] && chmod +x "$DIR/fosfeno" -# Nombre de red fijo: deja el panel accesible en http://.local/ -HOSTNAME_WANT="$(python3 -c "import json;print(json.load(open('$DIR/config.json')).get('network',{}).get('hostname','fosfeno'))" 2>/dev/null || echo fosfeno)" -if [ "$(hostname)" != "$HOSTNAME_WANT" ]; then - sudo hostnamectl set-hostname "$HOSTNAME_WANT" 2>/dev/null || true - if grep -q "^127.0.1.1" /etc/hosts; then - sudo sed -i "s/^127.0.1.1.*/127.0.1.1\t$HOSTNAME_WANT/" /etc/hosts - else - echo -e "127.0.1.1\t$HOSTNAME_WANT" | sudo tee -a /etc/hosts >/dev/null +HOSTNAME_WANT="$(hostname)" +if [ "$LAPTOP" = "yes" ]; then + log_info "Modo portatil: sin arranque automatico ni cambios en el sistema." + log_info "FOSFENO se lanza a mano con ./fosfeno cuando quieras usarlo." +else + mkdir -p "$HOME/.config/autostart" + if sed "s#__DIR__#$DIR#g" "$DIR/scripts/fosfeno-autostart.desktop" \ + > "$HOME/.config/autostart/fosfeno.desktop"; then + log_ok "Arranque automatico configurado (~/.config/autostart/fosfeno.desktop)" fi - log_ok "Nombre de red puesto a '$HOSTNAME_WANT' (panel en http://$HOSTNAME_WANT.local/)" -else - log_ok "Nombre de red: $HOSTNAME_WANT" -fi -if echo "$USER ALL=(ALL) NOPASSWD: /sbin/reboot, /sbin/poweroff" \ - | sudo tee /etc/sudoers.d/fosfeno >/dev/null \ - && sudo chmod 440 /etc/sudoers.d/fosfeno; then - log_ok "Permisos de reinicio/apagado configurados" + # Nombre de red fijo: deja el panel accesible en http://.local/ + HOSTNAME_WANT="$(python3 -c "import json;print(json.load(open('$DIR/config.json')).get('network',{}).get('hostname','fosfeno'))" 2>/dev/null || echo fosfeno)" + if [ "$(hostname)" != "$HOSTNAME_WANT" ]; then + sudo hostnamectl set-hostname "$HOSTNAME_WANT" 2>/dev/null || true + if grep -q "^127.0.1.1" /etc/hosts; then + sudo sed -i "s/^127.0.1.1.*/127.0.1.1\t$HOSTNAME_WANT/" /etc/hosts + else + echo -e "127.0.1.1\t$HOSTNAME_WANT" | sudo tee -a /etc/hosts >/dev/null + fi + log_ok "Nombre de red puesto a '$HOSTNAME_WANT' (panel en http://$HOSTNAME_WANT.local/)" + else + log_ok "Nombre de red: $HOSTNAME_WANT" + fi + + if echo "$USER ALL=(ALL) NOPASSWD: /sbin/reboot, /sbin/poweroff" \ + | sudo tee /etc/sudoers.d/fosfeno >/dev/null \ + && sudo chmod 440 /etc/sudoers.d/fosfeno; then + log_ok "Permisos de reinicio/apagado configurados" + fi fi # --------------------------------------------------------------------------- -# 9. Permiso para el puerto 80 +# 9. Acceso de red # --------------------------------------------------------------------------- -log_step "[9/9] Permitiendo a Python escuchar en el puerto 80" -PYBIN="$(readlink -f "$DIR/.venv/bin/python3")" -if sudo setcap 'cap_net_bind_service=+ep' "$PYBIN" 2>/dev/null; then - log_ok "Puerto 80 habilitado" +log_step "[9/9] Acceso de red" +if [ "$LAPTOP" = "yes" ]; then + log_info "Modo portatil: el panel usara el puerto 8080, no hace falta nada mas." else - warn "no se pudo habilitar el puerto 80; cambia 'server.port' a 8080 en config.json" + PYBIN="$(readlink -f "$DIR/.venv/bin/python3")" + if sudo setcap 'cap_net_bind_service=+ep' "$PYBIN" 2>/dev/null; then + log_ok "Puerto 80 habilitado" + else + warn "no se pudo habilitar el puerto 80; cambia 'server.port' a 8080 en config.json" + fi fi # --------------------------------------------------------------------------- @@ -271,6 +288,22 @@ else log_warn "FOSFENO instalado con $FOSFENO_WARNINGS aviso(s) (revisa arriba)." fi printf '%s\n' "===================================================$C_RST" +if [ "$LAPTOP" = "yes" ]; then +cat <