diff --git a/.env.dist b/.env.dist index c4cd2f5..b750865 100755 --- a/.env.dist +++ b/.env.dist @@ -1,4 +1,4 @@ DEBUG=false PORT=5000 SECRET_KEY=ANY_SECRET_KEY_HERE -AUTOCONFIGURE_LX_FILE=/home/pi/.config/lxsession/LXDE-pi/autostart # Replace by "/dev/null" if not needed +PLAYER_AUTOSTART_FILE=/home/pi/obscreen/system/start-chromium.sh # Replace by "/dev/null" if not needed diff --git a/Dockerfile b/Dockerfile index 1cac868..0c1b186 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,9 @@ FROM python:3.9.17-alpine3.17 -RUN apk add --no-cache git chromium \ - && apk add --no-cache --virtual .build-deps git gcc musl-dev - WORKDIR /app COPY . . -RUN pip install -r requirements.txt && apk del .build-deps gcc musl-dev +RUN pip install -r requirements.txt ENTRYPOINT ["python", "/app/obscreen.py"] diff --git a/README.md b/README.md index 9fa0eca..a4cd5a3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# Obscreen +# Obscreen + πŸ§‘β€πŸŽ„ Open to feature request and pull request @@ -22,112 +23,8 @@ Use a RaspberryPi (with desktop) to show a full-screen slideshow (Kiosk-mode) ![Obscreen Screenshot](https://github.com/jr-k/obscreen/blob/master/docs/screenshot.png "Obscreen Screenshot") -## 🐳 Run with docker -### With docker (for test) -```bash -# Prepare application data file tree -mkdir -p obscreen/data/db obscreen/data/uploads && cd obscreen +# Two setups available -# For RaspberryPi only - Prepare LXDE autostart file -touch /home/pi/.config/lxsession/LXDE-pi/autostart - -# Run the Docker container -# 🚨 If you ARE NOT on a RaspberryPi -# - replace '/home/pi/.config/lxsession/LXDE-pi/autostart' with '/dev/null' -# 🚨 Else make sure that -# - file '/home/pi/.config/lxsession/LXDE-pi/autostart' exists and is writeable -docker run --rm --name obscreen --pull=always \ - -e DEBUG=false \ - -e PORT=5000 \ - -e AUTOCONFIGURE_LX_FILE=/app/var/run/lxfile \ - -e SECRET_KEY=ANY_SECRET_KEY_HERE \ - -p 5000:5000 \ - -v ./data/db:/app/data/db \ - -v ./data/uploads:/app/data/uploads \ - -v /home/pi/.config/lxsession/LXDE-pi/autostart:/app/var/run/lxfile \ - jierka/obscreen:latest -``` - -### With docker-compose -```bash -# Prepare application data file tree -mkdir -p obscreen/data/db obscreen/data/uploads && cd obscreen - -# For RaspberryPi only - Prepare LXDE autostart file -touch /home/pi/.config/lxsession/LXDE-pi/autostart - -# Download docker-compose.yml -# 🚨 If you ARE NOT on a RaspberryPi -curl https://raw.githubusercontent.com/jr-k/obscreen/master/docker-compose.norpi.yml > docker-compose.yml -# 🚨 If you ARE on a RaspberryPi -curl https://raw.githubusercontent.com/jr-k/obscreen/master/docker-compose.yml > docker-compose.yml - -# Run -docker compose up -``` - -## πŸ“  Run system wide -### Install -```bash -# Install system dependencies -sudo apt-get update -sudo apt-get install -y git chromium-browser unclutter - -# Get files -git clone https://github.com/jr-k/obscreen.git && cd obscreen - -# Install application dependencies -pip3 install -r requirements.txt - -# Add some sample data -cp data/db/slideshow.json.dist data/db/slideshow.json - -# Customize server default values -cp .env.dist .env - -# For RaspberryPi only - Prepare LXDE autostart file -touch /home/pi/.config/lxsession/LXDE-pi/autostart -``` - -### Configure -- Server configuration is editable in `.env` file. -- Application configuration is available in `http://localhost:5000/settings` page. - -### Start server (for test) -```bash -./obscreen.py -``` - -### Start server forever with systemctl -```bash -sudo ln -s "$(pwd)/system/obscreen.service" /etc/systemd/system/obscreen.service -sudo systemctl daemon-reload -sudo systemctl enable obscreen.service -sudo systemctl start obscreen.service -``` - -To troubleshoot you can check logs -```bash -sudo journalctl -u obscreen -f -``` - -## πŸ‘Œ Usage -- Page which plays slideshow is reachable at `http://localhost:5000` -- Slideshow manager is reachable at `http://localhost:5000/manage` - -## ✨ You are done now :) -- If everything is set up correctly, the RaspberryPi shall start chromium in fullscreen directly after boot screen and after some seconds of showing the date & time (`views/player/default.jinja.html`) your slideshow shall start and loop endlessly. -- Make sure that `AUTOCONFIGURE_LX_FILE` exists and is writeable ! - -## πŸ“Ž Additional - -### Hardware checks -- Basic Setup -For basic RaspberryPi setup you can use most of the available guides, for example this one: -https://gist.github.com/blackjid/dfde6bedef148253f987 - -- HDMI Mode -You may need to set the HDMI Mode on the raspi to ensure the hdmi resolution matches your screen exactly. Here is the official documentation: -https://www.raspberrypi.org/documentation/configuration/config-txt/video.md - -However, I used this one: `(2,82) = 1920x1080 60Hz 1080p` +### πŸ”΄ [I want to power RaspberryPi and automatically see my slideshow on a screen connected to it and manage the slideshow](docs/setup-run-on-rpi.md) +### πŸ”΅ [I want to start browser and setup playlist url manually on my device and just want a slideshow manager](docs/setup-run-headless.md) + diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 6bfb574..4d15f21 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -9,10 +9,10 @@ services: environment: - DEBUG=${DEBUG-false} - PORT=${PORT-5000} - - AUTOCONFIGURE_LX_FILE=/app/var/run/lxfile + - PLAYER_AUTOSTART_FILE=/app/var/run/play - SECRET_KEY=${SECRET_KEY-ANY_SECRET_KEY_HERE} volumes: - .:/app - - ${AUTOCONFIGURE_LX_FILE-/dev/null}:/app/var/run/lxfile + - ${PLAYER_AUTOSTART_FILE-/dev/null}:/app/var/run/play ports: - ${PORT}:${PORT} \ No newline at end of file diff --git a/docker-compose.norpi.yml b/docker-compose.headless.yml similarity index 78% rename from docker-compose.norpi.yml rename to docker-compose.headless.yml index 450c8c1..052dea2 100644 --- a/docker-compose.norpi.yml +++ b/docker-compose.headless.yml @@ -6,10 +6,10 @@ services: environment: - DEBUG=false - PORT=5000 - - AUTOCONFIGURE_LX_FILE=/app/var/run/lxfile + - PLAYER_AUTOSTART_FILE=/app/var/run/play - SECRET_KEY=ANY_SECRET_KEY_HERE volumes: - - /dev/null:/app/var/run/lxfile + - /dev/null:/app/var/run/play - ./data/db:/app/data/db - ./data/uploads:/app/data/uploads ports: diff --git a/docker-compose.yml b/docker-compose.yml index 44e71c9..eb7cbc2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,10 +6,10 @@ services: environment: - DEBUG=false - PORT=5000 - - AUTOCONFIGURE_LX_FILE=/app/var/run/lxfile + - PLAYER_AUTOSTART_FILE=/app/var/run/play - SECRET_KEY=ANY_SECRET_KEY_HERE volumes: - - /home/pi/.config/lxsession/LXDE-pi/autostart:/app/var/run/lxfile + - /home/pi/obscreen/system/start-chromium.sh:/app/var/run/play - ./data/db:/app/data/db - ./data/uploads:/app/data/uploads ports: diff --git a/docs/obscreen.png b/docs/img/obscreen.png similarity index 100% rename from docs/obscreen.png rename to docs/img/obscreen.png diff --git a/docs/setup-run-headless.md b/docs/setup-run-headless.md new file mode 100644 index 0000000..c5c7da9 --- /dev/null +++ b/docs/setup-run-headless.md @@ -0,0 +1,83 @@ +# Obscreen - Headless run on any server + +> #### πŸ‘ˆ [back to readme](README.md) + +#### πŸ”΅ You want to start browser and setup playlist url manually on your device and just want a slideshow manager ? You're in the right place. + + +## 🐳 Run with docker +### With docker (for test) +```bash +# Prepare application data file tree +mkdir -p obscreen/data/db obscreen/data/uploads && cd obscreen + +# Run the Docker container +docker run --rm --name obscreen --pull=always \ + -e DEBUG=false \ + -e PORT=5000 \ + -e PLAYER_AUTOSTART_FILE=/app/var/run/play \ + -e SECRET_KEY=ANY_SECRET_KEY_HERE \ + -p 5000:5000 \ + -v ./data/db:/app/data/db \ + -v ./data/uploads:/app/data/uploads \ + -v /dev/null:/app/var/run/play \ + jierka/obscreen:latest +``` + +### With docker-compose +```bash +# Prepare application data file tree +mkdir -p obscreen/data/db obscreen/data/uploads && cd obscreen + +# Download docker-compose.yml +curl https://raw.githubusercontent.com/jr-k/obscreen/master/docker-compose.headless.yml > docker-compose.yml + +# Run +docker compose up --detach --pull always +``` + +## πŸ“  Run system wide +### Install +```bash +# Install system dependencies +sudo apt-get update +sudo apt-get install -y git + +# Get files +git clone https://github.com/jr-k/obscreen.git && cd obscreen + +# Install application dependencies +pip3 install -r requirements.txt + +# Add some sample data +cp data/db/slideshow.json.dist data/db/slideshow.json + +# Customize server default values +cp .env.dist .env +``` + +### Configure +- Server configuration is editable in `.env` file. +- Application configuration will be available at `http://localhost:5000/settings` page after run. + +### Start server (for test) +```bash +./obscreen.py +``` + +### Start server forever with systemctl +```bash +sudo ln -s "$(pwd)/system/obscreen-manager.service" /etc/systemd/system/obscreen-manager.service +sudo systemctl daemon-reload +sudo systemctl enable obscreen-manager.service +sudo systemctl start obscreen-manager.service +``` + +To troubleshoot you can check logs +```bash +sudo journalctl -u obscreen -f +``` + +## πŸ‘Œ Usage +- Page which plays slideshow is reachable at `http://localhost:5000` +- Slideshow manager is reachable at `http://localhost:5000/manage` diff --git a/docs/setup-run-on-rpi.md b/docs/setup-run-on-rpi.md new file mode 100644 index 0000000..3cc22e1 --- /dev/null +++ b/docs/setup-run-on-rpi.md @@ -0,0 +1,117 @@ +# Obscreen - Autorun on RaspberryPi + +> #### πŸ‘ˆ [back to readme](README.md) + +#### πŸ”΄ You want to power RaspberryPi and automatically see your slideshow on a screen connected to it and manage your slideshow ? You're in the right place. + +## πŸŽ›οΈ Hardware installation + +1. Download RaspberryPi Imager and setup an sdcard with `Raspberry Pi OS Lite` (🚨without desktop, only `Lite` version!). You'll find it under category `Raspberry PI OS (other)` +2. Log into your pi user locally or via ssh (`ssh pi@raspberrypi.local`) +3. Install player autorun by executing `curl -fsSL https://raw.githubusercontent.com/jr-k/obscreen/master/system/install-autorun-rpi.sh | sudo bash` + +--- +## 🐳 Run with docker +### Install docker if needed +```bash +curl -sSL get.docker.com | sh +sudo usermod -aG docker $(whoami) +logout +#then login again +``` + + +### With docker (for test) +```bash +# Prepare application data file tree and prepare player autostart file +cd /home/pi && mkdir -p obscreen/data/db obscreen/data/uploads obscreen/system && touch system/start-chromium.sh && cd obscreen + +# Run the Docker container +ocker run --rm --name obscreen --pull=always \ + -e DEBUG=false \ + -e PORT=5000 \ + -e PLAYER_AUTOSTART_FILE=/app/var/run/play \ + -e SECRET_KEY=ANY_SECRET_KEY_HERE \ + -p 5000:5000 \ + -v ./data/db:/app/data/db \ + -v ./data/uploads:/app/data/uploads \ + -v /home/pi/obscreen/system/start-chromium.sh:/app/var/run/play \ + jierka/obscreen:latest +``` + +### Or with docker-compose +```bash +# Prepare application data file tree +cd /home/pi && mkdir -p obscreen/data/db obscreen/data/uploads obscreen/system && touch system/start-chromium.sh && cd obscreen + +# Download docker-compose.yml +curl https://raw.githubusercontent.com/jr-k/obscreen/master/docker-compose.yml > docker-compose.yml + +# Run +docker compose up --detach --pull always +``` +--- +## πŸ“  Run system wide +### Install +```bash +# Install system dependencies +sudo apt-get update +sudo apt-get install -y git xinit xserver-xorg chromium-browser unclutter + +# Get files +git clone https://github.com/jr-k/obscreen.git && cd obscreen + +# Install application dependencies +pip3 install -r requirements.txt + +# Add some sample data +cp data/db/slideshow.json.dist data/db/slideshow.json + +# Customize server default values +cp .env.dist .env +``` + +### Configure +- Server configuration is editable in `.env` file. +- Application configuration will be available at `http://raspberrypi.local:5000/settings` page after run. + +### Start server (for test) +```bash +./obscreen.py +``` + +### Start server forever with systemctl +```bash +sudo ln -s "$(pwd)/system/obscreen-manager.service" /etc/systemd/system/obscreen-manager.service +sudo systemctl daemon-reload +sudo systemctl enable obscreen-manager.service +sudo systemctl start obscreen-manager.service +``` + +To troubleshoot you can check logs +```bash +sudo journalctl -u obscreen -f +``` +--- +## πŸ‘Œ Usage +- Page which plays slideshow is reachable at `http://raspberrypi.local:5000` +- Slideshow manager is reachable at `http://raspberrypi.local:5000/manage` + +## ✨ You are done now :) +- If everything is set up correctly, the RaspberryPi shall start chromium in fullscreen directly after boot screen and after some seconds of showing the date & time (`views/player/default.jinja.html`) your slideshow shall start and loop endlessly. +- Make sure that `PLAYER_AUTOSTART_FILE` exists and is writeable ! + +## πŸ“Ž Additional + +### Hardware checks +- Basic Setup +For basic RaspberryPi setup you can use most of the available guides, for example this one: +https://gist.github.com/blackjid/dfde6bedef148253f987 + +- HDMI Mode +You may need to set the HDMI Mode on the raspi to ensure the hdmi resolution matches your screen exactly. Here is the official documentation: +https://www.raspberrypi.org/documentation/configuration/config-txt/video.md + +However, I used this one: `(2,82) = 1920x1080 60Hz 1080p` + + diff --git a/src/manager/ConfigManager.py b/src/manager/ConfigManager.py index 97b0cc4..712817b 100644 --- a/src/manager/ConfigManager.py +++ b/src/manager/ConfigManager.py @@ -12,7 +12,7 @@ load_dotenv() class ConfigManager: DEFAULT_PORT = 5000 - DEFAULT_LXDE_AUTOSTART_PATH = '/home/pi/.config/lxsession/LXDE-pi/autostart' + DEFAULT_PLAYER_AUTOSTART_PATH = '/home/pi/obscreen/system/start-chromium.sh' VERSION_FILE = 'version.txt' def __init__(self, variable_manager: VariableManager): @@ -22,7 +22,7 @@ class ConfigManager: 'port': self.DEFAULT_PORT, 'bind': '0.0.0.0', 'debug': False, - 'autoconfigure_lx_file': self.DEFAULT_LXDE_AUTOSTART_PATH, + 'player_autostart_file': self.DEFAULT_PLAYER_AUTOSTART_PATH, 'log_file': None, 'log_level': 'INFO', 'log_stdout': True, @@ -50,7 +50,7 @@ class ConfigManager: parser.add_argument('--port', '-p', default=self._CONFIG['port'], help='Application port') parser.add_argument('--bind', '-b', default=self._CONFIG['bind'], help='Application bind address') parser.add_argument('--secret-key', '-s', default=self._CONFIG['secret_key'], help='Application secret key (any random string)') - parser.add_argument('--autoconfigure-lx-file', '-x', default=self._CONFIG['autoconfigure_lx_file'], help='Path to lx autostart file') + parser.add_argument('--player-autostart-file', '-x', default=self._CONFIG['player_autostart_file'], help='Path to player autostart file') parser.add_argument('--log-file', '-lf', default=self._CONFIG['log_file'], help='Log File path') parser.add_argument('--log-level', '-ll', default=self._CONFIG['log_level'], help='Log Level') parser.add_argument('--log-stdout', '-ls', default=self._CONFIG['log_stdout'], action='store_true', help='Log to standard output') @@ -67,8 +67,8 @@ class ConfigManager: if args.debug: self._CONFIG['debug'] = args.debug - if args.autoconfigure_lx_file: - self._CONFIG['autoconfigure_lx_file'] = args.autoconfigure_lx_file + if args.player_autostart_file: + self._CONFIG['player_autostart_file'] = args.player_autostart_file if args.log_file: self._CONFIG['log_file'] = args.log_file if args.secret_key: @@ -95,8 +95,8 @@ class ConfigManager: def autoconfigure(self) -> None: self.autoconfigure_player_url() - if self.map().get('autoconfigure_lx_file'): - self.autoconfigure_lxconf() + if self.map().get('player_autostart_file'): + self.autoconfigure_player_autostart_file() def autoconfigure_player_url(self) -> str: @@ -104,36 +104,46 @@ class ConfigManager: return self._CONFIG['player_url'] - def autoconfigure_lxconf(self) -> None: - path = self.map().get('autoconfigure_lx_file') + def autoconfigure_player_autostart_file(self) -> None: + path = self.map().get('player_autostart_file') in_docker = am_i_in_docker() - lx_path = self.DEFAULT_LXDE_AUTOSTART_PATH if in_docker else path + player_autostart_path = self.DEFAULT_PLAYER_AUTOSTART_PATH if in_docker else path if os.path.isdir(path) or not os.path.exists(path): logging.error( - "LXDE autostart file {} doesn't exist on your server, please create it by executing follow command: \n'rm -rf ./var/run/lxfile 2>/dev/null ; sudo rm -rf /home/pi/.config/lxsession/LXDE-pi 2>/dev/null; sudo mkdir -p /home/pi/.config/lxsession/LXDE-pi 2>/dev/null ; sudo touch {}'\n".format( - lx_path, - self.DEFAULT_LXDE_AUTOSTART_PATH + "Player autostart file {} doesn't exist on your server, please create it by executing follow command: \n'rm -rf ./var/run/play 2>/dev/null ; sudo touch {}'\n".format( + player_autostart_path, + self.DEFAULT_PLAYER_AUTOSTART_PATH ) ) sys.exit(1) else: - logging.info("Overriding LXDE autostart file {}".format(lx_path)) + logging.info("Overriding player autostart file {}".format(player_autostart_path)) player_url = self.map().get('player_url') - os.makedirs(os.path.dirname(lx_path), exist_ok=True) - xenv_presets = f""" -@lxpanel --profile LXDE-pi -@pcmanfm --desktop --profile LXDE-pi -@xscreensaver -no-splash -#@point-rpi -@xset s off -@xset -dpms -@xset s noblank -@unclutter -display :0 -noevents -grab -@sed -i 's/"exited_cleanly": false/"exited_cleanly": true/' ~/.config/chromium/Default/Preferences -#@sleep 10 -@chromium-browser --disable-features=Translate --ignore-certificate-errors --disable-web-security --disable-restore-session-state --autoplay-policy=no-user-gesture-required --start-maximized --allow-running-insecure-content --remember-cert-error-decisions --disable-restore-session-state --noerrdialogs --kiosk --incognito --window-position=0,0 --display=:0 {player_url} - """ - with open(lx_path, 'w') as file: + os.makedirs(os.path.dirname(player_autostart_path), exist_ok=True) + xenv_presets = """ +#!/bin/bash + +# Disable screensaver and DPMS +xset s off +xset -dpms +xset s noblank + +# Start unclutter to hide the mouse cursor +unclutter -display :0 -noevents -grab & + +# Modify Chromium preferences to avoid restore messages +mkdir -p /home/pi/.config/chromium/Default 2>/dev/null +touch /home/pi/.config/chromium/Default/Preferences +sed -i 's/"exited_cleanly": false/"exited_cleanly": true/' /home/pi/.config/chromium/Default/Preferences + +RESOLUTION=$(DISPLAY=:0 xrandr | grep '*' | awk '{print $1}') +WIDTH=$(echo $RESOLUTION | cut -d 'x' -f 1) +HEIGHT=$(echo $RESOLUTION | cut -d 'x' -f 2) + +# Start Chromium in kiosk mode +chromium-browser --disable-features=Translate --ignore-certificate-errors --disable-web-security --disable-restore-session-state --autoplay-policy=no-user-gesture-required --start-maximized --allow-running-insecure-content --remember-cert-error-decisions --noerrdialogs --kiosk --incognito --window-position=0,0 --window-size=${WIDTH},${HEIGHT} --display=:0 __PLAYER_URL__ + """.replace('__PLAYER_URL__', player_url) + with open(player_autostart_path, 'w') as file: file.write(xenv_presets) diff --git a/system/install-autorun-rpi.sh b/system/install-autorun-rpi.sh new file mode 100644 index 0000000..4539619 --- /dev/null +++ b/system/install-autorun-rpi.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Update and install necessary packages +apt update +apt install -y xinit xserver-xorg chromium-browser unclutter + +# Add user pi to tty and video groups +usermod -aG tty pi +usermod -aG video pi + +# Configure Xwrapper +touch /etc/X11/Xwrapper.config +grep -qxF "allowed_users=anybody" /etc/X11/Xwrapper.config || echo "allowed_users=anybody" | tee -a /etc/X11/Xwrapper.config +grep -qxF "needs_root_rights=yes" /etc/X11/Xwrapper.config || echo "needs_root_rights=yes" | tee -a /etc/X11/Xwrapper.config + +# Create the systemd service to start Chromium in kiosk mode +curl https://raw.githubusercontent.com/jr-k/obscreen/master/system/obscreen-player.yml > /etc/systemd/system/obscreen-player.service + +# Reload systemd, enable and start the service +systemctl daemon-reload +systemctl enable obscreen-player.service +systemctl set-default graphical.target + +# Reboot the system +reboot \ No newline at end of file diff --git a/system/obscreen.service b/system/obscreen-manager.service similarity index 89% rename from system/obscreen.service rename to system/obscreen-manager.service index 2cc76ec..8bc752f 100644 --- a/system/obscreen.service +++ b/system/obscreen-manager.service @@ -1,5 +1,5 @@ [Unit] -Description=Obscreen +Description=Obscreen Manager After=network.target [Service] diff --git a/system/obscreen-player.service b/system/obscreen-player.service new file mode 100644 index 0000000..c631117 --- /dev/null +++ b/system/obscreen-player.service @@ -0,0 +1,14 @@ +[Unit] +Description=Obscreen Player +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/xinit /home/pi/obscreen/system/start-chromium.sh -- :0 -nolisten tcp vt7 -keeptty +User=pi +Group=pi +Restart=on-failure +Environment=DISPLAY=:0 + +[Install] +WantedBy=graphical.target \ No newline at end of file diff --git a/system/start-chromium.sh b/system/start-chromium.sh new file mode 100755 index 0000000..2ba2b8a --- /dev/null +++ b/system/start-chromium.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# Disable screensaver and DPMS +xset s off +xset -dpms +xset s noblank + +# Start unclutter to hide the mouse cursor +unclutter -display :0 -noevents -grab & + +# Modify Chromium preferences to avoid restore messages +mkdir -p /home/pi/.config/chromium/Default 2>/dev/null +touch /home/pi/.config/chromium/Default/Preferences +sed -i 's/"exited_cleanly": false/"exited_cleanly": true/' /home/pi/.config/chromium/Default/Preferences + +RESOLUTION=$(DISPLAY=:0 xrandr | grep '*' | awk '{print $1}') +WIDTH=$(echo $RESOLUTION | cut -d 'x' -f 1) +HEIGHT=$(echo $RESOLUTION | cut -d 'x' -f 2) + +# Start Chromium in kiosk mode +chromium-browser --disable-features=Translate --ignore-certificate-errors --disable-web-security --disable-restore-session-state --autoplay-policy=no-user-gesture-required --start-maximized --allow-running-insecure-content --remember-cert-error-decisions --noerrdialogs --kiosk --incognito --window-position=0,0 --window-size=${WIDTH},${HEIGHT} --display=:0 http://localhost:5000