From 16629f77c95f28d507b22f528538e9ff57e83c66 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 19:43:54 -0500 Subject: [PATCH 001/154] Delete proxmox directory --- proxmox/README.md | 136 ------- proxmox/build.func | 734 -------------------------------------- proxmox/create_lxc.sh | 158 -------- proxmox/media-emby.sh | 112 ------ proxmox/media-jellyfin.sh | 68 ---- proxmox/media-plex.sh | 78 ---- proxmox/riven-install.sh | 384 -------------------- proxmox/riven.sh | 137 ------- 8 files changed, 1807 deletions(-) delete mode 100644 proxmox/README.md delete mode 100644 proxmox/build.func delete mode 100644 proxmox/create_lxc.sh delete mode 100644 proxmox/media-emby.sh delete mode 100644 proxmox/media-jellyfin.sh delete mode 100644 proxmox/media-plex.sh delete mode 100644 proxmox/riven-install.sh delete mode 100644 proxmox/riven.sh diff --git a/proxmox/README.md b/proxmox/README.md deleted file mode 100644 index 8bdab24..0000000 --- a/proxmox/README.md +++ /dev/null @@ -1,136 +0,0 @@ -# Proxmox LXC Helper Script for Riven - -This repository contains a Proxmox helper script that creates a Debian 12, unprivileged -LXC container running the Riven backend and frontend on bare metal (no Docker). - ---- - -## Requirements - -- Proxmox VE **8.1 or later** (including 9.x) -- Internet connectivity from the Proxmox host and the LXC template mirrors -- A storage pool that can host LXC containers - -The helper will create an **unprivileged** container (CT_TYPE=1) with sensible defaults: - -- OS: Debian 12 -- CPU: 4 vCPU -- RAM: 8 GB -- Disk: 40 GB - -You can override these values via the script's **Advanced Settings** dialog. - ---- - -## Creating the Riven LXC - -Run this from a **Proxmox VE host shell**: - -```bash -bash -c "$(wget -qLO - https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/riven.sh)" -``` - -The script will: - -- Validate your Proxmox version (8.1+) -- Create a new **unprivileged** Debian 12 LXC -- Enable FUSE and mount `/dev/fuse` inside the container -- Install and configure PostgreSQL inside the LXC -- Install the Riven backend (Python/uv) and frontend (Node/pnpm) bare metal -- Create systemd services for both backend and frontend so they start on boot - -After the script completes, you should be able to reach: - -- Riven backend at: `http://:8080` -- Riven frontend at: `http://:3000` - -`` is the IP address assigned to the LXC (shown in the script output and in `pct list`). - ---- - -## What the installer sets up - -Inside the Riven LXC, the installer configures: - -- **Directories** - - `/riven` – Riven backend checkout & virtualenv - - `/riven/data` – data directory (used by the frontend's SQLite DB by default) - - `/mount` – FUSE mountpoint for the Riven virtual filesystem (VFS) - - `/opt/riven-frontend` – Riven frontend app - - `/etc/riven` – configuration directory - -- **Database** - - PostgreSQL with database `riven` - - `postgres` user password set to `postgres` (local-only, inside the CT) - -- **Environment files** - - Backend: `/etc/riven/backend.env` - - `RIVEN_API_KEY` – randomly generated hex key used by the backend - - `RIVEN_DATABASE_HOST=postgresql+psycopg2://postgres:postgres@127.0.0.1/riven` - - `RIVEN_FILESYSTEM_MOUNT_PATH=/mount` - - `RIVEN_LIBRARY_PATH=/mnt/riven` (path the media servers will see) - - `RIVEN_FILESYSTEM_CACHE_DIR=/dev/shm/riven-cache` - - Frontend: `/etc/riven/frontend.env` - - `DATABASE_URL=/riven/data/riven.db` (SQLite) - - `BACKEND_URL=http://127.0.0.1:8080` - - `BACKEND_API_KEY=$RIVEN_API_KEY` (same value as backend) - - `AUTH_SECRET` – randomly generated, used by the frontend for auth - - `ORIGIN=http://localhost:3000` - -- **Systemd services** (inside the CT) - - `riven-backend.service` - - `riven-frontend.service` - -Both services are enabled and will start automatically when the LXC boots. - ---- - -## Checking status and logs - -Assuming your Riven container ID is `106`. - -### Enter the container - -```bash -pct enter 106 -``` - -### Check service status - -```bash -systemctl status riven-backend -systemctl status riven-frontend -``` - -### View live logs - -```bash -journalctl -u riven-backend -f -journalctl -u riven-frontend -f -``` - -You can also run these directly from the Proxmox host without entering the CT: - -```bash -lxc-attach -n 106 -- journalctl -u riven-backend -f -lxc-attach -n 106 -- journalctl -u riven-frontend -f -``` - ---- - -## Customizing configuration - -You can edit the environment files inside the Riven CT to customize settings: - -- `/etc/riven/backend.env` -- `/etc/riven/frontend.env` - -After making changes, restart the services: - -```bash -systemctl restart riven-backend riven-frontend -``` - -For advanced configuration (content providers, scrapers, ranking, etc.), -refer to the upstream Riven documentation and `.env.example` file in the -Riven repository. diff --git a/proxmox/build.func b/proxmox/build.func deleted file mode 100644 index 99e1080..0000000 --- a/proxmox/build.func +++ /dev/null @@ -1,734 +0,0 @@ -variables() { - NSAPP=$(echo ${APP,,} | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. -} - -# This function sets various color variables using ANSI escape codes for formatting text in the terminal. -color() { - YW=$(echo "\033[33m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - BGN=$(echo "\033[4;92m") - GN=$(echo "\033[1;92m") - DGN=$(echo "\033[32m") - CL=$(echo "\033[m") - CM="${GN}✓${CL}" - CROSS="${RD}✗${CL}" - BFR="\\r\\033[K" - HOLD=" " -} - -# This function enables error handling in the script by setting options and defining a trap for the ERR signal. -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. -error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" -} - -# This function displays a spinner. -spinner() { - local chars="/-\|" - local spin_i=0 - printf "\e[?25l" - while true; do - printf "\r \e[36m%s\e[0m" "${chars:spin_i++%${#chars}:1}" - sleep 0.1 - done -} - -# This function displays an informational message with a yellow color. -msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg} " - spinner & - SPINNER_PID=$! -} - -# This function displays a success message with a green color. -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -msg_error() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" -} - -# Check if the shell is using bash -shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# Run as root only -root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -pve_check() { - # Extract the numeric pve-manager version, e.g. 8.1.3 or 9.0.10 - local PVE_VER - PVE_VER=$(pveversion | awk -F/ '/pve-manager/ {print $2}' | cut -d- -f1) - # Require 8.1 or later (supports future 9.x, 10.x, ...) - if dpkg --compare-versions "$PVE_VER" lt "8.1"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Detected PVE version: $PVE_VER" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit - fi -} - -# This function checks the system architecture and exits if it's not "amd64". -arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${CROSS} This script will not work with PiMox! \n" - echo -e "\n Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -# This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. -ssh_check() { - if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit - fi - fi -} - -# This function displays the default values for various settings. -echo_default() { - echo -e "${DGN}Using Distribution: ${BGN}$var_os${CL}" - echo -e "${DGN}Using $var_os Version: ${BGN}$var_version${CL}" - echo -e "${DGN}Using Container Type: ${BGN}$CT_TYPE${CL}" - echo -e "${DGN}Using Root Password: ${BGN}Automatic Login${CL}" - echo -e "${DGN}Using Container ID: ${BGN}$NEXTID${CL}" - echo -e "${DGN}Using Hostname: ${BGN}$NSAPP${CL}" - echo -e "${DGN}Using Disk Size: ${BGN}$var_disk${CL}${DGN}GB${CL}" - echo -e "${DGN}Allocated Cores ${BGN}$var_cpu${CL}" - echo -e "${DGN}Allocated Ram ${BGN}$var_ram${CL}" - echo -e "${DGN}Using Bridge: ${BGN}vmbr0${CL}" - echo -e "${DGN}Using Static IP Address: ${BGN}dhcp${CL}" - echo -e "${DGN}Using Gateway IP Address: ${BGN}Default${CL}" - echo -e "${DGN}Using Apt-Cacher IP Address: ${BGN}Default${CL}" - echo -e "${DGN}Disable IPv6: ${BGN}No${CL}" - echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" - echo -e "${DGN}Using DNS Search Domain: ${BGN}Host${CL}" - echo -e "${DGN}Using DNS Server Address: ${BGN}Host${CL}" - echo -e "${DGN}Using MAC Address: ${BGN}Default${CL}" - echo -e "${DGN}Using VLAN Tag: ${BGN}Default${CL}" - echo -e "${DGN}Enable Root SSH Access: ${BGN}No${CL}" - echo -e "${DGN}Enable Verbose Mode: ${BGN}No${CL}" - echo -e "${BL}Creating a ${APP} LXC using the above default settings${CL}" -} - -# This function is called when the user decides to exit the script. It clears the screen and displays an exit message. -exit-script() { - clear - echo -e "⚠ User exited script \n" - exit -} - -# This function allows the user to configure advanced settings for the script. -advanced_settings() { - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - - if [ "${APP:-}" = "Riven" ]; then - # For Riven we fix the distribution to the defaults defined in riven.sh - # (Debian 12) and do not offer an OS/version choice here. - whiptail --backtitle "Proxmox VE Helper Scripts" \ - --msgbox --title "Default distribution for $APP" \ - "${var_os} ${var_version} \n \nThis installer is fixed to this distribution for support." 10 58 - else - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Default distribution for $APP" "${var_os} ${var_version} \n \nIf the default Linux distribution is not adhered to, script support will be discontinued. \n" 10 58 - if [ "$var_os" != "alpine" ]; then - var_os="" - while [ -z "$var_os" ]; do - if var_os=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISTRIBUTION" --radiolist "Choose Distribution:" 10 58 2 \ - "debian" "" OFF \ - "ubuntu" "" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$var_os" ]; then - echo -e "${DGN}Using Distribution: ${BGN}$var_os${CL}" - fi - else - exit-script - fi - done - fi - - if [ "$var_os" == "debian" ]; then - var_version="" - while [ -z "$var_version" ]; do - if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DEBIAN VERSION" --radiolist "Choose Version" 10 58 2 \ - "11" "Bullseye" OFF \ - "12" "Bookworm" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$var_version" ]; then - echo -e "${DGN}Using $var_os Version: ${BGN}$var_version${CL}" - fi - else - exit-script - fi - done - fi - - if [ "$var_os" == "ubuntu" ]; then - var_version="" - while [ -z "$var_version" ]; do - if var_version=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UBUNTU VERSION" --radiolist "Choose Version" 10 58 3 \ - "20.04" "Focal" OFF \ - "22.04" "Jammy" OFF \ - "24.04" "Noble" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$var_version" ]; then - echo -e "${DGN}Using $var_os Version: ${BGN}$var_version${CL}" - fi - else - exit-script - fi - done - fi - fi - - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if CT_TYPE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - echo -e "${DGN}Using Container Type: ${BGN}$CT_TYPE${CL}" - fi - else - exit-script - fi - done - - while true; do - if PW1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ ! -z "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${DGN}Using Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit-script - fi - fi - else - PW1="Automatic Login" - PW="" - echo -e "${DGN}Using Root Password: ${BGN}$PW1${CL}" - break - fi - else - exit-script - fi - done - - - if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 $NEXTID --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${DGN}Using Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else - exit - fi - - if CT_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 $NSAPP --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo ${CT_NAME,,} | tr -d ' ') - fi - echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" - else - exit-script - fi - - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 $var_disk --title "DISK SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - echo -e "${DGN}Using Disk Size: ${BGN}$DISK_SIZE${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "${RD}⚠ DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings - fi - echo -e "${DGN}Using Disk Size: ${BGN}$DISK_SIZE${CL}" - fi - else - exit-script - fi - - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 $var_cpu --title "CORE COUNT" 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" - else - echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" - fi - else - exit-script - fi - - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 $var_ram --title "RAM" 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" - else - echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" - fi - else - exit-script - fi - - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" 3>&1 1>&2 2>&3); then - if [ -z "$BRG" ]; then - BRG="vmbr0" - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" - else - echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" - fi - else - exit-script - fi - - while true; do - NET=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) - exit_status=$? - if [ $exit_status -eq 0 ]; then - if [ "$NET" = "dhcp" ]; then - echo -e "${DGN}Using IP Address: ${BGN}$NET${CL}" - break - else - if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${DGN}Using IP Address: ${BGN}$NET${CL}" - break - else - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 - fi - fi - else - exit-script - fi - done - - if [ "$NET" != "dhcp" ]; then - while true; do - GATE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 - else - GATE=",gw=$GATE1" - echo -e "${DGN}Using Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done - else - GATE="" - echo -e "${DGN}Using Gateway IP Address: ${BGN}Default${CL}" - fi - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for default)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${DGN}Using APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - else - exit-script - fi - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - DISABLEIP6="yes" - else - DISABLEIP6="no" - fi - echo -e "${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z $MTU1 ]; then - MTU1="Default" - MTU="" - else - MTU=",mtu=$MTU1" - fi - echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" - else - exit-script - fi - - if SD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z $SD ]; then - SX=Host - SD="" - else - SX=$SD - SD="-searchdomain=$SD" - fi - echo -e "${DGN}Using DNS Search Domain: ${BGN}$SX${CL}" - else - exit-script - fi - - if NX=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z $NX ]; then - NX=Host - NS="" - else - NS="-nameserver=$NX" - fi - echo -e "${DGN}Using DNS Server IP Address: ${BGN}$NX${CL}" - else - exit-script - fi - - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for default)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z $MAC1 ]; then - MAC1="Default" - MAC="" - else - MAC=",hwaddr=$MAC1" - echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" - fi - else - exit-script - fi - - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z $VLAN1 ]; then - VLAN1="Default" - VLAN="" - else - VLAN=",tag=$VLAN1" - fi - echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" - else - exit-script - fi - - # Ask whether to install and host the Riven frontend in this container. - # If disabled, only the backend API will be installed here and you can - # host the frontend elsewhere. - if (whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "RIVEN FRONTEND" \ - --yesno "Install and host the Riven frontend in this container?\n\nIf you select No, only the backend API will be installed here. You can host the frontend on another machine or container." \ - 12 78); then - RIVEN_INSTALL_FRONTEND="yes" - else - RIVEN_INSTALL_FRONTEND="no" - fi - echo -e "${DGN}Install frontend in this CT: ${BGN}$RIVEN_INSTALL_FRONTEND${CL}" - - # Optional: allow user to specify the external frontend URL / origin. - # If left blank, the installer will auto-detect and use http://:3000. - if [ "$RIVEN_INSTALL_FRONTEND" != "no" ]; then - if RIVEN_FRONTEND_ORIGIN=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Set the external URL you'll use to access the Riven frontend (ORIGIN).\n\nExamples:\n http://:3000\n https://riven.example.com\n\nLeave blank to auto-detect using the CT IP on port 3000." \ - 15 78 --title "RIVEN FRONTEND ORIGIN" 3>&1 1>&2 2>&3); then - if [ -n "$RIVEN_FRONTEND_ORIGIN" ]; then - echo -e "${DGN}Using Frontend Origin: ${BGN}$RIVEN_FRONTEND_ORIGIN${CL}" - else - echo -e "${DGN}Using Frontend Origin: ${BGN}Auto-detect (CT IP:3000)${CL}" - fi - else - exit-script - fi - else - RIVEN_FRONTEND_ORIGIN="" - fi - - # Ask which optional media servers to install inside this Riven container. - # Default is that no media servers are installed unless explicitly chosen. - if MEDIA_SELECTION=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --checklist "Select optional media servers to install INSIDE this Riven container.\n\nIf you leave all options OFF, no media servers will be installed." \ - 20 78 6 \ - "plex" "Plex Media Server" OFF \ - "jellyfin" "Jellyfin media server" OFF \ - "emby" "Emby media server" OFF \ - 3>&1 1>&2 2>&3); then - # Initialize all media selections to "no"; we'll flip to "yes" when chosen. - RIVEN_MEDIA_PLEX="no" - RIVEN_MEDIA_JELLYFIN="no" - RIVEN_MEDIA_EMBY="no" - # whiptail returns tokens like: "plex" "emby"; strip quotes and iterate. - MEDIA_SELECTION=$(echo "$MEDIA_SELECTION" | tr -d '"') - for choice in $MEDIA_SELECTION; do - case "$choice" in - plex) RIVEN_MEDIA_PLEX="yes" ;; - jellyfin) RIVEN_MEDIA_JELLYFIN="yes" ;; - emby) RIVEN_MEDIA_EMBY="yes" ;; - esac - done - echo -e "${DGN}Install Plex in this CT: ${BGN}$RIVEN_MEDIA_PLEX${CL}" - echo -e "${DGN}Install Jellyfin in this CT: ${BGN}$RIVEN_MEDIA_JELLYFIN${CL}" - echo -e "${DGN}Install Emby in this CT: ${BGN}$RIVEN_MEDIA_EMBY${CL}" - else - exit-script - fi - - if [[ "$PW" == -password* ]]; then - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - echo -e "${DGN}Enable Root SSH Access: ${BGN}$SSH${CL}" - else - SSH="no" - echo -e "${DGN}Enable Root SSH Access: ${BGN}$SSH${CL}" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERB="yes" - else - VERB="no" - fi - echo -e "${DGN}Enable Verbose Mode: ${BGN}$VERB${CL}" - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - else - clear - header_info - echo -e "${RD}Using Advanced Settings${CL}" - advanced_settings - fi -} - -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - echo -e "${RD}Using Advanced Settings${CL}" - advanced_settings -} - -start() { - if command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a new ${APP} LXC. Proceed?" 10 58); then - clear - echo -e "⚠ User exited script \n" - exit - fi - SPINNER_PID="" - install_script - fi - - if ! command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "Proxmox VE Helper Scripts" --title "${APP} LXC UPDATE" --yesno "Support/Update functions for ${APP} LXC. Proceed?" 10 58); then - clear - echo -e "⚠ User exited script \n" - exit - fi - SPINNER_PID="" - update_script - fi -} - -# This function collects user settings and integrates all the collected information. -build_container() { -# if [ "$VERB" == "yes" ]; then set -x; fi - - if [ "$CT_TYPE" == "1" ]; then - # Unprivileged container - FEATURES="keyctl=1,nesting=1,fuse=1" - else - # Privileged container - FEATURES="nesting=1,fuse=1" - fi - - - TEMP_DIR=$(mktemp -d) - pushd $TEMP_DIR >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -s https://raw.githubusercontent.com/tteck/Proxmox/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -s https://raw.githubusercontent.com/tteck/Proxmox/main/misc/install.func)" - fi - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - export DISABLEIPV6="$DISABLEIP6" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERB" - export SSH_ROOT="${SSH}" - export RIVEN_FRONTEND_ORIGIN - export RIVEN_INSTALL_FRONTEND - export RIVEN_MEDIA_PLEX - export RIVEN_MEDIA_JELLYFIN - export RIVEN_MEDIA_EMBY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="$var_version" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" - -features $FEATURES - -hostname $HN - -tags proxmox-helper-scripts - $SD - $NS - -net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU - -onboot 1 - -cores $CORE_COUNT - -memory $RAM_SIZE - -unprivileged $CT_TYPE - $PW - " - # This executes create_lxc.sh and creates the container and .conf file - bash -c "$(wget -qLO - https://raw.githubusercontent.com/tteck/Proxmox/main/ct/create_lxc.sh)" || exit - - LXC_CONFIG=/etc/pve/lxc/${CTID}.conf - if [ "$CT_TYPE" == "0" ]; then - cat <>$LXC_CONFIG -# USB passthrough -lxc.cgroup2.devices.allow: a -lxc.cap.drop: -lxc.cgroup2.devices.allow: c 188:* rwm -lxc.cgroup2.devices.allow: c 189:* rwm -lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir -lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file -# FUSE support -lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file,rw 0 0 -EOF - else - # For unprivileged containers, we need to ensure FUSE device is accessible - cat <>$LXC_CONFIG -# FUSE support -lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file,rw 0 0 -EOF - fi - - if [ "$CT_TYPE" == "0" ]; then - if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" ]]; then - cat <>$LXC_CONFIG -# VAAPI hardware transcoding -lxc.cgroup2.devices.allow: c 226:0 rwm -lxc.cgroup2.devices.allow: c 226:128 rwm -lxc.cgroup2.devices.allow: c 29:0 rwm -lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file -lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir -lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file -EOF - fi - else - if [[ "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" ]]; then - if [[ -e "/dev/dri/renderD128" ]]; then - if [[ -e "/dev/dri/card0" ]]; then - cat <>$LXC_CONFIG -# VAAPI hardware transcoding -dev0: /dev/dri/card0,gid=44 -dev1: /dev/dri/renderD128,gid=104 -EOF - else - cat <>$LXC_CONFIG -# VAAPI hardware transcoding -dev0: /dev/dri/card1,gid=44 -dev1: /dev/dri/renderD128,gid=104 -EOF - fi - fi - fi - fi - - # This starts the container and executes -install.sh - msg_info "Starting LXC Container" - pct start "$CTID" - msg_ok "Started LXC Container" - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories -http://dl-cdn.alpinelinux.org/alpine/latest-stable/main -http://dl-cdn.alpinelinux.org/alpine/latest-stable/community -#http://dl-cdn.alpinelinux.org/alpine/v3.19/main -#http://dl-cdn.alpinelinux.org/alpine/v3.19/community -EOF' - pct exec "$CTID" -- ash -c "apk add bash >/dev/null" - fi - lxc-attach -n "$CTID" -- bash -c "$(wget -qLO - https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/$var_install.sh)" || exit - -} - -# This function sets the description of the container. -description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - pct set "$CTID" -description "
- - # ${APP} LXC - - -
" - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi -} \ No newline at end of file diff --git a/proxmox/create_lxc.sh b/proxmox/create_lxc.sh deleted file mode 100644 index ff4e918..0000000 --- a/proxmox/create_lxc.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env bash - -# This function sets color variables for formatting output in the terminal -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -GN=$(echo "\033[1;92m") -CL=$(echo "\033[m") -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" -BFR="\\r\\033[K" -HOLD=" " - -# This sets error handling options and defines the error_handler function to handle errors -set -Eeuo pipefail -trap 'error_handler $LINENO "$BASH_COMMAND"' ERR - -# This function handles errors -function error_handler() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID > /dev/null; then kill $SPINNER_PID > /dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" -} - -# This function displays an informational message with a yellow color. -function msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}...${CL}" -} - -# This function displays a success message with a green color. -function msg_ok() { - local msg="$1" - echo -e "\r ${CM} ${GN}${msg}${CL}" -} - -# This function displays a error message with a red color. -function msg_error() { - local msg="$1" - echo -e "\r ${CROSS} ${RD}${msg}${CL}" -} - -# This checks for the presence of valid Container Storage and Template Storage locations -msg_info "Validating Storage" -VALIDCT=$(pvesm status -content rootdir | awk 'NR>1') -if [ -z "$VALIDCT" ]; then - msg_error "Unable to detect a valid Container Storage location." - exit 1 -fi -VALIDTMP=$(pvesm status -content vztmpl | awk 'NR>1') -if [ -z "$VALIDTMP" ]; then - msg_error "Unable to detect a valid Template Storage location." - exit 1 -fi - -# This function is used to select the storage class and determine the corresponding storage content type and label. -function select_storage() { - local CLASS=$1 - local CONTENT - local CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - *) false || exit "Invalid storage class." ;; - esac - - # This Queries all storage locations - local -a MENU - while read -r line; do - local TAG=$(echo $line | awk '{print $1}') - local TYPE=$(echo $line | awk '{printf "%-10s", $2}') - local FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - local ITEM=" Type: $TYPE Free: $FREE " - local OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - local MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - MENU+=("$TAG" "$ITEM" "OFF") - done < <(pvesm status -content $CONTENT | awk 'NR>1') - - # Select storage location - if [ $((${#MENU[@]}/3)) -eq 1 ]; then - printf ${MENU[0]} - else - local STORAGE - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool you would like to use for the ${CONTENT_LABEL,,}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${MENU[@]}" 3>&1 1>&2 2>&3) || exit "Menu aborted." - done - printf $STORAGE - fi -} - -# Test if required variables are set -[[ "${CTID:-}" ]] || exit "You need to set 'CTID' variable." -[[ "${PCT_OSTYPE:-}" ]] || exit "You need to set 'PCT_OSTYPE' variable." - -# Test if ID is valid -[ "$CTID" -ge "100" ] || exit "ID cannot be less than 100." - -# Test if ID is in use -if pct status $CTID &>/dev/null; then - echo -e "ID '$CTID' is already in use." - unset CTID - exit "Cannot use ID that is already in use." -fi - -# Get template storage -TEMPLATE_STORAGE=$(select_storage template) || exit -msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." - -# Get container storage -CONTAINER_STORAGE=$(select_storage container) || exit -msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." - -# Update LXC template list -msg_info "Updating LXC Template List" -pveam update >/dev/null -msg_ok "Updated LXC Template List" - -# Get LXC template string -TEMPLATE_SEARCH=${PCT_OSTYPE}-${PCT_OSVERSION:-} -mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) -[ ${#TEMPLATES[@]} -gt 0 ] || exit "Unable to find a template when searching for '$TEMPLATE_SEARCH'." -TEMPLATE="${TEMPLATES[-1]}" - -# Download LXC template if needed -if ! pveam list $TEMPLATE_STORAGE | grep -q $TEMPLATE; then - msg_info "Downloading LXC Template" - pveam download $TEMPLATE_STORAGE $TEMPLATE >/dev/null || - exit "A problem occured while downloading the LXC template." - msg_ok "Downloaded LXC Template" -fi - -# Combine all options -DEFAULT_PCT_OPTIONS=( - -arch $(dpkg --print-architecture)) - -PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) -[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs $CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}) - -# Create container -msg_info "Creating LXC Container" -pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[@]} >/dev/null || - exit "A problem occured while trying to create container." -msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." \ No newline at end of file diff --git a/proxmox/media-emby.sh b/proxmox/media-emby.sh deleted file mode 100644 index cb97848..0000000 --- a/proxmox/media-emby.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env bash - -# Emby Media Server install helper for the Riven LXC. -# -# This file is intended to be sourced by proxmox/riven-install.sh via: -# source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-emby.sh)" -# -# It assumes the following are already available in the environment: -# - msg_info/msg_ok/msg_error functions (from tteck install.func) -# - CTTYPE, STD (optional), etc. -# -# It MUST NOT call exit; instead it should return non-zero on error so the -# caller can decide whether to continue. - -install_emby_media_server() { - local APT_STD="${STD:-}" - - msg_info "Installing Emby Media Server dependencies" - if ! ${APT_STD} apt-get install -y curl sudo mc gpg jq; then - msg_error "Failed to install Emby dependencies; skipping Emby installation" - return 1 - fi - msg_ok "Installed Emby dependencies" - - msg_info "Setting up Emby hardware acceleration packages" - if ! ${APT_STD} apt-get -y install va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools; then - msg_error "Failed to install Emby GPU/VAAPI packages (continuing without hardware acceleration)" - else - if [[ "${CTTYPE:-1}" == "0" && -d /dev/dri ]]; then - chgrp video /dev/dri || true - chmod 755 /dev/dri || true - chmod 660 /dev/dri/* 2>/dev/null || true - ${APT_STD} adduser "$(id -u -n)" video || true - ${APT_STD} adduser "$(id -u -n)" render || true - fi - msg_ok "Set up Emby hardware acceleration packages" - fi - - local LATEST JSON_FILE - JSON_FILE="/tmp/emby-releases.json" - msg_info "Determining latest Emby release" - if ! curl -fsSL https://api.github.com/repos/MediaBrowser/Emby.Releases/releases/latest -o "$JSON_FILE"; then - msg_error "Failed to query Emby releases API; skipping Emby installation" - rm -f "$JSON_FILE" - return 1 - fi - # Use jq to parse the tag_name from the JSON payload. - LATEST="$(jq -r '.tag_name // empty' "$JSON_FILE" 2>/dev/null || true)" - rm -f "$JSON_FILE" - if [[ -z "${LATEST:-}" ]]; then - msg_error "Could not determine latest Emby release tag; skipping Emby installation" - return 1 - fi - - local DEB_PATH="/tmp/emby-server-deb_${LATEST}_amd64.deb" - msg_info "Downloading Emby Media Server (${LATEST})" - if ! curl -fsSL "https://github.com/MediaBrowser/Emby.Releases/releases/download/${LATEST}/emby-server-deb_${LATEST}_amd64.deb" \ - -o "${DEB_PATH}"; then - msg_error "Failed to download Emby .deb; skipping Emby installation" - return 1 - fi - - msg_info "Installing Emby Media Server (${LATEST})" - if ! ${APT_STD} dpkg -i "${DEB_PATH}"; then - msg_error "dpkg reported issues while installing Emby; attempting to fix dependencies" - if ! ${APT_STD} apt-get install -f -y; then - msg_error "Failed to resolve Emby dependencies" - rm -f "${DEB_PATH}" - return 1 - fi - fi - rm -f "${DEB_PATH}" - - # Emby and Jellyfin both default to port 8096. To avoid a conflict when - # both are installed in the same container, move Emby to 8097. - # Emby's config is typically stored under /var/lib/emby/config/system.xml, - # but it may not exist immediately. Give it a short window to appear. - local EMBY_CONFIG="/var/lib/emby/config/system.xml" - local wait_secs=0 - if systemctl list-unit-files | grep -q '^emby-server\.service'; then - systemctl start emby-server 2>/dev/null || true - fi - while [ "$wait_secs" -lt 10 ] && [ ! -f "$EMBY_CONFIG" ]; do - sleep 1 - wait_secs=$((wait_secs + 1)) - done - if [ -f "$EMBY_CONFIG" ]; then - msg_info "Reconfiguring Emby HTTP port to 8097 to avoid Jellyfin conflict" - sed -i \ - -e 's#8096#8097#' \ - -e 's#8096#8097#' \ - "$EMBY_CONFIG" 2>/dev/null || true - systemctl restart emby-server 2>/dev/null || true - fi - - # Adjust ssl-cert/render groups for Emby (best-effort). - if [[ "${CTTYPE:-1}" == "0" ]]; then - sed -i -e 's/^ssl-cert:x:104:emby$/render:x:104:root,emby/' \ - -e 's/^render:x:108:root$/ssl-cert:x:108:emby/' /etc/group 2>/dev/null || true - else - sed -i -e 's/^ssl-cert:x:104:emby$/render:x:104:emby/' \ - -e 's/^render:x:108:$/ssl-cert:x:108:/' /etc/group 2>/dev/null || true - fi - - # Ensure Emby can read Riven's VFS by joining the riven group if it exists. - if getent group riven >/dev/null 2>&1; then - usermod -aG riven emby 2>/dev/null || true - fi - - msg_ok "Installed Emby Media Server (${LATEST})" -} - diff --git a/proxmox/media-jellyfin.sh b/proxmox/media-jellyfin.sh deleted file mode 100644 index 3b03af1..0000000 --- a/proxmox/media-jellyfin.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash - -# Jellyfin Media Server install helper for the Riven LXC. -# -# This file is intended to be sourced by proxmox/riven-install.sh via: -# source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-jellyfin.sh)" -# -# It assumes the following are already available in the environment: -# - msg_info/msg_ok/msg_error functions (from tteck install.func) -# - CTTYPE, PCT_OSTYPE, STD (optional), etc. -# -# It MUST NOT call exit; instead it should return non-zero on error so the -# caller can decide whether to continue. - -install_jellyfin_media_server() { - local APT_STD="${STD:-}" - local INSTALL_SCRIPT="/tmp/jellyfin-install-debuntu.sh" - - # Ensure curl is available (normally installed earlier, but be safe). - if ! command -v curl >/dev/null 2>&1; then - msg_info "Installing curl for Jellyfin installer" - if ! ${APT_STD} apt-get install -y curl; then - msg_error "Failed to install curl; skipping Jellyfin installation" - return 1 - fi - fi - - msg_info "Downloading official Jellyfin installer script" - if ! curl -fsSL https://repo.jellyfin.org/install-debuntu.sh -o "${INSTALL_SCRIPT}"; then - msg_error "Failed to download Jellyfin installer script; skipping Jellyfin installation" - return 1 - fi - - msg_info "Running official Jellyfin installer script" - if ! SKIP_CONFIRM=true bash "${INSTALL_SCRIPT}" >/dev/null 2>&1; then - msg_error "Jellyfin installer script failed; skipping Jellyfin installation" - rm -f "${INSTALL_SCRIPT}" - return 1 - fi - rm -f "${INSTALL_SCRIPT}" - - # Best-effort hardware acceleration tweaks (optional). - msg_info "Setting up Jellyfin hardware acceleration packages" - if ! ${APT_STD} apt-get -y install va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools; then - msg_error "Failed to install Jellyfin GPU/VAAPI packages (continuing without hardware acceleration)" - else - if [[ "${CTTYPE:-1}" == "0" && -d /dev/dri ]]; then - chgrp video /dev/dri || true - chmod 755 /dev/dri || true - chmod 660 /dev/dri/* 2>/dev/null || true - ${APT_STD} adduser "$(id -u -n)" video || true - ${APT_STD} adduser "$(id -u -n)" render || true - fi - msg_ok "Set up Jellyfin hardware acceleration packages" - fi - - # Ensure permissions are sane; installer already does this, so best-effort. - chown -R jellyfin:adm /etc/jellyfin 2>/dev/null || true - systemctl restart jellyfin 2>/dev/null || true - - # Ensure Jellyfin can read Riven's VFS by joining the riven group if it exists. - if getent group riven >/dev/null 2>&1; then - usermod -aG riven jellyfin 2>/dev/null || true - fi - - msg_ok "Installed Jellyfin Media Server" -} - diff --git a/proxmox/media-plex.sh b/proxmox/media-plex.sh deleted file mode 100644 index 21913aa..0000000 --- a/proxmox/media-plex.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env bash - -# Plex Media Server install helper for the Riven LXC. -# -# This file is intended to be sourced by proxmox/riven-install.sh via: -# source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-plex.sh)" -# -# It assumes the following are already available in the environment: -# - msg_info/msg_ok/msg_error functions (from tteck install.func) -# - CTTYPE, STD (optional), etc. -# -# It MUST NOT call exit; instead it should return non-zero on error so the -# caller can decide whether to continue. - -install_plex_media_server() { - local APT_STD="${STD:-}" - - msg_info "Installing Plex Media Server dependencies" - if ! ${APT_STD} apt-get install -y curl sudo mc gpg; then - msg_error "Failed to install Plex dependencies; skipping Plex installation" - return 1 - fi - msg_ok "Installed Plex Media Server dependencies" - - msg_info "Setting up Plex hardware acceleration packages" - if ! ${APT_STD} apt-get -y install va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools; then - msg_error "Failed to install Plex GPU/VAAPI packages (continuing without hardware acceleration)" - else - if [[ "${CTTYPE:-1}" == "0" && -d /dev/dri ]]; then - chgrp video /dev/dri || true - chmod 755 /dev/dri || true - chmod 660 /dev/dri/* 2>/dev/null || true - ${APT_STD} adduser "$(id -u -n)" video || true - ${APT_STD} adduser "$(id -u -n)" render || true - fi - msg_ok "Set up Plex hardware acceleration packages" - fi - - msg_info "Setting up Plex Media Server repository" - if ! curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key \ - >/usr/share/keyrings/PlexSign.asc; then - msg_error "Failed to download Plex signing key; skipping Plex installation" - return 1 - fi - if ! echo "deb [signed-by=/usr/share/keyrings/PlexSign.asc] https://downloads.plex.tv/repo/deb/ public main" \ - >/etc/apt/sources.list.d/plexmediaserver.list; then - msg_error "Failed to configure Plex apt source; skipping Plex installation" - return 1 - fi - msg_ok "Configured Plex Media Server repository" - - msg_info "Installing Plex Media Server" - if ! ${APT_STD} apt-get update; then - msg_error "apt-get update failed before Plex installation; skipping Plex" - return 1 - fi - if ! ${APT_STD} apt-get -o Dpkg::Options::="--force-confold" install -y plexmediaserver; then - msg_error "Failed to install Plex Media Server package" - return 1 - fi - - # Adjust ssl-cert/render groups for Plex (best-effort, do not fail install). - if [[ "${CTTYPE:-1}" == "0" ]]; then - sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:root,plex/' \ - -e 's/^render:x:108:root$/ssl-cert:x:108:plex/' /etc/group 2>/dev/null || true - else - sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:plex/' \ - -e 's/^render:x:108:$/ssl-cert:x:108:/' /etc/group 2>/dev/null || true - fi - - # Ensure Plex can read Riven's VFS by joining the riven group if it exists. - if getent group riven >/dev/null 2>&1; then - usermod -aG riven plex 2>/dev/null || true - fi - - msg_ok "Installed Plex Media Server" -} - diff --git a/proxmox/riven-install.sh b/proxmox/riven-install.sh deleted file mode 100644 index 1f75014..0000000 --- a/proxmox/riven-install.sh +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env bash - -# Baremetal Riven installer for Debian LXC (unprivileged) -# - Installs system dependencies (Python, Node, Postgres, FUSE, build tools, ffmpeg, etc.) -# - Configures FUSE and Python capabilities for RivenVFS -# - Sets up local PostgreSQL -# - Installs Riven backend (Python/uv) and frontend (Node/pnpm) -# - Creates env config in /etc/riven and systemd services for both components - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -export DEBIAN_FRONTEND=noninteractive - -# Determine whether to install the Riven frontend in this container. -# Default is "yes" unless overridden by the host helper via -# RIVEN_INSTALL_FRONTEND (values like yes/no/true/false/1/0). -INSTALL_FRONTEND_RAW="${RIVEN_INSTALL_FRONTEND:-yes}" -INSTALL_FRONTEND_RAW="$(echo "$INSTALL_FRONTEND_RAW" | tr '[:upper:]' '[:lower:]')" -if [[ "$INSTALL_FRONTEND_RAW" == "yes" || "$INSTALL_FRONTEND_RAW" == "true" || "$INSTALL_FRONTEND_RAW" == "1" ]]; then - INSTALL_FRONTEND="yes" -else - INSTALL_FRONTEND="no" -fi - -# ------------------------------------------------------------ -# Optional media server installers inside this Riven container -# -# The heavy install logic for Plex/Jellyfin/Emby lives in separate -# proxmox/media-*.sh scripts. We fetch and source those on demand so -# this main installer stays tidy. Errors in media server installs -# must never abort the core Riven installation. -# ------------------------------------------------------------ - -run_media_installer() { - local NAME="$1" URL="$2" FUNC="$3" - local SRC - - if ! SRC="$(curl -fsSL "$URL")"; then - msg_error "Failed to download ${NAME} installer script; skipping ${NAME} installation" - # Do not propagate non-zero status; media installs are optional. - return 0 - fi - - if ! source /dev/stdin <<<"$SRC"; then - msg_error "Failed to load ${NAME} installer script; skipping ${NAME} installation" - # Do not propagate non-zero status; media installs are optional. - return 0 - fi - - if ! "$FUNC"; then - msg_error "${NAME} installation encountered an error; continuing without ${NAME}" - # Do not propagate non-zero status; media installs are optional. - return 0 - fi - - # Record successful installation so the host helper can show - # accurate media server URLs in its completion message. - local MEDIA_FILE="/etc/riven/media-servers.txt" - local ID - ID=$(echo "$NAME" | tr '[:upper:] ' '[:lower:]-') - mkdir -p /etc/riven - if ! grep -qx "$ID" "$MEDIA_FILE" 2>/dev/null; then - printf '%s\n' "$ID" >>"$MEDIA_FILE" - fi - - return 0 -} - -install_selected_media_servers() { - # Use host-provided selections (RIVEN_MEDIA_*) to decide what to install. - # Default for all is "no", so if the user did not explicitly select a - # media server on the host, nothing is installed here. - local WANT_PLEX WANT_JELLYFIN WANT_EMBY - WANT_PLEX="${RIVEN_MEDIA_PLEX:-no}" - WANT_JELLYFIN="${RIVEN_MEDIA_JELLYFIN:-no}" - WANT_EMBY="${RIVEN_MEDIA_EMBY:-no}" - - # Normalize to lowercase for robustness. - WANT_PLEX=$(echo "$WANT_PLEX" | tr '[:upper:]' '[:lower:]') - WANT_JELLYFIN=$(echo "$WANT_JELLYFIN" | tr '[:upper:]' '[:lower:]') - WANT_EMBY=$(echo "$WANT_EMBY" | tr '[:upper:]' '[:lower:]') - - if [[ "$WANT_PLEX" != "yes" && "$WANT_JELLYFIN" != "yes" && "$WANT_EMBY" != "yes" ]]; then - msg_info "Skipping media server installation (none selected from host)" - rm -f /etc/riven/media-servers.txt 2>/dev/null || true - return - fi - - if [[ "$WANT_PLEX" == "yes" ]]; then - run_media_installer \ - "Plex" \ - "https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-plex.sh" \ - install_plex_media_server - fi - if [[ "$WANT_JELLYFIN" == "yes" ]]; then - run_media_installer \ - "Jellyfin" \ - "https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-jellyfin.sh" \ - install_jellyfin_media_server - fi - if [[ "$WANT_EMBY" == "yes" ]]; then - run_media_installer \ - "Emby" \ - "https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/media-emby.sh" \ - install_emby_media_server - fi -} - -msg_info "Installing Dependencies" -$STD apt-get update -$STD apt-get install -y \ - curl sudo mc git ffmpeg vim whiptail \ - python3 python3-venv python3-dev build-essential libffi-dev libpq-dev libfuse3-dev pkg-config \ - fuse3 libcap2-bin ca-certificates openssl \ - postgresql postgresql-contrib postgresql-client -msg_ok "Installed Dependencies" - -msg_info "Configuring FUSE" -echo 'user_allow_other' > /etc/fuse.conf -msg_ok "Configured FUSE" - -msg_info "Configuring Python capabilities for FUSE" -PY_BIN=$(command -v python3 || true) -if [ -n "$PY_BIN" ]; then - setcap cap_sys_admin+ep "$PY_BIN" 2>/dev/null || true -fi -msg_ok "Configured Python capabilities" - -if [ "$INSTALL_FRONTEND" = "yes" ]; then - msg_info "Installing Node.js (24.x) and pnpm" - curl -fsSL https://deb.nodesource.com/setup_24.x | bash - >/dev/null 2>&1 || { - msg_error "Failed to configure NodeSource repository for Node.js" - exit 1 - } - $STD apt-get install -y nodejs - npm install -g pnpm >/dev/null 2>&1 || { - msg_error "Failed to install pnpm globally" - exit 1 - } - msg_ok "Installed Node.js and pnpm" -else - msg_info "Skipping Node.js/pnpm install (frontend disabled)" - msg_ok "Frontend components will not be installed in this container" -fi - -msg_info "Configuring PostgreSQL" -$STD systemctl enable postgresql -$STD systemctl start postgresql -if ! sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='riven'" | grep -q 1; then - sudo -u postgres psql -c "CREATE DATABASE riven;" >/dev/null 2>&1 || true -fi -sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'postgres';" >/dev/null 2>&1 || true -msg_ok "Configured PostgreSQL" - -msg_info "Creating Riven user and directories" -if ! id -u riven >/dev/null 2>&1; then - useradd -r -d /riven -s /usr/sbin/nologin riven || true -fi - -# Core application directories -mkdir -p /riven /riven/data /mount /mnt/riven /etc/riven -if [ "$INSTALL_FRONTEND" = "yes" ]; then - mkdir -p /opt/riven-frontend -fi -chown -R riven:riven /riven /riven/data /mount /mnt/riven -if [ "$INSTALL_FRONTEND" = "yes" ]; then - chown -R riven:riven /opt/riven-frontend -fi - -# Make the library-related mountpoints world-readable so media from other -# containers (e.g. Plex/Jellyfin/Emby) can be shared via /mnt/riven. -chmod 755 /riven /mount /mnt/riven || true - -# Keep internal data more restricted; only the riven user should need this. -chmod 700 /riven/data || true - -# Cache directory for RivenVFS (not shared across LXCs) -mkdir -p /dev/shm/riven-cache -chown riven:riven /dev/shm/riven-cache || true -chmod 700 /dev/shm/riven-cache || true -msg_ok "Created Riven user and directories" - -msg_info "Installing uv package manager" -curl -LsSf https://astral.sh/uv/install.sh | sh >/dev/null 2>&1 || true -export PATH="${HOME}/.local/bin:$PATH" -UV_BIN="${HOME}/.local/bin/uv" -if [ ! -x "$UV_BIN" ]; then - msg_error "uv was not installed correctly" - exit 1 -fi -install -m 755 "$UV_BIN" /usr/local/bin/uv >/dev/null 2>&1 || true -UV_BIN="/usr/local/bin/uv" -msg_ok "Installed uv" - -msg_info "Installing Riven backend" -if [ ! -d /riven/src ]; then - git clone https://github.com/rivenmedia/riven.git /riven/src >/dev/null 2>&1 || { - msg_error "Failed to clone Riven backend repository" - exit 1 - } -else - cd /riven/src - git pull --rebase >/dev/null 2>&1 || true -fi -chown -R riven:riven /riven/src || true -cd /riven/src -# Ensure project virtual environment exists -if [ ! -d .venv ]; then - sudo -u riven -H "$UV_BIN" venv >/dev/null 2>&1 || { - msg_error "Failed to create Python virtual environment with uv" - exit 1 - } -fi - -VENV_PY_BIN="/riven/src/.venv/bin/python3" -if [ -x "$VENV_PY_BIN" ]; then - setcap cap_sys_admin+ep "$VENV_PY_BIN" 2>/dev/null || true -fi - -sudo -u riven -H "$UV_BIN" sync --no-dev --frozen >/dev/null 2>&1 || \ - sudo -u riven -H "$UV_BIN" sync --no-dev >/dev/null 2>&1 || { - msg_error "Failed to install Riven backend dependencies with uv" - exit 1 -} -chown -R riven:riven /riven -msg_ok "Installed Riven backend" - -msg_info "Configuring Riven backend environment" - -BACKEND_ENV="/etc/riven/backend.env" -FRONTEND_ENV="/etc/riven/frontend.env" -mkdir -p /etc/riven - -# Reuse existing API key if present to keep backend and frontend in sync -if [ -f "$BACKEND_ENV" ]; then - RIVEN_API_KEY=$(grep '^RIVEN_API_KEY=' "$BACKEND_ENV" | head -n1 | cut -d= -f2- || true) -fi -if [ -z "${RIVEN_API_KEY:-}" ]; then - RIVEN_API_KEY=$(openssl rand -hex 16) -fi - -if [ ! -f "$BACKEND_ENV" ]; then - cat <"$BACKEND_ENV" -RIVEN_API_KEY=$RIVEN_API_KEY -RIVEN_DATABASE_HOST=postgresql+psycopg2://postgres:postgres@127.0.0.1/riven -RIVEN_FILESYSTEM_MOUNT_PATH=/mount -RIVEN_UPDATERS_LIBRARY_PATH=/mnt/riven -RIVEN_FILESYSTEM_CACHE_DIR=/dev/shm/riven-cache -EOF - chown riven:riven "$BACKEND_ENV" - chmod 600 "$BACKEND_ENV" -fi -msg_ok "Configured Riven backend environment" - -msg_info "Creating systemd service for Riven backend" -cat <<'EOF' >/etc/systemd/system/riven-backend.service -[Unit] -Description=Riven Backend -After=network-online.target postgresql.service -Wants=network-online.target - -[Service] -Type=simple -User=riven -Group=riven -WorkingDirectory=/riven/src -EnvironmentFile=/etc/riven/backend.env -Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin -ExecStart=/usr/local/bin/uv run python src/main.py -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl daemon-reload -$STD systemctl enable riven-backend.service -$STD systemctl restart riven-backend.service -msg_ok "Created systemd service for Riven backend" - -if [ "$INSTALL_FRONTEND" = "yes" ]; then - msg_info "Installing Riven frontend" - if [ ! -d /opt/riven-frontend/.git ]; then - rm -rf /opt/riven-frontend - git clone https://github.com/rivenmedia/riven-frontend.git /opt/riven-frontend >/dev/null 2>&1 || { - msg_error "Failed to clone Riven frontend repository" - exit 1 - } - else - cd /opt/riven-frontend - git pull --rebase >/dev/null 2>&1 || true - fi - cd /opt/riven-frontend - if command -v pnpm >/dev/null 2>&1; then - if ! pnpm install >/dev/null 2>&1; then - msg_error "pnpm install failed while installing Riven frontend" - exit 1 - fi - if ! pnpm run build >/dev/null 2>&1; then - msg_error "pnpm run build failed while building Riven frontend" - exit 1 - fi - pnpm prune --prod >/dev/null 2>&1 || true - else - msg_error "pnpm is not available; cannot build Riven frontend" - exit 1 - fi - chown -R riven:riven /opt/riven-frontend - msg_ok "Installed Riven frontend" - - msg_info "Configuring Riven frontend environment" - AUTH_SECRET=$(openssl rand -base64 32) - - # If the host script provided a specific origin (e.g. a reverse proxy URL), use it. - # Otherwise, fall back to auto-detecting the CT's primary IPv4 and using :3000. - if [ -n "${RIVEN_FRONTEND_ORIGIN:-}" ]; then - FRONTEND_ORIGIN_DEFAULT="$RIVEN_FRONTEND_ORIGIN" - else - CT_IP=$(ip -4 -o addr show scope global 2>/dev/null | awk 'NR==1{print $4}' | cut -d/ -f1) - if [ -z "$CT_IP" ]; then - CT_IP="127.0.0.1" - fi - FRONTEND_ORIGIN_DEFAULT="http://$CT_IP:3000" - fi - - if [ ! -f "$FRONTEND_ENV" ]; then - cat <"$FRONTEND_ENV" -DATABASE_URL=/riven/data/riven.db -BACKEND_URL=http://127.0.0.1:8080 -BACKEND_API_KEY=$RIVEN_API_KEY -AUTH_SECRET=$AUTH_SECRET -ORIGIN=$FRONTEND_ORIGIN_DEFAULT -EOF - chown root:root "$FRONTEND_ENV" - chmod 600 "$FRONTEND_ENV" - fi - msg_ok "Configured Riven frontend environment" - - msg_info "Creating systemd service for Riven frontend" - cat <<'EOF' >/etc/systemd/system/riven-frontend.service -[Unit] -Description=Riven Frontend -After=network-online.target riven-backend.service -Wants=network-online.target - -[Service] -Type=simple -User=riven -Group=riven -WorkingDirectory=/opt/riven-frontend -EnvironmentFile=/etc/riven/frontend.env -ExecStart=ORIGIN=${ORIGIN} /usr/bin/node /opt/riven-frontend/build -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - - systemctl daemon-reload - $STD systemctl enable riven-frontend.service - $STD systemctl restart riven-frontend.service - msg_ok "Created systemd service for Riven frontend" -else - msg_info "Skipping Riven frontend installation (disabled via installer)" - msg_ok "Only the Riven backend API was installed in this container" -fi - -motd_ssh -customize - -install_selected_media_servers - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/proxmox/riven.sh b/proxmox/riven.sh deleted file mode 100644 index 1ffd908..0000000 --- a/proxmox/riven.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bash - -# Proxmox helper script to create a Riven LXC (Debian 12, unprivileged) - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -BUILD_FUNC_LOCAL="${SCRIPT_DIR}/build.func" -if [ -f "$BUILD_FUNC_LOCAL" ]; then - source "$BUILD_FUNC_LOCAL" -else - # Fallback to remote build.func when running directly via curl from GitHub - source <(curl -s https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/build.func) -fi - -function header_info { -clear -cat <<'EOF' -.______ __ ____ ____ _______ .__ __. -| _ \ | | \ \ / / | ____|| \ | | -| |_) | | | \ \/ / | |__ | \| | -| / | | \ / | __| | . ` | -| |\ \----.| | \ / | |____ | |\ | -| _| `._____||__| \__/ |_______||__| \__| - -Riven LXC Helper -EOF -} - -header_info -echo -e "Loading..." - -APP="Riven" -var_disk="40" -var_cpu="4" -var_ram="8192" -var_os="debian" -var_version="12" - -variables -color -catch_errors - -function default_settings() { - CT_TYPE="1" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - DISK_SIZE="$var_disk" - CORE_COUNT="$var_cpu" - RAM_SIZE="$var_ram" - BRG="vmbr0" - NET="dhcp" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - VERB="no" - RIVEN_INSTALL_FRONTEND="yes" - RIVEN_FRONTEND_ORIGIN="" - echo_default -} - -function update_script() { - msg_error "No ${APP} update script is available yet." - exit 1 -} - -start -build_container -description - -RIVEN_CT_ID="${CTID:-}" -if [ -z "$RIVEN_CT_ID" ]; then - RIVEN_CT_ID="" -fi - -RIVEN_CT_IP="" -if command -v pct >/dev/null 2>&1 && [ -n "${CTID:-}" ]; then - RIVEN_CT_IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1) -fi -if [ -z "$RIVEN_CT_IP" ]; then - RIVEN_CT_IP="" -fi - -msg_ok "Completed Successfully!\n" - -echo -e "Riven container ID: ${BL}${RIVEN_CT_ID}${CL}" -echo -e "Riven container IP: ${BL}${RIVEN_CT_IP}${CL}\n" - -echo -e "${APP} backend (API) URL:" -echo -e " ${BL}http://${RIVEN_CT_IP}:8080/scalar${CL}\n" - -if [ "${RIVEN_INSTALL_FRONTEND:-yes}" != "no" ]; then - echo -e "${APP} frontend (web UI) URL:" - echo -e " ${BL}http://${RIVEN_CT_IP}:3000${CL}\n" -else - echo -e "${APP} frontend was ${RD}not installed${CL} in this container." - echo -e "You can host it elsewhere and point it at the backend URL above.\n" -fi - -# If the in-container installer recorded any media servers, show their -# default URLs here. The file is managed by proxmox/riven-install.sh. -MEDIA_SERVERS="" -if command -v pct >/dev/null 2>&1 && [ -n "${CTID:-}" ]; then - MEDIA_SERVERS="$(pct exec "$CTID" -- bash -c 'if [ -f /etc/riven/media-servers.txt ]; then cat /etc/riven/media-servers.txt; fi' 2>/dev/null || true)" -fi - -if [ -n "$MEDIA_SERVERS" ]; then - MEDIA_SERVERS_ONELINE=$(printf '%s' "$MEDIA_SERVERS" | tr '\n' ' ' | sed -e 's/[[:space:]]\+$//') - echo -e "Optional media servers installed in this container: ${BL}${MEDIA_SERVERS_ONELINE}${CL}\n" - while IFS= read -r srv; do - case "$srv" in - plex) - echo -e " Plex: ${BL}http://${RIVEN_CT_IP}:32400/web${CL}" - ;; - jellyfin) - echo -e " Jellyfin: ${BL}http://${RIVEN_CT_IP}:8096${CL}" - ;; - emby) - echo -e " Emby: ${BL}http://${RIVEN_CT_IP}:8097${CL}" - ;; - *) - ;; - esac - done <<<"$MEDIA_SERVERS" - echo -else - echo -e "No optional media servers were selected for this container.\n" -fi - -echo -e "Backend settings file inside the container:" -echo -e " ${BL}/riven/src/data/settings.json${CL}\n" From c553786323453f04f1bd6b2bf756d358141fc434 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 19:45:08 -0500 Subject: [PATCH 002/154] Add files via upload --- proxmox/README.md | 54 +++++++++++++ proxmox/install.sh | 133 +++++++++++++++++++++++++++++++++ proxmox/lxc/docker-compose.yml | 123 ++++++++++++++++++++++++++++++ proxmox/lxc/lxc-bootstrap.sh | 33 ++++++++ proxmox/lxc/lxc-create.sh | 111 +++++++++++++++++++++++++++ proxmox/lxc/riven-install.sh | 115 ++++++++++++++++++++++++++++ proxmox/lxc/upgrade.sh | 18 +++++ 7 files changed, 587 insertions(+) create mode 100644 proxmox/README.md create mode 100644 proxmox/install.sh create mode 100644 proxmox/lxc/docker-compose.yml create mode 100644 proxmox/lxc/lxc-bootstrap.sh create mode 100644 proxmox/lxc/lxc-create.sh create mode 100644 proxmox/lxc/riven-install.sh create mode 100644 proxmox/lxc/upgrade.sh diff --git a/proxmox/README.md b/proxmox/README.md new file mode 100644 index 0000000..11d9159 --- /dev/null +++ b/proxmox/README.md @@ -0,0 +1,54 @@ +# Riven Proxmox LXC Installer (Docker) + +## Run on Proxmox host +Unzip on the Proxmox host, then: + +```bash +cd riven-proxmox-lxc-installer +bash install.sh +``` + +## What it does +- Creates an **unprivileged Debian 12** LXC +- Enables **nesting + keyctl + fuse** +- Passes **/dev/fuse** into the CT +- Optionally passes **/dev/dri** (GPU) if you select it +- Installs **Docker + Docker Compose plugin** inside the CT +- Deploys **Riven backend + frontend + PostgreSQL** +- Supports optional media servers via compose profiles: + - Jellyfin / Plex / Emby + +## Access +- Backend: `http://:8080` +- Frontend: `http://:3000` + +Get CT IP: +```bash +pct exec -- hostname -I +``` + +## Required configuration +🚨 **YOU MUST CONFIGURE A MEDIA SERVER OR RIVEN WONT START** 🚨 + +Edit: +- `/mnt/riven/backend/settings.json` + +Then restart: +```bash +pct exec -- docker restart riven +``` + +## Optional media servers +Inside the CT: +```bash +cd /opt/riven +docker compose --profile jellyfin up -d +docker compose --profile plex up -d +docker compose --profile emby up -d +``` + +## Upgrade +Inside the CT: +```bash +/opt/riven/upgrade.sh +``` diff --git a/proxmox/install.sh b/proxmox/install.sh new file mode 100644 index 0000000..a94b6cc --- /dev/null +++ b/proxmox/install.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Riven Proxmox LXC Installer (Docker-based) +# Run on Proxmox host as root: +# bash install.sh + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LXC_DIR="$SCRIPT_DIR/lxc" + +require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: Missing required command: $1"; exit 1; }; } + +require_cmd pveversion +require_cmd pct +require_cmd pvesm + +# --- Proxmox version check (8.1+) --- +PVE_VER_RAW="$(pveversion | head -n1)" +PVE_VER="$(echo "$PVE_VER_RAW" | sed -nE 's/.*pve-manager\/([0-9]+)\.([0-9]+).*/\1.\2/p')" +if [[ -z "${PVE_VER}" ]]; then + echo "WARNING: Could not parse Proxmox version from: $PVE_VER_RAW" +else + MAJ="${PVE_VER%.*}"; MIN="${PVE_VER#*.}" + if (( MAJ < 8 )) || (( MAJ == 8 && MIN < 1 )); then + echo "ERROR: Proxmox 8.1+ required. Detected: $PVE_VER_RAW" + exit 1 + fi +fi + +echo "== Riven Proxmox LXC (Docker) Installer ==" + +# --- Defaults --- +DEFAULT_CTID="" +while :; do + CANDIDATE=$(( (RANDOM % 900) + 100 )) + if ! pct status "$CANDIDATE" >/dev/null 2>&1; then DEFAULT_CTID="$CANDIDATE"; break; fi +done + +read -rp "Container ID (CTID) [${DEFAULT_CTID}]: " CTID +CTID="${CTID:-$DEFAULT_CTID}" + +if pct status "$CTID" >/dev/null 2>&1; then + echo "ERROR: CTID $CTID already exists." + exit 1 +fi + +read -rp "Hostname [riven]: " HOSTNAME +HOSTNAME="${HOSTNAME:-riven}" + +read -rp "Rootfs disk size (GB) [16]: " DISK_GB +DISK_GB="${DISK_GB:-16}" + +read -rp "Memory (MB) [4096]: " MEM_MB +MEM_MB="${MEM_MB:-4096}" + +read -rp "CPU cores [4]: " CORES +CORES="${CORES:-4}" + +# Storage for rootfs (default: first local-lvm, else local) +DEFAULT_STORAGE="$(pvesm status | awk 'NR>1{print $1}' | head -n1)" +DEFAULT_STORAGE="${DEFAULT_STORAGE:-local}" +read -rp "Proxmox storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE +STORAGE="${STORAGE:-$DEFAULT_STORAGE}" + +# Network: DHCP by default on vmbr0 +read -rp "Bridge [vmbr0]: " BRIDGE +BRIDGE="${BRIDGE:-vmbr0}" +read -rp "Network config (dhcp or static like 192.168.1.50/24,gw=192.168.1.1) [dhcp]: " NETCFG +NETCFG="${NETCFG:-dhcp}" + +# Choose static or DHCP formatting for pct +NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" +if [[ "${NETCFG}" == "dhcp" ]]; then + NET0="name=eth0,bridge=${BRIDGE},ip=dhcp" +fi + +# Optional: mount host path into LXC for media VFS (recommended) +echo +echo "Optional: bind-mount a host path into the LXC as /mnt/riven (recommended for persistence)." +echo "Examples:" +echo " /srv/riven (ZFS/LVM dataset)" +echo " /mnt/rivenhost (separate disk)" +read -rp "Host path to mount into CT as /mnt/riven (leave blank to use CT disk only): " HOST_RIVEN_PATH + +# GPU passthrough (Intel/AMD /dev/dri; NVIDIA users may still want /dev/dri for iGPU) +GPU_ENABLE="no" +if [[ -d /dev/dri ]]; then + read -rp "Detected /dev/dri on host. Pass GPU device into LXC? [Y/n]: " GPU_ANS + GPU_ANS="${GPU_ANS:-Y}" + if [[ "${GPU_ANS}" =~ ^[Yy]$ ]]; then GPU_ENABLE="yes"; fi +else + read -rp "No /dev/dri detected on host. Pass GPU anyway? [y/N]: " GPU_ANS + GPU_ANS="${GPU_ANS:-N}" + if [[ "${GPU_ANS}" =~ ^[Yy]$ ]]; then GPU_ENABLE="yes"; fi +fi + +# --- Create LXC --- +bash "$LXC_DIR/lxc-create.sh" \ + --ctid "$CTID" \ + --hostname "$HOSTNAME" \ + --storage "$STORAGE" \ + --disk-gb "$DISK_GB" \ + --mem-mb "$MEM_MB" \ + --cores "$CORES" \ + --net0 "$NET0" \ + --host-riven-path "$HOST_RIVEN_PATH" \ + --gpu "$GPU_ENABLE" + +# --- Push scripts into CT --- +echo "Pushing installer files into CT $CTID..." +pct push "$CTID" "$LXC_DIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 +pct push "$CTID" "$LXC_DIR/riven-install.sh" /root/riven-install.sh -perms 0755 +pct push "$CTID" "$LXC_DIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 +pct push "$CTID" "$LXC_DIR/upgrade.sh" /root/upgrade.sh -perms 0755 + +# --- Bootstrap docker --- +echo "Bootstrapping Docker inside CT..." +pct exec "$CTID" -- bash /root/lxc-bootstrap.sh + +# --- Install & run Riven stack --- +echo "Installing and starting Riven stack inside CT..." +pct exec "$CTID" -- bash /root/riven-install.sh + +# --- Print access info --- +echo +echo "✅ Done." +echo "Find CT IP via: pct exec $CTID -- hostname -I" +echo "Backend : http://:8080" +echo "Frontend : http://:3000" +echo +echo "To view logs:" +echo " pct exec $CTID -- docker logs -f riven" +echo " pct exec $CTID -- docker logs -f riven-db" diff --git a/proxmox/lxc/docker-compose.yml b/proxmox/lxc/docker-compose.yml new file mode 100644 index 0000000..8316412 --- /dev/null +++ b/proxmox/lxc/docker-compose.yml @@ -0,0 +1,123 @@ +version: "3.9" + +services: + riven-db: + image: postgres:17-alpine + container_name: riven-db + restart: unless-stopped + env_file: [.env] + environment: + PGDATA: /var/lib/postgresql/data/pgdata + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - riven-pg-data:/var/lib/postgresql/data/pgdata + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + riven: + image: spoked/riven:dev + container_name: riven + restart: unless-stopped + shm_size: 1024m + ports: + - "8080:8080" + tty: true + cap_add: + - SYS_ADMIN + security_opt: + - apparmor:unconfined + devices: + - /dev/fuse + env_file: [.env] + environment: + TZ: ${TZ} + PUID: 1000 + PGID: 1000 + RIVEN_FORCE_ENV: "true" + RIVEN_API_KEY: ${BACKEND_API_KEY} + RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} + RIVEN_FILESYSTEM_MOUNT_PATH: /mount + RIVEN_UPDATERS_LIBRARY_PATH: /mnt/riven + RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache + volumes: + - /mnt/riven/backend:/riven/data + - /mnt/riven/mount:/mount:rshared,z + depends_on: + riven-db: + condition: service_healthy + healthcheck: + test: curl -s http://localhost:8080 >/dev/null || exit 1 + interval: 30s + timeout: 10s + retries: 10 + + riven-frontend: + image: spoked/riven-frontend:dev + container_name: riven-frontend + restart: unless-stopped + ports: + - "3000:3000" + env_file: [.env] + environment: + TZ: ${TZ} + DATABASE_URL: /riven/data/riven.db + BACKEND_URL: http://riven:8080 + BACKEND_API_KEY: ${BACKEND_API_KEY} + AUTH_SECRET: ${AUTH_SECRET} + ORIGIN: http://localhost:3000 + volumes: + - riven-frontend-data:/riven/data + depends_on: + riven: + condition: service_healthy + + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + profiles: ["jellyfin"] + restart: unless-stopped + ports: + - "8096:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/jellyfin:/config + + plex: + image: lscr.io/linuxserver/plex + container_name: plex + profiles: ["plex"] + restart: unless-stopped + network_mode: host + environment: + - PUID=1000 + - PGID=1000 + - VERSION=docker + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/plex:/config + + emby: + image: emby/embyserver + container_name: emby + profiles: ["emby"] + restart: unless-stopped + ports: + - "8097:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/emby:/config + +volumes: + riven-frontend-data: + riven-pg-data: diff --git a/proxmox/lxc/lxc-bootstrap.sh b/proxmox/lxc/lxc-bootstrap.sh new file mode 100644 index 0000000..6e7f2f4 --- /dev/null +++ b/proxmox/lxc/lxc-bootstrap.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "== LXC Bootstrap: install Docker + Compose ==" + +export DEBIAN_FRONTEND=noninteractive + +apt-get update +apt-get install -y ca-certificates curl gnupg lsb-release fuse3 uidmap jq + +# Install Docker from Docker's official repo +install -m 0755 -d /etc/apt/keyrings +if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg +fi + +ARCH="$(dpkg --print-architecture)" +CODENAME="$(. /etc/os-release && echo "$VERSION_CODENAME")" + +cat >/etc/apt/sources.list.d/docker.list </dev/null 2>&1 || { echo "ERROR: Missing $1"; exit 1; }; } +require_cmd pveam +require_cmd pvesm +require_cmd pct + +# Find Debian 12 template (download if missing) +TPL_NAME="$(pveam available --section system | awk '/debian-12-standard/ {print $2}' | tail -n1)" +if [[ -z "$TPL_NAME" ]]; then + echo "ERROR: Could not find Debian 12 template in pveam catalog." + exit 1 +fi + +if ! pveam list "$STORAGE" | awk '{print $1}' | grep -qx "$TPL_NAME"; then + echo "Downloading Debian 12 template to storage '$STORAGE'..." + pveam download "$STORAGE" "$TPL_NAME" +fi + +# Root password (random) for convenience +ROOT_PASS="$(openssl rand -base64 18 | tr -d '\n' | tr -d '=+/')" +echo "CT root password (SAVE THIS): $ROOT_PASS" + +# Create container +echo "Creating CT $CTID..." +pct create "$CTID" "$STORAGE:vztmpl/$TPL_NAME" \ + --hostname "$HOSTNAME" \ + --unprivileged 1 \ + --features "nesting=1,keyctl=1,fuse=1" \ + --cores "$CORES" \ + --memory "$MEM_MB" \ + --swap 1024 \ + --rootfs "${STORAGE}:${DISK_GB}" \ + --net0 "$NET0" \ + --password "$ROOT_PASS" \ + --onboot 1 \ + --start 1 + +CONF="/etc/pve/lxc/${CTID}.conf" + +# Docker-in-LXC hardening/compat +# (These are common requirements for Docker in an unprivileged LXC on Proxmox.) +{ + echo "" + echo "# --- Riven/Docker requirements ---" + echo "lxc.apparmor.profile: unconfined" + echo "lxc.cgroup2.devices.allow: a" + echo "lxc.mount.auto: proc:rw sys:rw" +} >> "$CONF" + +# Pass /dev/fuse +{ + echo "# Pass FUSE" + echo "lxc.cgroup2.devices.allow: c 10:229 rwm" + echo "lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file,optional 0 0" +} >> "$CONF" + +# Pass GPU (optional) +if [[ "${GPU}" == "yes" ]]; then + { + echo "# Pass GPU (DRI)" + echo "lxc.cgroup2.devices.allow: c 226:* rwm" + echo "lxc.mount.entry: /dev/dri dev/dri none bind,create=dir,optional 0 0" + } >> "$CONF" +fi + +# Bind-mount host path into CT as /mnt/riven (optional, recommended) +if [[ -n "$HOST_RIVEN_PATH" ]]; then + if [[ ! -d "$HOST_RIVEN_PATH" ]]; then + echo "Creating host path: $HOST_RIVEN_PATH" + mkdir -p "$HOST_RIVEN_PATH" + fi + echo "# Bind host storage into CT" + echo "mp0: ${HOST_RIVEN_PATH},mp=/mnt/riven" >> "$CONF" +fi + +echo "Restarting CT to apply config changes..." +pct stop "$CTID" +pct start "$CTID" + +echo "CT $CTID created and configured." diff --git a/proxmox/lxc/riven-install.sh b/proxmox/lxc/riven-install.sh new file mode 100644 index 0000000..1922e54 --- /dev/null +++ b/proxmox/lxc/riven-install.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "== Riven Docker Install (inside LXC) ==" + +INSTALL_DIR="/opt/riven" +DATA_DIR="/mnt/riven" +BACKEND_DIR="$DATA_DIR/backend" +MOUNT_DIR="$DATA_DIR/mount" + +RIVEN_UID=1000 +RIVEN_GID=1000 + +mkdir -p "$INSTALL_DIR" +mkdir -p "$BACKEND_DIR" "$MOUNT_DIR" +chown -R "$RIVEN_UID:$RIVEN_GID" "$DATA_DIR" + +# Ensure bind + rshared mount (inside CT) +if ! mountpoint -q "$MOUNT_DIR"; then + mount --bind "$MOUNT_DIR" "$MOUNT_DIR" +fi +mount --make-rshared "$MOUNT_DIR" + +PROP="$(findmnt -T "$MOUNT_DIR" -o PROPAGATION -n || true)" +if [[ "$PROP" != "shared" && "$PROP" != "rshared" ]]; then + echo "ERROR: $MOUNT_DIR is not shared (got: $PROP)" + exit 1 +fi + +# Persist on boot (inside CT) - ensures rshared is set before docker starts +cat >/etc/systemd/system/riven-bind-shared.service < .env < Date: Sat, 3 Jan 2026 20:12:32 -0500 Subject: [PATCH 003/154] Refactor Proxmox LXC installer script for clarity Updated the Proxmox LXC installer script to improve structure and clarity. Added comments and organized sections for better readability. --- proxmox/install.sh | 152 +++++++++++++++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 46 deletions(-) diff --git a/proxmox/install.sh b/proxmox/install.sh index a94b6cc..985cf8b 100644 --- a/proxmox/install.sh +++ b/proxmox/install.sh @@ -1,26 +1,45 @@ #!/usr/bin/env bash set -euo pipefail +############################################################################### # Riven Proxmox LXC Installer (Docker-based) -# Run on Proxmox host as root: -# bash install.sh - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -LXC_DIR="$SCRIPT_DIR/lxc" - -require_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: Missing required command: $1"; exit 1; }; } +# Run on Proxmox host as root +# +# One-liner supported: +# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" +############################################################################### + +[[ "$EUID" -ne 0 ]] && { echo "ERROR: Run as root on Proxmox host"; exit 1; } + +############################################################################### +# Helpers +############################################################################### +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "ERROR: Missing required command: $1" + exit 1 + } +} +############################################################################### +# Requirements +############################################################################### require_cmd pveversion require_cmd pct require_cmd pvesm +require_cmd curl -# --- Proxmox version check (8.1+) --- +############################################################################### +# Proxmox version check (8.1+) +############################################################################### PVE_VER_RAW="$(pveversion | head -n1)" PVE_VER="$(echo "$PVE_VER_RAW" | sed -nE 's/.*pve-manager\/([0-9]+)\.([0-9]+).*/\1.\2/p')" -if [[ -z "${PVE_VER}" ]]; then + +if [[ -z "$PVE_VER" ]]; then echo "WARNING: Could not parse Proxmox version from: $PVE_VER_RAW" else - MAJ="${PVE_VER%.*}"; MIN="${PVE_VER#*.}" + MAJ="${PVE_VER%.*}" + MIN="${PVE_VER#*.}" if (( MAJ < 8 )) || (( MAJ == 8 && MIN < 1 )); then echo "ERROR: Proxmox 8.1+ required. Detected: $PVE_VER_RAW" exit 1 @@ -29,11 +48,40 @@ fi echo "== Riven Proxmox LXC (Docker) Installer ==" -# --- Defaults --- +############################################################################### +# Download LXC installer components from GitHub +############################################################################### +BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/lxc" +WORKDIR="/tmp/riven-proxmox-lxc" + +echo "📥 Downloading installer components from GitHub..." +mkdir -p "$WORKDIR" +cd "$WORKDIR" + +fetch() { + local file="$1" + echo " - $file" + curl -fsSL "$BASE_URL/$file" -o "$file" +} + +fetch lxc-create.sh +fetch lxc-bootstrap.sh +fetch riven-install.sh +fetch docker-compose.yml +fetch upgrade.sh + +chmod +x lxc-create.sh lxc-bootstrap.sh riven-install.sh upgrade.sh + +############################################################################### +# Defaults +############################################################################### DEFAULT_CTID="" while :; do CANDIDATE=$(( (RANDOM % 900) + 100 )) - if ! pct status "$CANDIDATE" >/dev/null 2>&1; then DEFAULT_CTID="$CANDIDATE"; break; fi + if ! pct status "$CANDIDATE" >/dev/null 2>&1; then + DEFAULT_CTID="$CANDIDATE" + break + fi done read -rp "Container ID (CTID) [${DEFAULT_CTID}]: " CTID @@ -56,46 +104,48 @@ MEM_MB="${MEM_MB:-4096}" read -rp "CPU cores [4]: " CORES CORES="${CORES:-4}" -# Storage for rootfs (default: first local-lvm, else local) DEFAULT_STORAGE="$(pvesm status | awk 'NR>1{print $1}' | head -n1)" DEFAULT_STORAGE="${DEFAULT_STORAGE:-local}" -read -rp "Proxmox storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE +read -rp "Storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE STORAGE="${STORAGE:-$DEFAULT_STORAGE}" -# Network: DHCP by default on vmbr0 read -rp "Bridge [vmbr0]: " BRIDGE BRIDGE="${BRIDGE:-vmbr0}" + read -rp "Network config (dhcp or static like 192.168.1.50/24,gw=192.168.1.1) [dhcp]: " NETCFG NETCFG="${NETCFG:-dhcp}" -# Choose static or DHCP formatting for pct -NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" -if [[ "${NETCFG}" == "dhcp" ]]; then +if [[ "$NETCFG" == "dhcp" ]]; then NET0="name=eth0,bridge=${BRIDGE},ip=dhcp" +else + NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" fi -# Optional: mount host path into LXC for media VFS (recommended) +############################################################################### +# Optional host mount +############################################################################### echo -echo "Optional: bind-mount a host path into the LXC as /mnt/riven (recommended for persistence)." -echo "Examples:" -echo " /srv/riven (ZFS/LVM dataset)" -echo " /mnt/rivenhost (separate disk)" -read -rp "Host path to mount into CT as /mnt/riven (leave blank to use CT disk only): " HOST_RIVEN_PATH +echo "Optional: bind-mount a host path into the LXC as /mnt/riven" +read -rp "Host path to mount (leave blank to skip): " HOST_RIVEN_PATH -# GPU passthrough (Intel/AMD /dev/dri; NVIDIA users may still want /dev/dri for iGPU) +############################################################################### +# GPU passthrough +############################################################################### GPU_ENABLE="no" if [[ -d /dev/dri ]]; then - read -rp "Detected /dev/dri on host. Pass GPU device into LXC? [Y/n]: " GPU_ANS + read -rp "Detected /dev/dri. Enable GPU passthrough? [Y/n]: " GPU_ANS GPU_ANS="${GPU_ANS:-Y}" - if [[ "${GPU_ANS}" =~ ^[Yy]$ ]]; then GPU_ENABLE="yes"; fi + [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" else - read -rp "No /dev/dri detected on host. Pass GPU anyway? [y/N]: " GPU_ANS + read -rp "No /dev/dri detected. Force GPU passthrough anyway? [y/N]: " GPU_ANS GPU_ANS="${GPU_ANS:-N}" - if [[ "${GPU_ANS}" =~ ^[Yy]$ ]]; then GPU_ENABLE="yes"; fi + [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" fi -# --- Create LXC --- -bash "$LXC_DIR/lxc-create.sh" \ +############################################################################### +# Create LXC +############################################################################### +bash "$WORKDIR/lxc-create.sh" \ --ctid "$CTID" \ --hostname "$HOSTNAME" \ --storage "$STORAGE" \ @@ -106,28 +156,38 @@ bash "$LXC_DIR/lxc-create.sh" \ --host-riven-path "$HOST_RIVEN_PATH" \ --gpu "$GPU_ENABLE" -# --- Push scripts into CT --- -echo "Pushing installer files into CT $CTID..." -pct push "$CTID" "$LXC_DIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 -pct push "$CTID" "$LXC_DIR/riven-install.sh" /root/riven-install.sh -perms 0755 -pct push "$CTID" "$LXC_DIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 -pct push "$CTID" "$LXC_DIR/upgrade.sh" /root/upgrade.sh -perms 0755 - -# --- Bootstrap docker --- -echo "Bootstrapping Docker inside CT..." +############################################################################### +# Push files into CT +############################################################################### +echo "📦 Pushing installer files into CT $CTID..." +pct push "$CTID" "$WORKDIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 +pct push "$CTID" "$WORKDIR/riven-install.sh" /root/riven-install.sh -perms 0755 +pct push "$CTID" "$WORKDIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 +pct push "$CTID" "$WORKDIR/upgrade.sh" /root/upgrade.sh -perms 0755 + +############################################################################### +# Bootstrap Docker +############################################################################### +echo "🐳 Installing Docker inside CT..." pct exec "$CTID" -- bash /root/lxc-bootstrap.sh -# --- Install & run Riven stack --- -echo "Installing and starting Riven stack inside CT..." +############################################################################### +# Install Riven +############################################################################### +echo "🚀 Deploying Riven stack..." pct exec "$CTID" -- bash /root/riven-install.sh -# --- Print access info --- +############################################################################### +# Final info +############################################################################### +echo +echo "✅ Installation complete." +echo "Find CT IP:" +echo " pct exec $CTID -- hostname -I" echo -echo "✅ Done." -echo "Find CT IP via: pct exec $CTID -- hostname -I" echo "Backend : http://:8080" echo "Frontend : http://:3000" echo -echo "To view logs:" +echo "Logs:" echo " pct exec $CTID -- docker logs -f riven" echo " pct exec $CTID -- docker logs -f riven-db" From 53d542068345747bf1723daa454633ca2f0ab5d3 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:31:05 -0500 Subject: [PATCH 004/154] Add files via upload --- ubuntu/New Text Document.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 ubuntu/New Text Document.txt diff --git a/ubuntu/New Text Document.txt b/ubuntu/New Text Document.txt new file mode 100644 index 0000000..e69de29 From e1c792cdee0fb81cd1fdf763e28f920005112690 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:31:28 -0500 Subject: [PATCH 005/154] Delete ubuntu/New Text Document.txt --- ubuntu/New Text Document.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 ubuntu/New Text Document.txt diff --git a/ubuntu/New Text Document.txt b/ubuntu/New Text Document.txt deleted file mode 100644 index e69de29..0000000 From f14547781f2eba0ef281eba93f21f06b13b565bd Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:39:28 -0500 Subject: [PATCH 006/154] Add files via upload --- ubuntu/docker-compose.yml | 81 ++++++++++++++++ ubuntu/install-riven.sh | 188 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 ubuntu/docker-compose.yml create mode 100644 ubuntu/install-riven.sh diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml new file mode 100644 index 0000000..f2216e8 --- /dev/null +++ b/ubuntu/docker-compose.yml @@ -0,0 +1,81 @@ +version: "3.9" + +services: + riven-db: + image: postgres:17-alpine + container_name: riven-db + restart: unless-stopped + env_file: [.env] + environment: + PGDATA: /var/lib/postgresql/data/pgdata + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - riven-pg-data:/var/lib/postgresql/data/pgdata + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + riven: + image: spoked/riven:dev + container_name: riven + restart: unless-stopped + shm_size: 1024m + ports: + - "8080:8080" + tty: true + cap_add: + - SYS_ADMIN + security_opt: + - apparmor:unconfined + devices: + - /dev/fuse + env_file: [.env] + environment: + TZ: ${TZ} + PUID: 1000 + PGID: 1000 + RIVEN_FORCE_ENV: "true" + RIVEN_API_KEY: ${BACKEND_API_KEY} + RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} + RIVEN_FILESYSTEM_MOUNT_PATH: /mount + RIVEN_UPDATERS_LIBRARY_PATH: /mnt/riven + RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache + volumes: + - /mnt/riven/backend:/riven/data + - /mnt/riven/mount:/mount:rshared,z + depends_on: + riven-db: + condition: service_healthy + healthcheck: + test: curl -s http://localhost:8080 >/dev/null || exit 1 + interval: 30s + timeout: 10s + retries: 10 + + riven-frontend: + image: spoked/riven-frontend:dev + container_name: riven-frontend + restart: unless-stopped + ports: + - "3000:3000" + env_file: [.env] + environment: + TZ: ${TZ} + DATABASE_URL: /riven/data/riven.db + BACKEND_URL: http://riven:8080 + BACKEND_API_KEY: ${BACKEND_API_KEY} + AUTH_SECRET: ${AUTH_SECRET} + ORIGIN: http://localhost:3000 + volumes: + - riven-frontend-data:/riven/data + depends_on: + riven: + condition: service_healthy + +volumes: + riven-frontend-data: + riven-pg-data: diff --git a/ubuntu/install-riven.sh b/ubuntu/install-riven.sh new file mode 100644 index 0000000..eb80f55 --- /dev/null +++ b/ubuntu/install-riven.sh @@ -0,0 +1,188 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################ +# USER CONFIG (EDIT THESE) +############################################ +DOWNLOAD_DIR="/opt/riven" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/refs/heads/main/ubuntu/docker-compose.yml" +ENV_FILE=".env" + +############################################ +# COLORS / OUTPUT +############################################ +GREEN="\033[1;32m" +RED="\033[1;31m" +BLUE="\033[1;34m" +YELLOW="\033[1;33m" +NC="\033[0m" + +log() { echo -e "${GREEN}[✔]${NC} $1"; } +step() { echo -e "${BLUE}▶${NC} $1"; } +warn() { echo -e "${YELLOW}[!]${NC} $1"; } +err() { echo -e "${RED}[✖]${NC} $1"; exit 1; } + +############################################ +# PRECHECKS +############################################ +[[ $EUID -eq 0 ]] || err "Run as root (sudo)" +[[ "$(uname -s)" == "Linux" ]] || err "Linux required" + +. /etc/os-release || err "Cannot detect OS" +[[ "$ID" == "ubuntu" ]] || err "Ubuntu required" + +############################################ +# BASE DEPS +############################################ +step "Installing base dependencies (curl, ca-certificates, gnupg)" + +apt-get update +apt-get install -y \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + openssl + +############################################ +# TIMEZONE +############################################ +step "Detecting timezone" + +DEFAULT_TZ="$(timedatectl show --property=Timezone --value 2>/dev/null || true)" +DEFAULT_TZ="${DEFAULT_TZ:-UTC}" + +echo +echo -e "Detected timezone: ${GREEN}$DEFAULT_TZ${NC}" +read -rp "Press ENTER to accept or type another (e.g. America/New_York): " USER_TZ +TZ_SELECTED="${USER_TZ:-$DEFAULT_TZ}" + +timedatectl list-timezones | grep -qx "$TZ_SELECTED" || err "Invalid timezone" +timedatectl set-timezone "$TZ_SELECTED" +log "Timezone set to $TZ_SELECTED" + +############################################ +# DIRECTORY SETUP +############################################ +step "Preparing download directory" + +mkdir -p "$DOWNLOAD_DIR" +cd "$DOWNLOAD_DIR" + +############################################ +# DOWNLOAD FILES +############################################ +step "Downloading docker-compose.yml" + +curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || err "Failed to download compose file" + +############################################ +# ENV GENERATION +############################################ +if [[ ! -f "$ENV_FILE" ]]; then + step "Generating .env" + + POSTGRES_PASSWORD="$(openssl rand -hex 24)" + BACKEND_API_KEY="$(openssl rand -hex 32)" + AUTH_SECRET="$(openssl rand -hex 32)" + + cat > "$ENV_FILE" </dev/null; then + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ + | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ + https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" \ + > /etc/apt/sources.list.d/docker.list + + apt-get update + apt-get install -y \ + docker-ce docker-ce-cli containerd.io \ + docker-buildx-plugin docker-compose-plugin + + systemctl enable docker +else + log "Docker already installed" +fi + +############################################ +# MOUNT DIRECTORIES +############################################ +step "Creating mount directories" + +mkdir -p /mnt/riven/backend /mnt/riven/mount /mnt/jellyfin /mnt/plex /mnt/emby +chown -R 1000:1000 /mnt/riven /mnt/jellyfin /mnt/plex /mnt/emby + +############################################ +# SYSTEMD MOUNT SERVICE +############################################ +step "Configuring shared mount systemd service" + +cat > /etc/systemd/system/riven-bind-shared.service < Date: Sat, 3 Jan 2026 20:39:50 -0500 Subject: [PATCH 007/154] Rename install-riven.sh to install.sh --- ubuntu/{install-riven.sh => install.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ubuntu/{install-riven.sh => install.sh} (100%) diff --git a/ubuntu/install-riven.sh b/ubuntu/install.sh similarity index 100% rename from ubuntu/install-riven.sh rename to ubuntu/install.sh From 470938f90e627d61d6ae59a2e5a13f93b8e5625d Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:45:05 -0500 Subject: [PATCH 008/154] Refactor install.sh for better readability and logic Refactor install script for improved structure and clarity. --- ubuntu/install.sh | 76 ++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index eb80f55..a66e071 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -2,14 +2,14 @@ set -euo pipefail ############################################ -# USER CONFIG (EDIT THESE) +# CONFIG ############################################ DOWNLOAD_DIR="/opt/riven" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/refs/heads/main/ubuntu/docker-compose.yml" +COMPOSE_URL="https://example.com/docker-compose.yml" ENV_FILE=".env" ############################################ -# COLORS / OUTPUT +# OUTPUT ############################################ GREEN="\033[1;32m" RED="\033[1;31m" @@ -32,37 +32,55 @@ err() { echo -e "${RED}[✖]${NC} $1"; exit 1; } [[ "$ID" == "ubuntu" ]] || err "Ubuntu required" ############################################ -# BASE DEPS +# DEPENDENCY CHECK ############################################ -step "Installing base dependencies (curl, ca-certificates, gnupg)" - -apt-get update -apt-get install -y \ - ca-certificates \ - curl \ - gnupg \ - lsb-release \ +REQUIRED_PKGS=( + curl + ca-certificates + gnupg + lsb-release openssl +) + +MISSING_PKGS=() + +for pkg in "${REQUIRED_PKGS[@]}"; do + if ! dpkg -s "$pkg" >/dev/null 2>&1; then + MISSING_PKGS+=("$pkg") + fi +done + +if (( ${#MISSING_PKGS[@]} > 0 )); then + step "Installing missing dependencies: ${MISSING_PKGS[*]}" + apt-get update + apt-get install -y "${MISSING_PKGS[@]}" +else + log "All base dependencies already installed" +fi ############################################ -# TIMEZONE +# TIMEZONE (NON-INTERACTIVE SAFE) ############################################ step "Detecting timezone" DEFAULT_TZ="$(timedatectl show --property=Timezone --value 2>/dev/null || true)" DEFAULT_TZ="${DEFAULT_TZ:-UTC}" -echo -echo -e "Detected timezone: ${GREEN}$DEFAULT_TZ${NC}" -read -rp "Press ENTER to accept or type another (e.g. America/New_York): " USER_TZ -TZ_SELECTED="${USER_TZ:-$DEFAULT_TZ}" +if [[ ! -t 0 ]]; then + TZ_SELECTED="$DEFAULT_TZ" + log "Non-interactive install — using timezone: $TZ_SELECTED" +else + echo + echo -e "Detected timezone: ${GREEN}$DEFAULT_TZ${NC}" + read -rp "Press ENTER to accept or type another: " USER_TZ + TZ_SELECTED="${USER_TZ:-$DEFAULT_TZ}" +fi -timedatectl list-timezones | grep -qx "$TZ_SELECTED" || err "Invalid timezone" timedatectl set-timezone "$TZ_SELECTED" log "Timezone set to $TZ_SELECTED" ############################################ -# DIRECTORY SETUP +# DOWNLOAD DIRECTORY ############################################ step "Preparing download directory" @@ -70,14 +88,15 @@ mkdir -p "$DOWNLOAD_DIR" cd "$DOWNLOAD_DIR" ############################################ -# DOWNLOAD FILES +# DOWNLOAD COMPOSE ############################################ step "Downloading docker-compose.yml" -curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || err "Failed to download compose file" +curl -fsSL "$COMPOSE_URL" -o docker-compose.yml \ + || err "Failed to download docker-compose.yml" ############################################ -# ENV GENERATION +# ENV FILE GENERATION ############################################ if [[ ! -f "$ENV_FILE" ]]; then step "Generating .env" @@ -107,8 +126,9 @@ fi ############################################ step "Installing Docker if missing" -if ! command -v docker &>/dev/null; then +if ! command -v docker >/dev/null 2>&1; then install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg @@ -130,7 +150,7 @@ else fi ############################################ -# MOUNT DIRECTORIES +# MOUNTS ############################################ step "Creating mount directories" @@ -138,7 +158,7 @@ mkdir -p /mnt/riven/backend /mnt/riven/mount /mnt/jellyfin /mnt/plex /mnt/emby chown -R 1000:1000 /mnt/riven /mnt/jellyfin /mnt/plex /mnt/emby ############################################ -# SYSTEMD MOUNT SERVICE +# SYSTEMD SHARED MOUNT ############################################ step "Configuring shared mount systemd service" @@ -170,7 +190,7 @@ findmnt -T /mnt/riven/mount -o PROPAGATION | grep -q shared \ || err "Mount is NOT shared" ############################################ -# START DOCKER + STACK +# START STACK ############################################ step "Starting Docker" systemctl start docker @@ -183,6 +203,6 @@ docker compose up -d # DONE ############################################ log "Riven installed successfully" -log "Compose location: $DOWNLOAD_DIR/docker-compose.yml" -log "Env file: $DOWNLOAD_DIR/.env" log "Timezone: $TZ_SELECTED" +log "Compose: $DOWNLOAD_DIR/docker-compose.yml" +log "Env: $DOWNLOAD_DIR/.env" From d6bedffc40919157834ceb83e9445ffa50921a90 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:46:46 -0500 Subject: [PATCH 009/154] Refactor dependency check and command detection --- ubuntu/install.sh | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index a66e071..9e04019 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -32,30 +32,34 @@ err() { echo -e "${RED}[✖]${NC} $1"; exit 1; } [[ "$ID" == "ubuntu" ]] || err "Ubuntu required" ############################################ -# DEPENDENCY CHECK +# COMMAND DETECTION (NO APT UNLESS NEEDED) ############################################ -REQUIRED_PKGS=( +REQUIRED_CMDS=( curl - ca-certificates - gnupg - lsb-release openssl + gpg + lsb_release ) -MISSING_PKGS=() +MISSING_CMDS=() -for pkg in "${REQUIRED_PKGS[@]}"; do - if ! dpkg -s "$pkg" >/dev/null 2>&1; then - MISSING_PKGS+=("$pkg") +for cmd in "${REQUIRED_CMDS[@]}"; do + if ! command -v "$cmd" >/dev/null 2>&1; then + MISSING_CMDS+=("$cmd") fi done -if (( ${#MISSING_PKGS[@]} > 0 )); then - step "Installing missing dependencies: ${MISSING_PKGS[*]}" +if (( ${#MISSING_CMDS[@]} > 0 )); then + step "Installing missing system commands: ${MISSING_CMDS[*]}" apt-get update - apt-get install -y "${MISSING_PKGS[@]}" + apt-get install -y \ + curl \ + ca-certificates \ + gnupg \ + lsb-release \ + openssl else - log "All base dependencies already installed" + log "All required system commands detected — skipping apt" fi ############################################ @@ -96,7 +100,7 @@ curl -fsSL "$COMPOSE_URL" -o docker-compose.yml \ || err "Failed to download docker-compose.yml" ############################################ -# ENV FILE GENERATION +# ENV AUTO-GEN ############################################ if [[ ! -f "$ENV_FILE" ]]; then step "Generating .env" @@ -122,11 +126,13 @@ else fi ############################################ -# DOCKER INSTALL +# DOCKER DETECTION + INSTALL ############################################ -step "Installing Docker if missing" +step "Checking Docker" if ! command -v docker >/dev/null 2>&1; then + step "Installing Docker" + install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ From af2750978f0d767d4734b87a3d836f9b8d2e4e66 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:47:22 -0500 Subject: [PATCH 010/154] Update Docker Compose URL in install script --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 9e04019..8977689 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -5,7 +5,7 @@ set -euo pipefail # CONFIG ############################################ DOWNLOAD_DIR="/opt/riven" -COMPOSE_URL="https://example.com/docker-compose.yml" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/refs/heads/main/ubuntu/docker-compose.yml" ENV_FILE=".env" ############################################ From 83a1eb8955d76730a561c80584bc3ba841239112 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:48:49 -0500 Subject: [PATCH 011/154] Fix docker-compose URL in install script --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 8977689..82a66e5 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -5,7 +5,7 @@ set -euo pipefail # CONFIG ############################################ DOWNLOAD_DIR="/opt/riven" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/refs/heads/main/ubuntu/docker-compose.yml" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" ENV_FILE=".env" ############################################ From 02866f7a9d211ca317ae91a9bab07e8bd4f0e75e Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:51:19 -0500 Subject: [PATCH 012/154] Refactor command detection and update comments Refactor command detection and improve comments. --- ubuntu/install.sh | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 82a66e5..e4a1e07 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -euo pipefail +# installer-version: 2026-01-04T02:05Z + ############################################ # CONFIG ############################################ @@ -32,25 +34,19 @@ err() { echo -e "${RED}[✖]${NC} $1"; exit 1; } [[ "$ID" == "ubuntu" ]] || err "Ubuntu required" ############################################ -# COMMAND DETECTION (NO APT UNLESS NEEDED) +# COMMAND DETECTION ############################################ -REQUIRED_CMDS=( - curl - openssl - gpg - lsb_release -) - -MISSING_CMDS=() +REQUIRED_CMDS=(curl openssl gpg lsb_release) +MISSING=false for cmd in "${REQUIRED_CMDS[@]}"; do if ! command -v "$cmd" >/dev/null 2>&1; then - MISSING_CMDS+=("$cmd") + MISSING=true fi done -if (( ${#MISSING_CMDS[@]} > 0 )); then - step "Installing missing system commands: ${MISSING_CMDS[*]}" +if $MISSING; then + step "Installing missing system commands" apt-get update apt-get install -y \ curl \ @@ -72,7 +68,6 @@ DEFAULT_TZ="${DEFAULT_TZ:-UTC}" if [[ ! -t 0 ]]; then TZ_SELECTED="$DEFAULT_TZ" - log "Non-interactive install — using timezone: $TZ_SELECTED" else echo echo -e "Detected timezone: ${GREEN}$DEFAULT_TZ${NC}" @@ -84,10 +79,9 @@ timedatectl set-timezone "$TZ_SELECTED" log "Timezone set to $TZ_SELECTED" ############################################ -# DOWNLOAD DIRECTORY +# DOWNLOAD DIR ############################################ step "Preparing download directory" - mkdir -p "$DOWNLOAD_DIR" cd "$DOWNLOAD_DIR" @@ -95,12 +89,13 @@ cd "$DOWNLOAD_DIR" # DOWNLOAD COMPOSE ############################################ step "Downloading docker-compose.yml" +log "Using COMPOSE_URL=$COMPOSE_URL" curl -fsSL "$COMPOSE_URL" -o docker-compose.yml \ || err "Failed to download docker-compose.yml" ############################################ -# ENV AUTO-GEN +# ENV FILE ############################################ if [[ ! -f "$ENV_FILE" ]]; then step "Generating .env" @@ -109,7 +104,7 @@ if [[ ! -f "$ENV_FILE" ]]; then BACKEND_API_KEY="$(openssl rand -hex 32)" AUTH_SECRET="$(openssl rand -hex 32)" - cat > "$ENV_FILE" < "$ENV_FILE" </dev/null 2>&1; then | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg - echo \ - "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ - https://download.docker.com/linux/ubuntu \ - $(lsb_release -cs) stable" \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ +https://download.docker.com/linux/ubuntu \ +$(lsb_release -cs) stable" \ > /etc/apt/sources.list.d/docker.list apt-get update @@ -191,7 +185,6 @@ systemctl enable --now riven-bind-shared.service # VERIFY MOUNT ############################################ step "Verifying mount propagation" - findmnt -T /mnt/riven/mount -o PROPAGATION | grep -q shared \ || err "Mount is NOT shared" From 39a4d474d3afe185c1865c89e448c3fd01bf73b5 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:53:11 -0500 Subject: [PATCH 013/154] Refactor install script for clarity and efficiency --- ubuntu/install.sh | 200 ++++++++++++---------------------------------- 1 file changed, 53 insertions(+), 147 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index e4a1e07..a716160 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,102 +1,63 @@ #!/usr/bin/env bash set -euo pipefail -# installer-version: 2026-01-04T02:05Z - -############################################ -# CONFIG -############################################ +# ===== CONFIG ===== DOWNLOAD_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" ENV_FILE=".env" -############################################ -# OUTPUT -############################################ -GREEN="\033[1;32m" -RED="\033[1;31m" -BLUE="\033[1;34m" -YELLOW="\033[1;33m" -NC="\033[0m" - -log() { echo -e "${GREEN}[✔]${NC} $1"; } -step() { echo -e "${BLUE}▶${NC} $1"; } -warn() { echo -e "${YELLOW}[!]${NC} $1"; } -err() { echo -e "${RED}[✖]${NC} $1"; exit 1; } - -############################################ -# PRECHECKS -############################################ -[[ $EUID -eq 0 ]] || err "Run as root (sudo)" -[[ "$(uname -s)" == "Linux" ]] || err "Linux required" +# ===== OUTPUT ===== +log() { echo "[✔] $1"; } +step() { echo "▶ $1"; } +err() { echo "[✖] $1"; exit 1; } +# ===== PRECHECK ===== +[[ $EUID -eq 0 ]] || err "Run as root" . /etc/os-release || err "Cannot detect OS" [[ "$ID" == "ubuntu" ]] || err "Ubuntu required" -############################################ -# COMMAND DETECTION -############################################ -REQUIRED_CMDS=(curl openssl gpg lsb_release) -MISSING=false - -for cmd in "${REQUIRED_CMDS[@]}"; do - if ! command -v "$cmd" >/dev/null 2>&1; then - MISSING=true - fi -done +# ===== TIMEZONE (AUTO, NO PROMPTS) ===== +step "Detecting timezone" +TZ_SELECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" +timedatectl set-timezone "$TZ_SELECTED" +log "Timezone set to $TZ_SELECTED" -if $MISSING; then - step "Installing missing system commands" +# ===== ENSURE CURL ===== +if ! command -v curl >/dev/null 2>&1; then + step "Installing curl" apt-get update - apt-get install -y \ - curl \ - ca-certificates \ - gnupg \ - lsb-release \ - openssl -else - log "All required system commands detected — skipping apt" + apt-get install -y curl ca-certificates fi -############################################ -# TIMEZONE (NON-INTERACTIVE SAFE) -############################################ -step "Detecting timezone" +# ===== ENSURE DOCKER ===== +if ! command -v docker >/dev/null 2>&1; then + step "Installing Docker" + apt-get update + apt-get install -y ca-certificates gnupg lsb-release -DEFAULT_TZ="$(timedatectl show --property=Timezone --value 2>/dev/null || true)" -DEFAULT_TZ="${DEFAULT_TZ:-UTC}" + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg -if [[ ! -t 0 ]]; then - TZ_SELECTED="$DEFAULT_TZ" -else - echo - echo -e "Detected timezone: ${GREEN}$DEFAULT_TZ${NC}" - read -rp "Press ENTER to accept or type another: " USER_TZ - TZ_SELECTED="${USER_TZ:-$DEFAULT_TZ}" -fi + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ +https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ +> /etc/apt/sources.list.d/docker.list -timedatectl set-timezone "$TZ_SELECTED" -log "Timezone set to $TZ_SELECTED" + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + systemctl enable docker +fi -############################################ -# DOWNLOAD DIR -############################################ -step "Preparing download directory" +# ===== SETUP DIR ===== +step "Preparing directory" mkdir -p "$DOWNLOAD_DIR" cd "$DOWNLOAD_DIR" -############################################ -# DOWNLOAD COMPOSE -############################################ +# ===== DOWNLOAD COMPOSE ===== step "Downloading docker-compose.yml" -log "Using COMPOSE_URL=$COMPOSE_URL" - -curl -fsSL "$COMPOSE_URL" -o docker-compose.yml \ - || err "Failed to download docker-compose.yml" +curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || err "Compose download failed" -############################################ -# ENV FILE -############################################ +# ===== GENERATE .ENV ===== if [[ ! -f "$ENV_FILE" ]]; then step "Generating .env" @@ -104,66 +65,24 @@ if [[ ! -f "$ENV_FILE" ]]; then BACKEND_API_KEY="$(openssl rand -hex 32)" AUTH_SECRET="$(openssl rand -hex 32)" -cat > "$ENV_FILE" < "$ENV_FILE" -POSTGRES_DB=riven -POSTGRES_USER=postgres -POSTGRES_PASSWORD=$POSTGRES_PASSWORD - -BACKEND_API_KEY=$BACKEND_API_KEY -AUTH_SECRET=$AUTH_SECRET -EOF - - warn "SAVE $DOWNLOAD_DIR/.env — it contains secrets" -else - log ".env already exists — leaving unchanged" + log ".env created" fi -############################################ -# DOCKER -############################################ -step "Checking Docker" - -if ! command -v docker >/dev/null 2>&1; then - step "Installing Docker" - - install -m 0755 -d /etc/apt/keyrings +# ===== MOUNTS ===== +step "Creating mount paths" +mkdir -p /mnt/riven/backend /mnt/riven/mount +chown -R 1000:1000 /mnt/riven - curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ - | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ -https://download.docker.com/linux/ubuntu \ -$(lsb_release -cs) stable" \ - > /etc/apt/sources.list.d/docker.list +# ===== SYSTEMD SERVICE ===== +step "Creating mount service" - apt-get update - apt-get install -y \ - docker-ce docker-ce-cli containerd.io \ - docker-buildx-plugin docker-compose-plugin - - systemctl enable docker -else - log "Docker already installed" -fi - -############################################ -# MOUNTS -############################################ -step "Creating mount directories" - -mkdir -p /mnt/riven/backend /mnt/riven/mount /mnt/jellyfin /mnt/plex /mnt/emby -chown -R 1000:1000 /mnt/riven /mnt/jellyfin /mnt/plex /mnt/emby - -############################################ -# SYSTEMD SHARED MOUNT -############################################ -step "Configuring shared mount systemd service" - -cat > /etc/systemd/system/riven-bind-shared.service < /etc/systemd/system/riven-bind-shared.service systemctl daemon-reload systemctl enable --now riven-bind-shared.service -############################################ -# VERIFY MOUNT -############################################ -step "Verifying mount propagation" -findmnt -T /mnt/riven/mount -o PROPAGATION | grep -q shared \ - || err "Mount is NOT shared" - -############################################ -# START STACK -############################################ +# ===== START STACK ===== step "Starting Docker" systemctl start docker -step "Starting Riven stack" +step "Starting containers" docker compose pull docker compose up -d -############################################ -# DONE -############################################ -log "Riven installed successfully" -log "Timezone: $TZ_SELECTED" +log "Riven install complete" log "Compose: $DOWNLOAD_DIR/docker-compose.yml" log "Env: $DOWNLOAD_DIR/.env" From acda2fc0dbbee0e33c7092d5cea775ef843411ef Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:55:01 -0500 Subject: [PATCH 014/154] Delete ubuntu/install.sh --- ubuntu/install.sh | 113 ---------------------------------------------- 1 file changed, 113 deletions(-) delete mode 100644 ubuntu/install.sh diff --git a/ubuntu/install.sh b/ubuntu/install.sh deleted file mode 100644 index a716160..0000000 --- a/ubuntu/install.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# ===== CONFIG ===== -DOWNLOAD_DIR="/opt/riven" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" -ENV_FILE=".env" - -# ===== OUTPUT ===== -log() { echo "[✔] $1"; } -step() { echo "▶ $1"; } -err() { echo "[✖] $1"; exit 1; } - -# ===== PRECHECK ===== -[[ $EUID -eq 0 ]] || err "Run as root" -. /etc/os-release || err "Cannot detect OS" -[[ "$ID" == "ubuntu" ]] || err "Ubuntu required" - -# ===== TIMEZONE (AUTO, NO PROMPTS) ===== -step "Detecting timezone" -TZ_SELECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" -timedatectl set-timezone "$TZ_SELECTED" -log "Timezone set to $TZ_SELECTED" - -# ===== ENSURE CURL ===== -if ! command -v curl >/dev/null 2>&1; then - step "Installing curl" - apt-get update - apt-get install -y curl ca-certificates -fi - -# ===== ENSURE DOCKER ===== -if ! command -v docker >/dev/null 2>&1; then - step "Installing Docker" - apt-get update - apt-get install -y ca-certificates gnupg lsb-release - - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ -https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ -> /etc/apt/sources.list.d/docker.list - - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - systemctl enable docker -fi - -# ===== SETUP DIR ===== -step "Preparing directory" -mkdir -p "$DOWNLOAD_DIR" -cd "$DOWNLOAD_DIR" - -# ===== DOWNLOAD COMPOSE ===== -step "Downloading docker-compose.yml" -curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || err "Compose download failed" - -# ===== GENERATE .ENV ===== -if [[ ! -f "$ENV_FILE" ]]; then - step "Generating .env" - - POSTGRES_PASSWORD="$(openssl rand -hex 24)" - BACKEND_API_KEY="$(openssl rand -hex 32)" - AUTH_SECRET="$(openssl rand -hex 32)" - - printf "TZ=%s\n\nPOSTGRES_DB=riven\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=%s\n\nBACKEND_API_KEY=%s\nAUTH_SECRET=%s\n" \ - "$TZ_SELECTED" \ - "$POSTGRES_PASSWORD" \ - "$BACKEND_API_KEY" \ - "$AUTH_SECRET" > "$ENV_FILE" - - log ".env created" -fi - -# ===== MOUNTS ===== -step "Creating mount paths" -mkdir -p /mnt/riven/backend /mnt/riven/mount -chown -R 1000:1000 /mnt/riven - -# ===== SYSTEMD SERVICE ===== -step "Creating mount service" - -printf "[Unit] -Description=Make Riven mount bind shared -After=local-fs.target -Before=docker.service - -[Service] -Type=oneshot -ExecStart=/usr/bin/mount --bind /mnt/riven/mount /mnt/riven/mount -ExecStart=/usr/bin/mount --make-rshared /mnt/riven/mount -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target -" > /etc/systemd/system/riven-bind-shared.service - -systemctl daemon-reload -systemctl enable --now riven-bind-shared.service - -# ===== START STACK ===== -step "Starting Docker" -systemctl start docker - -step "Starting containers" -docker compose pull -docker compose up -d - -log "Riven install complete" -log "Compose: $DOWNLOAD_DIR/docker-compose.yml" -log "Env: $DOWNLOAD_DIR/.env" From cfe06f83d0b5f982d9b4c377fdbc0beee44f8c02 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 20:55:21 -0500 Subject: [PATCH 015/154] Add installation script for Riven on Ubuntu This script installs Docker and Docker Compose on Ubuntu, sets the timezone, and creates necessary directories and environment files for the Riven application. --- ubuntu/install.sh | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 ubuntu/install.sh diff --git a/ubuntu/install.sh b/ubuntu/install.sh new file mode 100644 index 0000000..3db8165 --- /dev/null +++ b/ubuntu/install.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +set -e + +# ================= CONFIG ================= +DOWNLOAD_DIR="/opt/riven" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" + +# ================= PRECHECK ================= +if [ "$(id -u)" -ne 0 ]; then + echo "Run as root" + exit 1 +fi + +. /etc/os-release || exit 1 +[ "$ID" = "ubuntu" ] || exit 1 + +# ================= TIMEZONE ================= +TZ_SELECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" +timedatectl set-timezone "$TZ_SELECTED" +echo "Timezone: $TZ_SELECTED" + +# ================= DEPENDENCIES ================= +if ! command -v curl >/dev/null; then + apt-get update + apt-get install -y curl ca-certificates +fi + +if ! command -v docker >/dev/null; then + apt-get update + apt-get install -y ca-certificates gnupg lsb-release + + mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor > /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ + > /etc/apt/sources.list.d/docker.list + + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + systemctl enable docker +fi + +# ================= FILES ================= +mkdir -p "$DOWNLOAD_DIR" +cd "$DOWNLOAD_DIR" + +curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || exit 1 + +if [ ! -f .env ]; then + printf "TZ=%s\nPOSTGRES_DB=riven\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=%s\nBACKEND_API_KEY=%s\nAUTH_SECRET=%s\n" \ + "$TZ_SELECTED" \ + "$(openssl rand -hex 24)" \ + "$(openssl rand -hex 32)" \ + "$(openssl rand -hex 32)" > .env +fi + +# ================= MOUNTS ================= +mkdir -p /mnt/riven/backend /mnt/riven/mount +chown -R 1000:1000 /mnt/riven + +printf "[Unit] +Description=Make Riven mount bind shared +After=local-fs.target +Before=docker.service + +[Service] +Type=oneshot +ExecStart=/usr/bin/mount --bind /mnt/riven/mount /mnt/riven/mount +ExecStart=/usr/bin/mount --make-rshared /mnt/riven/mount +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +" > /etc/systemd/system/riven-bind-shared.service + +systemctl daemon-reload +systemctl enable --now riven-bind-shared.service + +# ================= START ================= +systemctl start docker +docker compose pull +docker compose up -d + +echo "Riven install complete" From f08672b1a7bd4723e0e3c0f742e28e5f3811a902 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:25:40 -0500 Subject: [PATCH 016/154] Update docker-compose.yml --- ubuntu/docker-compose.yml | 299 +++++++++++++++++++++++++++----------- 1 file changed, 218 insertions(+), 81 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index f2216e8..49e60c7 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -1,81 +1,218 @@ -version: "3.9" - -services: - riven-db: - image: postgres:17-alpine - container_name: riven-db - restart: unless-stopped - env_file: [.env] - environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - riven-pg-data:/var/lib/postgresql/data/pgdata - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] - interval: 10s - timeout: 5s - retries: 5 - - riven: - image: spoked/riven:dev - container_name: riven - restart: unless-stopped - shm_size: 1024m - ports: - - "8080:8080" - tty: true - cap_add: - - SYS_ADMIN - security_opt: - - apparmor:unconfined - devices: - - /dev/fuse - env_file: [.env] - environment: - TZ: ${TZ} - PUID: 1000 - PGID: 1000 - RIVEN_FORCE_ENV: "true" - RIVEN_API_KEY: ${BACKEND_API_KEY} - RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} - RIVEN_FILESYSTEM_MOUNT_PATH: /mount - RIVEN_UPDATERS_LIBRARY_PATH: /mnt/riven - RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache - volumes: - - /mnt/riven/backend:/riven/data - - /mnt/riven/mount:/mount:rshared,z - depends_on: - riven-db: - condition: service_healthy - healthcheck: - test: curl -s http://localhost:8080 >/dev/null || exit 1 - interval: 30s - timeout: 10s - retries: 10 - - riven-frontend: - image: spoked/riven-frontend:dev - container_name: riven-frontend - restart: unless-stopped - ports: - - "3000:3000" - env_file: [.env] - environment: - TZ: ${TZ} - DATABASE_URL: /riven/data/riven.db - BACKEND_URL: http://riven:8080 - BACKEND_API_KEY: ${BACKEND_API_KEY} - AUTH_SECRET: ${AUTH_SECRET} - ORIGIN: http://localhost:3000 - volumes: - - riven-frontend-data:/riven/data - depends_on: - riven: - condition: service_healthy - -volumes: - riven-frontend-data: - riven-pg-data: +#!/usr/bin/env bash +set -e + +############################################ +# CONFIG +############################################ +INSTALL_DIR="/opt/riven" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" +FRONTEND_PORT="3000" + +############################################ +# OUTPUT HELPERS +############################################ +banner() { + echo + echo "========================================" + echo " $1" + echo "========================================" +} +ok() { echo "[✔] $1"; } +warn() { echo "[!] $1"; } +fail() { echo "[✖] $1"; exit 1; } + +############################################ +# PRECHECKS +############################################ +[ "$(id -u)" -eq 0 ] || fail "Run this script as root (sudo)" +. /etc/os-release || fail "Cannot detect OS" +[ "$ID" = "ubuntu" ] || fail "Ubuntu is required" + +############################################ +# TIMEZONE (INTERACTIVE, SAFE) +############################################ +banner "Timezone Configuration" + +TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" +echo "Detected timezone: $TZ_DETECTED" + +if [ -t 0 ]; then + read -rp "Press ENTER to accept or type another (e.g. America/New_York): " TZ_INPUT + TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" +else + TZ_SELECTED="$TZ_DETECTED" +fi + +timedatectl set-timezone "$TZ_SELECTED" +ok "Timezone set to $TZ_SELECTED" + +############################################ +# DEPENDENCIES (ONLY IF MISSING) +############################################ +banner "Dependency Check" + +REQUIRED_CMDS=(curl openssl gpg) +MISSING=() + +for cmd in "${REQUIRED_CMDS[@]}"; do + command -v "$cmd" >/dev/null || MISSING+=("$cmd") +done + +if [ "${#MISSING[@]}" -gt 0 ]; then + warn "Installing missing packages: ${MISSING[*]}" + apt-get update + apt-get install -y ca-certificates curl gnupg lsb-release openssl +else + ok "All required system commands detected — skipping apt" +fi + +############################################ +# DOCKER INSTALL (ONLY IF MISSING) +############################################ +banner "Docker" + +if ! command -v docker >/dev/null; then + warn "Docker not detected — installing" + + mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ + | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ +https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ + > /etc/apt/sources.list.d/docker.list + + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + systemctl enable docker +else + ok "Docker already installed" +fi + +############################################ +# DOCKER IPv4 ONLY (NO SYSTEM IPV6 CHANGES) +############################################ +banner "Docker IPv4 Configuration" + +mkdir -p /etc/docker +cat >/etc/docker/daemon.json </etc/systemd/system/riven-bind-shared.service < .env < Date: Sat, 3 Jan 2026 21:26:56 -0500 Subject: [PATCH 017/154] Refactor docker-compose.yml for Riven services Updated docker-compose.yml to define services for riven-db, riven, and riven-frontend with health checks and environment variables. --- ubuntu/docker-compose.yml | 299 +++++++++++--------------------------- 1 file changed, 81 insertions(+), 218 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 49e60c7..f2216e8 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -1,218 +1,81 @@ -#!/usr/bin/env bash -set -e - -############################################ -# CONFIG -############################################ -INSTALL_DIR="/opt/riven" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" -FRONTEND_PORT="3000" - -############################################ -# OUTPUT HELPERS -############################################ -banner() { - echo - echo "========================================" - echo " $1" - echo "========================================" -} -ok() { echo "[✔] $1"; } -warn() { echo "[!] $1"; } -fail() { echo "[✖] $1"; exit 1; } - -############################################ -# PRECHECKS -############################################ -[ "$(id -u)" -eq 0 ] || fail "Run this script as root (sudo)" -. /etc/os-release || fail "Cannot detect OS" -[ "$ID" = "ubuntu" ] || fail "Ubuntu is required" - -############################################ -# TIMEZONE (INTERACTIVE, SAFE) -############################################ -banner "Timezone Configuration" - -TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" -echo "Detected timezone: $TZ_DETECTED" - -if [ -t 0 ]; then - read -rp "Press ENTER to accept or type another (e.g. America/New_York): " TZ_INPUT - TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" -else - TZ_SELECTED="$TZ_DETECTED" -fi - -timedatectl set-timezone "$TZ_SELECTED" -ok "Timezone set to $TZ_SELECTED" - -############################################ -# DEPENDENCIES (ONLY IF MISSING) -############################################ -banner "Dependency Check" - -REQUIRED_CMDS=(curl openssl gpg) -MISSING=() - -for cmd in "${REQUIRED_CMDS[@]}"; do - command -v "$cmd" >/dev/null || MISSING+=("$cmd") -done - -if [ "${#MISSING[@]}" -gt 0 ]; then - warn "Installing missing packages: ${MISSING[*]}" - apt-get update - apt-get install -y ca-certificates curl gnupg lsb-release openssl -else - ok "All required system commands detected — skipping apt" -fi - -############################################ -# DOCKER INSTALL (ONLY IF MISSING) -############################################ -banner "Docker" - -if ! command -v docker >/dev/null; then - warn "Docker not detected — installing" - - mkdir -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ - | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ -https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ - > /etc/apt/sources.list.d/docker.list - - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - systemctl enable docker -else - ok "Docker already installed" -fi - -############################################ -# DOCKER IPv4 ONLY (NO SYSTEM IPV6 CHANGES) -############################################ -banner "Docker IPv4 Configuration" - -mkdir -p /etc/docker -cat >/etc/docker/daemon.json </etc/systemd/system/riven-bind-shared.service < .env </dev/null || exit 1 + interval: 30s + timeout: 10s + retries: 10 + + riven-frontend: + image: spoked/riven-frontend:dev + container_name: riven-frontend + restart: unless-stopped + ports: + - "3000:3000" + env_file: [.env] + environment: + TZ: ${TZ} + DATABASE_URL: /riven/data/riven.db + BACKEND_URL: http://riven:8080 + BACKEND_API_KEY: ${BACKEND_API_KEY} + AUTH_SECRET: ${AUTH_SECRET} + ORIGIN: http://localhost:3000 + volumes: + - riven-frontend-data:/riven/data + depends_on: + riven: + condition: service_healthy + +volumes: + riven-frontend-data: + riven-pg-data: From 9e6cb5ecacadb7d2372081bfa74aa99f26873526 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:28:03 -0500 Subject: [PATCH 018/154] Enhance install script with improved checks and layout Refactor install script for better readability and structure, add interactive timezone selection, and improve dependency checks. --- ubuntu/install.sh | 221 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 177 insertions(+), 44 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 3db8165..49e60c7 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,65 +1,134 @@ #!/usr/bin/env bash set -e -# ================= CONFIG ================= -DOWNLOAD_DIR="/opt/riven" +############################################ +# CONFIG +############################################ +INSTALL_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" - -# ================= PRECHECK ================= -if [ "$(id -u)" -ne 0 ]; then - echo "Run as root" - exit 1 +FRONTEND_PORT="3000" + +############################################ +# OUTPUT HELPERS +############################################ +banner() { + echo + echo "========================================" + echo " $1" + echo "========================================" +} +ok() { echo "[✔] $1"; } +warn() { echo "[!] $1"; } +fail() { echo "[✖] $1"; exit 1; } + +############################################ +# PRECHECKS +############################################ +[ "$(id -u)" -eq 0 ] || fail "Run this script as root (sudo)" +. /etc/os-release || fail "Cannot detect OS" +[ "$ID" = "ubuntu" ] || fail "Ubuntu is required" + +############################################ +# TIMEZONE (INTERACTIVE, SAFE) +############################################ +banner "Timezone Configuration" + +TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" +echo "Detected timezone: $TZ_DETECTED" + +if [ -t 0 ]; then + read -rp "Press ENTER to accept or type another (e.g. America/New_York): " TZ_INPUT + TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" +else + TZ_SELECTED="$TZ_DETECTED" fi -. /etc/os-release || exit 1 -[ "$ID" = "ubuntu" ] || exit 1 - -# ================= TIMEZONE ================= -TZ_SELECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" timedatectl set-timezone "$TZ_SELECTED" -echo "Timezone: $TZ_SELECTED" +ok "Timezone set to $TZ_SELECTED" + +############################################ +# DEPENDENCIES (ONLY IF MISSING) +############################################ +banner "Dependency Check" + +REQUIRED_CMDS=(curl openssl gpg) +MISSING=() + +for cmd in "${REQUIRED_CMDS[@]}"; do + command -v "$cmd" >/dev/null || MISSING+=("$cmd") +done -# ================= DEPENDENCIES ================= -if ! command -v curl >/dev/null; then +if [ "${#MISSING[@]}" -gt 0 ]; then + warn "Installing missing packages: ${MISSING[*]}" apt-get update - apt-get install -y curl ca-certificates + apt-get install -y ca-certificates curl gnupg lsb-release openssl +else + ok "All required system commands detected — skipping apt" fi +############################################ +# DOCKER INSTALL (ONLY IF MISSING) +############################################ +banner "Docker" + if ! command -v docker >/dev/null; then - apt-get update - apt-get install -y ca-certificates gnupg lsb-release + warn "Docker not detected — installing" mkdir -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor > /etc/apt/keyrings/docker.gpg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ + | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ - > /etc/apt/sources.list.d/docker.list + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ +https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ + > /etc/apt/sources.list.d/docker.list apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable docker +else + ok "Docker already installed" fi -# ================= FILES ================= -mkdir -p "$DOWNLOAD_DIR" -cd "$DOWNLOAD_DIR" - -curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || exit 1 - -if [ ! -f .env ]; then - printf "TZ=%s\nPOSTGRES_DB=riven\nPOSTGRES_USER=postgres\nPOSTGRES_PASSWORD=%s\nBACKEND_API_KEY=%s\nAUTH_SECRET=%s\n" \ - "$TZ_SELECTED" \ - "$(openssl rand -hex 24)" \ - "$(openssl rand -hex 32)" \ - "$(openssl rand -hex 32)" > .env -fi - -# ================= MOUNTS ================= -mkdir -p /mnt/riven/backend /mnt/riven/mount -chown -R 1000:1000 /mnt/riven - -printf "[Unit] +############################################ +# DOCKER IPv4 ONLY (NO SYSTEM IPV6 CHANGES) +############################################ +banner "Docker IPv4 Configuration" + +mkdir -p /etc/docker +cat >/etc/docker/daemon.json </etc/systemd/system/riven-bind-shared.service < /etc/systemd/system/riven-bind-shared.service +EOF systemctl daemon-reload systemctl enable --now riven-bind-shared.service +ok "Shared mount enabled" + +############################################ +# RIVEN DEPLOYMENT +############################################ +banner "Riven Deployment" + +cd "$INSTALL_DIR" +curl -fsSL "$COMPOSE_URL" -o docker-compose.yml || fail "Failed to download docker-compose.yml" +ok "docker-compose.yml downloaded" + +if [ ! -f .env ]; then + warn ".env not found — generating one (SAVE THIS FILE)" + cat > .env < Date: Sat, 3 Jan 2026 21:34:00 -0500 Subject: [PATCH 019/154] Revise configuration instructions in install.sh Updated warning message for Riven configuration steps. --- ubuntu/install.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 49e60c7..566cd7b 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -201,11 +201,20 @@ done SERVER_IP="$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')" [ -z "$SERVER_IP" ] && SERVER_IP="SERVER_IP" -banner "IMPORTANT NEXT STEPS" - -echo "• Edit Riven configuration:" +echo "⚠️ REQUIRED CONFIGURATION (DO NOT SKIP)" +echo +echo "• You MUST edit the Riven configuration file:" echo " /mnt/riven/backend/settings.json" echo +echo "• If you do NOT:" +echo " - Add at least ONE scraper" +echo " - Configure at least ONE media server (Plex / Jellyfin / Emby)" +echo +echo "❌ RIVEN WILL NOT WORK" +echo +echo "The backend will start, but NO content will appear and" +echo "scraping will silently fail until this is configured." +echo echo "• Movies / TV / Anime will appear in:" echo " /mnt/riven/mount" echo From 442b758201e64fddf04459d478494dba104de4ca Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:52:37 -0500 Subject: [PATCH 020/154] Add README for Riven Ubuntu Installer This README provides detailed instructions for installing and configuring Riven on Ubuntu using Docker and Docker Compose, including supported systems, filesystem setup, deployment steps, required configurations, and troubleshooting tips. --- ubuntu/readme.md | 90 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 ubuntu/readme.md diff --git a/ubuntu/readme.md b/ubuntu/readme.md new file mode 100644 index 0000000..8613252 --- /dev/null +++ b/ubuntu/readme.md @@ -0,0 +1,90 @@ +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" + +# Riven Ubuntu Installer + +This script installs and configures Riven on Ubuntu using Docker and Docker Compose. + +SUPPORTED SYSTEMS +- Ubuntu Server +- Ubuntu Desktop +- Virtual Machines +- Headless servers +- Advanced WSL setups + +WHAT THIS SCRIPT DOES + +SYSTEM & DOCKER +- Installs Docker and Docker Compose ONLY if missing +- Configures Docker to use IPv4 only (IPv6 disabled inside Docker only) +- Sets reliable DNS for containers + +FILESYSTEM & MOUNTS +Creates the following directories: +- /opt/riven + - docker-compose.yml + - .env +- /mnt/riven/backend + - Riven backend data + - settings.json configuration +- /mnt/riven/mount + - ALL movies, TV shows, and anime live here + +Sets up a systemd service to ensure /mnt/riven/mount is bind-mounted as rshared. +This is REQUIRED for Riven to function. + +RIVEN DEPLOYMENT +- Downloads docker-compose.yml automatically +- Generates a secure .env file +- Pulls and starts all containers +- Retries containers that fail to start (especially frontend) +- Verifies containers are running + +REQUIRED CONFIGURATION (DO NOT SKIP) + +AFTER INSTALL YOU MUST EDIT: +- /mnt/riven/backend/settings.json + +YOU MUST: +- Add at least ONE scraper +- Configure at least ONE media server (Plex, Jellyfin, or Emby) + +IF YOU DO NOT DO THIS: +- Containers WILL appear healthy +- NO content will load +- Scraping will silently fail + +RIVEN WILL NOT WORK UNTIL THIS IS CONFIGURED. + +ACCESSING THE FRONTEND + +After installation completes, the script will print: +- http://:3000 + +IMPORTANT PATHS SUMMARY + +Docker Compose: +- /opt/riven/docker-compose.yml + +Environment file: +- /opt/riven/.env + +Backend configuration: +- /mnt/riven/backend/settings.json + +Media library location: +- /mnt/riven/mount + +TROUBLESHOOTING + +Check running containers: +- docker ps + +Restart everything: +- cd /opt/riven +- docker compose down +- docker compose up -d + +View backend logs: +- docker logs riven +``` From b21c2c969d7551b6b9006aa7623c9c6b820f8526 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:53:04 -0500 Subject: [PATCH 021/154] Fix formatting in ubuntu readme.md --- ubuntu/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 8613252..9a6a5dc 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,6 +1,6 @@ ```bash sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" - +``` # Riven Ubuntu Installer This script installs and configures Riven on Ubuntu using Docker and Docker Compose. @@ -87,4 +87,4 @@ Restart everything: View backend logs: - docker logs riven -``` + From 10808bf84b13a62419c48f64910db37f6a625371 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:54:00 -0500 Subject: [PATCH 022/154] Add installation instructions to readme Added instructions for running the Ubuntu installer script. --- ubuntu/readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 9a6a5dc..9da204d 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,3 +1,5 @@ +## ▶️ How to run the installer (Ubuntu Script) + ```bash sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` From efa89211acbf4196bd3f2fa877e1847a2470494b Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:54:21 -0500 Subject: [PATCH 023/154] Improve readme formatting and section headers Updated formatting and section headers in readme. --- ubuntu/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 9da204d..4cd14e6 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -14,7 +14,7 @@ SUPPORTED SYSTEMS - Headless servers - Advanced WSL setups -WHAT THIS SCRIPT DOES +## WHAT THIS SCRIPT DOES SYSTEM & DOCKER - Installs Docker and Docker Compose ONLY if missing From 334e22fb287c3db9aa0168973d495d7bba5b6902 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:20:15 -0500 Subject: [PATCH 024/154] Revise Proxmox host installation instructions Updated instructions for running on Proxmox host. --- proxmox/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/proxmox/README.md b/proxmox/README.md index 11d9159..a9db217 100644 --- a/proxmox/README.md +++ b/proxmox/README.md @@ -1,13 +1,10 @@ # Riven Proxmox LXC Installer (Docker) -## Run on Proxmox host -Unzip on the Proxmox host, then: +## ▶️ Run on Proxmox host ```bash -cd riven-proxmox-lxc-installer -bash install.sh +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" ``` - ## What it does - Creates an **unprivileged Debian 12** LXC - Enables **nesting + keyctl + fuse** From 4aaeb3cc6883e348ac29ef28e041da8f938b0dc2 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:26:55 -0500 Subject: [PATCH 025/154] Revise README for better organization and detail Updated README to improve clarity and structure. --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 456a75b..c34b844 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,68 @@ -## Riven Distributables +# Riven Distributables -This repository contains helper scripts and installers for deploying Riven on -different platforms (for example, Proxmox VE and Unraid). Each platform has its -own subdirectory and documentation. +This repository contains official helper scripts and installers for deploying +**Riven** on supported platforms. + +Each platform has its own subdirectory with a dedicated installer and +documentation. --- -## Quick start: Proxmox VE LXC +## Quick Start + +### Proxmox VE (LXC) -To create a Debian 12, unprivileged LXC running Riven on a Proxmox VE host, -run this from the **Proxmox host shell**: +To create a **Debian 12 unprivileged LXC** configured to run Riven on a +**Proxmox VE host**, run the following command **from the Proxmox host shell**: ```bash bash -c "$(wget -qLO - https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/riven.sh)" ``` -For detailed Proxmox instructions (requirements, configuration, and troubleshooting), -see: +This installer handles: +- LXC creation and configuration +- Docker + FUSE setup +- Required mount propagation for Riven +- Optional GPU passthrough support +Full Proxmox documentation: - [`proxmox/README.md`](proxmox/README.md) -Additional installers (such as Unraid) will live in their own subdirectories -with their own README files. +--- + +### Ubuntu (Bare Metal / VM) + +To install Riven directly on an **Ubuntu system** (VM or bare metal), +run the installer below: + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/rivenmedia/distributables/main/ubuntu/install.sh)" +``` + +This installer handles: +- System dependency installation +- Docker and Docker Compose setup +- Riven service preparation and directory layout + +Full Ubuntu documentation: +- [`ubuntu/README.md`](ubuntu/README.md) + +--- + +## Repository Structure + +```text +distributables/ +├── proxmox/ # Proxmox VE LXC installer + docs +├── ubuntu/ # Ubuntu installer + docs +└── README.md # This file +``` + +--- + +## Notes + +- Each platform is self-contained and documented independently +- Always follow the README inside the platform directory for configuration + and troubleshooting +- Additional platforms can be added later using the same structure From a32a6857453072f5d0a4fb07054f98a192fd9c95 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:27:59 -0500 Subject: [PATCH 026/154] Change script URLs in README for installation Updated installation script URLs to point to AquaHorizonGaming repository. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c34b844..6eadde7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To create a **Debian 12 unprivileged LXC** configured to run Riven on a **Proxmox VE host**, run the following command **from the Proxmox host shell**: ```bash -bash -c "$(wget -qLO - https://raw.githubusercontent.com/rivenmedia/distributables/main/proxmox/riven.sh)" +bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/riven.sh)" ``` This installer handles: @@ -36,7 +36,7 @@ To install Riven directly on an **Ubuntu system** (VM or bare metal), run the installer below: ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/rivenmedia/distributables/main/ubuntu/install.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` This installer handles: From 6bc6a797b13b8959c77ad2da2879977e06304ce6 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:30:58 -0500 Subject: [PATCH 027/154] Remove version from docker-compose.yml Remove version specification from docker-compose file --- proxmox/lxc/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/proxmox/lxc/docker-compose.yml b/proxmox/lxc/docker-compose.yml index 8316412..68d6909 100644 --- a/proxmox/lxc/docker-compose.yml +++ b/proxmox/lxc/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: riven-db: image: postgres:17-alpine From 39b9455d480f71a4ce8075590f7180186bdd94b3 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:36:48 -0500 Subject: [PATCH 028/154] Update healthcheck syntax for services --- proxmox/lxc/docker-compose.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proxmox/lxc/docker-compose.yml b/proxmox/lxc/docker-compose.yml index 68d6909..35041c9 100644 --- a/proxmox/lxc/docker-compose.yml +++ b/proxmox/lxc/docker-compose.yml @@ -49,7 +49,7 @@ services: riven-db: condition: service_healthy healthcheck: - test: curl -s http://localhost:8080 >/dev/null || exit 1 + test: ["CMD-SHELL", "curl -s http://localhost:8080 >/dev/null || exit 1"] interval: 30s timeout: 10s retries: 10 @@ -73,6 +73,11 @@ services: depends_on: riven: condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:3000 >/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 5 jellyfin: image: jellyfin/jellyfin From a8233a88519fd4b6cb4650a5276e6d33e6e0e169 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:42:31 -0500 Subject: [PATCH 029/154] Update docker-compose.yml with new services and healthchecks --- ubuntu/docker-compose.yml | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index f2216e8..35041c9 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: riven-db: image: postgres:17-alpine @@ -51,7 +49,7 @@ services: riven-db: condition: service_healthy healthcheck: - test: curl -s http://localhost:8080 >/dev/null || exit 1 + test: ["CMD-SHELL", "curl -s http://localhost:8080 >/dev/null || exit 1"] interval: 30s timeout: 10s retries: 10 @@ -75,6 +73,53 @@ services: depends_on: riven: condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:3000 >/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + profiles: ["jellyfin"] + restart: unless-stopped + ports: + - "8096:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/jellyfin:/config + + plex: + image: lscr.io/linuxserver/plex + container_name: plex + profiles: ["plex"] + restart: unless-stopped + network_mode: host + environment: + - PUID=1000 + - PGID=1000 + - VERSION=docker + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/plex:/config + + emby: + image: emby/embyserver + container_name: emby + profiles: ["emby"] + restart: unless-stopped + ports: + - "8097:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/emby:/config volumes: riven-frontend-data: From fd12c05107db8b02507605b48f0bb79530c263fc Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:54:35 -0500 Subject: [PATCH 030/154] Refactor riven-install.sh for clarity and functionality Updated the riven-install.sh script to improve comments and ensure proper mounting and service setup for the Riven application. --- proxmox/lxc/riven-install.sh | 54 ++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/proxmox/lxc/riven-install.sh b/proxmox/lxc/riven-install.sh index 1922e54..2d17c53 100644 --- a/proxmox/lxc/riven-install.sh +++ b/proxmox/lxc/riven-install.sh @@ -11,14 +11,20 @@ MOUNT_DIR="$DATA_DIR/mount" RIVEN_UID=1000 RIVEN_GID=1000 +# ---------------------------- +# Directory setup +# ---------------------------- mkdir -p "$INSTALL_DIR" mkdir -p "$BACKEND_DIR" "$MOUNT_DIR" chown -R "$RIVEN_UID:$RIVEN_GID" "$DATA_DIR" -# Ensure bind + rshared mount (inside CT) +# ---------------------------- +# Ensure bind + rshared mount +# ---------------------------- if ! mountpoint -q "$MOUNT_DIR"; then mount --bind "$MOUNT_DIR" "$MOUNT_DIR" fi + mount --make-rshared "$MOUNT_DIR" PROP="$(findmnt -T "$MOUNT_DIR" -o PROPAGATION -n || true)" @@ -27,7 +33,9 @@ if [[ "$PROP" != "shared" && "$PROP" != "rshared" ]]; then exit 1 fi -# Persist on boot (inside CT) - ensures rshared is set before docker starts +# ---------------------------- +# Persist rshared mount on boot +# ---------------------------- cat >/etc/systemd/system/riven-bind-shared.service < .env < Date: Mon, 5 Jan 2026 08:23:55 -0500 Subject: [PATCH 031/154] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6eadde7..9f601b5 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ This installer handles: - Riven service preparation and directory layout Full Ubuntu documentation: -- [`ubuntu/README.md`](ubuntu/README.md) +- [`ubuntu/readme.md`](ubuntu/readme.md) --- From 9ed9403913859268b4e5d050d871eb32dadabd6e Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:16:38 -0500 Subject: [PATCH 032/154] Refactor install script for improved clarity and safety --- ubuntu/install.sh | 94 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 566cd7b..ca1cf93 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -euo pipefail ############################################ # CONFIG @@ -7,6 +7,7 @@ set -e INSTALL_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" FRONTEND_PORT="3000" +DEFAULT_ORIGIN="http://localhost:3000" ############################################ # OUTPUT HELPERS @@ -21,6 +22,13 @@ ok() { echo "[✔] $1"; } warn() { echo "[!] $1"; } fail() { echo "[✖] $1"; exit 1; } +############################################ +# URL VALIDATION +############################################ +is_valid_url() { + [[ "$1" =~ ^https?:// ]] +} + ############################################ # PRECHECKS ############################################ @@ -29,7 +37,7 @@ fail() { echo "[✖] $1"; exit 1; } [ "$ID" = "ubuntu" ] || fail "Ubuntu is required" ############################################ -# TIMEZONE (INTERACTIVE, SAFE) +# TIMEZONE ############################################ banner "Timezone Configuration" @@ -47,7 +55,7 @@ timedatectl set-timezone "$TZ_SELECTED" ok "Timezone set to $TZ_SELECTED" ############################################ -# DEPENDENCIES (ONLY IF MISSING) +# DEPENDENCIES ############################################ banner "Dependency Check" @@ -67,9 +75,9 @@ else fi ############################################ -# DOCKER INSTALL (ONLY IF MISSING) +# DOCKER ############################################ -banner "Docker" +banner "Docker Installation" if ! command -v docker >/dev/null; then warn "Docker not detected — installing" @@ -91,7 +99,7 @@ else fi ############################################ -# DOCKER IPv4 ONLY (NO SYSTEM IPV6 CHANGES) +# DOCKER IPv4 ONLY ############################################ banner "Docker IPv4 Configuration" @@ -104,10 +112,10 @@ cat >/etc/docker/daemon.json < .env < Date: Mon, 5 Jan 2026 13:17:14 -0500 Subject: [PATCH 033/154] Update ORIGIN environment variable in docker-compose.yml --- ubuntu/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 35041c9..c9f7a93 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -67,7 +67,7 @@ services: BACKEND_URL: http://riven:8080 BACKEND_API_KEY: ${BACKEND_API_KEY} AUTH_SECRET: ${AUTH_SECRET} - ORIGIN: http://localhost:3000 + ORIGIN: ${ORIGIN} volumes: - riven-frontend-data:/riven/data depends_on: From 26238d11eddacba98c7c81e18276cc7d91661326 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:19:55 -0500 Subject: [PATCH 034/154] Update docker-compose.yml for NVIDIA and health checks Added NVIDIA support and health checks for services. --- ubuntu/docker-compose.yml | 41 ++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index c9f7a93..54b5603 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -16,6 +16,7 @@ services: interval: 10s timeout: 5s retries: 5 + networks: [media] riven: image: spoked/riven:dev @@ -48,11 +49,7 @@ services: depends_on: riven-db: condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "curl -s http://localhost:8080 >/dev/null || exit 1"] - interval: 30s - timeout: 10s - retries: 10 + networks: [media] riven-frontend: image: spoked/riven-frontend:dev @@ -63,7 +60,6 @@ services: env_file: [.env] environment: TZ: ${TZ} - DATABASE_URL: /riven/data/riven.db BACKEND_URL: http://riven:8080 BACKEND_API_KEY: ${BACKEND_API_KEY} AUTH_SECRET: ${AUTH_SECRET} @@ -73,11 +69,7 @@ services: depends_on: riven: condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "curl -s http://localhost:3000 >/dev/null || exit 1"] - interval: 30s - timeout: 10s - retries: 5 + networks: [media] jellyfin: image: jellyfin/jellyfin @@ -88,25 +80,33 @@ services: - "8096:8096" devices: - /dev/dri:/dev/dri + environment: + NVIDIA_VISIBLE_DEVICES: all + NVIDIA_DRIVER_CAPABILITIES: compute,video,utility volumes: - /mnt/riven/mount:/media:ro - /mnt/jellyfin:/config + networks: [media] plex: image: lscr.io/linuxserver/plex container_name: plex profiles: ["plex"] restart: unless-stopped - network_mode: host - environment: - - PUID=1000 - - PGID=1000 - - VERSION=docker + ports: + - "32400:32400" devices: - /dev/dri:/dev/dri + environment: + PUID: 1000 + PGID: 1000 + VERSION: docker + NVIDIA_VISIBLE_DEVICES: all + NVIDIA_DRIVER_CAPABILITIES: compute,video,utility volumes: - /mnt/riven/mount:/media:ro - /mnt/plex:/config + networks: [media] emby: image: emby/embyserver @@ -117,10 +117,19 @@ services: - "8097:8096" devices: - /dev/dri:/dev/dri + environment: + NVIDIA_VISIBLE_DEVICES: all + NVIDIA_DRIVER_CAPABILITIES: compute,video,utility volumes: - /mnt/riven/mount:/media:ro - /mnt/emby:/config + networks: [media] volumes: riven-frontend-data: riven-pg-data: + +networks: + media: + name: media + driver: bridge From 79fbcb125ac9c3c899bbee0a678ca5ad32d6f5d1 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:51:16 -0500 Subject: [PATCH 035/154] Refactor install script for clarity and structure --- ubuntu/install.sh | 347 +++++++++++++++++++++++----------------------- 1 file changed, 171 insertions(+), 176 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index ca1cf93..55ab468 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -6,256 +6,251 @@ set -euo pipefail ############################################ INSTALL_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" -FRONTEND_PORT="3000" DEFAULT_ORIGIN="http://localhost:3000" ############################################ -# OUTPUT HELPERS +# HELPERS ############################################ -banner() { - echo - echo "========================================" - echo " $1" - echo "========================================" +banner(){ echo -e "\n========================================\n $1\n========================================"; } +ok(){ echo "[✔] $1"; } +warn(){ echo "[!] $1"; } +fail(){ echo "[✖] $1"; exit 1; } + +set_env() { + local key="$1" + local value="$2" + if grep -q "^${key}=" .env 2>/dev/null; then + sed -i "s|^${key}=.*|${key}=${value}|" .env + else + echo "${key}=${value}" >> .env + fi } -ok() { echo "[✔] $1"; } -warn() { echo "[!] $1"; } -fail() { echo "[✖] $1"; exit 1; } -############################################ -# URL VALIDATION -############################################ -is_valid_url() { - [[ "$1" =~ ^https?:// ]] +wait_for_url() { + local name="$1" + local url="$2" + banner "Waiting for $name" + until curl -fs "$url" >/dev/null; do sleep 5; done + ok "$name is online" } ############################################ # PRECHECKS ############################################ -[ "$(id -u)" -eq 0 ] || fail "Run this script as root (sudo)" +[ "$(id -u)" -eq 0 ] || fail "Run as root" . /etc/os-release || fail "Cannot detect OS" -[ "$ID" = "ubuntu" ] || fail "Ubuntu is required" +[ "$ID" = "ubuntu" ] || fail "Ubuntu required" ############################################ # TIMEZONE ############################################ -banner "Timezone Configuration" - -TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" -echo "Detected timezone: $TZ_DETECTED" - -if [ -t 0 ]; then - read -rp "Press ENTER to accept or type another (e.g. America/New_York): " TZ_INPUT - TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" -else - TZ_SELECTED="$TZ_DETECTED" -fi - -timedatectl set-timezone "$TZ_SELECTED" -ok "Timezone set to $TZ_SELECTED" +banner "Timezone" +TZ_DETECTED="$(timedatectl show --property=Timezone --value || echo UTC)" +read -rp "Timezone [$TZ_DETECTED]: " TZ_INPUT +TZ="${TZ_INPUT:-$TZ_DETECTED}" +timedatectl set-timezone "$TZ" ############################################ -# DEPENDENCIES +# DEPENDENCIES + DOCKER ############################################ -banner "Dependency Check" - -REQUIRED_CMDS=(curl openssl gpg) -MISSING=() - -for cmd in "${REQUIRED_CMDS[@]}"; do - command -v "$cmd" >/dev/null || MISSING+=("$cmd") -done - -if [ "${#MISSING[@]}" -gt 0 ]; then - warn "Installing missing packages: ${MISSING[*]}" - apt-get update - apt-get install -y ca-certificates curl gnupg lsb-release openssl -else - ok "All required system commands detected — skipping apt" -fi - -############################################ -# DOCKER -############################################ -banner "Docker Installation" +banner "System Setup" +apt-get update +apt-get install -y ca-certificates curl gnupg lsb-release openssl if ! command -v docker >/dev/null; then - warn "Docker not detected — installing" - mkdir -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ - | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ > /etc/apt/sources.list.d/docker.list - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + apt-get install -y docker-ce docker-compose-plugin systemctl enable docker -else - ok "Docker already installed" fi ############################################ -# DOCKER IPv4 ONLY -############################################ -banner "Docker IPv4 Configuration" - -mkdir -p /etc/docker -cat >/etc/docker/daemon.json </etc/systemd/system/riven-bind-shared.service < .env < Date: Mon, 5 Jan 2026 13:57:21 -0500 Subject: [PATCH 036/154] Refactor install.sh for better structure and validation Refactor install script for improved readability and functionality. Added new functions for input validation and streamlined environment variable handling. --- ubuntu/install.sh | 153 +++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 77 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 55ab468..529806d 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -17,28 +17,46 @@ warn(){ echo "[!] $1"; } fail(){ echo "[✖] $1"; exit 1; } set_env() { - local key="$1" - local value="$2" - if grep -q "^${key}=" .env 2>/dev/null; then - sed -i "s|^${key}=.*|${key}=${value}|" .env - else - echo "${key}=${value}" >> .env - fi + local key="$1" value="$2" + grep -q "^$key=" .env 2>/dev/null \ + && sed -i "s|^$key=.*|$key=$value|" .env \ + || echo "$key=$value" >> .env } wait_for_url() { - local name="$1" - local url="$2" + local name="$1" url="$2" max=60 count=0 banner "Waiting for $name" - until curl -fs "$url" >/dev/null; do sleep 5; done + until curl -fs "$url" >/dev/null; do + sleep 5 + count=$((count+1)) + [ "$count" -ge "$max" ] && fail "$name failed to start after 5 minutes" + done ok "$name is online" } +require_non_empty() { + local prompt="$1" value + while true; do + read -rsp "$prompt: " value; echo + [ -n "$value" ] && { echo "$value"; return; } + warn "Value cannot be empty" + done +} + +require_url() { + local prompt="$1" value + while true; do + read -rp "$prompt: " value + [[ "$value" =~ ^https?://[^[:space:]]+$ ]] && { echo "$value"; return; } + warn "Invalid URL" + done +} + ############################################ # PRECHECKS ############################################ -[ "$(id -u)" -eq 0 ] || fail "Run as root" -. /etc/os-release || fail "Cannot detect OS" +[ "$(id -u)" -eq 0 ] || fail "Run with sudo" +. /etc/os-release [ "$ID" = "ubuntu" ] || fail "Ubuntu required" ############################################ @@ -51,7 +69,7 @@ TZ="${TZ_INPUT:-$TZ_DETECTED}" timedatectl set-timezone "$TZ" ############################################ -# DEPENDENCIES + DOCKER +# SYSTEM + DOCKER ############################################ banner "System Setup" apt-get update @@ -59,7 +77,8 @@ apt-get install -y ca-certificates curl gnupg lsb-release openssl if ! command -v docker >/dev/null; then mkdir -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ + | gpg --dearmor -o /etc/apt/keyrings/docker.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ > /etc/apt/sources.list.d/docker.list @@ -73,7 +92,11 @@ fi ############################################ banner "Filesystem" mkdir -p /mnt/riven/{backend,mount} "$INSTALL_DIR" -chown -R 1000:1000 /mnt/riven || true + +TARGET_USER="${SUDO_USER:-}" +TARGET_UID="$(id -u "$TARGET_USER" 2>/dev/null || echo 1000)" +TARGET_GID="$(id -g "$TARGET_USER" 2>/dev/null || echo 1000)" +chown -R "$TARGET_UID:$TARGET_GID" /mnt/riven || true cat >/etc/systemd/system/riven-bind-shared.service < .env < .env < Date: Mon, 5 Jan 2026 14:11:14 -0500 Subject: [PATCH 037/154] Refactor install.sh for clarity and enhancements Refactored install script for improved readability and functionality. Enhanced URL validation, added optional parameters, and updated environment variable handling. --- ubuntu/install.sh | 442 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 332 insertions(+), 110 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 529806d..c4e6029 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -7,6 +7,7 @@ set -euo pipefail INSTALL_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" DEFAULT_ORIGIN="http://localhost:3000" +LIBRARY_PATH="/mnt/riven/backend" ############################################ # HELPERS @@ -18,198 +19,404 @@ fail(){ echo "[✖] $1"; exit 1; } set_env() { local key="$1" value="$2" - grep -q "^$key=" .env 2>/dev/null \ - && sed -i "s|^$key=.*|$key=$value|" .env \ - || echo "$key=$value" >> .env -} - -wait_for_url() { - local name="$1" url="$2" max=60 count=0 - banner "Waiting for $name" - until curl -fs "$url" >/dev/null; do - sleep 5 - count=$((count+1)) - [ "$count" -ge "$max" ] && fail "$name failed to start after 5 minutes" - done - ok "$name is online" + if grep -q "^${key}=" .env 2>/dev/null; then + sed -i "s|^${key}=.*|${key}=${value}|" .env + else + echo "${key}=${value}" >> .env + fi } require_non_empty() { local prompt="$1" value while true; do - read -rsp "$prompt: " value; echo - [ -n "$value" ] && { echo "$value"; return; } + read -rsp "${prompt}: " value; echo + if [ -n "$value" ]; then + echo "$value" + return + fi warn "Value cannot be empty" done } +# Strengthened URL validation pattern (CodeRabbit request) +# - http/https +# - domain with TLD OR localhost OR IPv4 +# - optional :port +# - optional /path require_url() { local prompt="$1" value + local regex='^https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|localhost|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$' while true; do - read -rp "$prompt: " value - [[ "$value" =~ ^https?://[^[:space:]]+$ ]] && { echo "$value"; return; } - warn "Invalid URL" + read -rp "${prompt}: " value + if [[ "$value" =~ $regex ]]; then + echo "$value" + return + fi + warn "Invalid URL (must be http(s)://host[:port][/path])" done } +wait_for_url() { + local name="$1" url="$2" max_seconds="${3:-300}" + local waited=0 + banner "Waiting for ${name} (${url})" + until curl -fs "$url" >/dev/null; do + sleep 5 + waited=$((waited + 5)) + if [ "$waited" -ge "$max_seconds" ]; then + fail "${name} failed to become reachable after ${max_seconds}s" + fi + done + ok "${name} is reachable" +} + ############################################ # PRECHECKS ############################################ [ "$(id -u)" -eq 0 ] || fail "Run with sudo" -. /etc/os-release -[ "$ID" = "ubuntu" ] || fail "Ubuntu required" +. /etc/os-release || fail "Cannot detect OS" +[ "${ID:-}" = "ubuntu" ] || fail "Ubuntu is required" ############################################ # TIMEZONE ############################################ banner "Timezone" -TZ_DETECTED="$(timedatectl show --property=Timezone --value || echo UTC)" -read -rp "Timezone [$TZ_DETECTED]: " TZ_INPUT -TZ="${TZ_INPUT:-$TZ_DETECTED}" -timedatectl set-timezone "$TZ" +TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" +read -rp "Timezone [${TZ_DETECTED}]: " TZ_INPUT +TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" +timedatectl set-timezone "$TZ_SELECTED" +ok "Timezone set: $TZ_SELECTED" ############################################ -# SYSTEM + DOCKER +# DEPENDENCIES ############################################ -banner "System Setup" +banner "System Dependencies" apt-get update apt-get install -y ca-certificates curl gnupg lsb-release openssl -if ! command -v docker >/dev/null; then +############################################ +# DOCKER +############################################ +banner "Docker" +if ! command -v docker >/dev/null 2>&1; then + warn "Docker not detected — installing" mkdir -p /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg \ - | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ > /etc/apt/sources.list.d/docker.list + apt-get update - apt-get install -y docker-ce docker-compose-plugin + apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable docker +else + ok "Docker already installed" fi ############################################ -# FILESYSTEM + MOUNT +# DOCKER IPv4 ONLY (kept from your script) +############################################ +banner "Docker IPv4 Configuration" +mkdir -p /etc/docker +cat >/etc/docker/daemon.json </dev/null || echo 1000)" TARGET_GID="$(id -g "$TARGET_USER" 2>/dev/null || echo 1000)" -chown -R "$TARGET_UID:$TARGET_GID" /mnt/riven || true +# IMPORTANT: no "|| true" (CodeRabbit requirement) — surface permission issues +chown -R "$TARGET_UID:$TARGET_GID" /mnt/riven + +ok "Backend path: /mnt/riven/backend" +ok "Mount path: /mnt/riven/mount" +ok "Install dir: $INSTALL_DIR" + +banner "Mount Propagation (rshared)" cat >/etc/systemd/system/riven-bind-shared.service < .env < .env <= 1 ############################################ -banner "Downloaders (REQUIRED)" +banner "Downloader Selection (REQUIRED)" + +# reset to false each run, then enable selections +set_env RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED "false" +set_env RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED "false" +set_env RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED "false" + DL_OK=false while ! $DL_OK; do echo "1) Real-Debrid" echo "2) All-Debrid" echo "3) Debrid-Link" - read -rp "Select (space-separated): " DL - for d in $DL; do - case "$d" in - 1) set_env RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED true - set_env RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY "$(require_non_empty "Real-Debrid API Key")" - DL_OK=true;; - 2) set_env RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED true - set_env RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY "$(require_non_empty "All-Debrid API Key")" - DL_OK=true;; - 3) set_env RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED true - set_env RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY "$(require_non_empty "Debrid-Link API Key")" - DL_OK=true;; + read -rp "Select at least ONE downloader (space-separated): " DL_SEL + + for sel in $DL_SEL; do + case "$sel" in + 1) + set_env RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED "true" + set_env RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY "$(require_non_empty "Real-Debrid API Key")" + DL_OK=true + ;; + 2) + set_env RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED "true" + set_env RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY "$(require_non_empty "All-Debrid API Key")" + DL_OK=true + ;; + 3) + set_env RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED "true" + set_env RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY "$(require_non_empty "Debrid-Link API Key")" + DL_OK=true + ;; esac done - $DL_OK || warn "Downloader REQUIRED" + + $DL_OK || warn "At least ONE downloader is REQUIRED" done ############################################ -# SCRAPERS (REQUIRED) +# SCRAPERS (REQUIRED) — >= 1 ############################################ -banner "Scrapers (REQUIRED)" +banner "Scraper Selection (REQUIRED)" + +# reset to false each run, then enable selections +set_env RIVEN_SCRAPING_TORRENTIO_ENABLED "false" +set_env RIVEN_SCRAPING_PROWLARR_ENABLED "false" +set_env RIVEN_SCRAPING_ZILEAN_ENABLED "false" +set_env RIVEN_SCRAPING_COMET_ENABLED "false" +set_env RIVEN_SCRAPING_JACKETT_ENABLED "false" + SC_OK=false while ! $SC_OK; do echo "1) Torrentio" @@ -217,39 +424,54 @@ while ! $SC_OK; do echo "3) Zilean" echo "4) Comet" echo "5) Jackett" - read -rp "Select (space-separated): " SC - for s in $SC; do - case "$s" in - 1) set_env RIVEN_SCRAPING_TORRENTIO_ENABLED true; SC_OK=true;; - 2) set_env RIVEN_SCRAPING_PROWLARR_ENABLED true - set_env RIVEN_SCRAPING_PROWLARR_URL "$(require_url "Prowlarr URL")" - set_env RIVEN_SCRAPING_PROWLARR_API_KEY "$(require_non_empty "Prowlarr API Key")" - SC_OK=true;; - 3) set_env RIVEN_SCRAPING_ZILEAN_ENABLED true - set_env RIVEN_SCRAPING_ZILEAN_URL "$(require_url "Zilean URL")" - SC_OK=true;; - 4) set_env RIVEN_SCRAPING_COMET_ENABLED true - set_env RIVEN_SCRAPING_COMET_URL "$(require_url "Comet URL")" - SC_OK=true;; - 5) set_env RIVEN_SCRAPING_JACKETT_ENABLED true - set_env RIVEN_SCRAPING_JACKETT_URL "$(require_url "Jackett URL")" - set_env RIVEN_SCRAPING_JACKETT_API_KEY "$(require_non_empty "Jackett API Key")" - SC_OK=true;; + read -rp "Select at least ONE scraper (space-separated): " SC_SEL + + for sel in $SC_SEL; do + case "$sel" in + 1) + set_env RIVEN_SCRAPING_TORRENTIO_ENABLED "true" + # ratelimit stays true by default in env template + SC_OK=true + ;; + 2) + set_env RIVEN_SCRAPING_PROWLARR_ENABLED "true" + set_env RIVEN_SCRAPING_PROWLARR_URL "$(require_url "Prowlarr URL")" + set_env RIVEN_SCRAPING_PROWLARR_API_KEY "$(require_non_empty "Prowlarr API Key")" + SC_OK=true + ;; + 3) + set_env RIVEN_SCRAPING_ZILEAN_ENABLED "true" + set_env RIVEN_SCRAPING_ZILEAN_URL "$(require_url "Zilean URL")" + SC_OK=true + ;; + 4) + set_env RIVEN_SCRAPING_COMET_ENABLED "true" + set_env RIVEN_SCRAPING_COMET_URL "$(require_url "Comet URL")" + SC_OK=true + ;; + 5) + set_env RIVEN_SCRAPING_JACKETT_ENABLED "true" + set_env RIVEN_SCRAPING_JACKETT_URL "$(require_url "Jackett URL")" + set_env RIVEN_SCRAPING_JACKETT_API_KEY "$(require_non_empty "Jackett API Key")" + SC_OK=true + ;; esac done - $SC_OK || warn "Scraper REQUIRED" + + $SC_OK || warn "At least ONE scraper is REQUIRED" done ############################################ -# START STACK +# STARTUP ORDER (REQUIRED): MEDIA -> RIVEN ############################################ -banner "Starting Media Server" +banner "Starting Media Server First" docker compose --profile "$MEDIA_PROFILE" up -d -wait_for_url "$MEDIA_PROFILE" "$HEALTH_URL" +wait_for_url "$MEDIA_PROFILE" "$MEDIA_HEALTH_URL" 300 banner "Starting Riven" docker compose pull docker compose up -d riven-db riven riven-frontend banner "Install Complete" -ok "Riven is fully configured and running" +ok "Riven is configured and running" +ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" From 895d33b36eff22e9e00a6f7afce26ff8305e98a0 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:12:57 +0000 Subject: [PATCH 038/154] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20?= =?UTF-8?q?`AquaHorizonGaming-patch-1`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @AquaHorizonGaming. * https://github.com/AquaHorizonGaming/distributables/pull/1#issuecomment-3711750383 The following files were modified: * `ubuntu/install.sh` --- ubuntu/install.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index c4e6029..f9398f8 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -11,12 +11,17 @@ LIBRARY_PATH="/mnt/riven/backend" ############################################ # HELPERS -############################################ +# banner prints a framed banner that displays the provided message. banner(){ echo -e "\n========================================\n $1\n========================================"; } +# ok prints a success message prefixed with a checkmark and the provided text. ok(){ echo "[✔] $1"; } +# warn prints a warning message prefixed with "[!]" followed by the provided text. warn(){ echo "[!] $1"; } +# fail prints an error message prefixed with "[✖]" and exits the script with status 1. fail(){ echo "[✖] $1"; exit 1; } +# set_env updates or appends a key=value pair in the .env file. +# key is the variable name to set; value is the value to assign. If the key exists its line is replaced, otherwise a new line is appended. set_env() { local key="$1" value="$2" if grep -q "^${key}=" .env 2>/dev/null; then @@ -26,6 +31,7 @@ set_env() { fi } +# require_non_empty prompts repeatedly with the given prompt until the user provides a non-empty value, then echoes that value. require_non_empty() { local prompt="$1" value while true; do @@ -42,7 +48,7 @@ require_non_empty() { # - http/https # - domain with TLD OR localhost OR IPv4 # - optional :port -# - optional /path +# require_url prompts for a URL using the given prompt, validates that it is http:// or https:// with a hostname (including TLD), `localhost`, or IPv4 address and optional port/path, echoes the validated URL, and repeats until a valid URL is entered. require_url() { local prompt="$1" value local regex='^https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|localhost|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$' @@ -56,6 +62,7 @@ require_url() { done } +# wait_for_url waits for the given named URL to become reachable by polling until the optional timeout (in seconds) is reached; on timeout it fails and exits with status 1. wait_for_url() { local name="$1" url="$2" max_seconds="${3:-300}" local waited=0 @@ -474,4 +481,4 @@ docker compose up -d riven-db riven riven-frontend banner "Install Complete" ok "Riven is configured and running" -ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" +ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" \ No newline at end of file From 3a7fdf9caa697178c8cfb07214c6e93b5161f982 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:23:39 -0500 Subject: [PATCH 039/154] Add environment variables for updaters and downloaders --- ubuntu/docker-compose.yml | 73 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 54b5603..abab0e1 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -32,17 +32,88 @@ services: - apparmor:unconfined devices: - /dev/fuse + env_file: [.env] + environment: + # ========================= + # CORE / RUNTIME + # ========================= TZ: ${TZ} PUID: 1000 PGID: 1000 RIVEN_FORCE_ENV: "true" RIVEN_API_KEY: ${BACKEND_API_KEY} + + # ========================= + # DATABASE + # ========================= RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} + + # ========================= + # FILESYSTEM + # ========================= RIVEN_FILESYSTEM_MOUNT_PATH: /mount - RIVEN_UPDATERS_LIBRARY_PATH: /mnt/riven RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache + + # ========================= + # UPDATERS — CORE + # ========================= + RIVEN_UPDATERS_LIBRARY_PATH: ${RIVEN_UPDATERS_LIBRARY_PATH} + RIVEN_UPDATERS_UPDATER_INTERVAL: ${RIVEN_UPDATERS_UPDATER_INTERVAL} + + # ========================= + # UPDATERS — JELLYFIN + # ========================= + RIVEN_UPDATERS_JELLYFIN_ENABLED: ${RIVEN_UPDATERS_JELLYFIN_ENABLED} + RIVEN_UPDATERS_JELLYFIN_URL: ${RIVEN_UPDATERS_JELLYFIN_URL} + RIVEN_UPDATERS_JELLYFIN_API_KEY: ${RIVEN_UPDATERS_JELLYFIN_API_KEY} + + # ========================= + # UPDATERS — PLEX + # ========================= + RIVEN_UPDATERS_PLEX_ENABLED: ${RIVEN_UPDATERS_PLEX_ENABLED} + RIVEN_UPDATERS_PLEX_URL: ${RIVEN_UPDATERS_PLEX_URL} + RIVEN_UPDATERS_PLEX_TOKEN: ${RIVEN_UPDATERS_PLEX_TOKEN} + + # ========================= + # UPDATERS — EMBY + # ========================= + RIVEN_UPDATERS_EMBY_ENABLED: ${RIVEN_UPDATERS_EMBY_ENABLED} + RIVEN_UPDATERS_EMBY_URL: ${RIVEN_UPDATERS_EMBY_URL} + RIVEN_UPDATERS_EMBY_API_KEY: ${RIVEN_UPDATERS_EMBY_API_KEY} + + # ========================= + # DOWNLOADERS + # ========================= + RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED: ${RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED} + RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY: ${RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY} + + RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED: ${RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED} + RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY: ${RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY} + + RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED: ${RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED} + RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY: ${RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY} + + # ========================= + # SCRAPERS + # ========================= + RIVEN_SCRAPING_TORRENTIO_ENABLED: ${RIVEN_SCRAPING_TORRENTIO_ENABLED} + + RIVEN_SCRAPING_PROWLARR_ENABLED: ${RIVEN_SCRAPING_PROWLARR_ENABLED} + RIVEN_SCRAPING_PROWLARR_URL: ${RIVEN_SCRAPING_PROWLARR_URL} + RIVEN_SCRAPING_PROWLARR_API_KEY: ${RIVEN_SCRAPING_PROWLARR_API_KEY} + + RIVEN_SCRAPING_ZILEAN_ENABLED: ${RIVEN_SCRAPING_ZILEAN_ENABLED} + RIVEN_SCRAPING_ZILEAN_URL: ${RIVEN_SCRAPING_ZILEAN_URL} + + RIVEN_SCRAPING_COMET_ENABLED: ${RIVEN_SCRAPING_COMET_ENABLED} + RIVEN_SCRAPING_COMET_URL: ${RIVEN_SCRAPING_COMET_URL} + + RIVEN_SCRAPING_JACKETT_ENABLED: ${RIVEN_SCRAPING_JACKETT_ENABLED} + RIVEN_SCRAPING_JACKETT_URL: ${RIVEN_SCRAPING_JACKETT_URL} + RIVEN_SCRAPING_JACKETT_API_KEY: ${RIVEN_SCRAPING_JACKETT_API_KEY} + volumes: - /mnt/riven/backend:/riven/data - /mnt/riven/mount:/mount:rshared,z From 81784871cdfbe13bbf9806be94916b74366955bc Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:38:05 -0500 Subject: [PATCH 040/154] Refactor media server setup in install.sh Updated install script to improve media server setup and configuration. Adjusted order of operations for starting services and added checks for environment variables. --- ubuntu/install.sh | 136 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 107 insertions(+), 29 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index f9398f8..1e847a0 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -11,6 +11,7 @@ LIBRARY_PATH="/mnt/riven/backend" ############################################ # HELPERS +############################################ # banner prints a framed banner that displays the provided message. banner(){ echo -e "\n========================================\n $1\n========================================"; } # ok prints a success message prefixed with a checkmark and the provided text. @@ -77,6 +78,14 @@ wait_for_url() { ok "${name} is reachable" } +############################################ +# ARGUMENTS +############################################ +MODE="${1:-install}" +if [[ "$MODE" != "install" && "$MODE" != "--update" ]]; then + fail "Usage: sudo bash install.sh [--update]" +fi + ############################################ # PRECHECKS ############################################ @@ -84,6 +93,39 @@ wait_for_url() { . /etc/os-release || fail "Cannot detect OS" [ "${ID:-}" = "ubuntu" ] || fail "Ubuntu is required" +############################################ +# UPDATE MODE (NO REINSTALL) +############################################ +if [[ "$MODE" == "--update" ]]; then + banner "Update Mode" + if [ ! -d "$INSTALL_DIR" ]; then + fail "Install dir not found: $INSTALL_DIR (run install first)" + fi + if ! command -v docker >/dev/null 2>&1; then + fail "Docker not installed (run install first)" + fi + + cd "$INSTALL_DIR" + if [ ! -f docker-compose.yml ] || [ ! -f .env ]; then + fail "Missing docker-compose.yml or .env in $INSTALL_DIR" + fi + + # If we saved MEDIA_PROFILE earlier, prefer it so media starts first + MEDIA_PROFILE="$(grep -E '^MEDIA_PROFILE=' .env | cut -d= -f2- || true)" + if [ -n "$MEDIA_PROFILE" ]; then + banner "Starting Media Server First (saved: $MEDIA_PROFILE)" + docker compose --profile "$MEDIA_PROFILE" up -d + else + warn "MEDIA_PROFILE not found in .env — media-first order cannot be enforced automatically" + fi + + banner "Updating Riven Stack" + docker compose pull + docker compose up -d + ok "Update complete" + exit 0 +fi + ############################################ # TIMEZONE ############################################ @@ -101,6 +143,18 @@ banner "System Dependencies" apt-get update apt-get install -y ca-certificates curl gnupg lsb-release openssl +############################################ +# FUSE +############################################ +banner "FUSE" +if [ ! -e /dev/fuse ]; then + warn "/dev/fuse not found — installing fuse" + apt-get install -y fuse3 || apt-get install -y fuse +fi +[ -e /dev/fuse ] || fail "FUSE is required but /dev/fuse still not present" + +ok "FUSE available" + ############################################ # DOCKER ############################################ @@ -201,9 +255,11 @@ if [ ! -f .env ]; then cat > .env <= 1 ############################################ @@ -471,14 +552,11 @@ done ############################################ # STARTUP ORDER (REQUIRED): MEDIA -> RIVEN ############################################ -banner "Starting Media Server First" -docker compose --profile "$MEDIA_PROFILE" up -d -wait_for_url "$MEDIA_PROFILE" "$MEDIA_HEALTH_URL" 300 - -banner "Starting Riven" +banner "Starting Riven (after media is ready)" docker compose pull docker compose up -d riven-db riven riven-frontend banner "Install Complete" ok "Riven is configured and running" -ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" \ No newline at end of file +ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" +ok "Backend: http://localhost:8080" From 5e64e8a7048e579ebdd1cab1e7192f677c500904 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:39:14 +0000 Subject: [PATCH 041/154] =?UTF-8?q?=F0=9F=93=9D=20Add=20docstrings=20to=20?= =?UTF-8?q?`AquaHorizonGaming-patch-2`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docstrings generation was requested by @AquaHorizonGaming. * https://github.com/AquaHorizonGaming/distributables/pull/3#issuecomment-3711832910 The following files were modified: * `ubuntu/install.sh` --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 1e847a0..388b64e 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -559,4 +559,4 @@ docker compose up -d riven-db riven riven-frontend banner "Install Complete" ok "Riven is configured and running" ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" -ok "Backend: http://localhost:8080" +ok "Backend: http://localhost:8080" \ No newline at end of file From 4dcad2063d28aba56829243aebfac22c07e73e6a Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:54:42 -0500 Subject: [PATCH 042/154] Refactor install.sh for clarity and structure Refactor install script to improve readability and maintainability. Update comments, rename functions, and streamline user prompts for media server and downloader selections. --- ubuntu/install.sh | 586 +++++++++++++--------------------------------- 1 file changed, 166 insertions(+), 420 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 388b64e..a1147a2 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -2,7 +2,7 @@ set -euo pipefail ############################################ -# CONFIG +# CONSTANTS ############################################ INSTALL_DIR="/opt/riven" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" @@ -12,551 +12,297 @@ LIBRARY_PATH="/mnt/riven/backend" ############################################ # HELPERS ############################################ -# banner prints a framed banner that displays the provided message. banner(){ echo -e "\n========================================\n $1\n========================================"; } -# ok prints a success message prefixed with a checkmark and the provided text. ok(){ echo "[✔] $1"; } -# warn prints a warning message prefixed with "[!]" followed by the provided text. warn(){ echo "[!] $1"; } -# fail prints an error message prefixed with "[✖]" and exits the script with status 1. fail(){ echo "[✖] $1"; exit 1; } -# set_env updates or appends a key=value pair in the .env file. -# key is the variable name to set; value is the value to assign. If the key exists its line is replaced, otherwise a new line is appended. -set_env() { - local key="$1" value="$2" - if grep -q "^${key}=" .env 2>/dev/null; then - sed -i "s|^${key}=.*|${key}=${value}|" .env - else - echo "${key}=${value}" >> .env - fi -} - -# require_non_empty prompts repeatedly with the given prompt until the user provides a non-empty value, then echoes that value. require_non_empty() { local prompt="$1" value while true; do - read -rsp "${prompt}: " value; echo - if [ -n "$value" ]; then - echo "$value" - return - fi - warn "Value cannot be empty" + read -rsp "$prompt: " value; echo + [[ -n "$value" ]] && { echo "$value"; return; } + warn "Value required" done } -# Strengthened URL validation pattern (CodeRabbit request) -# - http/https -# - domain with TLD OR localhost OR IPv4 -# - optional :port -# require_url prompts for a URL using the given prompt, validates that it is http:// or https:// with a hostname (including TLD), `localhost`, or IPv4 address and optional port/path, echoes the validated URL, and repeats until a valid URL is entered. require_url() { local prompt="$1" value local regex='^https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|localhost|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$' while true; do - read -rp "${prompt}: " value - if [[ "$value" =~ $regex ]]; then - echo "$value" - return - fi - warn "Invalid URL (must be http(s)://host[:port][/path])" + read -rp "$prompt: " value + [[ "$value" =~ $regex ]] && { echo "$value"; return; } + warn "Invalid URL" done } -# wait_for_url waits for the given named URL to become reachable by polling until the optional timeout (in seconds) is reached; on timeout it fails and exits with status 1. wait_for_url() { - local name="$1" url="$2" max_seconds="${3:-300}" - local waited=0 - banner "Waiting for ${name} (${url})" + local name="$1" url="$2" max="${3:-300}" waited=0 + banner "Waiting for $name" until curl -fs "$url" >/dev/null; do sleep 5 - waited=$((waited + 5)) - if [ "$waited" -ge "$max_seconds" ]; then - fail "${name} failed to become reachable after ${max_seconds}s" - fi + waited=$((waited+5)) + [[ "$waited" -ge "$max" ]] && fail "$name not reachable" done - ok "${name} is reachable" + ok "$name reachable" } ############################################ -# ARGUMENTS +# ARG MODE ############################################ MODE="${1:-install}" -if [[ "$MODE" != "install" && "$MODE" != "--update" ]]; then - fail "Usage: sudo bash install.sh [--update]" -fi - -############################################ -# PRECHECKS -############################################ -[ "$(id -u)" -eq 0 ] || fail "Run with sudo" -. /etc/os-release || fail "Cannot detect OS" -[ "${ID:-}" = "ubuntu" ] || fail "Ubuntu is required" +[[ "$MODE" == "install" || "$MODE" == "--update" ]] || fail "Usage: install.sh [--update]" ############################################ -# UPDATE MODE (NO REINSTALL) +# UPDATE MODE (PROFILE SAFE) ############################################ if [[ "$MODE" == "--update" ]]; then - banner "Update Mode" - if [ ! -d "$INSTALL_DIR" ]; then - fail "Install dir not found: $INSTALL_DIR (run install first)" - fi - if ! command -v docker >/dev/null 2>&1; then - fail "Docker not installed (run install first)" - fi + cd "$INSTALL_DIR" || fail "Missing install dir" + MEDIA_PROFILE="$(grep '^MEDIA_PROFILE=' .env | cut -d= -f2- || true)" - cd "$INSTALL_DIR" - if [ ! -f docker-compose.yml ] || [ ! -f .env ]; then - fail "Missing docker-compose.yml or .env in $INSTALL_DIR" - fi + banner "Update Mode" - # If we saved MEDIA_PROFILE earlier, prefer it so media starts first - MEDIA_PROFILE="$(grep -E '^MEDIA_PROFILE=' .env | cut -d= -f2- || true)" - if [ -n "$MEDIA_PROFILE" ]; then - banner "Starting Media Server First (saved: $MEDIA_PROFILE)" + if [[ -n "$MEDIA_PROFILE" ]]; then + docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" + docker compose --profile "$MEDIA_PROFILE" pull docker compose --profile "$MEDIA_PROFILE" up -d else - warn "MEDIA_PROFILE not found in .env — media-first order cannot be enforced automatically" + docker compose pull + docker compose up -d fi - banner "Updating Riven Stack" - docker compose pull - docker compose up -d ok "Update complete" exit 0 fi +############################################ +# PRECHECKS +############################################ +[ "$(id -u)" -eq 0 ] || fail "Run with sudo" +. /etc/os-release +[[ "${ID}" == "ubuntu" ]] || fail "Ubuntu required" + ############################################ # TIMEZONE ############################################ banner "Timezone" -TZ_DETECTED="$(timedatectl show --property=Timezone --value 2>/dev/null || echo UTC)" -read -rp "Timezone [${TZ_DETECTED}]: " TZ_INPUT +TZ_DETECTED="$(timedatectl show --property=Timezone --value || echo UTC)" +read -rp "Timezone [$TZ_DETECTED]: " TZ_INPUT TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" timedatectl set-timezone "$TZ_SELECTED" -ok "Timezone set: $TZ_SELECTED" ############################################ -# DEPENDENCIES +# SYSTEM DEPENDENCIES ############################################ banner "System Dependencies" apt-get update -apt-get install -y ca-certificates curl gnupg lsb-release openssl - -############################################ -# FUSE -############################################ -banner "FUSE" -if [ ! -e /dev/fuse ]; then - warn "/dev/fuse not found — installing fuse" - apt-get install -y fuse3 || apt-get install -y fuse -fi -[ -e /dev/fuse ] || fail "FUSE is required but /dev/fuse still not present" - -ok "FUSE available" +apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 ############################################ # DOCKER ############################################ banner "Docker" -if ! command -v docker >/dev/null 2>&1; then - warn "Docker not detected — installing" - mkdir -p /etc/apt/keyrings +if ! command -v docker >/dev/null; then curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ > /etc/apt/sources.list.d/docker.list - apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - systemctl enable docker -else - ok "Docker already installed" + systemctl enable --now docker fi ############################################ -# DOCKER IPv4 ONLY (kept from your script) +# FILESYSTEM ############################################ -banner "Docker IPv4 Configuration" -mkdir -p /etc/docker -cat >/etc/docker/daemon.json </dev/null || echo 1000)" -TARGET_GID="$(id -g "$TARGET_USER" 2>/dev/null || echo 1000)" +############################################ +# ============================= +# PHASE 1 — USER INPUT ONLY +# ============================= +############################################ -# IMPORTANT: no "|| true" (CodeRabbit requirement) — surface permission issues -chown -R "$TARGET_UID:$TARGET_GID" /mnt/riven +############################################ +# MEDIA SERVER (REQUIRED) +############################################ +banner "Media Server Selection (REQUIRED)" +echo "1) Jellyfin" +echo "2) Plex" +echo "3) Emby" +read -rp "Select ONE: " MEDIA_SEL + +case "$MEDIA_SEL" in + 1) MEDIA_PROFILE="jellyfin"; MEDIA_URL="http://localhost:8096" ;; + 2) MEDIA_PROFILE="plex"; MEDIA_URL="http://localhost:32400" ;; + 3) MEDIA_PROFILE="emby"; MEDIA_URL="http://localhost:8097" ;; + *) fail "Media server REQUIRED" ;; +esac -ok "Backend path: /mnt/riven/backend" -ok "Mount path: /mnt/riven/mount" -ok "Install dir: $INSTALL_DIR" +############################################ +# START MEDIA ONLY +############################################ +docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" +wait_for_url "$MEDIA_PROFILE" "$MEDIA_URL" -banner "Mount Propagation (rshared)" -cat >/etc/systemd/system/riven-bind-shared.service < .env < .env <= 1 -############################################ -banner "Downloader Selection (REQUIRED)" - -# reset to false each run, then enable selections -set_env RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED "false" -set_env RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED "false" -set_env RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED "false" - -DL_OK=false -while ! $DL_OK; do - echo "1) Real-Debrid" - echo "2) All-Debrid" - echo "3) Debrid-Link" - read -rp "Select at least ONE downloader (space-separated): " DL_SEL - - for sel in $DL_SEL; do - case "$sel" in - 1) - set_env RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED "true" - set_env RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY "$(require_non_empty "Real-Debrid API Key")" - DL_OK=true - ;; - 2) - set_env RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED "true" - set_env RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY "$(require_non_empty "All-Debrid API Key")" - DL_OK=true - ;; - 3) - set_env RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED "true" - set_env RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY "$(require_non_empty "Debrid-Link API Key")" - DL_OK=true - ;; - esac - done - - $DL_OK || warn "At least ONE downloader is REQUIRED" -done - -############################################ -# SCRAPERS (REQUIRED) — >= 1 -############################################ -banner "Scraper Selection (REQUIRED)" - -# reset to false each run, then enable selections -set_env RIVEN_SCRAPING_TORRENTIO_ENABLED "false" -set_env RIVEN_SCRAPING_PROWLARR_ENABLED "false" -set_env RIVEN_SCRAPING_ZILEAN_ENABLED "false" -set_env RIVEN_SCRAPING_COMET_ENABLED "false" -set_env RIVEN_SCRAPING_JACKETT_ENABLED "false" - -SC_OK=false -while ! $SC_OK; do - echo "1) Torrentio" - echo "2) Prowlarr" - echo "3) Zilean" - echo "4) Comet" - echo "5) Jackett" - read -rp "Select at least ONE scraper (space-separated): " SC_SEL - - for sel in $SC_SEL; do - case "$sel" in - 1) - set_env RIVEN_SCRAPING_TORRENTIO_ENABLED "true" - # ratelimit stays true by default in env template - SC_OK=true - ;; - 2) - set_env RIVEN_SCRAPING_PROWLARR_ENABLED "true" - set_env RIVEN_SCRAPING_PROWLARR_URL "$(require_url "Prowlarr URL")" - set_env RIVEN_SCRAPING_PROWLARR_API_KEY "$(require_non_empty "Prowlarr API Key")" - SC_OK=true - ;; - 3) - set_env RIVEN_SCRAPING_ZILEAN_ENABLED "true" - set_env RIVEN_SCRAPING_ZILEAN_URL "$(require_url "Zilean URL")" - SC_OK=true - ;; - 4) - set_env RIVEN_SCRAPING_COMET_ENABLED "true" - set_env RIVEN_SCRAPING_COMET_URL "$(require_url "Comet URL")" - SC_OK=true - ;; - 5) - set_env RIVEN_SCRAPING_JACKETT_ENABLED "true" - set_env RIVEN_SCRAPING_JACKETT_URL "$(require_url "Jackett URL")" - set_env RIVEN_SCRAPING_JACKETT_API_KEY "$(require_non_empty "Jackett API Key")" - SC_OK=true - ;; - esac - done +case "$DL_TYPE" in + REAL_DEBRID) + sed -i "s/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true/" .env + sed -i "s|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=$DL_KEY|" .env + ;; + ALL_DEBRID) + sed -i "s/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true/" .env + sed -i "s|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=$DL_KEY|" .env + ;; + DEBRID_LINK) + sed -i "s/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=false/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true/" .env + sed -i "s|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=.*|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=$DL_KEY|" .env + ;; +esac - $SC_OK || warn "At least ONE scraper is REQUIRED" -done +if [[ "$SCRAPER" == "TORRENTIO" ]]; then + sed -i "s/RIVEN_SCRAPING_TORRENTIO_ENABLED=false/RIVEN_SCRAPING_TORRENTIO_ENABLED=true/" .env +else + sed -i "s/RIVEN_SCRAPING_PROWLARR_ENABLED=false/RIVEN_SCRAPING_PROWLARR_ENABLED=true/" .env + sed -i "s|RIVEN_SCRAPING_PROWLARR_URL=.*|RIVEN_SCRAPING_PROWLARR_URL=$PROWLARR_URL|" .env + sed -i "s|RIVEN_SCRAPING_PROWLARR_API_KEY=.*|RIVEN_SCRAPING_PROWLARR_API_KEY=$PROWLARR_KEY|" .env +fi ############################################ -# STARTUP ORDER (REQUIRED): MEDIA -> RIVEN +# ============================= +# PHASE 3 — START RIVEN +# ============================= ############################################ -banner "Starting Riven (after media is ready)" +banner "Starting Riven Stack" docker compose pull docker compose up -d riven-db riven riven-frontend -banner "Install Complete" -ok "Riven is configured and running" -ok "Frontend: http://localhost:3000 (ORIGIN=${ORIGIN_SELECTED})" -ok "Backend: http://localhost:8080" \ No newline at end of file +banner "INSTALL COMPLETE" +ok "Frontend: http://localhost:3000" +ok "Backend: http://localhost:8080" From 15e0582552bca0a1c250a494384abba5d1bb7174 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 14:58:35 -0500 Subject: [PATCH 043/154] Remove /dev/dri device mapping from services Removed device mapping for /dev/dri from jellyfin, plex, and emby services. --- ubuntu/docker-compose.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index abab0e1..f851118 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -149,8 +149,6 @@ services: restart: unless-stopped ports: - "8096:8096" - devices: - - /dev/dri:/dev/dri environment: NVIDIA_VISIBLE_DEVICES: all NVIDIA_DRIVER_CAPABILITIES: compute,video,utility @@ -166,8 +164,6 @@ services: restart: unless-stopped ports: - "32400:32400" - devices: - - /dev/dri:/dev/dri environment: PUID: 1000 PGID: 1000 @@ -186,8 +182,6 @@ services: restart: unless-stopped ports: - "8097:8096" - devices: - - /dev/dri:/dev/dri environment: NVIDIA_VISIBLE_DEVICES: all NVIDIA_DRIVER_CAPABILITIES: compute,video,utility From 53c67dbccbf6e5a169f214c66326b2cad39127ee Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:01:54 -0500 Subject: [PATCH 044/154] Update docker-compose.yml --- ubuntu/docker-compose.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index f851118..1c80dba 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -155,7 +155,9 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/jellyfin:/config - networks: [media] + networks: + - media + - bridge plex: image: lscr.io/linuxserver/plex @@ -173,7 +175,9 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/plex:/config - networks: [media] + networks: + - media + - bridge emby: image: emby/embyserver @@ -188,7 +192,9 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/emby:/config - networks: [media] + networks: + - media + - bridge volumes: riven-frontend-data: @@ -198,3 +204,6 @@ networks: media: name: media driver: bridge + bridge: + external: true + name: bridge From b71ec72bc2058a12cc37aa0299d91e50e9718946 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:19:58 -0500 Subject: [PATCH 045/154] Update install.sh --- ubuntu/install.sh | 146 ++++++++++++++++++---------------------------- 1 file changed, 57 insertions(+), 89 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index a1147a2..b861aa3 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -5,9 +5,10 @@ set -euo pipefail # CONSTANTS ############################################ INSTALL_DIR="/opt/riven" +BACKEND_PATH="/mnt/riven/backend" +MOUNT_PATH="/mnt/riven/mount" COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" DEFAULT_ORIGIN="http://localhost:3000" -LIBRARY_PATH="/mnt/riven/backend" ############################################ # HELPERS @@ -17,6 +18,10 @@ ok(){ echo "[✔] $1"; } warn(){ echo "[!] $1"; } fail(){ echo "[✖] $1"; exit 1; } +escape_sed() { + printf '%s' "$1" | sed 's/[\/&|]/\\&/g' +} + require_non_empty() { local prompt="$1" value while true; do @@ -28,59 +33,25 @@ require_non_empty() { require_url() { local prompt="$1" value - local regex='^https?://([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|localhost|([0-9]{1,3}\.){3}[0-9]{1,3})(:[0-9]{1,5})?(/.*)?$' + local regex='^https?://[^[:space:]]+$' while true; do read -rp "$prompt: " value [[ "$value" =~ $regex ]] && { echo "$value"; return; } - warn "Invalid URL" + warn "Invalid URL (must include http:// or https://)" done } wait_for_url() { - local name="$1" url="$2" max="${3:-300}" waited=0 + local name="$1" url="$2" banner "Waiting for $name" - until curl -fs "$url" >/dev/null; do - sleep 5 - waited=$((waited+5)) - [[ "$waited" -ge "$max" ]] && fail "$name not reachable" - done - ok "$name reachable" + until curl -fs "$url" >/dev/null; do sleep 5; done + ok "$name is reachable" } ############################################ -# ARG MODE -############################################ -MODE="${1:-install}" -[[ "$MODE" == "install" || "$MODE" == "--update" ]] || fail "Usage: install.sh [--update]" - -############################################ -# UPDATE MODE (PROFILE SAFE) -############################################ -if [[ "$MODE" == "--update" ]]; then - cd "$INSTALL_DIR" || fail "Missing install dir" - MEDIA_PROFILE="$(grep '^MEDIA_PROFILE=' .env | cut -d= -f2- || true)" - - banner "Update Mode" - - if [[ -n "$MEDIA_PROFILE" ]]; then - docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" - docker compose --profile "$MEDIA_PROFILE" pull - docker compose --profile "$MEDIA_PROFILE" up -d - else - docker compose pull - docker compose up -d - fi - - ok "Update complete" - exit 0 -fi - -############################################ -# PRECHECKS +# ROOT CHECK ############################################ [ "$(id -u)" -eq 0 ] || fail "Run with sudo" -. /etc/os-release -[[ "${ID}" == "ubuntu" ]] || fail "Ubuntu required" ############################################ # TIMEZONE @@ -90,9 +61,10 @@ TZ_DETECTED="$(timedatectl show --property=Timezone --value || echo UTC)" read -rp "Timezone [$TZ_DETECTED]: " TZ_INPUT TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" timedatectl set-timezone "$TZ_SELECTED" +ok "Timezone set: $TZ_SELECTED" ############################################ -# SYSTEM DEPENDENCIES +# SYSTEM DEPS ############################################ banner "System Dependencies" apt-get update @@ -111,13 +83,15 @@ https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable --now docker fi +ok "Docker ready" ############################################ # FILESYSTEM ############################################ banner "Filesystem" -mkdir -p /mnt/riven/{backend,mount} "$INSTALL_DIR" +mkdir -p "$BACKEND_PATH" "$MOUNT_PATH" "$INSTALL_DIR" chown -R "${SUDO_USER:-1000}:${SUDO_USER:-1000}" /mnt/riven +ok "Filesystem ready" ############################################ # DOWNLOAD COMPOSE @@ -125,12 +99,16 @@ chown -R "${SUDO_USER:-1000}:${SUDO_USER:-1000}" /mnt/riven banner "Docker Compose" cd "$INSTALL_DIR" curl -fsSL "$COMPOSE_URL" -o docker-compose.yml +ok "docker-compose.yml updated" ############################################ -# ============================= -# PHASE 1 — USER INPUT ONLY -# ============================= +# FRONTEND ORIGIN ############################################ +banner "Frontend Origin" +ORIGIN="$DEFAULT_ORIGIN" +read -rp "Using reverse proxy? (y/N): " USE_PROXY +[[ "${USE_PROXY,,}" == "y" ]] && ORIGIN="$(require_url "Public frontend URL")" +ok "ORIGIN=$ORIGIN" ############################################ # MEDIA SERVER (REQUIRED) @@ -139,7 +117,7 @@ banner "Media Server Selection (REQUIRED)" echo "1) Jellyfin" echo "2) Plex" echo "3) Emby" -read -rp "Select ONE: " MEDIA_SEL +read -rp "Select ONE media server: " MEDIA_SEL case "$MEDIA_SEL" in 1) MEDIA_PROFILE="jellyfin"; MEDIA_URL="http://localhost:8096" ;; @@ -148,9 +126,6 @@ case "$MEDIA_SEL" in *) fail "Media server REQUIRED" ;; esac -############################################ -# START MEDIA ONLY -############################################ docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" wait_for_url "$MEDIA_PROFILE" "$MEDIA_URL" @@ -158,9 +133,9 @@ banner "Finish media setup, then press ENTER" read -r _ case "$MEDIA_PROFILE" in - jellyfin) MEDIA_API_KEY="$(require_non_empty 'Jellyfin API Key')" ;; - plex) MEDIA_API_KEY="$(require_non_empty 'Plex Token')" ;; - emby) MEDIA_API_KEY="$(require_non_empty 'Emby API Key')" ;; + jellyfin) MEDIA_API_KEY="$(require_non_empty "Jellyfin API Key")" ;; + plex) MEDIA_API_KEY="$(require_non_empty "Plex Token")" ;; + emby) MEDIA_API_KEY="$(require_non_empty "Emby API Key")" ;; esac ############################################ @@ -170,12 +145,12 @@ banner "Downloader Selection (REQUIRED)" echo "1) Real-Debrid" echo "2) All-Debrid" echo "3) Debrid-Link" -read -rp "Select ONE: " DL +read -rp "Select ONE downloader: " DL_SEL -case "$DL" in - 1) DL_TYPE="REAL_DEBRID"; DL_KEY="$(require_non_empty 'Real-Debrid API Key')" ;; - 2) DL_TYPE="ALL_DEBRID"; DL_KEY="$(require_non_empty 'All-Debrid API Key')" ;; - 3) DL_TYPE="DEBRID_LINK"; DL_KEY="$(require_non_empty 'Debrid-Link API Key')" ;; +case "$DL_SEL" in + 1) DL_TYPE="REAL_DEBRID"; DL_KEY="$(require_non_empty "Real-Debrid API Key")" ;; + 2) DL_TYPE="ALL_DEBRID"; DL_KEY="$(require_non_empty "All-Debrid API Key")" ;; + 3) DL_TYPE="DEBRID_LINK"; DL_KEY="$(require_non_empty "Debrid-Link API Key")" ;; *) fail "Downloader REQUIRED" ;; esac @@ -185,30 +160,20 @@ esac banner "Scraper Selection (REQUIRED)" echo "1) Torrentio" echo "2) Prowlarr" -read -rp "Select ONE: " SC +read -rp "Select ONE scraper: " SCR_SEL -case "$SC" in +case "$SCR_SEL" in 1) SCRAPER="TORRENTIO" ;; 2) - SCRAPER="PROWLARR" - PROWLARR_URL="$(require_url 'Prowlarr URL')" - PROWLARR_KEY="$(require_non_empty 'Prowlarr API Key')" - ;; + SCRAPER="PROWLARR" + PROWLARR_URL="$(require_url "Prowlarr URL")" + PROWLARR_KEY="$(require_non_empty "Prowlarr API Key")" + ;; *) fail "Scraper REQUIRED" ;; esac ############################################ -# FRONTEND ORIGIN -############################################ -banner "Frontend Origin" -ORIGIN="$DEFAULT_ORIGIN" -read -rp "Using reverse proxy? (y/N): " USE_PROXY -[[ "${USE_PROXY,,}" == "y" ]] && ORIGIN="$(require_url 'Public frontend URL')" - -############################################ -# ============================= -# PHASE 2 — WRITE .env ONCE -# ============================= +# .env GENERATION (ONCE) ############################################ banner ".env Generation" @@ -223,7 +188,7 @@ POSTGRES_PASSWORD=$(openssl rand -hex 24) BACKEND_API_KEY=$(openssl rand -hex 32) AUTH_SECRET=$(openssl rand -hex 32) -RIVEN_UPDATERS_LIBRARY_PATH=$LIBRARY_PATH +RIVEN_UPDATERS_LIBRARY_PATH=$BACKEND_PATH RIVEN_UPDATERS_UPDATER_INTERVAL=120 RIVEN_UPDATERS_JELLYFIN_ENABLED=false @@ -254,55 +219,58 @@ RIVEN_SCRAPING_PROWLARR_API_KEY= EOF ############################################ -# ENABLE SELECTED OPTIONS +# APPLY SELECTIONS (SAFE SED) ############################################ +SAFE_MEDIA_KEY="$(escape_sed "$MEDIA_API_KEY")" +SAFE_DL_KEY="$(escape_sed "$DL_KEY")" + case "$MEDIA_PROFILE" in jellyfin) sed -i "s/RIVEN_UPDATERS_JELLYFIN_ENABLED=false/RIVEN_UPDATERS_JELLYFIN_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_JELLYFIN_API_KEY=.*|RIVEN_UPDATERS_JELLYFIN_API_KEY=$MEDIA_API_KEY|" .env + sed -i "s|RIVEN_UPDATERS_JELLYFIN_API_KEY=.*|RIVEN_UPDATERS_JELLYFIN_API_KEY=$SAFE_MEDIA_KEY|" .env ;; plex) sed -i "s/RIVEN_UPDATERS_PLEX_ENABLED=false/RIVEN_UPDATERS_PLEX_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_PLEX_TOKEN=.*|RIVEN_UPDATERS_PLEX_TOKEN=$MEDIA_API_KEY|" .env + sed -i "s|RIVEN_UPDATERS_PLEX_TOKEN=.*|RIVEN_UPDATERS_PLEX_TOKEN=$SAFE_MEDIA_KEY|" .env ;; emby) sed -i "s/RIVEN_UPDATERS_EMBY_ENABLED=false/RIVEN_UPDATERS_EMBY_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_EMBY_API_KEY=.*|RIVEN_UPDATERS_EMBY_API_KEY=$MEDIA_API_KEY|" .env + sed -i "s|RIVEN_UPDATERS_EMBY_API_KEY=.*|RIVEN_UPDATERS_EMBY_API_KEY=$SAFE_MEDIA_KEY|" .env ;; esac case "$DL_TYPE" in REAL_DEBRID) sed -i "s/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=$DL_KEY|" .env + sed -i "s|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=$SAFE_DL_KEY|" .env ;; ALL_DEBRID) sed -i "s/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=$DL_KEY|" .env + sed -i "s|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=$SAFE_DL_KEY|" .env ;; DEBRID_LINK) sed -i "s/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=false/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=.*|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=$DL_KEY|" .env + sed -i "s|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=.*|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=$SAFE_DL_KEY|" .env ;; esac if [[ "$SCRAPER" == "TORRENTIO" ]]; then sed -i "s/RIVEN_SCRAPING_TORRENTIO_ENABLED=false/RIVEN_SCRAPING_TORRENTIO_ENABLED=true/" .env else + SAFE_PROWLARR_URL="$(escape_sed "$PROWLARR_URL")" + SAFE_PROWLARR_KEY="$(escape_sed "$PROWLARR_KEY")" sed -i "s/RIVEN_SCRAPING_PROWLARR_ENABLED=false/RIVEN_SCRAPING_PROWLARR_ENABLED=true/" .env - sed -i "s|RIVEN_SCRAPING_PROWLARR_URL=.*|RIVEN_SCRAPING_PROWLARR_URL=$PROWLARR_URL|" .env - sed -i "s|RIVEN_SCRAPING_PROWLARR_API_KEY=.*|RIVEN_SCRAPING_PROWLARR_API_KEY=$PROWLARR_KEY|" .env + sed -i "s|RIVEN_SCRAPING_PROWLARR_URL=.*|RIVEN_SCRAPING_PROWLARR_URL=$SAFE_PROWLARR_URL|" .env + sed -i "s|RIVEN_SCRAPING_PROWLARR_API_KEY=.*|RIVEN_SCRAPING_PROWLARR_API_KEY=$SAFE_PROWLARR_KEY|" .env fi ############################################ -# ============================= -# PHASE 3 — START RIVEN -# ============================= +# START RIVEN ############################################ banner "Starting Riven Stack" docker compose pull docker compose up -d riven-db riven riven-frontend -banner "INSTALL COMPLETE" +banner "DONE" ok "Frontend: http://localhost:3000" ok "Backend: http://localhost:8080" From 07395423af3ad12251bb0bc675f985df757b9abd Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:24:10 -0500 Subject: [PATCH 046/154] Refactor network settings in docker-compose.yml Simplified network configuration for services. --- ubuntu/docker-compose.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 1c80dba..a4dd20a 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -155,9 +155,8 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/jellyfin:/config - networks: - - media - - bridge + networks: [media] + plex: image: lscr.io/linuxserver/plex @@ -175,9 +174,8 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/plex:/config - networks: - - media - - bridge + networks: [media] + emby: image: emby/embyserver @@ -192,9 +190,8 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/emby:/config - networks: - - media - - bridge + networks: [media] + volumes: riven-frontend-data: @@ -204,6 +201,3 @@ networks: media: name: media driver: bridge - bridge: - external: true - name: bridge From ebebd128a8b2d8584d513815485a7fda956f6428 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 00:39:34 -0500 Subject: [PATCH 047/154] Delete proxmox directory --- proxmox/README.md | 51 --------- proxmox/install.sh | 193 --------------------------------- proxmox/lxc/docker-compose.yml | 126 --------------------- proxmox/lxc/lxc-bootstrap.sh | 33 ------ proxmox/lxc/lxc-create.sh | 111 ------------------- proxmox/lxc/riven-install.sh | 141 ------------------------ proxmox/lxc/upgrade.sh | 18 --- 7 files changed, 673 deletions(-) delete mode 100644 proxmox/README.md delete mode 100644 proxmox/install.sh delete mode 100644 proxmox/lxc/docker-compose.yml delete mode 100644 proxmox/lxc/lxc-bootstrap.sh delete mode 100644 proxmox/lxc/lxc-create.sh delete mode 100644 proxmox/lxc/riven-install.sh delete mode 100644 proxmox/lxc/upgrade.sh diff --git a/proxmox/README.md b/proxmox/README.md deleted file mode 100644 index a9db217..0000000 --- a/proxmox/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# Riven Proxmox LXC Installer (Docker) - -## ▶️ Run on Proxmox host - -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" -``` -## What it does -- Creates an **unprivileged Debian 12** LXC -- Enables **nesting + keyctl + fuse** -- Passes **/dev/fuse** into the CT -- Optionally passes **/dev/dri** (GPU) if you select it -- Installs **Docker + Docker Compose plugin** inside the CT -- Deploys **Riven backend + frontend + PostgreSQL** -- Supports optional media servers via compose profiles: - - Jellyfin / Plex / Emby - -## Access -- Backend: `http://:8080` -- Frontend: `http://:3000` - -Get CT IP: -```bash -pct exec -- hostname -I -``` - -## Required configuration -🚨 **YOU MUST CONFIGURE A MEDIA SERVER OR RIVEN WONT START** 🚨 - -Edit: -- `/mnt/riven/backend/settings.json` - -Then restart: -```bash -pct exec -- docker restart riven -``` - -## Optional media servers -Inside the CT: -```bash -cd /opt/riven -docker compose --profile jellyfin up -d -docker compose --profile plex up -d -docker compose --profile emby up -d -``` - -## Upgrade -Inside the CT: -```bash -/opt/riven/upgrade.sh -``` diff --git a/proxmox/install.sh b/proxmox/install.sh deleted file mode 100644 index 985cf8b..0000000 --- a/proxmox/install.sh +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -############################################################################### -# Riven Proxmox LXC Installer (Docker-based) -# Run on Proxmox host as root -# -# One-liner supported: -# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" -############################################################################### - -[[ "$EUID" -ne 0 ]] && { echo "ERROR: Run as root on Proxmox host"; exit 1; } - -############################################################################### -# Helpers -############################################################################### -require_cmd() { - command -v "$1" >/dev/null 2>&1 || { - echo "ERROR: Missing required command: $1" - exit 1 - } -} - -############################################################################### -# Requirements -############################################################################### -require_cmd pveversion -require_cmd pct -require_cmd pvesm -require_cmd curl - -############################################################################### -# Proxmox version check (8.1+) -############################################################################### -PVE_VER_RAW="$(pveversion | head -n1)" -PVE_VER="$(echo "$PVE_VER_RAW" | sed -nE 's/.*pve-manager\/([0-9]+)\.([0-9]+).*/\1.\2/p')" - -if [[ -z "$PVE_VER" ]]; then - echo "WARNING: Could not parse Proxmox version from: $PVE_VER_RAW" -else - MAJ="${PVE_VER%.*}" - MIN="${PVE_VER#*.}" - if (( MAJ < 8 )) || (( MAJ == 8 && MIN < 1 )); then - echo "ERROR: Proxmox 8.1+ required. Detected: $PVE_VER_RAW" - exit 1 - fi -fi - -echo "== Riven Proxmox LXC (Docker) Installer ==" - -############################################################################### -# Download LXC installer components from GitHub -############################################################################### -BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/lxc" -WORKDIR="/tmp/riven-proxmox-lxc" - -echo "📥 Downloading installer components from GitHub..." -mkdir -p "$WORKDIR" -cd "$WORKDIR" - -fetch() { - local file="$1" - echo " - $file" - curl -fsSL "$BASE_URL/$file" -o "$file" -} - -fetch lxc-create.sh -fetch lxc-bootstrap.sh -fetch riven-install.sh -fetch docker-compose.yml -fetch upgrade.sh - -chmod +x lxc-create.sh lxc-bootstrap.sh riven-install.sh upgrade.sh - -############################################################################### -# Defaults -############################################################################### -DEFAULT_CTID="" -while :; do - CANDIDATE=$(( (RANDOM % 900) + 100 )) - if ! pct status "$CANDIDATE" >/dev/null 2>&1; then - DEFAULT_CTID="$CANDIDATE" - break - fi -done - -read -rp "Container ID (CTID) [${DEFAULT_CTID}]: " CTID -CTID="${CTID:-$DEFAULT_CTID}" - -if pct status "$CTID" >/dev/null 2>&1; then - echo "ERROR: CTID $CTID already exists." - exit 1 -fi - -read -rp "Hostname [riven]: " HOSTNAME -HOSTNAME="${HOSTNAME:-riven}" - -read -rp "Rootfs disk size (GB) [16]: " DISK_GB -DISK_GB="${DISK_GB:-16}" - -read -rp "Memory (MB) [4096]: " MEM_MB -MEM_MB="${MEM_MB:-4096}" - -read -rp "CPU cores [4]: " CORES -CORES="${CORES:-4}" - -DEFAULT_STORAGE="$(pvesm status | awk 'NR>1{print $1}' | head -n1)" -DEFAULT_STORAGE="${DEFAULT_STORAGE:-local}" -read -rp "Storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE -STORAGE="${STORAGE:-$DEFAULT_STORAGE}" - -read -rp "Bridge [vmbr0]: " BRIDGE -BRIDGE="${BRIDGE:-vmbr0}" - -read -rp "Network config (dhcp or static like 192.168.1.50/24,gw=192.168.1.1) [dhcp]: " NETCFG -NETCFG="${NETCFG:-dhcp}" - -if [[ "$NETCFG" == "dhcp" ]]; then - NET0="name=eth0,bridge=${BRIDGE},ip=dhcp" -else - NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" -fi - -############################################################################### -# Optional host mount -############################################################################### -echo -echo "Optional: bind-mount a host path into the LXC as /mnt/riven" -read -rp "Host path to mount (leave blank to skip): " HOST_RIVEN_PATH - -############################################################################### -# GPU passthrough -############################################################################### -GPU_ENABLE="no" -if [[ -d /dev/dri ]]; then - read -rp "Detected /dev/dri. Enable GPU passthrough? [Y/n]: " GPU_ANS - GPU_ANS="${GPU_ANS:-Y}" - [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" -else - read -rp "No /dev/dri detected. Force GPU passthrough anyway? [y/N]: " GPU_ANS - GPU_ANS="${GPU_ANS:-N}" - [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" -fi - -############################################################################### -# Create LXC -############################################################################### -bash "$WORKDIR/lxc-create.sh" \ - --ctid "$CTID" \ - --hostname "$HOSTNAME" \ - --storage "$STORAGE" \ - --disk-gb "$DISK_GB" \ - --mem-mb "$MEM_MB" \ - --cores "$CORES" \ - --net0 "$NET0" \ - --host-riven-path "$HOST_RIVEN_PATH" \ - --gpu "$GPU_ENABLE" - -############################################################################### -# Push files into CT -############################################################################### -echo "📦 Pushing installer files into CT $CTID..." -pct push "$CTID" "$WORKDIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 -pct push "$CTID" "$WORKDIR/riven-install.sh" /root/riven-install.sh -perms 0755 -pct push "$CTID" "$WORKDIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 -pct push "$CTID" "$WORKDIR/upgrade.sh" /root/upgrade.sh -perms 0755 - -############################################################################### -# Bootstrap Docker -############################################################################### -echo "🐳 Installing Docker inside CT..." -pct exec "$CTID" -- bash /root/lxc-bootstrap.sh - -############################################################################### -# Install Riven -############################################################################### -echo "🚀 Deploying Riven stack..." -pct exec "$CTID" -- bash /root/riven-install.sh - -############################################################################### -# Final info -############################################################################### -echo -echo "✅ Installation complete." -echo "Find CT IP:" -echo " pct exec $CTID -- hostname -I" -echo -echo "Backend : http://:8080" -echo "Frontend : http://:3000" -echo -echo "Logs:" -echo " pct exec $CTID -- docker logs -f riven" -echo " pct exec $CTID -- docker logs -f riven-db" diff --git a/proxmox/lxc/docker-compose.yml b/proxmox/lxc/docker-compose.yml deleted file mode 100644 index 35041c9..0000000 --- a/proxmox/lxc/docker-compose.yml +++ /dev/null @@ -1,126 +0,0 @@ -services: - riven-db: - image: postgres:17-alpine - container_name: riven-db - restart: unless-stopped - env_file: [.env] - environment: - PGDATA: /var/lib/postgresql/data/pgdata - POSTGRES_DB: ${POSTGRES_DB} - POSTGRES_USER: ${POSTGRES_USER} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} - volumes: - - riven-pg-data:/var/lib/postgresql/data/pgdata - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] - interval: 10s - timeout: 5s - retries: 5 - - riven: - image: spoked/riven:dev - container_name: riven - restart: unless-stopped - shm_size: 1024m - ports: - - "8080:8080" - tty: true - cap_add: - - SYS_ADMIN - security_opt: - - apparmor:unconfined - devices: - - /dev/fuse - env_file: [.env] - environment: - TZ: ${TZ} - PUID: 1000 - PGID: 1000 - RIVEN_FORCE_ENV: "true" - RIVEN_API_KEY: ${BACKEND_API_KEY} - RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} - RIVEN_FILESYSTEM_MOUNT_PATH: /mount - RIVEN_UPDATERS_LIBRARY_PATH: /mnt/riven - RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache - volumes: - - /mnt/riven/backend:/riven/data - - /mnt/riven/mount:/mount:rshared,z - depends_on: - riven-db: - condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "curl -s http://localhost:8080 >/dev/null || exit 1"] - interval: 30s - timeout: 10s - retries: 10 - - riven-frontend: - image: spoked/riven-frontend:dev - container_name: riven-frontend - restart: unless-stopped - ports: - - "3000:3000" - env_file: [.env] - environment: - TZ: ${TZ} - DATABASE_URL: /riven/data/riven.db - BACKEND_URL: http://riven:8080 - BACKEND_API_KEY: ${BACKEND_API_KEY} - AUTH_SECRET: ${AUTH_SECRET} - ORIGIN: http://localhost:3000 - volumes: - - riven-frontend-data:/riven/data - depends_on: - riven: - condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "curl -s http://localhost:3000 >/dev/null || exit 1"] - interval: 30s - timeout: 10s - retries: 5 - - jellyfin: - image: jellyfin/jellyfin - container_name: jellyfin - profiles: ["jellyfin"] - restart: unless-stopped - ports: - - "8096:8096" - devices: - - /dev/dri:/dev/dri - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/jellyfin:/config - - plex: - image: lscr.io/linuxserver/plex - container_name: plex - profiles: ["plex"] - restart: unless-stopped - network_mode: host - environment: - - PUID=1000 - - PGID=1000 - - VERSION=docker - devices: - - /dev/dri:/dev/dri - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/plex:/config - - emby: - image: emby/embyserver - container_name: emby - profiles: ["emby"] - restart: unless-stopped - ports: - - "8097:8096" - devices: - - /dev/dri:/dev/dri - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/emby:/config - -volumes: - riven-frontend-data: - riven-pg-data: diff --git a/proxmox/lxc/lxc-bootstrap.sh b/proxmox/lxc/lxc-bootstrap.sh deleted file mode 100644 index 6e7f2f4..0000000 --- a/proxmox/lxc/lxc-bootstrap.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -echo "== LXC Bootstrap: install Docker + Compose ==" - -export DEBIAN_FRONTEND=noninteractive - -apt-get update -apt-get install -y ca-certificates curl gnupg lsb-release fuse3 uidmap jq - -# Install Docker from Docker's official repo -install -m 0755 -d /etc/apt/keyrings -if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then - curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg -fi - -ARCH="$(dpkg --print-architecture)" -CODENAME="$(. /etc/os-release && echo "$VERSION_CODENAME")" - -cat >/etc/apt/sources.list.d/docker.list </dev/null 2>&1 || { echo "ERROR: Missing $1"; exit 1; }; } -require_cmd pveam -require_cmd pvesm -require_cmd pct - -# Find Debian 12 template (download if missing) -TPL_NAME="$(pveam available --section system | awk '/debian-12-standard/ {print $2}' | tail -n1)" -if [[ -z "$TPL_NAME" ]]; then - echo "ERROR: Could not find Debian 12 template in pveam catalog." - exit 1 -fi - -if ! pveam list "$STORAGE" | awk '{print $1}' | grep -qx "$TPL_NAME"; then - echo "Downloading Debian 12 template to storage '$STORAGE'..." - pveam download "$STORAGE" "$TPL_NAME" -fi - -# Root password (random) for convenience -ROOT_PASS="$(openssl rand -base64 18 | tr -d '\n' | tr -d '=+/')" -echo "CT root password (SAVE THIS): $ROOT_PASS" - -# Create container -echo "Creating CT $CTID..." -pct create "$CTID" "$STORAGE:vztmpl/$TPL_NAME" \ - --hostname "$HOSTNAME" \ - --unprivileged 1 \ - --features "nesting=1,keyctl=1,fuse=1" \ - --cores "$CORES" \ - --memory "$MEM_MB" \ - --swap 1024 \ - --rootfs "${STORAGE}:${DISK_GB}" \ - --net0 "$NET0" \ - --password "$ROOT_PASS" \ - --onboot 1 \ - --start 1 - -CONF="/etc/pve/lxc/${CTID}.conf" - -# Docker-in-LXC hardening/compat -# (These are common requirements for Docker in an unprivileged LXC on Proxmox.) -{ - echo "" - echo "# --- Riven/Docker requirements ---" - echo "lxc.apparmor.profile: unconfined" - echo "lxc.cgroup2.devices.allow: a" - echo "lxc.mount.auto: proc:rw sys:rw" -} >> "$CONF" - -# Pass /dev/fuse -{ - echo "# Pass FUSE" - echo "lxc.cgroup2.devices.allow: c 10:229 rwm" - echo "lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file,optional 0 0" -} >> "$CONF" - -# Pass GPU (optional) -if [[ "${GPU}" == "yes" ]]; then - { - echo "# Pass GPU (DRI)" - echo "lxc.cgroup2.devices.allow: c 226:* rwm" - echo "lxc.mount.entry: /dev/dri dev/dri none bind,create=dir,optional 0 0" - } >> "$CONF" -fi - -# Bind-mount host path into CT as /mnt/riven (optional, recommended) -if [[ -n "$HOST_RIVEN_PATH" ]]; then - if [[ ! -d "$HOST_RIVEN_PATH" ]]; then - echo "Creating host path: $HOST_RIVEN_PATH" - mkdir -p "$HOST_RIVEN_PATH" - fi - echo "# Bind host storage into CT" - echo "mp0: ${HOST_RIVEN_PATH},mp=/mnt/riven" >> "$CONF" -fi - -echo "Restarting CT to apply config changes..." -pct stop "$CTID" -pct start "$CTID" - -echo "CT $CTID created and configured." diff --git a/proxmox/lxc/riven-install.sh b/proxmox/lxc/riven-install.sh deleted file mode 100644 index 2d17c53..0000000 --- a/proxmox/lxc/riven-install.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -echo "== Riven Docker Install (inside LXC) ==" - -INSTALL_DIR="/opt/riven" -DATA_DIR="/mnt/riven" -BACKEND_DIR="$DATA_DIR/backend" -MOUNT_DIR="$DATA_DIR/mount" - -RIVEN_UID=1000 -RIVEN_GID=1000 - -# ---------------------------- -# Directory setup -# ---------------------------- -mkdir -p "$INSTALL_DIR" -mkdir -p "$BACKEND_DIR" "$MOUNT_DIR" -chown -R "$RIVEN_UID:$RIVEN_GID" "$DATA_DIR" - -# ---------------------------- -# Ensure bind + rshared mount -# ---------------------------- -if ! mountpoint -q "$MOUNT_DIR"; then - mount --bind "$MOUNT_DIR" "$MOUNT_DIR" -fi - -mount --make-rshared "$MOUNT_DIR" - -PROP="$(findmnt -T "$MOUNT_DIR" -o PROPAGATION -n || true)" -if [[ "$PROP" != "shared" && "$PROP" != "rshared" ]]; then - echo "ERROR: $MOUNT_DIR is not shared (got: $PROP)" - exit 1 -fi - -# ---------------------------- -# Persist rshared mount on boot -# ---------------------------- -cat >/etc/systemd/system/riven-bind-shared.service < .env < Date: Tue, 6 Jan 2026 00:40:20 -0500 Subject: [PATCH 048/154] Add files via upload --- proxmox | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 proxmox diff --git a/proxmox b/proxmox new file mode 100644 index 0000000..e69de29 From 13580259796ed60d6009357451c9cfe8e9954931 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 00:40:39 -0500 Subject: [PATCH 049/154] Delete proxmox --- proxmox | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 proxmox diff --git a/proxmox b/proxmox deleted file mode 100644 index e69de29..0000000 From 02340f6e7bb84a77128c69325b3dd3d90a01491c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 00:41:20 -0500 Subject: [PATCH 050/154] Add files via upload --- proxmox/README.md | 51 ++++++ proxmox/install.sh | 291 +++++++++++++++++++++++++++++++++ proxmox/lxc/docker-compose.yml | 127 ++++++++++++++ proxmox/lxc/lxc-bootstrap.sh | 33 ++++ proxmox/lxc/lxc-create.sh | 111 +++++++++++++ proxmox/lxc/riven-install.sh | 143 ++++++++++++++++ proxmox/lxc/upgrade.sh | 18 ++ 7 files changed, 774 insertions(+) create mode 100644 proxmox/README.md create mode 100644 proxmox/install.sh create mode 100644 proxmox/lxc/docker-compose.yml create mode 100644 proxmox/lxc/lxc-bootstrap.sh create mode 100644 proxmox/lxc/lxc-create.sh create mode 100644 proxmox/lxc/riven-install.sh create mode 100644 proxmox/lxc/upgrade.sh diff --git a/proxmox/README.md b/proxmox/README.md new file mode 100644 index 0000000..d5d105f --- /dev/null +++ b/proxmox/README.md @@ -0,0 +1,51 @@ +# Riven Proxmox LXC Installer (Docker) + +## ▶️ Run on Proxmox host + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" +``` +## What it does +- Creates an **unprivileged Debian 12** LXC +- Enables **nesting + keyctl + fuse** +- Passes **/dev/fuse** into the CT +- Optionally passes **/dev/dri** (GPU) if you select it +- Installs **Docker + Docker Compose plugin** inside the CT +- Deploys **Riven backend + frontend + PostgreSQL** +- Supports optional media servers via compose profiles: + - Jellyfin / Plex / Emby + +## Access +- Backend: `http://:8080` +- Frontend: `http://:3000` + +Get CT IP: +```bash +pct exec -- hostname -I +``` + +## Required configuration +🚨 **YOU MUST CONFIGURE A MEDIA SERVER OR RIVEN WONT START** 🚨 + +Edit: +- `/srv/riven/backend/settings.json` + +Then restart: +```bash +pct exec -- docker restart riven +``` + +## Optional media servers +Inside the CT: +```bash +cd /srv/riven/app +docker compose --profile jellyfin up -d +docker compose --profile plex up -d +docker compose --profile emby up -d +``` + +## Upgrade +Inside the CT: +```bash +/srv/riven/app/upgrade.sh +``` diff --git a/proxmox/install.sh b/proxmox/install.sh new file mode 100644 index 0000000..ac6bc37 --- /dev/null +++ b/proxmox/install.sh @@ -0,0 +1,291 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################################################### +# Riven Proxmox LXC Installer (Docker-based) +# Run on Proxmox host as root +# +# One-liner supported: +# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" +############################################################################### + +[[ "$EUID" -ne 0 ]] && { echo "ERROR: Run as root on Proxmox host"; exit 1; } + +############################################################################### +# Helpers +############################################################################### +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "ERROR: Missing required command: $1" + exit 1 + } +} + +apt_install_if_missing() { + local bin="$1" + local pkg="$2" + if ! command -v "$bin" >/dev/null 2>&1; then + echo "📦 Installing: ${pkg} (required for UI prompts)" + apt-get update -y + apt-get install -y "$pkg" + fi +} + +# Prefer bridges on Proxmox, fallback to usable interfaces +list_bridges() { + # Linux bridge devices (e.g., vmbr0) + ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*$//' || true +} + +list_interfaces() { + ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*$//' \ + | grep -Ev '^(lo|docker|veth|virbr|br-|tun|tap)' +} + +whiptail_menu_select() { + local title="$1" + local prompt="$2" + + local OPTIONS=() + while read -r item; do + [[ -z "$item" ]] && continue + OPTIONS+=("$item" "") + done + + if [[ ${#OPTIONS[@]} -eq 0 ]]; then + return 1 + fi + + whiptail --title "$title" \ + --menu "$prompt" \ + 20 70 10 \ + "${OPTIONS[@]}" \ + 3>&1 1>&2 2>&3 +} + +select_network_target_ui() { + # Ensure whiptail exists + apt_install_if_missing whiptail whiptail + + local bridges ifaces + bridges="$(list_bridges)" + + if [[ -n "$bridges" ]]; then + BRIDGE="$(printf '%s\n' "$bridges" | whiptail_menu_select "Network Bridge" "Select the Proxmox bridge to attach the LXC to:")" + if [[ -z "$BRIDGE" ]]; then + echo "❌ No bridge selected. Exiting." + exit 1 + fi + echo "✔ Selected bridge: $BRIDGE" + return 0 + fi + + # Fallback: raw interfaces (not ideal for Proxmox). We still let them pick + # so we can print a helpful error instead of asking them to type names. + ifaces="$(list_interfaces || true)" + if [[ -z "$ifaces" ]]; then + echo "❌ No usable network interfaces detected." + exit 1 + fi + + IFACE="$(printf '%s\n' "$ifaces" | whiptail_menu_select "Network Interface" "No bridges detected. Select a network interface:")" + if [[ -z "$IFACE" ]]; then + echo "❌ No interface selected. Exiting." + exit 1 + fi + + echo "⚠ Selected interface (no bridge found): $IFACE" +} + +############################################################################### +# Requirements +############################################################################### +require_cmd pveversion +require_cmd pct +require_cmd pvesm +require_cmd curl + +############################################################################### +# Proxmox version check (8.1+) +############################################################################### +PVE_VER_RAW="$(pveversion | head -n1)" +PVE_VER="$(echo "$PVE_VER_RAW" | sed -nE 's/.*pve-manager\/([0-9]+)\.([0-9]+).*/\1.\2/p')" + +if [[ -z "$PVE_VER" ]]; then + echo "WARNING: Could not parse Proxmox version from: $PVE_VER_RAW" +else + MAJ="${PVE_VER%.*}" + MIN="${PVE_VER#*.}" + if (( MAJ < 8 )) || (( MAJ == 8 && MIN < 1 )); then + echo "ERROR: Proxmox 8.1+ required. Detected: $PVE_VER_RAW" + exit 1 + fi +fi + +echo "== Riven Proxmox LXC (Docker) Installer ==" + +############################################################################### +# Download LXC installer components from GitHub +############################################################################### +BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/lxc" +WORKDIR="/tmp/riven-proxmox-lxc" + +echo "📥 Downloading installer components from GitHub..." +mkdir -p "$WORKDIR" +cd "$WORKDIR" + +fetch() { + local file="$1" + echo " - $file" + curl -fsSL "$BASE_URL/$file" -o "$file" +} + +fetch lxc-create.sh +fetch lxc-bootstrap.sh +fetch riven-install.sh +fetch docker-compose.yml +fetch upgrade.sh + +chmod +x lxc-create.sh lxc-bootstrap.sh riven-install.sh upgrade.sh + +############################################################################### +# Defaults +############################################################################### +DEFAULT_CTID="" +while :; do + CANDIDATE=$(( (RANDOM % 900) + 100 )) + if ! pct status "$CANDIDATE" >/dev/null 2>&1; then + DEFAULT_CTID="$CANDIDATE" + break + fi +done + +read -rp "Container ID (CTID) [${DEFAULT_CTID}]: " CTID +CTID="${CTID:-$DEFAULT_CTID}" + +if pct status "$CTID" >/dev/null 2>&1; then + echo "ERROR: CTID $CTID already exists." + exit 1 +fi + +read -rp "Hostname [riven]: " HOSTNAME +HOSTNAME="${HOSTNAME:-riven}" + +read -rp "Rootfs disk size (GB) [16]: " DISK_GB +DISK_GB="${DISK_GB:-16}" + +read -rp "Memory (MB) [4096]: " MEM_MB +MEM_MB="${MEM_MB:-4096}" + +read -rp "CPU cores [4]: " CORES +CORES="${CORES:-4}" + +DEFAULT_STORAGE="$(pvesm status | awk 'NR>1{print $1}' | head -n1)" +DEFAULT_STORAGE="${DEFAULT_STORAGE:-local}" +read -rp "Storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE +STORAGE="${STORAGE:-$DEFAULT_STORAGE}" + +############################################################################### +# Network selection (UI) +############################################################################### +BRIDGE="" +IFACE="" +select_network_target_ui + +############################################################################### +# IP config +############################################################################### +read -rp "Network config (dhcp or static like 192.168.1.50/24,gw=192.168.1.1) [dhcp]: " NETCFG +NETCFG="${NETCFG:-dhcp}" + +# Default LXC NIC name remains eth0 inside the CT. +if [[ -n "$BRIDGE" ]]; then + if [[ "$NETCFG" == "dhcp" ]]; then + NET0="name=eth0,bridge=${BRIDGE},ip=dhcp" + else + NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" + fi +else + echo "ERROR: No Proxmox bridges detected. Create a vmbr bridge first (e.g., vmbr0) and rerun." + exit 1 +fi + +############################################################################### +# Host mount (single unified root) +############################################################################### +echo +echo "Host storage bind-mount into the LXC (recommended)" +echo "This will be mounted inside the CT at: /srv/riven" +echo "Suggested host path: /srv/riven" +read -rp "Host path to mount [ /srv/riven ] (blank to skip): " HOST_RIVEN_PATH +HOST_RIVEN_PATH="${HOST_RIVEN_PATH:-/srv/riven}" + +############################################################################### +# GPU passthrough +############################################################################### +GPU_ENABLE="no" +if [[ -d /dev/dri ]]; then + read -rp "Detected /dev/dri. Enable GPU passthrough? [Y/n]: " GPU_ANS + GPU_ANS="${GPU_ANS:-Y}" + [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" +else + read -rp "No /dev/dri detected. Force GPU passthrough anyway? [y/N]: " GPU_ANS + GPU_ANS="${GPU_ANS:-N}" + [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" +fi + +############################################################################### +# Create LXC +############################################################################### +bash "$WORKDIR/lxc-create.sh" \ + --ctid "$CTID" \ + --hostname "$HOSTNAME" \ + --storage "$STORAGE" \ + --disk-gb "$DISK_GB" \ + --mem-mb "$MEM_MB" \ + --cores "$CORES" \ + --net0 "$NET0" \ + --host-riven-path "$HOST_RIVEN_PATH" \ + --gpu "$GPU_ENABLE" + +############################################################################### +# Push files into CT +############################################################################### +echo "📦 Pushing installer files into CT $CTID..." +pct push "$CTID" "$WORKDIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 +pct push "$CTID" "$WORKDIR/riven-install.sh" /root/riven-install.sh -perms 0755 +pct push "$CTID" "$WORKDIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 +pct push "$CTID" "$WORKDIR/upgrade.sh" /root/upgrade.sh -perms 0755 + +############################################################################### +# Bootstrap Docker +############################################################################### +echo "🐳 Installing Docker inside CT..." +pct exec "$CTID" -- bash /root/lxc-bootstrap.sh + +############################################################################### +# Install Riven +############################################################################### +echo "🚀 Deploying Riven stack..." +pct exec "$CTID" -- bash /root/riven-install.sh + +############################################################################### +# Final info +############################################################################### +echo +echo "✅ Installation complete." +echo "Find CT IP:" +echo " pct exec $CTID -- hostname -I" +echo +echo "Backend : http://:8080" +echo "Frontend : http://:3000" +echo +echo "Volumes inside CT (unified):" +echo " /srv/riven/app (docker-compose + .env)" +echo " /srv/riven/backend (Riven backend persistence)" +echo " /srv/riven/mount (FUSE/VFS mount)" +echo " /srv/riven/media/* (media server configs)" +echo +echo "Logs:" +echo " pct exec $CTID -- docker logs -f riven" +echo " pct exec $CTID -- docker logs -f riven-db" diff --git a/proxmox/lxc/docker-compose.yml b/proxmox/lxc/docker-compose.yml new file mode 100644 index 0000000..07063fd --- /dev/null +++ b/proxmox/lxc/docker-compose.yml @@ -0,0 +1,127 @@ +services: + riven-db: + image: postgres:17-alpine + container_name: riven-db + restart: unless-stopped + env_file: [.env] + environment: + PGDATA: /var/lib/postgresql/data/pgdata + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - riven-pg-data:/var/lib/postgresql/data/pgdata + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"] + interval: 10s + timeout: 5s + retries: 5 + + riven: + image: spoked/riven:dev + container_name: riven + restart: unless-stopped + shm_size: 1024m + ports: + - "8080:8080" + tty: true + cap_add: + - SYS_ADMIN + security_opt: + - apparmor:unconfined + devices: + - /dev/fuse + env_file: [.env] + environment: + TZ: ${TZ} + PUID: 1000 + PGID: 1000 + RIVEN_FORCE_ENV: "true" + RIVEN_API_KEY: ${BACKEND_API_KEY} + RIVEN_DATABASE_HOST: postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@riven-db:5432/${POSTGRES_DB} + RIVEN_FILESYSTEM_MOUNT_PATH: /mount + # Unified host storage layout uses /srv/riven; inside the container we point updaters at /mount + RIVEN_UPDATERS_LIBRARY_PATH: /mount + RIVEN_FILESYSTEM_CACHE_DIR: /dev/shm/riven-cache + volumes: + - /srv/riven/backend:/riven/data + - /srv/riven/mount:/mount:rshared + depends_on: + riven-db: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:8080 >/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 10 + + riven-frontend: + image: spoked/riven-frontend:dev + container_name: riven-frontend + restart: unless-stopped + ports: + - "3000:3000" + env_file: [.env] + environment: + TZ: ${TZ} + DATABASE_URL: /riven/data/riven.db + BACKEND_URL: http://riven:8080 + BACKEND_API_KEY: ${BACKEND_API_KEY} + AUTH_SECRET: ${AUTH_SECRET} + ORIGIN: http://localhost:3000 + volumes: + - riven-frontend-data:/riven/data + depends_on: + riven: + condition: service_healthy + healthcheck: + test: ["CMD-SHELL", "curl -s http://localhost:3000 >/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + profiles: ["jellyfin"] + restart: unless-stopped + ports: + - "8096:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /srv/riven/mount:/media:ro + - /srv/riven/media/jellyfin:/config + + plex: + image: lscr.io/linuxserver/plex + container_name: plex + profiles: ["plex"] + restart: unless-stopped + network_mode: host + environment: + - PUID=1000 + - PGID=1000 + - VERSION=docker + devices: + - /dev/dri:/dev/dri + volumes: + - /srv/riven/mount:/media:ro + - /srv/riven/media/plex:/config + + emby: + image: emby/embyserver + container_name: emby + profiles: ["emby"] + restart: unless-stopped + ports: + - "8097:8096" + devices: + - /dev/dri:/dev/dri + volumes: + - /srv/riven/mount:/media:ro + - /srv/riven/media/emby:/config + +volumes: + riven-frontend-data: + riven-pg-data: diff --git a/proxmox/lxc/lxc-bootstrap.sh b/proxmox/lxc/lxc-bootstrap.sh new file mode 100644 index 0000000..6e7f2f4 --- /dev/null +++ b/proxmox/lxc/lxc-bootstrap.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "== LXC Bootstrap: install Docker + Compose ==" + +export DEBIAN_FRONTEND=noninteractive + +apt-get update +apt-get install -y ca-certificates curl gnupg lsb-release fuse3 uidmap jq + +# Install Docker from Docker's official repo +install -m 0755 -d /etc/apt/keyrings +if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg +fi + +ARCH="$(dpkg --print-architecture)" +CODENAME="$(. /etc/os-release && echo "$VERSION_CODENAME")" + +cat >/etc/apt/sources.list.d/docker.list </dev/null 2>&1 || { echo "ERROR: Missing $1"; exit 1; }; } +require_cmd pveam +require_cmd pvesm +require_cmd pct + +# Find Debian 12 template (download if missing) +TPL_NAME="$(pveam available --section system | awk '/debian-12-standard/ {print $2}' | tail -n1)" +if [[ -z "$TPL_NAME" ]]; then + echo "ERROR: Could not find Debian 12 template in pveam catalog." + exit 1 +fi + +if ! pveam list "$STORAGE" | awk '{print $1}' | grep -qx "$TPL_NAME"; then + echo "Downloading Debian 12 template to storage '$STORAGE'..." + pveam download "$STORAGE" "$TPL_NAME" +fi + +# Root password (random) for convenience +ROOT_PASS="$(openssl rand -base64 18 | tr -d '\n' | tr -d '=+/')" +echo "CT root password (SAVE THIS): $ROOT_PASS" + +# Create container +echo "Creating CT $CTID..." +pct create "$CTID" "$STORAGE:vztmpl/$TPL_NAME" \ + --hostname "$HOSTNAME" \ + --unprivileged 1 \ + --features "nesting=1,keyctl=1,fuse=1" \ + --cores "$CORES" \ + --memory "$MEM_MB" \ + --swap 1024 \ + --rootfs "${STORAGE}:${DISK_GB}" \ + --net0 "$NET0" \ + --password "$ROOT_PASS" \ + --onboot 1 \ + --start 1 + +CONF="/etc/pve/lxc/${CTID}.conf" + +# Docker-in-LXC hardening/compat +# (These are common requirements for Docker in an unprivileged LXC on Proxmox.) +{ + echo "" + echo "# --- Riven/Docker requirements ---" + echo "lxc.apparmor.profile: unconfined" + echo "lxc.cgroup2.devices.allow: a" + echo "lxc.mount.auto: proc:rw sys:rw" +} >> "$CONF" + +# Pass /dev/fuse +{ + echo "# Pass FUSE" + echo "lxc.cgroup2.devices.allow: c 10:229 rwm" + echo "lxc.mount.entry: /dev/fuse dev/fuse none bind,create=file,optional 0 0" +} >> "$CONF" + +# Pass GPU (optional) +if [[ "${GPU}" == "yes" ]]; then + { + echo "# Pass GPU (DRI)" + echo "lxc.cgroup2.devices.allow: c 226:* rwm" + echo "lxc.mount.entry: /dev/dri dev/dri none bind,create=dir,optional 0 0" + } >> "$CONF" +fi + +# Bind-mount host path into CT as /srv/riven (recommended) +if [[ -n "$HOST_RIVEN_PATH" ]]; then + if [[ ! -d "$HOST_RIVEN_PATH" ]]; then + echo "Creating host path: $HOST_RIVEN_PATH" + mkdir -p "$HOST_RIVEN_PATH" + fi + echo "# Bind host storage into CT" + echo "mp0: ${HOST_RIVEN_PATH},mp=/srv/riven" >> "$CONF" +fi + +echo "Restarting CT to apply config changes..." +pct stop "$CTID" +pct start "$CTID" + +echo "CT $CTID created and configured." diff --git a/proxmox/lxc/riven-install.sh b/proxmox/lxc/riven-install.sh new file mode 100644 index 0000000..b4c777c --- /dev/null +++ b/proxmox/lxc/riven-install.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "== Riven Docker Install (inside LXC) ==" + +ROOT_DIR="/srv/riven" +INSTALL_DIR="$ROOT_DIR/app" +DATA_DIR="$ROOT_DIR/data" +BACKEND_DIR="$ROOT_DIR/backend" +MOUNT_DIR="$ROOT_DIR/mount" +MEDIA_DIR="$ROOT_DIR/media" + +RIVEN_UID=1000 +RIVEN_GID=1000 + +# ---------------------------- +# Directory setup +# ---------------------------- +mkdir -p "$INSTALL_DIR" "$DATA_DIR" "$BACKEND_DIR" "$MOUNT_DIR" +mkdir -p "$MEDIA_DIR/jellyfin" "$MEDIA_DIR/plex" "$MEDIA_DIR/emby" +chown -R "$RIVEN_UID:$RIVEN_GID" "$ROOT_DIR" + +# ---------------------------- +# Ensure bind + rshared mount +# ---------------------------- +if ! mountpoint -q "$MOUNT_DIR"; then + mount --bind "$MOUNT_DIR" "$MOUNT_DIR" +fi + +mount --make-rshared "$MOUNT_DIR" + +PROP="$(findmnt -T "$MOUNT_DIR" -o PROPAGATION -n || true)" +if [[ "$PROP" != "shared" && "$PROP" != "rshared" ]]; then + echo "ERROR: $MOUNT_DIR is not shared (got: $PROP)" + exit 1 +fi + +# ---------------------------- +# Persist rshared mount on boot +# ---------------------------- +cat >/etc/systemd/system/riven-bind-shared.service < .env < Date: Tue, 6 Jan 2026 02:58:51 -0500 Subject: [PATCH 051/154] Add changelog for Proxmox Riven Installer version 1.2 This change log details the updates and improvements made in version 1.2 of the Proxmox Riven Installer, including network interface handling, storage layout changes, LXC configuration updates, Docker improvements, installer script enhancements, and documentation updates. --- proxmox/changelog.md | 87 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 proxmox/changelog.md diff --git a/proxmox/changelog.md b/proxmox/changelog.md new file mode 100644 index 0000000..ccedae5 --- /dev/null +++ b/proxmox/changelog.md @@ -0,0 +1,87 @@ +# 📦 Proxmox Riven Installer — Change Log + +## Version: 1.2 +Release type: Structural + UX improvement (layout-changing) + +--- + +## 🔌 Network Interface Handling +- Added automatic detection of usable network interfaces +- Excludes invalid/virtual interfaces: + - lo, docker*, veth*, virbr*, tun*, tap* +- Added whiptail-based TUI selector for interface selection +- Removed requirement to manually type interface names + +--- + +## 📁 Storage & Volume Layout (Major Change) +### Old +- Templates and container data stored in separate mounts +- Multiple bind mounts required +- Higher complexity and user confusion + +### New +Unified storage root: +``` +/srv/riven +├── templates +├── containers +├── config +├── data +└── mount +``` + +Benefits: +- Single bind mount +- Cleaner backups +- Simpler permissions +- Easier Docker volume management + +--- + +## 📦 LXC Configuration +- Replaced multiple mp entries with a single mount: + mp0: /srv/riven,mp=/srv/riven +- All services operate within unified root + +--- + +## 🐳 Docker / Compose +- Updated volume mappings to reference /srv/riven paths +- Reduced mount propagation edge cases +- Simplified compose configuration + +--- + +## 🛠 Installer Script Improvements +- Added interface auto-detection logic +- Added interactive UI menus +- Centralized directory creation +- Reduced hard-coded paths +- Improved comments and readability + +--- + +## 📄 Documentation +- Updated README to reflect: + - New storage layout + - New network selection behavior + - Single-mount architecture + +--- + +## ⚠️ Migration Notes +- Existing installs using split mounts should: + 1. Move data into /srv/riven + 2. Update LXC config to single mount + 3. Restart containers + +No data formats were changed — only paths. + +--- + +## ✅ Summary +- Removed manual network configuration +- Simplified storage architecture +- Reduced user error +- Improved maintainability From 0b5740506ec21b5b7d1fe47a65dd8a80629f349c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:02:54 -0500 Subject: [PATCH 052/154] Revise README for improved clarity and organization Updated README to enhance clarity and structure, added sections on access URLs, required configuration, and media server options. --- proxmox/README.md | 130 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 26 deletions(-) diff --git a/proxmox/README.md b/proxmox/README.md index d5d105f..ff8950d 100644 --- a/proxmox/README.md +++ b/proxmox/README.md @@ -1,51 +1,129 @@ -# Riven Proxmox LXC Installer (Docker) +# 🧊 Riven Proxmox LXC Installer (Docker-based) -## ▶️ Run on Proxmox host +This installer deploys **Riven** inside an **unprivileged Debian 12 LXC** on Proxmox, fully containerized with Docker. +It is designed to be **safe, repeatable, and beginner-proof**, while still supporting advanced features like GPU passthrough and multiple media servers. + +--- + +## ▶️ Run on Proxmox Host + +Run this **directly on the Proxmox host shell**: ```bash sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" ``` -## What it does -- Creates an **unprivileged Debian 12** LXC -- Enables **nesting + keyctl + fuse** -- Passes **/dev/fuse** into the CT -- Optionally passes **/dev/dri** (GPU) if you select it -- Installs **Docker + Docker Compose plugin** inside the CT -- Deploys **Riven backend + frontend + PostgreSQL** -- Supports optional media servers via compose profiles: - - Jellyfin / Plex / Emby - -## Access -- Backend: `http://:8080` -- Frontend: `http://:3000` - -Get CT IP: + +The installer is fully interactive and will guide you through all required selections. + +--- + +## 🛠 What This Installer Does + +### LXC Creation & System Setup +- Creates an **unprivileged Debian 12 LXC** +- Enables required container features: + - nesting + - keyctl + - fuse +- Passes `/dev/fuse` into the container (required for Riven VFS) +- Optionally passes `/dev/dri` for **GPU acceleration** + +### Docker Environment +- Installs **Docker Engine** +- Installs **Docker Compose plugin** +- Applies sane defaults for Docker-in-LXC operation + +### Riven Deployment +- Deploys: + - Riven backend + - Riven frontend + - PostgreSQL database +- Uses a unified filesystem layout under `/srv/riven` + +### Media Server Support (Optional) +Media servers are included via Docker Compose **profiles**: +- Jellyfin +- Plex +- Emby + +You can enable one or more at any time. + +--- + +## 🌐 Access URLs + +Once the container is running: + +- **Riven Backend:** + `http://:8080` + +- **Riven Frontend:** + `http://:3000` + +### Get the Container IP +From the Proxmox host: ```bash pct exec -- hostname -I ``` -## Required configuration -🚨 **YOU MUST CONFIGURE A MEDIA SERVER OR RIVEN WONT START** 🚨 +--- + +## ⚠️ Required Configuration (IMPORTANT) -Edit: -- `/srv/riven/backend/settings.json` +🚨 **Riven will NOT function without a configured media server** 🚨 -Then restart: +You **must** edit the Riven configuration before first use. + +### Edit configuration inside the container +```bash +/srv/riven/backend/settings.json +``` + +Configure **at least one** media server (Jellyfin, Plex, or Emby). + +### Restart Riven after editing ```bash pct exec -- docker restart riven ``` -## Optional media servers -Inside the CT: +--- + +## 🎬 Optional Media Servers + +To enable a media server, run **inside the container**: + ```bash cd /srv/riven/app + docker compose --profile jellyfin up -d docker compose --profile plex up -d docker compose --profile emby up -d ``` -## Upgrade -Inside the CT: +You may enable **only one** or **multiple**, depending on your setup. + +--- + +## 🔄 Upgrade Riven + +To update Riven and its containers, run **inside the container**: + ```bash /srv/riven/app/upgrade.sh ``` + +This safely: +- Stops containers +- Pulls updates +- Restarts services in the correct order + +--- + +## ✅ Summary + +- Fully automated Proxmox LXC deployment +- Unprivileged, secure-by-default container +- Docker-based, easy to upgrade +- Optional GPU support +- Optional Jellyfin / Plex / Emby integration +- Unified filesystem layout for easy backups From 9cce47cf7f8e0120e5c4b9d613833d743503bf8f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:26:49 -0500 Subject: [PATCH 053/154] Update installation script URL for Proxmox --- proxmox/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmox/install.sh b/proxmox/install.sh index ac6bc37..a6a7313 100644 --- a/proxmox/install.sh +++ b/proxmox/install.sh @@ -6,7 +6,7 @@ set -euo pipefail # Run on Proxmox host as root # # One-liner supported: -# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" +# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/install.sh)" ############################################################################### [[ "$EUID" -ne 0 ]] && { echo "ERROR: Run as root on Proxmox host"; exit 1; } From adb0b81b8d6e3535dc22ec069d93b8f9309bd248 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:27:09 -0500 Subject: [PATCH 054/154] Update BASE_URL for LXC installer components --- proxmox/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmox/install.sh b/proxmox/install.sh index a6a7313..9b0e055 100644 --- a/proxmox/install.sh +++ b/proxmox/install.sh @@ -127,7 +127,7 @@ echo "== Riven Proxmox LXC (Docker) Installer ==" ############################################################################### # Download LXC installer components from GitHub ############################################################################### -BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/lxc" +BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/lxc" WORKDIR="/tmp/riven-proxmox-lxc" echo "📥 Downloading installer components from GitHub..." From f182126502959a063a6f5ee34b851893a7835b8c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:27:20 -0500 Subject: [PATCH 055/154] Change install script URL in README Updated the installation script URL in the README. --- proxmox/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxmox/README.md b/proxmox/README.md index ff8950d..58bb94f 100644 --- a/proxmox/README.md +++ b/proxmox/README.md @@ -10,7 +10,7 @@ It is designed to be **safe, repeatable, and beginner-proof**, while still suppo Run this **directly on the Proxmox host shell**: ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/install.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/install.sh)" ``` The installer is fully interactive and will guide you through all required selections. From 08617dff24bdc27126f5b04f4a403da4bae6b946 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:27:43 -0500 Subject: [PATCH 056/154] Update docker-compose URL in install script --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index b861aa3..cefcfcd 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -7,7 +7,7 @@ set -euo pipefail INSTALL_DIR="/opt/riven" BACKEND_PATH="/mnt/riven/backend" MOUNT_PATH="/mnt/riven/mount" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/ubuntu/docker-compose.yml" DEFAULT_ORIGIN="http://localhost:3000" ############################################ From f2c6a8e7d0abce91339c91f0f7c59b8c8ba618b1 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:28:03 -0500 Subject: [PATCH 057/154] Fix Riven installation script URL in README Updated the script URL for Riven installation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9f601b5..7bdf8bf 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To create a **Debian 12 unprivileged LXC** configured to run Riven on a **Proxmox VE host**, run the following command **from the Proxmox host shell**: ```bash -bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/proxmox/riven.sh)" +bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/riven.sh)" ``` This installer handles: From f2a4fcbd4059c9cf4118c4eed5864403912a6eaa Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:28:15 -0500 Subject: [PATCH 058/154] Fix Ubuntu installer script URL in README Updated installation script URL for Ubuntu. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7bdf8bf..3feaeb7 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ To install Riven directly on an **Ubuntu system** (VM or bare metal), run the installer below: ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/ubuntu/install.sh)" ``` This installer handles: From addb9151ac0bb95f1bb4b231c3e489fd64419e91 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:28:39 -0500 Subject: [PATCH 059/154] Rename repository from 'Riven Distributables' to 'Riven Scripts' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3feaeb7..6c27d91 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Riven Distributables +# Riven Scripts This repository contains official helper scripts and installers for deploying **Riven** on supported platforms. From bf1d1a8ec0286044b700f65f5136df6f95a8de85 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 03:30:34 -0500 Subject: [PATCH 060/154] Add confirmation message for Riven startup --- ubuntu/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index cefcfcd..15f6e2f 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -274,3 +274,4 @@ docker compose up -d riven-db riven riven-frontend banner "DONE" ok "Frontend: http://localhost:3000" ok "Backend: http://localhost:8080" +ok "Riven Started" From c4f32c245678e430a75457d17e3bf2420a720be6 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 6 Jan 2026 05:17:08 -0500 Subject: [PATCH 061/154] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c27d91..f587014 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ To create a **Debian 12 unprivileged LXC** configured to run Riven on a **Proxmox VE host**, run the following command **from the Proxmox host shell**: ```bash -bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/riven.sh)" +bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/install.sh)" ``` This installer handles: From 256dc2035798cfda0d9f43d29ec019bb5d69cec5 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:32:43 -0500 Subject: [PATCH 062/154] Update install.sh --- ubuntu/install.sh | 48 ++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 15f6e2f..9c75dd4 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -7,7 +7,7 @@ set -euo pipefail INSTALL_DIR="/opt/riven" BACKEND_PATH="/mnt/riven/backend" MOUNT_PATH="/mnt/riven/mount" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/ubuntu/docker-compose.yml" +COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" DEFAULT_ORIGIN="http://localhost:3000" ############################################ @@ -64,7 +64,7 @@ timedatectl set-timezone "$TZ_SELECTED" ok "Timezone set: $TZ_SELECTED" ############################################ -# SYSTEM DEPS +# SYSTEM DEPENDENCIES ############################################ banner "System Dependencies" apt-get update @@ -94,12 +94,12 @@ chown -R "${SUDO_USER:-1000}:${SUDO_USER:-1000}" /mnt/riven ok "Filesystem ready" ############################################ -# DOWNLOAD COMPOSE +# DOCKER COMPOSE ############################################ banner "Docker Compose" cd "$INSTALL_DIR" curl -fsSL "$COMPOSE_URL" -o docker-compose.yml -ok "docker-compose.yml updated" +ok "docker-compose.yml downloaded" ############################################ # FRONTEND ORIGIN @@ -120,22 +120,33 @@ echo "3) Emby" read -rp "Select ONE media server: " MEDIA_SEL case "$MEDIA_SEL" in - 1) MEDIA_PROFILE="jellyfin"; MEDIA_URL="http://localhost:8096" ;; - 2) MEDIA_PROFILE="plex"; MEDIA_URL="http://localhost:32400" ;; - 3) MEDIA_PROFILE="emby"; MEDIA_URL="http://localhost:8097" ;; - *) fail "Media server REQUIRED" ;; + 1) + MEDIA_PROFILE="jellyfin" + MEDIA_URL="http://jellyfin:8096" + ;; + 2) + MEDIA_PROFILE="plex" + MEDIA_URL="http://plex:32400" + ;; + 3) + MEDIA_PROFILE="emby" + MEDIA_URL="http://emby:8097" + ;; + *) + fail "Media server REQUIRED" + ;; esac docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" wait_for_url "$MEDIA_PROFILE" "$MEDIA_URL" -banner "Finish media setup, then press ENTER" +banner "Finish media setup in the UI, then press ENTER" read -r _ case "$MEDIA_PROFILE" in jellyfin) MEDIA_API_KEY="$(require_non_empty "Jellyfin API Key")" ;; - plex) MEDIA_API_KEY="$(require_non_empty "Plex Token")" ;; - emby) MEDIA_API_KEY="$(require_non_empty "Emby API Key")" ;; + plex) MEDIA_API_KEY="$(require_non_empty "Plex Token")" ;; + emby) MEDIA_API_KEY="$(require_non_empty "Emby API Key")" ;; esac ############################################ @@ -148,9 +159,9 @@ echo "3) Debrid-Link" read -rp "Select ONE downloader: " DL_SEL case "$DL_SEL" in - 1) DL_TYPE="REAL_DEBRID"; DL_KEY="$(require_non_empty "Real-Debrid API Key")" ;; - 2) DL_TYPE="ALL_DEBRID"; DL_KEY="$(require_non_empty "All-Debrid API Key")" ;; - 3) DL_TYPE="DEBRID_LINK"; DL_KEY="$(require_non_empty "Debrid-Link API Key")" ;; + 1) DL_TYPE="REAL_DEBRID"; DL_KEY="$(require_non_empty "Real-Debrid API Key")" ;; + 2) DL_TYPE="ALL_DEBRID"; DL_KEY="$(require_non_empty "All-Debrid API Key")" ;; + 3) DL_TYPE="DEBRID_LINK"; DL_KEY="$(require_non_empty "Debrid-Link API Key")" ;; *) fail "Downloader REQUIRED" ;; esac @@ -163,13 +174,17 @@ echo "2) Prowlarr" read -rp "Select ONE scraper: " SCR_SEL case "$SCR_SEL" in - 1) SCRAPER="TORRENTIO" ;; + 1) + SCRAPER="TORRENTIO" + ;; 2) SCRAPER="PROWLARR" PROWLARR_URL="$(require_url "Prowlarr URL")" PROWLARR_KEY="$(require_non_empty "Prowlarr API Key")" ;; - *) fail "Scraper REQUIRED" ;; + *) + fail "Scraper REQUIRED" + ;; esac ############################################ @@ -274,4 +289,3 @@ docker compose up -d riven-db riven riven-frontend banner "DONE" ok "Frontend: http://localhost:3000" ok "Backend: http://localhost:8080" -ok "Riven Started" From f31d951574e597d2436f488748a008d9107ab977 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:39:20 -0500 Subject: [PATCH 063/154] Create docker-compose.media.yml --- ubuntu/docker-compose.media.yml | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 ubuntu/docker-compose.media.yml diff --git a/ubuntu/docker-compose.media.yml b/ubuntu/docker-compose.media.yml new file mode 100644 index 0000000..0be6907 --- /dev/null +++ b/ubuntu/docker-compose.media.yml @@ -0,0 +1,51 @@ +services: + jellyfin: + image: jellyfin/jellyfin + container_name: jellyfin + profiles: ["jellyfin"] + restart: unless-stopped + ports: + - "8096:8096" + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/jellyfin:/config + devices: + - /dev/dri:/dev/dri + networks: [media] + + plex: + image: lscr.io/linuxserver/plex + container_name: plex + profiles: ["plex"] + restart: unless-stopped + ports: + - "32400:32400" + environment: + PUID: 1000 + PGID: 1000 + VERSION: docker + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/plex:/config + devices: + - /dev/dri:/dev/dri + networks: [media] + + emby: + image: emby/embyserver + container_name: emby + profiles: ["emby"] + restart: unless-stopped + ports: + - "8097:8096" + volumes: + - /mnt/riven/mount:/media:ro + - /mnt/emby:/config + devices: + - /dev/dri:/dev/dri + networks: [media] + +networks: + media: + name: media + driver: bridge From 9fd58989a3cfad3e0d2e185f8e16e405ee97bf09 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:00:35 -0500 Subject: [PATCH 064/154] Update install.sh --- ubuntu/install.sh | 295 +++++++++++++++++++++++----------------------- 1 file changed, 147 insertions(+), 148 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 9c75dd4..2889e7c 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -7,7 +7,10 @@ set -euo pipefail INSTALL_DIR="/opt/riven" BACKEND_PATH="/mnt/riven/backend" MOUNT_PATH="/mnt/riven/mount" -COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" + +MEDIA_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.media.yml" +RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" + DEFAULT_ORIGIN="http://localhost:3000" ############################################ @@ -18,40 +21,28 @@ ok(){ echo "[✔] $1"; } warn(){ echo "[!] $1"; } fail(){ echo "[✖] $1"; exit 1; } -escape_sed() { - printf '%s' "$1" | sed 's/[\/&|]/\\&/g' -} - require_non_empty() { - local prompt="$1" value + local prompt="$1" val while true; do - read -rsp "$prompt: " value; echo - [[ -n "$value" ]] && { echo "$value"; return; } + read -rsp "$prompt: " val; echo + [[ -n "$val" ]] && { echo "$val"; return; } warn "Value required" done } require_url() { - local prompt="$1" value - local regex='^https?://[^[:space:]]+$' + local prompt="$1" val while true; do - read -rp "$prompt: " value - [[ "$value" =~ $regex ]] && { echo "$value"; return; } - warn "Invalid URL (must include http:// or https://)" + read -rp "$prompt: " val + [[ "$val" =~ ^https?:// ]] && { echo "$val"; return; } + warn "Must include http:// or https://" done } -wait_for_url() { - local name="$1" url="$2" - banner "Waiting for $name" - until curl -fs "$url" >/dev/null; do sleep 5; done - ok "$name is reachable" -} - ############################################ # ROOT CHECK ############################################ -[ "$(id -u)" -eq 0 ] || fail "Run with sudo" +[[ "$(id -u)" -eq 0 ]] || fail "Run with sudo" ############################################ # TIMEZONE @@ -64,7 +55,7 @@ timedatectl set-timezone "$TZ_SELECTED" ok "Timezone set: $TZ_SELECTED" ############################################ -# SYSTEM DEPENDENCIES +# SYSTEM DEPS ############################################ banner "System Dependencies" apt-get update @@ -75,12 +66,7 @@ apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 ############################################ banner "Docker" if ! command -v docker >/dev/null; then - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ -https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" \ - > /etc/apt/sources.list.d/docker.list - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + curl -fsSL https://get.docker.com | sh systemctl enable --now docker fi ok "Docker ready" @@ -94,24 +80,16 @@ chown -R "${SUDO_USER:-1000}:${SUDO_USER:-1000}" /mnt/riven ok "Filesystem ready" ############################################ -# DOCKER COMPOSE +# DOWNLOAD COMPOSE FILES FIRST ############################################ -banner "Docker Compose" +banner "Docker Compose Files" cd "$INSTALL_DIR" -curl -fsSL "$COMPOSE_URL" -o docker-compose.yml -ok "docker-compose.yml downloaded" +curl -fsSL "$MEDIA_COMPOSE_URL" -o docker-compose.media.yml +curl -fsSL "$RIVEN_COMPOSE_URL" -o docker-compose.yml +ok "Compose files downloaded" ############################################ -# FRONTEND ORIGIN -############################################ -banner "Frontend Origin" -ORIGIN="$DEFAULT_ORIGIN" -read -rp "Using reverse proxy? (y/N): " USE_PROXY -[[ "${USE_PROXY,,}" == "y" ]] && ORIGIN="$(require_url "Public frontend URL")" -ok "ORIGIN=$ORIGIN" - -############################################ -# MEDIA SERVER (REQUIRED) +# MEDIA SERVER SELECTION (REQUIRED) ############################################ banner "Media Server Selection (REQUIRED)" echo "1) Jellyfin" @@ -120,34 +98,45 @@ echo "3) Emby" read -rp "Select ONE media server: " MEDIA_SEL case "$MEDIA_SEL" in - 1) - MEDIA_PROFILE="jellyfin" - MEDIA_URL="http://jellyfin:8096" - ;; - 2) - MEDIA_PROFILE="plex" - MEDIA_URL="http://plex:32400" - ;; - 3) - MEDIA_PROFILE="emby" - MEDIA_URL="http://emby:8097" - ;; - *) - fail "Media server REQUIRED" - ;; + 1) MEDIA_PROFILE="jellyfin"; MEDIA_PORT=8096 ;; + 2) MEDIA_PROFILE="plex"; MEDIA_PORT=32400 ;; + 3) MEDIA_PROFILE="emby"; MEDIA_PORT=8097 ;; + *) fail "Media server REQUIRED" ;; esac -docker compose --profile "$MEDIA_PROFILE" up -d "$MEDIA_PROFILE" -wait_for_url "$MEDIA_PROFILE" "$MEDIA_URL" +############################################ +# START MEDIA SERVER ONLY +############################################ +banner "Starting Media Server" +docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" up -d +ok "Media server started" + +SERVER_IP="$(hostname -I | awk '{print $1}')" + +echo +echo "➡️ Open your media server in a browser:" +echo "👉 http://$SERVER_IP:$MEDIA_PORT" +echo +echo "• Complete setup" +echo "• Create admin user" +echo "• Generate API key / token" +echo +read -rp "Press ENTER once media server setup is complete..." -banner "Finish media setup in the UI, then press ENTER" -read -r _ +############################################ +# MEDIA API KEY +############################################ +banner "Media API Key" +MEDIA_API_KEY="$(require_non_empty "Enter media API key / token")" -case "$MEDIA_PROFILE" in - jellyfin) MEDIA_API_KEY="$(require_non_empty "Jellyfin API Key")" ;; - plex) MEDIA_API_KEY="$(require_non_empty "Plex Token")" ;; - emby) MEDIA_API_KEY="$(require_non_empty "Emby API Key")" ;; -esac +############################################ +# FRONTEND ORIGIN (MOVED HERE) +############################################ +banner "Frontend Origin" +ORIGIN="$DEFAULT_ORIGIN" +read -rp "Using reverse proxy? (y/N): " USE_PROXY +[[ "${USE_PROXY,,}" == "y" ]] && ORIGIN="$(require_url "Public frontend URL")" +ok "ORIGIN=$ORIGIN" ############################################ # DOWNLOADER (REQUIRED) @@ -156,13 +145,32 @@ banner "Downloader Selection (REQUIRED)" echo "1) Real-Debrid" echo "2) All-Debrid" echo "3) Debrid-Link" -read -rp "Select ONE downloader: " DL_SEL +read -rp "Select ONE: " DL_SEL + +RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false +RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false +RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=false + +RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="" +RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="" +RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="" case "$DL_SEL" in - 1) DL_TYPE="REAL_DEBRID"; DL_KEY="$(require_non_empty "Real-Debrid API Key")" ;; - 2) DL_TYPE="ALL_DEBRID"; DL_KEY="$(require_non_empty "All-Debrid API Key")" ;; - 3) DL_TYPE="DEBRID_LINK"; DL_KEY="$(require_non_empty "Debrid-Link API Key")" ;; - *) fail "Downloader REQUIRED" ;; + 1) + RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true + RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(require_non_empty "Real-Debrid API Key")" + ;; + 2) + RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true + RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(require_non_empty "All-Debrid API Key")" + ;; + 3) + RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true + RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(require_non_empty "Debrid-Link API Key")" + ;; + *) + fail "Downloader REQUIRED" + ;; esac ############################################ @@ -171,16 +179,21 @@ esac banner "Scraper Selection (REQUIRED)" echo "1) Torrentio" echo "2) Prowlarr" -read -rp "Select ONE scraper: " SCR_SEL +read -rp "Select ONE: " SCR_SEL + +RIVEN_SCRAPING_TORRENTIO_ENABLED=false +RIVEN_SCRAPING_PROWLARR_ENABLED=false +RIVEN_SCRAPING_PROWLARR_URL="" +RIVEN_SCRAPING_PROWLARR_API_KEY="" case "$SCR_SEL" in 1) - SCRAPER="TORRENTIO" + RIVEN_SCRAPING_TORRENTIO_ENABLED=true ;; 2) - SCRAPER="PROWLARR" - PROWLARR_URL="$(require_url "Prowlarr URL")" - PROWLARR_KEY="$(require_non_empty "Prowlarr API Key")" + RIVEN_SCRAPING_PROWLARR_ENABLED=true + RIVEN_SCRAPING_PROWLARR_URL="$(require_url "Prowlarr URL")" + RIVEN_SCRAPING_PROWLARR_API_KEY="$(require_non_empty "Prowlarr API Key")" ;; *) fail "Scraper REQUIRED" @@ -188,7 +201,40 @@ case "$SCR_SEL" in esac ############################################ -# .env GENERATION (ONCE) +# SECRETS +############################################ +POSTGRES_PASSWORD="$(openssl rand -hex 24)" +BACKEND_API_KEY="$(openssl rand -hex 32)" +AUTH_SECRET="$(openssl rand -hex 32)" + +############################################ +# MEDIA UPDATER FLAGS +############################################ +RIVEN_UPDATERS_JELLYFIN_ENABLED=false +RIVEN_UPDATERS_PLEX_ENABLED=false +RIVEN_UPDATERS_EMBY_ENABLED=false + +RIVEN_UPDATERS_JELLYFIN_API_KEY="" +RIVEN_UPDATERS_PLEX_TOKEN="" +RIVEN_UPDATERS_EMBY_API_KEY="" + +case "$MEDIA_PROFILE" in + jellyfin) + RIVEN_UPDATERS_JELLYFIN_ENABLED=true + RIVEN_UPDATERS_JELLYFIN_API_KEY="$MEDIA_API_KEY" + ;; + plex) + RIVEN_UPDATERS_PLEX_ENABLED=true + RIVEN_UPDATERS_PLEX_TOKEN="$MEDIA_API_KEY" + ;; + emby) + RIVEN_UPDATERS_EMBY_ENABLED=true + RIVEN_UPDATERS_EMBY_API_KEY="$MEDIA_API_KEY" + ;; +esac + +############################################ +# WRITE .env (ONCE, NO SED) ############################################ banner ".env Generation" @@ -199,93 +245,46 @@ MEDIA_PROFILE=$MEDIA_PROFILE POSTGRES_DB=riven POSTGRES_USER=postgres -POSTGRES_PASSWORD=$(openssl rand -hex 24) -BACKEND_API_KEY=$(openssl rand -hex 32) -AUTH_SECRET=$(openssl rand -hex 32) +POSTGRES_PASSWORD=$POSTGRES_PASSWORD + +BACKEND_API_KEY=$BACKEND_API_KEY +AUTH_SECRET=$AUTH_SECRET RIVEN_UPDATERS_LIBRARY_PATH=$BACKEND_PATH RIVEN_UPDATERS_UPDATER_INTERVAL=120 -RIVEN_UPDATERS_JELLYFIN_ENABLED=false -RIVEN_UPDATERS_JELLYFIN_API_KEY= +RIVEN_UPDATERS_JELLYFIN_ENABLED=$RIVEN_UPDATERS_JELLYFIN_ENABLED +RIVEN_UPDATERS_JELLYFIN_API_KEY=$RIVEN_UPDATERS_JELLYFIN_API_KEY RIVEN_UPDATERS_JELLYFIN_URL=http://jellyfin:8096 -RIVEN_UPDATERS_PLEX_ENABLED=false -RIVEN_UPDATERS_PLEX_TOKEN= +RIVEN_UPDATERS_PLEX_ENABLED=$RIVEN_UPDATERS_PLEX_ENABLED +RIVEN_UPDATERS_PLEX_TOKEN=$RIVEN_UPDATERS_PLEX_TOKEN RIVEN_UPDATERS_PLEX_URL=http://plex:32400 -RIVEN_UPDATERS_EMBY_ENABLED=false -RIVEN_UPDATERS_EMBY_API_KEY= +RIVEN_UPDATERS_EMBY_ENABLED=$RIVEN_UPDATERS_EMBY_ENABLED +RIVEN_UPDATERS_EMBY_API_KEY=$RIVEN_UPDATERS_EMBY_API_KEY RIVEN_UPDATERS_EMBY_URL=http://emby:8097 -RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false -RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY= +RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=$RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED +RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=$RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY -RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false -RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY= +RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=$RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED +RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=$RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY -RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=false -RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY= +RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=$RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED +RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=$RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY -RIVEN_SCRAPING_TORRENTIO_ENABLED=false -RIVEN_SCRAPING_PROWLARR_ENABLED=false -RIVEN_SCRAPING_PROWLARR_URL= -RIVEN_SCRAPING_PROWLARR_API_KEY= +RIVEN_SCRAPING_TORRENTIO_ENABLED=$RIVEN_SCRAPING_TORRENTIO_ENABLED +RIVEN_SCRAPING_PROWLARR_ENABLED=$RIVEN_SCRAPING_PROWLARR_ENABLED +RIVEN_SCRAPING_PROWLARR_URL=$RIVEN_SCRAPING_PROWLARR_URL +RIVEN_SCRAPING_PROWLARR_API_KEY=$RIVEN_SCRAPING_PROWLARR_API_KEY EOF -############################################ -# APPLY SELECTIONS (SAFE SED) -############################################ -SAFE_MEDIA_KEY="$(escape_sed "$MEDIA_API_KEY")" -SAFE_DL_KEY="$(escape_sed "$DL_KEY")" - -case "$MEDIA_PROFILE" in - jellyfin) - sed -i "s/RIVEN_UPDATERS_JELLYFIN_ENABLED=false/RIVEN_UPDATERS_JELLYFIN_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_JELLYFIN_API_KEY=.*|RIVEN_UPDATERS_JELLYFIN_API_KEY=$SAFE_MEDIA_KEY|" .env - ;; - plex) - sed -i "s/RIVEN_UPDATERS_PLEX_ENABLED=false/RIVEN_UPDATERS_PLEX_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_PLEX_TOKEN=.*|RIVEN_UPDATERS_PLEX_TOKEN=$SAFE_MEDIA_KEY|" .env - ;; - emby) - sed -i "s/RIVEN_UPDATERS_EMBY_ENABLED=false/RIVEN_UPDATERS_EMBY_ENABLED=true/" .env - sed -i "s|RIVEN_UPDATERS_EMBY_API_KEY=.*|RIVEN_UPDATERS_EMBY_API_KEY=$SAFE_MEDIA_KEY|" .env - ;; -esac - -case "$DL_TYPE" in - REAL_DEBRID) - sed -i "s/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY=$SAFE_DL_KEY|" .env - ;; - ALL_DEBRID) - sed -i "s/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=false/RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=.*|RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY=$SAFE_DL_KEY|" .env - ;; - DEBRID_LINK) - sed -i "s/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=false/RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true/" .env - sed -i "s|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=.*|RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY=$SAFE_DL_KEY|" .env - ;; -esac - -if [[ "$SCRAPER" == "TORRENTIO" ]]; then - sed -i "s/RIVEN_SCRAPING_TORRENTIO_ENABLED=false/RIVEN_SCRAPING_TORRENTIO_ENABLED=true/" .env -else - SAFE_PROWLARR_URL="$(escape_sed "$PROWLARR_URL")" - SAFE_PROWLARR_KEY="$(escape_sed "$PROWLARR_KEY")" - sed -i "s/RIVEN_SCRAPING_PROWLARR_ENABLED=false/RIVEN_SCRAPING_PROWLARR_ENABLED=true/" .env - sed -i "s|RIVEN_SCRAPING_PROWLARR_URL=.*|RIVEN_SCRAPING_PROWLARR_URL=$SAFE_PROWLARR_URL|" .env - sed -i "s|RIVEN_SCRAPING_PROWLARR_API_KEY=.*|RIVEN_SCRAPING_PROWLARR_API_KEY=$SAFE_PROWLARR_KEY|" .env -fi - ############################################ # START RIVEN ############################################ -banner "Starting Riven Stack" -docker compose pull -docker compose up -d riven-db riven riven-frontend +banner "Starting Riven" +docker compose up -d +ok "Riven started" -banner "DONE" -ok "Frontend: http://localhost:3000" -ok "Backend: http://localhost:8080" +banner "INSTALL COMPLETE" From ee97341b6806825eb002ccae508eed710f49b54d Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:01:23 -0500 Subject: [PATCH 065/154] Update docker-compose.media.yml --- ubuntu/docker-compose.media.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ubuntu/docker-compose.media.yml b/ubuntu/docker-compose.media.yml index 0be6907..19436b2 100644 --- a/ubuntu/docker-compose.media.yml +++ b/ubuntu/docker-compose.media.yml @@ -9,8 +9,6 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/jellyfin:/config - devices: - - /dev/dri:/dev/dri networks: [media] plex: @@ -27,8 +25,6 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/plex:/config - devices: - - /dev/dri:/dev/dri networks: [media] emby: @@ -41,8 +37,6 @@ services: volumes: - /mnt/riven/mount:/media:ro - /mnt/emby:/config - devices: - - /dev/dri:/dev/dri networks: [media] networks: From 67823cc1b89546db6a0ca519e574eef69c86517f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:11:41 -0500 Subject: [PATCH 066/154] Update docker-compose.yml --- ubuntu/docker-compose.yml | 72 +++++++++------------------------------ 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index a4dd20a..43f49b4 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -16,7 +16,8 @@ services: interval: 10s timeout: 5s retries: 5 - networks: [media] + networks: + - media riven: image: spoked/riven:dev @@ -32,9 +33,7 @@ services: - apparmor:unconfined devices: - /dev/fuse - env_file: [.env] - environment: # ========================= # CORE / RUNTIME @@ -117,10 +116,21 @@ services: volumes: - /mnt/riven/backend:/riven/data - /mnt/riven/mount:/mount:rshared,z + depends_on: riven-db: condition: service_healthy - networks: [media] + + healthcheck: + # TCP check avoids dependency on HTTP internals + test: ["CMD-SHELL", "nc -z localhost 8080"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + + networks: + - media riven-frontend: image: spoked/riven-frontend:dev @@ -140,58 +150,8 @@ services: depends_on: riven: condition: service_healthy - networks: [media] - - jellyfin: - image: jellyfin/jellyfin - container_name: jellyfin - profiles: ["jellyfin"] - restart: unless-stopped - ports: - - "8096:8096" - environment: - NVIDIA_VISIBLE_DEVICES: all - NVIDIA_DRIVER_CAPABILITIES: compute,video,utility - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/jellyfin:/config - networks: [media] - - - plex: - image: lscr.io/linuxserver/plex - container_name: plex - profiles: ["plex"] - restart: unless-stopped - ports: - - "32400:32400" - environment: - PUID: 1000 - PGID: 1000 - VERSION: docker - NVIDIA_VISIBLE_DEVICES: all - NVIDIA_DRIVER_CAPABILITIES: compute,video,utility - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/plex:/config - networks: [media] - - - emby: - image: emby/embyserver - container_name: emby - profiles: ["emby"] - restart: unless-stopped - ports: - - "8097:8096" - environment: - NVIDIA_VISIBLE_DEVICES: all - NVIDIA_DRIVER_CAPABILITIES: compute,video,utility - volumes: - - /mnt/riven/mount:/media:ro - - /mnt/emby:/config - networks: [media] - + networks: + - media volumes: riven-frontend-data: From 5f3ae753a3a388d6213dfa3806b68277022c6999 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:21:12 -0500 Subject: [PATCH 067/154] Update install.sh --- ubuntu/install.sh | 65 +++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 2889e7c..4c832e2 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -24,8 +24,11 @@ fail(){ echo "[✖] $1"; exit 1; } require_non_empty() { local prompt="$1" val while true; do - read -rsp "$prompt: " val; echo - [[ -n "$val" ]] && { echo "$val"; return; } + read -rsp "$prompt: " val + echo + # Strip ALL newlines / carriage returns defensively + val="$(printf '%s' "$val" | tr -d '\r\n')" + [[ -n "$val" ]] && { printf '%s' "$val"; return; } warn "Value required" done } @@ -239,45 +242,45 @@ esac banner ".env Generation" cat > .env < Date: Wed, 7 Jan 2026 15:33:22 -0500 Subject: [PATCH 068/154] Update install.sh --- ubuntu/install.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 4c832e2..be24bea 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -24,24 +24,32 @@ fail(){ echo "[✖] $1"; exit 1; } require_non_empty() { local prompt="$1" val while true; do - read -rsp "$prompt: " val + # -r = raw, no backslash escapes + IFS= read -r -s -p "$prompt: " val echo - # Strip ALL newlines / carriage returns defensively - val="$(printf '%s' "$val" | tr -d '\r\n')" + # FORCE single-line: remove CR, LF, tabs, spaces at ends + val="$(printf '%s' "$val" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" [[ -n "$val" ]] && { printf '%s' "$val"; return; } warn "Value required" done } + require_url() { local prompt="$1" val while true; do - read -rp "$prompt: " val - [[ "$val" =~ ^https?:// ]] && { echo "$val"; return; } + IFS= read -r -p "$prompt: " val + val="$(printf '%s' "$val" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + [[ "$val" =~ ^https?:// ]] && { printf '%s' "$val"; return; } warn "Must include http:// or https://" done } + +sanitize() { + printf "%s" "$1" | tr -d '\r\n' +} + ############################################ # ROOT CHECK ############################################ From c7b44a74e2f2ea15b3d4762f405b98c54e135cc1 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:37:57 -0500 Subject: [PATCH 069/154] Update install.sh --- ubuntu/install.sh | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index be24bea..5e89a9c 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -82,13 +82,53 @@ if ! command -v docker >/dev/null; then fi ok "Docker ready" + +############################################ +# USER / UID / GID DETECTION +############################################ +detect_uid_gid() { + # Prefer the sudo user if present + if [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then + TARGET_UID="$(id -u "$SUDO_USER")" + TARGET_GID="$(id -g "$SUDO_USER")" + return + fi + + # Fallback: first non-root user with UID >= 1000 + local user + user="$(awk -F: '$3>=1000 && $3<65534 {print $1; exit}' /etc/passwd)" + + if [[ -n "$user" ]]; then + TARGET_UID="$(id -u "$user")" + TARGET_GID="$(id -g "$user")" + return + fi + + # Absolute fallback + TARGET_UID=1000 + TARGET_GID=1000 +} + +detect_uid_gid + +ok "Detected user ownership: UID=$TARGET_UID GID=$TARGET_GID" + + ############################################ # FILESYSTEM ############################################ banner "Filesystem" + mkdir -p "$BACKEND_PATH" "$MOUNT_PATH" "$INSTALL_DIR" -chown -R "${SUDO_USER:-1000}:${SUDO_USER:-1000}" /mnt/riven -ok "Filesystem ready" + +chown "$TARGET_UID:$TARGET_GID" "$BACKEND_PATH" "$MOUNT_PATH" \ + || fail "Failed to chown backend or mount path" + +chown "$TARGET_UID:$TARGET_GID" "$INSTALL_DIR" \ + || fail "Failed to chown install dir" + +ok "Filesystem ready (owner: $TARGET_UID:$TARGET_GID)" + ############################################ # DOWNLOAD COMPOSE FILES FIRST From d35053a4a9fd626b48d4c8e1d1307b304fa59425 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:44:00 -0500 Subject: [PATCH 070/154] Update install.sh --- ubuntu/install.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 5e89a9c..b61db07 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -284,6 +284,7 @@ case "$MEDIA_PROFILE" in ;; esac + ############################################ # WRITE .env (ONCE, NO SED) ############################################ @@ -331,6 +332,46 @@ RIVEN_SCRAPING_PROWLARR_URL="$RIVEN_SCRAPING_PROWLARR_URL" RIVEN_SCRAPING_PROWLARR_API_KEY="$RIVEN_SCRAPING_PROWLARR_API_KEY" EOF +############################################ +# FIX BROKEN MULTILINE ENV VALUES +############################################ +banner "Fixing .env formatting issues" + +awk ' + BEGIN { key=""; val="" } + { + # If we are currently accumulating a broken value + if (key != "") { + val = val $0 + if ($0 ~ /"$/) { + gsub(/\n/, "", val) + sub(/"$/, "", val) + print key "\"" val "\"" + key="" + val="" + } + next + } + + # Detect start of broken quoted value + if ($0 ~ /^[A-Z0-9_]+="$/) { + split($0, a, "=") + key = a[1] "=" + val = "" + next + } + + # Normal line + print + } +' .env > .env.fixed + +mv .env.fixed .env +chmod 600 .env + +ok ".env repaired and sanitized" + + ############################################ # START RIVEN ############################################ From ec8f23d5da414a79fe31d6715cfcd5fc92b77c57 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:54:37 -0500 Subject: [PATCH 071/154] Update install.sh --- ubuntu/install.sh | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index b61db07..e6dae7f 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -130,6 +130,54 @@ chown "$TARGET_UID:$TARGET_GID" "$INSTALL_DIR" \ ok "Filesystem ready (owner: $TARGET_UID:$TARGET_GID)" +############################################ +# RIVEN rshared MOUNT MODULE (REQUIRED) +############################################ +ensure_riven_rshared_mount() { + local MOUNT_PATH="/mnt/riven/mount" + local SERVICE_NAME="riven-bind-shared.service" + + banner "Ensuring rshared mount for Riven" + + mkdir -p "$MOUNT_PATH" + + # If already shared, do nothing + if findmnt -no PROPAGATION "$MOUNT_PATH" 2>/dev/null | grep -q shared; then + ok "Mount already rshared" + return + fi + + warn "Mount is not rshared — installing systemd unit" + + cat >/etc/systemd/system/$SERVICE_NAME < Date: Wed, 7 Jan 2026 16:12:21 -0500 Subject: [PATCH 072/154] Update install.sh --- ubuntu/install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index e6dae7f..456d88c 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -178,8 +178,14 @@ EOF } +sudo mount --bind $MOUNT_PATH $MOUNT_PATH +sudo mount --make-rshared $MOUNT_PATH + + banner "Mounted $MOUNT_PATH" + + ############################################ -# DOWNLOAD COMPOSE FILES FIRST +# DOWNLOAD COMPOSE FILES ############################################ banner "Docker Compose Files" cd "$INSTALL_DIR" @@ -229,7 +235,7 @@ banner "Media API Key" MEDIA_API_KEY="$(require_non_empty "Enter media API key / token")" ############################################ -# FRONTEND ORIGIN (MOVED HERE) +# FRONTEND ORIGIN ############################################ banner "Frontend Origin" ORIGIN="$DEFAULT_ORIGIN" @@ -238,7 +244,7 @@ read -rp "Using reverse proxy? (y/N): " USE_PROXY ok "ORIGIN=$ORIGIN" ############################################ -# DOWNLOADER (REQUIRED) +# DOWNLOADER ############################################ banner "Downloader Selection (REQUIRED)" echo "1) Real-Debrid" From 58512188121ac0f93039bd35b5c0dde200623039 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:20:37 -0500 Subject: [PATCH 073/154] Update install.sh --- ubuntu/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 456d88c..a9e8596 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -309,8 +309,8 @@ esac # SECRETS ############################################ POSTGRES_PASSWORD="$(openssl rand -hex 24)" -BACKEND_API_KEY="$(openssl rand -hex 32)" -AUTH_SECRET="$(openssl rand -hex 32)" +BACKEND_API_KEY="$(openssl rand -hex 16)" +AUTH_SECRET="$(openssl rand -base64 32)" ############################################ # MEDIA UPDATER FLAGS From 36f5852b7f5f16bf98a8aa1e81f6288748e4f051 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:42:09 -0500 Subject: [PATCH 074/154] Update docker-compose.yml --- ubuntu/docker-compose.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 43f49b4..091354d 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -119,16 +119,8 @@ services: depends_on: riven-db: - condition: service_healthy - - healthcheck: - # TCP check avoids dependency on HTTP internals - test: ["CMD-SHELL", "nc -z localhost 8080"] - interval: 10s - timeout: 5s - retries: 10 - start_period: 20s - + condition: service_healthy + networks: - media From 46cf8fc0111364be28338c8958b1f4ca5eb115ac Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:51:42 -0500 Subject: [PATCH 075/154] Update docker-compose.yml --- ubuntu/docker-compose.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ubuntu/docker-compose.yml b/ubuntu/docker-compose.yml index 091354d..086989f 100644 --- a/ubuntu/docker-compose.yml +++ b/ubuntu/docker-compose.yml @@ -139,9 +139,7 @@ services: ORIGIN: ${ORIGIN} volumes: - riven-frontend-data:/riven/data - depends_on: - riven: - condition: service_healthy + networks: - media From 5b08a874dd1fe3026637cbdfc9de4be37bcc8d5c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:56:52 -0500 Subject: [PATCH 076/154] Update install.sh --- ubuntu/install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index a9e8596..fa015b1 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -492,9 +492,9 @@ echo "🗄️ Database" echo " • Postgres DB: riven" echo " • User: postgres" echo -echo " • POSTGRES PASSWORD: "$POSTGRES_PASSWORD" -echo " • BACKEND API KEY: "$BACKEND_API_KEY" -echo " • AUTH SECRET: "$AUTH_SECRET" +echo " • POSTGRES PASSWORD: $POSTGRES_PASSWORD" +echo " • BACKEND API KEY: $BACKEND_API_KEY" +echo " • AUTH SECRET: $AUTH_SECRET" From 19f22854cae35e9bda2fef7bf9bbe59d73b3f8c7 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:26:54 -0500 Subject: [PATCH 077/154] Update readme.md --- ubuntu/readme.md | 147 ++++++++++++++++++++++++++++++----------------- 1 file changed, 94 insertions(+), 53 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 4cd14e6..ed107d7 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,92 +1,133 @@ ## ▶️ How to run the installer (Ubuntu Script) -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" +Run this command on Ubuntu: +``` bash + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` + # Riven Ubuntu Installer -This script installs and configures Riven on Ubuntu using Docker and Docker Compose. +This installer deploys **Riven** on Ubuntu using Docker and Docker Compose with a fully interactive guided setup. + +--- + +## SUPPORTED SYSTEMS -SUPPORTED SYSTEMS - Ubuntu Server - Ubuntu Desktop - Virtual Machines - Headless servers - Advanced WSL setups +--- + ## WHAT THIS SCRIPT DOES -SYSTEM & DOCKER +### SYSTEM & DOCKER - Installs Docker and Docker Compose ONLY if missing - Configures Docker to use IPv4 only (IPv6 disabled inside Docker only) -- Sets reliable DNS for containers +- Sets reliable DNS defaults for containers + +--- + +### FILESYSTEM & MOUNTS + +Creates and manages the following paths: + + /opt/riven + ├─ docker-compose.yml + └─ .env + + /mnt/riven/backend + ├─ Riven backend data + └─ settings.json (auto-generated) + + /mnt/riven/mount + └─ Media library (movies, TV, anime) -FILESYSTEM & MOUNTS -Creates the following directories: -- /opt/riven - - docker-compose.yml - - .env -- /mnt/riven/backend - - Riven backend data - - settings.json configuration -- /mnt/riven/mount - - ALL movies, TV shows, and anime live here +- Configures a systemd mount service +- Ensures /mnt/riven/mount is bind-mounted as rshared +- This behavior is REQUIRED for Riven to function -Sets up a systemd service to ensure /mnt/riven/mount is bind-mounted as rshared. -This is REQUIRED for Riven to function. +--- -RIVEN DEPLOYMENT -- Downloads docker-compose.yml automatically -- Generates a secure .env file -- Pulls and starts all containers -- Retries containers that fail to start (especially frontend) -- Verifies containers are running +## RIVEN DEPLOYMENT (AUTOMATED) -REQUIRED CONFIGURATION (DO NOT SKIP) +The installer performs a fully interactive configuration. -AFTER INSTALL YOU MUST EDIT: -- /mnt/riven/backend/settings.json +### DURING INSTALL YOU WILL BE PROMPTED TO: +- Select a Downloader (e.g. Real-Debrid) +- Select a Scraper +- Select a Media Server: + - Plex + - Jellyfin + - Emby +- Enter required API keys / tokens -YOU MUST: -- Add at least ONE scraper -- Configure at least ONE media server (Plex, Jellyfin, or Emby) +### THE SCRIPT WILL: +- Auto-generate settings.json +- Generate a secure .env file +- Download docker-compose.yml +- Pull all containers +- Start containers with retry logic +- Verify all services are running -IF YOU DO NOT DO THIS: -- Containers WILL appear healthy -- NO content will load -- Scraping will silently fail +NO manual configuration is required after install. -RIVEN WILL NOT WORK UNTIL THIS IS CONFIGURED. +--- -ACCESSING THE FRONTEND +## IMPORTANT CHANGE -After installation completes, the script will print: -- http://:3000 +OLD BEHAVIOR: +- Manual editing of /mnt/riven/backend/settings.json was required -IMPORTANT PATHS SUMMARY +NEW BEHAVIOR: +- All configuration is handled during the installer +- Riven starts fully configured +- Scraping and media integration work immediately -Docker Compose: -- /opt/riven/docker-compose.yml +--- -Environment file: -- /opt/riven/.env +## ACCESSING THE FRONTEND -Backend configuration: -- /mnt/riven/backend/settings.json +After installation completes, the script prints: -Media library location: -- /mnt/riven/mount + http://:3000 -TROUBLESHOOTING +--- + +## IMPORTANT PATHS + +- Docker Compose: /opt/riven/docker-compose.yml +- Environment file: /opt/riven/.env +- Backend config: /mnt/riven/backend/settings.json +- Media library: /mnt/riven/mount + +--- + +## TROUBLESHOOTING Check running containers: -- docker ps + + docker ps Restart everything: -- cd /opt/riven -- docker compose down -- docker compose up -d + + cd /opt/riven + docker compose down + docker compose up -d View backend logs: -- docker logs riven + + docker logs riven + +--- + +## INSTALL COMPLETE + +If containers are running and no errors are shown: +- Riven is installed +- Media servers are connected +- Scraping is active +- No further setup is required From a248d2768cba00ab3ced8b83662a448d4c083b7d Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:54:01 -0500 Subject: [PATCH 078/154] Update install.sh --- ubuntu/install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index fa015b1..f0c1d22 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -421,7 +421,6 @@ awk ' ' .env > .env.fixed mv .env.fixed .env -chmod 600 .env ok ".env repaired and sanitized" From 88fbc2ee412450ca072b2d11c511cafbc4e030ca Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:40:15 -0500 Subject: [PATCH 079/154] Update install.sh --- ubuntu/install.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index f0c1d22..7233cf1 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -210,7 +210,7 @@ case "$MEDIA_SEL" in esac ############################################ -# START MEDIA SERVER ONLY +# START MEDIA SERVER ############################################ banner "Starting Media Server" docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" up -d @@ -309,9 +309,30 @@ esac # SECRETS ############################################ POSTGRES_PASSWORD="$(openssl rand -hex 24)" -BACKEND_API_KEY="$(openssl rand -hex 16)" AUTH_SECRET="$(openssl rand -base64 32)" + +############################################ +# RIVEN API KEY MODULE +# Order: Generate → Validate → Continue +############################################ + +# ------------------------------------------ +# PART 1: Generate a candidate API key +# (matches Riven charset + length intent) +# ------------------------------------------ +BACKEND_API_KEY="$(tr -dc 'A-Za-z0-9' Date: Thu, 8 Jan 2026 21:46:35 -0500 Subject: [PATCH 080/154] Update readme.md --- ubuntu/readme.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index ed107d7..8b09ea8 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -65,12 +65,13 @@ The installer performs a fully interactive configuration. - Enter required API keys / tokens ### THE SCRIPT WILL: -- Auto-generate settings.json -- Generate a secure .env file -- Download docker-compose.yml -- Pull all containers +- Generate a secure `.env` file +- Download the `docker-compose.yml` +- Pull all required container images - Start containers with retry logic -- Verify all services are running +- Verify that all services are running +- Use the `.env` file to pass configuration into the containers +- (mapped to `settings.json`) NO manual configuration is required after install. @@ -93,7 +94,8 @@ NEW BEHAVIOR: After installation completes, the script prints: http://:3000 - + Or + http:// --- ## IMPORTANT PATHS From 32da6bcb229a6fb4102aad302400f9609df6de5f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Thu, 8 Jan 2026 21:47:17 -0500 Subject: [PATCH 081/154] Update readme.md --- ubuntu/readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 8b09ea8..04e91d6 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -70,8 +70,7 @@ The installer performs a fully interactive configuration. - Pull all required container images - Start containers with retry logic - Verify that all services are running -- Use the `.env` file to pass configuration into the containers -- (mapped to `settings.json`) +- Use the `.env` file to pass configuration into the containers (mapped to `settings.json`) NO manual configuration is required after install. From 386d48deeebfa517e16b5ed113699a169b8353fc Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 07:42:41 -0500 Subject: [PATCH 082/154] Update install.sh --- ubuntu/install.sh | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 7233cf1..c846a29 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -318,20 +318,38 @@ AUTH_SECRET="$(openssl rand -base64 32)" ############################################ # ------------------------------------------ -# PART 1: Generate a candidate API key -# (matches Riven charset + length intent) +# PART 1: Generate API key # ------------------------------------------ BACKEND_API_KEY="$(tr -dc 'A-Za-z0-9' Date: Fri, 9 Jan 2026 07:43:05 -0500 Subject: [PATCH 083/154] Update install.sh --- ubuntu/install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index c846a29..d624f81 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -349,7 +349,6 @@ if [ "${#BACKEND_API_KEY}" -ne 32 ]; then echo "============================================" echo exit 1 -fi ############################################ # MEDIA UPDATER FLAGS From 8ddd4abfe77925b5368422d5ee2b275fb4fb3087 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 08:36:44 -0500 Subject: [PATCH 084/154] Update install.sh --- ubuntu/install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index d624f81..c846a29 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -349,6 +349,7 @@ if [ "${#BACKEND_API_KEY}" -ne 32 ]; then echo "============================================" echo exit 1 +fi ############################################ # MEDIA UPDATER FLAGS From 4a3de970c15806429a99416302e2389fbf30c23b Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 08:45:58 -0500 Subject: [PATCH 085/154] Update install.sh --- ubuntu/install.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index c846a29..59b082a 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -7,6 +7,7 @@ set -euo pipefail INSTALL_DIR="/opt/riven" BACKEND_PATH="/mnt/riven/backend" MOUNT_PATH="/mnt/riven/mount" +LOG_DIR="/tmp/logs/riven" MEDIA_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.media.yml" RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/docker-compose.yml" @@ -55,6 +56,28 @@ sanitize() { ############################################ [[ "$(id -u)" -eq 0 ]] || fail "Run with sudo" +############################################ +# LOGGING MODULE +############################################ + +LOG_FILE="$LOG_DIR/install-$(date +%Y%m%d-%H%M%S).log" + +mkdir -p "$LOG_DIR" +touch "$LOG_FILE" + +# Mirror stdout + stderr to terminal AND log +exec > >(tee -a "$LOG_FILE") 2>&1 + +log() { echo "[INFO] $*"; } +log_warn() { echo "[WARN] $*"; } +log_error() { echo "[ERROR] $*"; } +log_section(){ echo -e "\n========== $* ==========\n"; } + +trap 'log_error "Installer exited unexpectedly at line $LINENO"' ERR + +log "Logging initialized" +log "Log file: $LOG_FILE" + ############################################ # TIMEZONE ############################################ From 6b053d4f7c7bd0f0de12272ddfdc97aea1433713 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:19:54 -0500 Subject: [PATCH 086/154] Create riven-uninstall.sh --- ubuntu/riven-uninstall.sh | 165 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 ubuntu/riven-uninstall.sh diff --git a/ubuntu/riven-uninstall.sh b/ubuntu/riven-uninstall.sh new file mode 100644 index 0000000..c892462 --- /dev/null +++ b/ubuntu/riven-uninstall.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################ +# CONSTANTS (MUST MATCH INSTALLER) +############################################ +INSTALL_DIR="/opt/riven" +RIVEN_ROOT="/mnt/riven" +BACKEND_PATH="$RIVEN_ROOT/backend" +MOUNT_PATH="$RIVEN_ROOT/mount" +LOG_DIR="/tmp/logs/riven" + +SERVICE_NAME="riven-bind-shared.service" + +############################################ +# HELPERS +############################################ +banner(){ echo -e "\n========================================\n $1\n========================================"; } +ok(){ echo "[✔] $1"; } +warn(){ echo "[!] $1"; } +fail(){ echo "[✖] $1"; exit 1; } + +############################################ +# ROOT CHECK +############################################ +[[ "$(id -u)" -eq 0 ]] || fail "Run with sudo" + +############################################ +# CONFIRMATION +############################################ +banner "RIVEN UNINSTALLER" + +echo "⚠️ WARNING" +echo "This will COMPLETELY REMOVE:" +echo " • Riven containers" +echo " • Media containers" +echo " • systemd rshared mount unit" +echo " • $INSTALL_DIR" +echo " • $RIVEN_ROOT (backend + mount)" +echo " • Logs in $LOG_DIR" +echo +read -rp "Type UNINSTALL to continue: " CONFIRM +[[ "$CONFIRM" == "UNINSTALL" ]] || fail "Aborted by user" + +############################################ +# STOP CONTAINERS +############################################ +banner "Stopping Containers" + +if command -v docker >/dev/null; then + if [[ -d "$INSTALL_DIR" ]]; then + cd "$INSTALL_DIR" + + [[ -f docker-compose.yml ]] \ + && docker compose down --volumes --remove-orphans || true + + [[ -f docker-compose.media.yml ]] \ + && docker compose -f docker-compose.media.yml down --volumes --remove-orphans || true + fi +else + warn "Docker not installed — skipping container shutdown" +fi + +ok "Containers stopped" + +############################################ +# REMOVE SYSTEMD MOUNT UNIT +############################################ +banner "Removing rshared mount service" + +if systemctl list-unit-files | grep -q "$SERVICE_NAME"; then + systemctl disable --now "$SERVICE_NAME" || true + rm -f "/etc/systemd/system/$SERVICE_NAME" + systemctl daemon-reexec + systemctl daemon-reload + ok "systemd mount unit removed" +else + warn "No rshared mount service found" +fi + +############################################ +# UNMOUNT RIVEN MOUNT (SAFE) +############################################ +banner "Unmounting Riven mount" + +if mountpoint -q "$MOUNT_PATH"; then + umount -R "$MOUNT_PATH" || warn "Failed to fully unmount $MOUNT_PATH" + ok "Unmounted $MOUNT_PATH" +else + warn "$MOUNT_PATH is not mounted" +fi + +############################################ +# REMOVE BACKEND + MOUNT PATHS +############################################ +banner "Removing Riven filesystem" + +rm -rf "$BACKEND_PATH" +ok "Removed $BACKEND_PATH" + +rm -rf "$MOUNT_PATH" +ok "Removed $MOUNT_PATH" + +############################################ +# REMOVE /mnt/riven IF EMPTY +############################################ +if [[ -d "$RIVEN_ROOT" ]] && [[ -z "$(ls -A "$RIVEN_ROOT")" ]]; then + rmdir "$RIVEN_ROOT" + ok "Removed empty $RIVEN_ROOT" +else + warn "$RIVEN_ROOT not empty — leaving in place" +fi + +############################################ +# REMOVE INSTALL DIR + LOGS +############################################ +banner "Removing install artifacts" + +rm -rf "$INSTALL_DIR" +ok "Removed $INSTALL_DIR" + +rm -rf "$LOG_DIR" +ok "Removed logs" + +############################################ +# DOCKER CLEANUP (SAFE) +############################################ +banner "Docker Cleanup" + +if command -v docker >/dev/null; then + docker network prune -f || true + docker volume prune -f || true + ok "Docker cleanup complete" +else + warn "Docker not installed — skipping cleanup" +fi + +############################################ +# OPTIONAL: DOCKER REMOVAL +############################################ +banner "Optional Docker Removal" + +read -rp "Remove Docker entirely? (y/N): " REMOVE_DOCKER +if [[ "${REMOVE_DOCKER,,}" == "y" ]]; then + apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || true + apt-get autoremove -y + rm -rf /var/lib/docker /var/lib/containerd + ok "Docker fully removed" +else + ok "Docker preserved" +fi + +############################################ +# FINAL SUMMARY +############################################ +banner "UNINSTALL COMPLETE" + +echo "✔ Riven fully removed" +echo "✔ /mnt/riven cleaned" +echo "✔ systemd mount removed" +echo "✔ Containers removed" +echo +echo "System restored to pre-install state." + +ok "Cleanup complete 🧹" \ No newline at end of file From c0a094acff9ece12aa2269c9c6a342f08d0f9a59 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:21:25 -0500 Subject: [PATCH 087/154] Update readme.md --- ubuntu/readme.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 04e91d6..b7b1975 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -132,3 +132,32 @@ If containers are running and no errors are shown: - Scraping is active - No further setup is required + + +## 🗑️ Riven Ubuntu Uninstaller + +This command **completely removes Riven and all related components** installed by the Riven Ubuntu installer. + +### ⚠️ What this removes + +- Riven containers +- Media containers (Jellyfin / Plex / Emby) +- Docker volumes created by Riven +- Riven systemd mount service +- `/opt/riven` +- `/mnt/riven/backend` +- `/mnt/riven/mount` +- `/mnt/riven` (if empty) +- Riven installer logs + +> Docker itself is **preserved by default** (you will be prompted). + +--- + +### ▶️ Run this command on Ubuntu + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/uninstall.sh)" + + + From de234ca8d2d9160265da87d0c83e20a217a87344 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:22:35 -0500 Subject: [PATCH 088/154] Update readme.md --- ubuntu/readme.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index b7b1975..2f58e9f 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -138,6 +138,13 @@ If containers are running and no errors are shown: This command **completely removes Riven and all related components** installed by the Riven Ubuntu installer. +--- + +### ▶️ Run this command on Ubuntu + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-uninstall.sh)" + ### ⚠️ What this removes - Riven containers @@ -152,12 +159,6 @@ This command **completely removes Riven and all related components** installed b > Docker itself is **preserved by default** (you will be prompted). ---- - -### ▶️ Run this command on Ubuntu - -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/uninstall.sh)" From 89d7a80ba0e1b33d0ffe291bed5f8c2cf90003ce Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:23:01 -0500 Subject: [PATCH 089/154] Update readme.md --- ubuntu/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 2f58e9f..8f472e0 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -144,7 +144,7 @@ This command **completely removes Riven and all related components** installed b ```bash sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-uninstall.sh)" - +``` ### ⚠️ What this removes - Riven containers From d18725e42f715d178af68cf11872d10e7021e95a Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:00:48 -0500 Subject: [PATCH 090/154] Create riven-update.sh --- ubuntu/riven-update.sh | 120 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 ubuntu/riven-update.sh diff --git a/ubuntu/riven-update.sh b/ubuntu/riven-update.sh new file mode 100644 index 0000000..63cbde3 --- /dev/null +++ b/ubuntu/riven-update.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################ +# CONSTANTS +############################################ +INSTALL_DIR="/opt/riven" +MEDIA_COMPOSE="docker-compose.media.yml" +RIVEN_COMPOSE="docker-compose.yml" + +############################################ +# HELPERS +############################################ +banner(){ echo -e "\n========================================\n $1\n========================================"; } +ok(){ echo "[✔] $1"; } +warn(){ echo "[!] $1"; } +fail(){ echo "[✖] $1"; exit 1; } + +############################################ +# ROOT CHECK +############################################ +[[ "$(id -u)" -eq 0 ]] || fail "Run with sudo" + +############################################ +# DOCKER CHECK +############################################ +banner "Docker Check" + +command -v docker >/dev/null \ + || fail "Docker is not installed" + +docker info >/dev/null 2>&1 \ + || fail "Docker is installed but not running" + +docker compose version >/dev/null 2>&1 \ + || fail "Docker Compose plugin is missing" + +ok "Docker and Docker Compose are available" + +############################################ +# PRE-FLIGHT CHECKS +############################################ +banner "Riven Update" + +[[ -d "$INSTALL_DIR" ]] \ + || fail "Riven is not installed ($INSTALL_DIR missing)" + +cd "$INSTALL_DIR" + +[[ -f "$RIVEN_COMPOSE" ]] \ + || fail "Missing $RIVEN_COMPOSE" + +[[ -f ".env" ]] \ + || fail "Missing .env file" + +ok "Riven installation detected" + +############################################ +# MEDIA SERVER PROMPT +############################################ +banner "Media Server Update" + +UPDATE_MEDIA=false +read -rp "Update media server containers too? (y/N): " ANSWER +[[ "${ANSWER,,}" == "y" ]] && UPDATE_MEDIA=true + +############################################ +# UPDATE RIVEN +############################################ +banner "Updating Riven" + +docker compose pull +docker compose up -d + +ok "Riven updated successfully" + +############################################ +# UPDATE MEDIA SERVER (OPTIONAL) +############################################ +if [[ "$UPDATE_MEDIA" == "true" ]]; then + banner "Updating Media Server" + + if [[ -f "$MEDIA_COMPOSE" ]]; then + docker compose -f "$MEDIA_COMPOSE" pull + docker compose -f "$MEDIA_COMPOSE" up -d + ok "Media server updated" + else + warn "Media compose file not found — skipping media update" + fi +else + ok "Media server update skipped" +fi + +############################################ +# OPTIONAL IMAGE CLEANUP +############################################ +banner "Optional Docker Image Cleanup" + +read -rp "Prune unused Docker images? (y/N): " PRUNE +if [[ "${PRUNE,,}" == "y" ]]; then + docker image prune + ok "Unused images pruned" +else + ok "Image cleanup skipped" +fi + +############################################ +# SUMMARY +############################################ +banner "UPDATE COMPLETE" + +echo "✔ Riven updated" +[[ "$UPDATE_MEDIA" == "true" ]] \ + && echo "✔ Media server updated" \ + || echo "• Media server unchanged" + +echo +echo "No volumes, mounts, or configuration files were modified." + +ok "Update finished 🚀" \ No newline at end of file From 742a3d4ee22cdaf493b0168d73d7d7228054cc46 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:04:45 -0500 Subject: [PATCH 091/154] Update readme.md --- ubuntu/readme.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 8f472e0..f05de56 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,3 +1,10 @@ +## 📚 Table of Contents + +- [Install Riven](#-riven-ubuntu-installer) +- [Update Riven](#-riven-ubuntu-updater) +- [Uninstall Riven](#-riven-ubuntu-uninstaller) + + ## ▶️ How to run the installer (Ubuntu Script) Run this command on Ubuntu: @@ -160,5 +167,22 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/d > Docker itself is **preserved by default** (you will be prompted). +## 🔁 Riven Ubuntu Updater +This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. + +The updater is **safe by default** and does **not** remove: +- Volumes +- Bind mounts +- Configuration files +- `.env` +- Media libraries + +--- + +### ▶️ Run this command on Ubuntu + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" +``` From 7197573d3b3d8bbbf689d43f5a3e2bd474822130 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:12:47 -0500 Subject: [PATCH 092/154] Update readme.md --- ubuntu/readme.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index f05de56..b5e5d8b 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -12,6 +12,7 @@ Run this command on Ubuntu: sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` + # Riven Ubuntu Installer This installer deploys **Riven** on Ubuntu using Docker and Docker Compose with a fully interactive guided setup. @@ -140,7 +141,7 @@ If containers are running and no errors are shown: - No further setup is required - + ## 🗑️ Riven Ubuntu Uninstaller This command **completely removes Riven and all related components** installed by the Riven Ubuntu installer. @@ -166,9 +167,15 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/d > Docker itself is **preserved by default** (you will be prompted). - + ## 🔁 Riven Ubuntu Updater +### ▶️ Run this command on Ubuntu + +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" +``` + This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. The updater is **safe by default** and does **not** remove: @@ -180,9 +187,4 @@ The updater is **safe by default** and does **not** remove: --- -### ▶️ Run this command on Ubuntu - -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" -``` From fad468c6e3dd5e40c2a04f2f91c109fe4b27b2ff Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:13:29 -0500 Subject: [PATCH 093/154] Update readme.md --- ubuntu/readme.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index b5e5d8b..3e870ab 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,9 +1,8 @@ ## 📚 Table of Contents -- [Install Riven](#-riven-ubuntu-installer) -- [Update Riven](#-riven-ubuntu-updater) -- [Uninstall Riven](#-riven-ubuntu-uninstaller) - +- [Install Riven](#installer) +- [Update Riven](#updater) +- [Uninstall Riven](#uninstaller) ## ▶️ How to run the installer (Ubuntu Script) From 4865d5f687620ab7fc4c46ef1aae4ab23837d127 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:17:28 -0500 Subject: [PATCH 094/154] Update readme.md --- ubuntu/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 3e870ab..2e439cb 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -11,7 +11,7 @@ Run this command on Ubuntu: sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` - +# 🔁 Riven Ubuntu Installer # Riven Ubuntu Installer This installer deploys **Riven** on Ubuntu using Docker and Docker Compose with a fully interactive guided setup. From 7cc408fb04539e9701fb4fda0b448c49064dc2d5 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:17:50 -0500 Subject: [PATCH 095/154] Update readme.md --- ubuntu/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 2e439cb..1abb875 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,8 +1,8 @@ ## 📚 Table of Contents - [Install Riven](#installer) -- [Update Riven](#updater) - [Uninstall Riven](#uninstaller) +- [Update Riven](#updater) ## ▶️ How to run the installer (Ubuntu Script) From 6f7a7d702b944aea086a6cfc2b9001e8b369d7f4 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:21:38 -0500 Subject: [PATCH 096/154] Update readme.md --- ubuntu/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 1abb875..677b814 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -7,8 +7,8 @@ ## ▶️ How to run the installer (Ubuntu Script) Run this command on Ubuntu: -``` bash - sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" +```bash +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" ``` # 🔁 Riven Ubuntu Installer @@ -172,7 +172,7 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/d ### ▶️ Run this command on Ubuntu ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-updater.sh)" ``` This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. From c3d0cdac3d42b6cd159192a54187f7c76d40aab1 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:22:07 -0500 Subject: [PATCH 097/154] Update readme.md --- ubuntu/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 677b814..4ba0c46 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -172,7 +172,7 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/d ### ▶️ Run this command on Ubuntu ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-updater.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" ``` This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. From 244dddcaa2c0d3ac5f59492197bee21c94fae707 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:10:36 -0500 Subject: [PATCH 098/154] Update install.sh Adding extra info for media server token / api --- ubuntu/install.sh | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 59b082a..caf95de 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -252,10 +252,45 @@ echo read -rp "Press ENTER once media server setup is complete..." ############################################ -# MEDIA API KEY +# MEDIA AUTH TOKEN / API KEY ############################################ -banner "Media API Key" -MEDIA_API_KEY="$(require_non_empty "Enter media API key / token")" +banner "Media Server Authentication" + +case "$MEDIA_PROFILE" in + jellyfin) + echo "Jellyfin requires an API key." + echo + echo "How to get it:" + echo " 1) Open Jellyfin Web UI" + echo " 2) Dashboard → API Keys" + echo " 3) Create New API Key" + echo + MEDIA_API_KEY="$(require_non_empty "Enter Jellyfin API Key")" + ;; + plex) + echo "Plex requires a USER TOKEN (NOT an API key)." + echo + echo "How to get it:" + echo " 1) Open Plex Web App" + echo " 2) Visit: https://plex.tv/devices.xml while logged in" + echo " 3) Copy the value of X-Plex-Token" + echo + echo "⚠️ Example:" + echo " " + echo + MEDIA_API_KEY="$(require_non_empty "Enter Plex X-Plex-Token")" + ;; + emby) + echo "Emby requires an API key." + echo + echo "How to get it:" + echo " 1) Open Emby Web UI" + echo " 2) Settings → Advanced → API Keys" + echo " 3) Create new key" + echo + MEDIA_API_KEY="$(require_non_empty "Enter Emby API Key")" + ;; +esac ############################################ # FRONTEND ORIGIN From f514614d49d899d0be3eb0f5ff90490928af34c3 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:15:13 -0500 Subject: [PATCH 099/154] Update install.sh installer: add provider-specific API token guidance after downloader selection --- ubuntu/install.sh | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index caf95de..e3e1f23 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -302,12 +302,17 @@ read -rp "Using reverse proxy? (y/N): " USE_PROXY ok "ORIGIN=$ORIGIN" ############################################ -# DOWNLOADER +# DOWNLOADER SELECTION (REQUIRED) ############################################ banner "Downloader Selection (REQUIRED)" + +echo "Choose ONE downloader service:" +echo echo "1) Real-Debrid" echo "2) All-Debrid" echo "3) Debrid-Link" +echo + read -rp "Select ONE: " DL_SEL RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=false @@ -321,18 +326,39 @@ RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="" case "$DL_SEL" in 1) RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED=true - RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(require_non_empty "Real-Debrid API Key")" + echo + echo "Real-Debrid API Token required." + echo + echo "How to get it:" + echo " 1) Visit https://real-debrid.com/apitoken" + echo " 2) Copy the API Token shown" + echo + RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(require_non_empty "Enter Real-Debrid API Token")" ;; 2) RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true - RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(require_non_empty "All-Debrid API Key")" + echo + echo "All-Debrid API Key required." + echo + echo "How to get it:" + echo " 1) Visit https://alldebrid.com/apikeys" + echo " 2) Generate or copy an existing key" + echo + RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(require_non_empty "Enter All-Debrid API Key")" ;; 3) RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true - RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(require_non_empty "Debrid-Link API Key")" + echo + echo "Debrid-Link API Key required." + echo + echo "How to get it:" + echo " 1) Visit https://debrid-link.com/webapp/apikey" + echo " 2) Copy your API key" + echo + RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(require_non_empty "Enter Debrid-Link API Key")" ;; *) - fail "Downloader REQUIRED" + fail "Downloader selection REQUIRED" ;; esac From 193bcbfcb810ccedd2396588927d2c81bebb4c91 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:21:16 -0500 Subject: [PATCH 100/154] Update install.sh installer: clarify media auth input, explain hidden entry, and document Plex raw token usage --- ubuntu/install.sh | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index e3e1f23..94d6e53 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -256,6 +256,12 @@ read -rp "Press ENTER once media server setup is complete..." ############################################ banner "Media Server Authentication" +echo "⚠️ Note:" +echo " • When pasting keys/tokens below, the input will NOT be visible." +echo " • This is intentional for security." +echo " • Paste normally and press ENTER." +echo + case "$MEDIA_PROFILE" in jellyfin) echo "Jellyfin requires an API key." @@ -263,21 +269,25 @@ case "$MEDIA_PROFILE" in echo "How to get it:" echo " 1) Open Jellyfin Web UI" echo " 2) Dashboard → API Keys" - echo " 3) Create New API Key" + echo " 3) Create a new API key" echo + echo "Paste ONLY the API key value below:" MEDIA_API_KEY="$(require_non_empty "Enter Jellyfin API Key")" ;; plex) echo "Plex requires a USER TOKEN (NOT an API key)." echo echo "How to get it:" - echo " 1) Open Plex Web App" - echo " 2) Visit: https://plex.tv/devices.xml while logged in" + echo " 1) Open Plex Web App and ensure you are logged in" + echo " 2) Visit: https://plex.tv/devices.xml" echo " 3) Copy the value of X-Plex-Token" echo - echo "⚠️ Example:" - echo " " + echo "⚠️ IMPORTANT:" + echo " • Paste ONLY the token value" + echo " • Do NOT include 'token='" + echo " • Do NOT paste XML or URLs" echo + echo "Paste the token below:" MEDIA_API_KEY="$(require_non_empty "Enter Plex X-Plex-Token")" ;; emby) @@ -286,8 +296,9 @@ case "$MEDIA_PROFILE" in echo "How to get it:" echo " 1) Open Emby Web UI" echo " 2) Settings → Advanced → API Keys" - echo " 3) Create new key" + echo " 3) Create a new API key" echo + echo "Paste ONLY the API key value below:" MEDIA_API_KEY="$(require_non_empty "Enter Emby API Key")" ;; esac From 696605df618e896bbe3d97ed47d1644f8d6e9b6c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:25:14 -0500 Subject: [PATCH 101/154] Update install.sh installer: mask downloader API key input and clarify secure entry behavior --- ubuntu/install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 94d6e53..91f052b 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -317,6 +317,9 @@ ok "ORIGIN=$ORIGIN" ############################################ banner "Downloader Selection (REQUIRED)" +echo "• API keys entered below will be masked for security." +echo + echo "Choose ONE downloader service:" echo echo "1) Real-Debrid" @@ -344,7 +347,8 @@ case "$DL_SEL" in echo " 1) Visit https://real-debrid.com/apitoken" echo " 2) Copy the API Token shown" echo - RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(require_non_empty "Enter Real-Debrid API Token")" + echo "Paste ONLY the API token value below:" + RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(read_masked_non_empty "Enter Real-Debrid API Token")" ;; 2) RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true @@ -355,7 +359,8 @@ case "$DL_SEL" in echo " 1) Visit https://alldebrid.com/apikeys" echo " 2) Generate or copy an existing key" echo - RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(require_non_empty "Enter All-Debrid API Key")" + echo "Paste ONLY the API key value below:" + RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(read_masked_non_empty "Enter All-Debrid API Key")" ;; 3) RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true @@ -366,7 +371,8 @@ case "$DL_SEL" in echo " 1) Visit https://debrid-link.com/webapp/apikey" echo " 2) Copy your API key" echo - RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(require_non_empty "Enter Debrid-Link API Key")" + echo "Paste ONLY the API key value below:" + RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(read_masked_non_empty "Enter Debrid-Link API Key")" ;; *) fail "Downloader selection REQUIRED" From 38cb7bd84e58b5f9305fc49333ac5a6c8083ded8 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:27:36 -0500 Subject: [PATCH 102/154] Update install.sh installer: add masked input helper for secure secret entry --- ubuntu/install.sh | 50 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 91f052b..29a1f96 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -22,20 +22,61 @@ ok(){ echo "[✔] $1"; } warn(){ echo "[!] $1"; } fail(){ echo "[✖] $1"; exit 1; } +############################################ +# REQUIRED NON-EMPTY (SILENT) +# (keep for non-secret values if needed) +############################################ require_non_empty() { local prompt="$1" val while true; do - # -r = raw, no backslash escapes - IFS= read -r -s -p "$prompt: " val - echo - # FORCE single-line: remove CR, LF, tabs, spaces at ends + IFS= read -r -p "$prompt: " val val="$(printf '%s' "$val" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" [[ -n "$val" ]] && { printf '%s' "$val"; return; } warn "Value required" done } +############################################ +# REQUIRED NON-EMPTY (MASKED ****) +# For API keys / tokens / secrets +############################################ +read_masked_non_empty() { + local prompt="$1" + local val="" char + + while true; do + val="" + printf "%s: " "$prompt" + + while IFS= read -r -s -n1 char; do + [[ $char == $'\n' ]] && break + + # Handle backspace + if [[ $char == $'\177' ]]; then + if [[ -n "$val" ]]; then + val="${val%?}" + printf '\b \b' + fi + continue + fi + + val+="$char" + printf '*' + done + echo + + # Trim whitespace + val="$(printf '%s' "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + + [[ -n "$val" ]] && { printf '%s' "$val"; return; } + warn "Value required" + done +} + +############################################ +# URL VALIDATION +############################################ require_url() { local prompt="$1" val while true; do @@ -46,7 +87,6 @@ require_url() { done } - sanitize() { printf "%s" "$1" | tr -d '\r\n' } From 2dea217b0f32a0d855b24f1cbc3084da20f5730b Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:32:29 -0500 Subject: [PATCH 103/154] Update install.sh installer: add env support for additional scraper backends --- ubuntu/install.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 29a1f96..f7d5056 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -564,6 +564,16 @@ RIVEN_SCRAPING_TORRENTIO_ENABLED="$RIVEN_SCRAPING_TORRENTIO_ENABLED" RIVEN_SCRAPING_PROWLARR_ENABLED="$RIVEN_SCRAPING_PROWLARR_ENABLED" RIVEN_SCRAPING_PROWLARR_URL="$RIVEN_SCRAPING_PROWLARR_URL" RIVEN_SCRAPING_PROWLARR_API_KEY="$RIVEN_SCRAPING_PROWLARR_API_KEY" + +RIVEN_SCRAPING_COMET_ENABLED="$RIVEN_SCRAPING_COMET_ENABLED" +RIVEN_SCRAPING_COMET_URL="$RIVEN_SCRAPING_COMET_URL" + +RIVEN_SCRAPING_JACKETT_ENABLED="$RIVEN_SCRAPING_JACKETT_ENABLED" +RIVEN_SCRAPING_JACKETT_URL="$RIVEN_SCRAPING_JACKETT_URL" +RIVEN_SCRAPING_JACKETT_API_KEY="$RIVEN_SCRAPING_JACKETT_API_KEY" + +RIVEN_SCRAPING_ZILEAN_ENABLED="$RIVEN_SCRAPING_ZILEAN_ENABLED" +RIVEN_SCRAPING_ZILEAN_URL="$RIVEN_SCRAPING_ZILEAN_URL" EOF ############################################ From c39415a8dd92238cc37bbc4eb39c9cc1654da084 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Fri, 9 Jan 2026 13:40:10 -0500 Subject: [PATCH 104/154] Update install.sh installer: expand scraper selection description --- ubuntu/install.sh | 85 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index f7d5056..1e0e89a 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -420,29 +420,102 @@ case "$DL_SEL" in esac ############################################ -# SCRAPER (REQUIRED) +# SCRAPER SELECTION (REQUIRED) ############################################ banner "Scraper Selection (REQUIRED)" -echo "1) Torrentio" -echo "2) Prowlarr" + +echo "Choose ONE scraping backend:" +echo +echo "1) Torrentio (No config required)" +echo "2) Prowlarr (Local instance only)" +echo "3) Comet (Public or self-hosted)" +echo "4) Jackett (Local instance only)" +echo "5) Zilean (Public or self-hosted)" +echo + read -rp "Select ONE: " SCR_SEL +# Reset all flags RIVEN_SCRAPING_TORRENTIO_ENABLED=false RIVEN_SCRAPING_PROWLARR_ENABLED=false +RIVEN_SCRAPING_COMET_ENABLED=false +RIVEN_SCRAPING_JACKETT_ENABLED=false +RIVEN_SCRAPING_ZILEAN_ENABLED=false + RIVEN_SCRAPING_PROWLARR_URL="" RIVEN_SCRAPING_PROWLARR_API_KEY="" +RIVEN_SCRAPING_COMET_URL="" + +RIVEN_SCRAPING_JACKETT_URL="" +RIVEN_SCRAPING_JACKETT_API_KEY="" + +RIVEN_SCRAPING_ZILEAN_URL="" + case "$SCR_SEL" in 1) RIVEN_SCRAPING_TORRENTIO_ENABLED=true + echo + echo "Torrentio selected." + echo "• Uses public Torrentio endpoint" + echo "• No configuration required" ;; 2) RIVEN_SCRAPING_PROWLARR_ENABLED=true - RIVEN_SCRAPING_PROWLARR_URL="$(require_url "Prowlarr URL")" - RIVEN_SCRAPING_PROWLARR_API_KEY="$(require_non_empty "Prowlarr API Key")" + echo + echo "Prowlarr selected." + echo + echo "Example:" + echo " • http://localhost:9696" + echo + echo "API Key location:" + echo " Settings → General → API Key" + echo + RIVEN_SCRAPING_PROWLARR_URL="$(require_url "Enter Prowlarr URL")" + RIVEN_SCRAPING_PROWLARR_API_KEY="$(read_masked_non_empty "Enter Prowlarr API Key")" + ;; + 3) + RIVEN_SCRAPING_COMET_ENABLED=true + echo + echo "Comet selected." + echo + echo "Examples:" + echo " • Public: https://cometfortheweebs.midnightignite.me" + echo " • Local: http://localhost:" + echo + echo "No API key is required." + echo + RIVEN_SCRAPING_COMET_URL="$(require_url "Enter Comet base URL")" + ;; + 4) + RIVEN_SCRAPING_JACKETT_ENABLED=true + echo + echo "Jackett selected." + echo + echo "Example:" + echo " • http://localhost:9117" + echo + echo "API Key location:" + echo " Jackett Web UI → Top-right corner" + echo + RIVEN_SCRAPING_JACKETT_URL="$(require_url "Enter Jackett URL")" + RIVEN_SCRAPING_JACKETT_API_KEY="$(read_masked_non_empty "Enter Jackett API Key")" + ;; + 5) + RIVEN_SCRAPING_ZILEAN_ENABLED=true + echo + echo "Zilean selected." + echo + echo "Examples:" + echo " • Public: https://zilean.example.com" + echo " • Local: http://localhost:" + echo + echo "No API key is required." + echo + RIVEN_SCRAPING_ZILEAN_URL="$(require_url "Enter Zilean base URL")" ;; *) - fail "Scraper REQUIRED" + fail "Scraper selection REQUIRED" ;; esac From 395e1051c162cac3d3267ffb229b4ca6dfc45790 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:07:30 -0500 Subject: [PATCH 105/154] Fix Docker install logic and avoid installer hang - Replace get.docker.com with Ubuntu repo install - Avoid apt lock / non-interactive hangs - Add clear messaging when Docker is already installed - Ensure Docker service is enabled and started --- ubuntu/install.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 1e0e89a..b729b03 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -139,12 +139,15 @@ apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 # DOCKER ############################################ banner "Docker" -if ! command -v docker >/dev/null; then - curl -fsSL https://get.docker.com | sh - systemctl enable --now docker -fi -ok "Docker ready" +if command -v docker >/dev/null 2>&1; then + ok "Docker already installed" +else + apt-get update -y || fail "apt update failed" + apt-get install -y docker.io docker-compose-plugin || fail "Docker install failed" + systemctl enable --now docker || fail "Docker service failed" + ok "Docker installed" +fi ############################################ # USER / UID / GID DETECTION From 4330501f0c13ea52d05ffd3c33d79fff848ac256 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:11:07 -0500 Subject: [PATCH 106/154] Update install.sh --- ubuntu/install.sh | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index b729b03..8608fb9 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -140,15 +140,34 @@ apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 ############################################ banner "Docker" +# Wait for any existing apt locks +while fuser /var/lib/dpkg/lock >/dev/null 2>&1 \ + || fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do + echo "[*] Waiting for apt lock..." + sleep 3 +done + if command -v docker >/dev/null 2>&1; then ok "Docker already installed" else + echo "[*] Installing Docker (this can take a few minutes)..." + + # Stop unattended upgrades to prevent deadlock + systemctl stop unattended-upgrades >/dev/null 2>&1 || true + apt-get update -y || fail "apt update failed" - apt-get install -y docker.io docker-compose-plugin || fail "Docker install failed" + + # Disable dpkg TTY + hard timeout + timeout 600 apt-get install -y \ + -o Dpkg::Use-Pty=0 \ + docker.io docker-compose-plugin \ + || fail "Docker install failed or timed out" + systemctl enable --now docker || fail "Docker service failed" ok "Docker installed" fi + ############################################ # USER / UID / GID DETECTION ############################################ From d3ff0072fc574cd31710bf775715fa2a41572f3f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:17:53 -0500 Subject: [PATCH 107/154] Skip reinstall of existing system dependencies Avoid running apt update/install when all required packages are already present. --- ubuntu/install.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 8608fb9..99851fe 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -132,8 +132,15 @@ ok "Timezone set: $TZ_SELECTED" # SYSTEM DEPS ############################################ banner "System Dependencies" -apt-get update -apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 + +dpkg -s ca-certificates curl gnupg lsb-release openssl fuse3 >/dev/null 2>&1 \ + && ok "System dependencies already installed" \ + || { + apt-get update || fail "apt update failed" + apt-get install -y ca-certificates curl gnupg lsb-release openssl fuse3 \ + || fail "dependency install failed" + ok "System dependencies installed" + } ############################################ # DOCKER From 2e4ab1865f89ed19ff236aa4f2e0e026ed6fc643 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:22:10 -0500 Subject: [PATCH 108/154] Update install.sh --- ubuntu/install.sh | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 99851fe..5c6d309 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -147,34 +147,27 @@ dpkg -s ca-certificates curl gnupg lsb-release openssl fuse3 >/dev/null 2>&1 \ ############################################ banner "Docker" -# Wait for any existing apt locks -while fuser /var/lib/dpkg/lock >/dev/null 2>&1 \ - || fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do - echo "[*] Waiting for apt lock..." - sleep 3 -done - if command -v docker >/dev/null 2>&1; then ok "Docker already installed" else - echo "[*] Installing Docker (this can take a few minutes)..." + echo "[*] Installing Docker" - # Stop unattended upgrades to prevent deadlock systemctl stop unattended-upgrades >/dev/null 2>&1 || true - apt-get update -y || fail "apt update failed" + apt-get update || fail "apt update failed" + + # Install containerd FIRST (safe) + apt-get install -y containerd || fail "containerd install failed" - # Disable dpkg TTY + hard timeout - timeout 600 apt-get install -y \ - -o Dpkg::Use-Pty=0 \ + # Install docker WITHOUT recommends (prevents dnsmasq/ubuntu-fan) + apt-get install -y --no-install-recommends \ docker.io docker-compose-plugin \ - || fail "Docker install failed or timed out" + || fail "Docker install failed" systemctl enable --now docker || fail "Docker service failed" ok "Docker installed" fi - ############################################ # USER / UID / GID DETECTION ############################################ From 42f508e08aeafc397503b1eb3ab6814f3c9805e0 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:29:08 -0500 Subject: [PATCH 109/154] Update install.sh --- ubuntu/install.sh | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 5c6d309..46402e3 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -150,24 +150,13 @@ banner "Docker" if command -v docker >/dev/null 2>&1; then ok "Docker already installed" else - echo "[*] Installing Docker" - - systemctl stop unattended-upgrades >/dev/null 2>&1 || true - - apt-get update || fail "apt update failed" - - # Install containerd FIRST (safe) - apt-get install -y containerd || fail "containerd install failed" - - # Install docker WITHOUT recommends (prevents dnsmasq/ubuntu-fan) - apt-get install -y --no-install-recommends \ - docker.io docker-compose-plugin \ - || fail "Docker install failed" - - systemctl enable --now docker || fail "Docker service failed" + echo "[*] Installing Docker — this may take several minutes depending on your connection..." + curl -fsSL https://get.docker.com | sh + systemctl enable --now docker ok "Docker installed" fi + ############################################ # USER / UID / GID DETECTION ############################################ From 95da4feeecc6170e8ead7618cf21082e7873576c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:41:43 -0500 Subject: [PATCH 110/154] Add docker group setup and user assignment Create docker group if missing and add detected non-root user for non-sudo Docker access. --- ubuntu/install.sh | 67 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 46402e3..2b0fec7 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -99,6 +99,7 @@ sanitize() { ############################################ # LOGGING MODULE ############################################ +banner "Logging" LOG_FILE="$LOG_DIR/install-$(date +%Y%m%d-%H%M%S).log" @@ -142,24 +143,12 @@ dpkg -s ca-certificates curl gnupg lsb-release openssl fuse3 >/dev/null 2>&1 \ ok "System dependencies installed" } -############################################ -# DOCKER -############################################ -banner "Docker" - -if command -v docker >/dev/null 2>&1; then - ok "Docker already installed" -else - echo "[*] Installing Docker — this may take several minutes depending on your connection..." - curl -fsSL https://get.docker.com | sh - systemctl enable --now docker - ok "Docker installed" -fi - ############################################ # USER / UID / GID DETECTION ############################################ +banner "UserDetect" + detect_uid_gid() { # Prefer the sudo user if present if [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then @@ -187,6 +176,56 @@ detect_uid_gid ok "Detected user ownership: UID=$TARGET_UID GID=$TARGET_GID" +############################################ +# DOCKER +############################################ +banner "Docker" + +if command -v docker >/dev/null 2>&1; then + ok "Docker already installed" +else + echo "[*] Installing Docker — this may take several minutes depending on your connection..." + curl -fsSL https://get.docker.com | sh + systemctl enable --now docker + ok "Docker installed" +fi + +############################################ +# DOCKER GROUP / USER PERMISSIONS +############################################ +banner "DockerGroup" + +setup_docker_group() { + # Ensure docker group exists + if ! getent group docker >/dev/null 2>&1; then + groupadd docker || fail "Failed to create docker group" + ok "Docker group created" + else + ok "Docker group already exists" + fi + + # Determine target user + local user="" + if [[ -n "${SUDO_USER:-}" && "$SUDO_USER" != "root" ]]; then + user="$SUDO_USER" + else + user="$(awk -F: '$3>=1000 && $3<65534 {print $1; exit}' /etc/passwd)" + fi + + if [[ -z "$user" ]]; then + warn "No non-root user found to add to docker group" + return + fi + + # Add user to docker group if not already a member + if id -nG "$user" | grep -qw docker; then + ok "User '$user' already in docker group" + else + usermod -aG docker "$user" || fail "Failed to add $user to docker group" + ok "User '$user' added to docker group" + warn "Log out and back in for Docker permissions to apply" + fi +} ############################################ # FILESYSTEM From b1f3674d18d590b5afdda1e749343181fb265a0e Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:44:16 -0500 Subject: [PATCH 111/154] Update install.sh --- ubuntu/install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 2b0fec7..3d3d725 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -euo pipefail +INSTALL_VERSION="2026-01-11-docker-group" +echo "Installer version: $INSTALL_VERSION" + ############################################ # CONSTANTS ############################################ From 260d3b5917f674c51f0e39fa272de2edfd302fac Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:47:15 -0500 Subject: [PATCH 112/154] Update install.sh --- ubuntu/install.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 3d3d725..17c89e5 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,9 +1,6 @@ #!/usr/bin/env bash set -euo pipefail -INSTALL_VERSION="2026-01-11-docker-group" -echo "Installer version: $INSTALL_VERSION" - ############################################ # CONSTANTS ############################################ @@ -17,6 +14,10 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" +INSTALL_VERSION="v0.5" + +echo "Installer version: $INSTALL_VERSION" + ############################################ # HELPERS ############################################ @@ -99,6 +100,16 @@ sanitize() { ############################################ [[ "$(id -u)" -eq 0 ]] || fail "Run with sudo" +############################################ +# INSTALLER VERSION +############################################ +banner "Installer" + +print_installer_version() { + : "${INSTALL_VERSION:=unknown}" + echo "[*] Installer version: ${INSTALL_VERSION}" +} + ############################################ # LOGGING MODULE ############################################ From 91546fa26eea3cea57edd77b7cc86308e3e47077 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:49:25 -0500 Subject: [PATCH 113/154] Update install.sh --- ubuntu/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 17c89e5..b59d48a 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -103,11 +103,11 @@ sanitize() { ############################################ # INSTALLER VERSION ############################################ -banner "Installer" +banner "Version" print_installer_version() { : "${INSTALL_VERSION:=unknown}" - echo "[*] Installer version: ${INSTALL_VERSION}" + ok "[*] Installer version: ${INSTALL_VERSION}" } ############################################ From dbe720b3032f4b688323461b6edae6dff9e0ac66 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:51:49 -0500 Subject: [PATCH 114/154] Update install.sh --- ubuntu/install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index b59d48a..6871503 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -107,9 +107,12 @@ banner "Version" print_installer_version() { : "${INSTALL_VERSION:=unknown}" - ok "[*] Installer version: ${INSTALL_VERSION}" + ok "Installer version: ${INSTALL_VERSION}" } +print_installer_version + + ############################################ # LOGGING MODULE ############################################ From 2524630427e933b266aee54d114226e868f55648 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:52:48 -0500 Subject: [PATCH 115/154] Update install.sh --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 6871503..aad1e38 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5" +INSTALL_VERSION="v0.5.1" echo "Installer version: $INSTALL_VERSION" From 3ab9293a8d719683de78a29f47f288a14a997605 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:56:09 -0500 Subject: [PATCH 116/154] Update install.sh --- ubuntu/install.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index aad1e38..85f0fc9 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -95,6 +95,34 @@ sanitize() { printf "%s" "$1" | tr -d '\r\n' } +############################################ +# OS CHECK (Ubuntu only) +############################################ +banner "OS Check" + +require_ubuntu() { + # Must be Linux + if [[ "$(uname -s)" != "Linux" ]]; then + fail "This installer must be run on Linux (Ubuntu). Detected: $(uname -s)" + fi + + # Must have /etc/os-release + if [[ ! -f /etc/os-release ]]; then + fail "Cannot determine OS (missing /etc/os-release)" + fi + + # Must be Ubuntu + . /etc/os-release + + if [[ "${ID:-}" != "ubuntu" ]]; then + fail "Unsupported OS: ${PRETTY_NAME:-unknown}. Ubuntu required." + fi + + ok "Ubuntu detected (${PRETTY_NAME})" +} + +require_ubuntu + ############################################ # ROOT CHECK ############################################ From 6d7f4f3d3cfaf988fe3fa18a15bbecb147fac233 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:56:57 -0500 Subject: [PATCH 117/154] Update install.sh --- ubuntu/install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 85f0fc9..597cbb9 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,9 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.1" - -echo "Installer version: $INSTALL_VERSION" +INSTALL_VERSION="v0.5.2" ############################################ # HELPERS From 19b97bc503ffa605611042c80fe2b2a2508cda56 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:58:34 -0500 Subject: [PATCH 118/154] Update install.sh --- ubuntu/install.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 597cbb9..6b9fea8 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -94,17 +94,24 @@ sanitize() { } ############################################ -# OS CHECK (Ubuntu only) +# OS CHECK (Ubuntu only, WSL warned) ############################################ banner "OS Check" require_ubuntu() { # Must be Linux if [[ "$(uname -s)" != "Linux" ]]; then - fail "This installer must be run on Linux (Ubuntu). Detected: $(uname -s)" + fail "This installer must be run on Ubuntu Linux. Detected: $(uname -s)" fi - # Must have /etc/os-release + # Detect WSL + if grep -qi microsoft /proc/version 2>/dev/null; then + warn "WSL detected — this is not recommended" + read -rp "Continue anyway? [y/N]: " yn + [[ "${yn:-}" =~ ^[Yy]$ ]] || exit 1 + fi + + # Must have os-release if [[ ! -f /etc/os-release ]]; then fail "Cannot determine OS (missing /etc/os-release)" fi From 9afabfe58e31aa92100089f58708ef9be6f556d6 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:58:43 -0500 Subject: [PATCH 119/154] Update install.sh --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 6b9fea8..5b5081d 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.2" +INSTALL_VERSION="v0.5.3" ############################################ # HELPERS From fb9dd228eeca04881288e5a9314017b3367426aa Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:03:17 -0500 Subject: [PATCH 120/154] Update install.sh --- ubuntu/install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 5b5081d..1fa0376 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -20,9 +20,9 @@ INSTALL_VERSION="v0.5.3" # HELPERS ############################################ banner(){ echo -e "\n========================================\n $1\n========================================"; } -ok(){ echo "[✔] $1"; } -warn(){ echo "[!] $1"; } -fail(){ echo "[✖] $1"; exit 1; } +ok() { echo "✔ $1"; } +warn() { echo "⚠ $1"; } +fail() { echo "✖ $1"; exit 1; } ############################################ # REQUIRED NON-EMPTY (SILENT) From 28543136301568062543b1939fc27b8e9ec683dd Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:03:26 -0500 Subject: [PATCH 121/154] Update install.sh --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 1fa0376..9970d0a 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.3" +INSTALL_VERSION="v0.5.4" ############################################ # HELPERS From 11e20bfa12efd261aa9effa82b39c360163d1c3d Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:05:16 -0500 Subject: [PATCH 122/154] Update install.sh --- ubuntu/install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 9970d0a..1520371 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,15 +14,15 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.4" +INSTALL_VERSION="v0.5.5" ############################################ # HELPERS ############################################ banner(){ echo -e "\n========================================\n $1\n========================================"; } -ok() { echo "✔ $1"; } -warn() { echo "⚠ $1"; } -fail() { echo "✖ $1"; exit 1; } +ok() { printf "✔ %s\n" "$1"; } +warn() { printf "⚠ %s\n" "$1"; } +fail() { printf "✖ %s\n" "$1"; exit 1; } ############################################ # REQUIRED NON-EMPTY (SILENT) From 747ca6f4ddaf7b804c3012d563d979b8ba17c216 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:18:04 -0500 Subject: [PATCH 123/154] Update install.sh --- ubuntu/install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 1520371..74147fc 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -488,7 +488,7 @@ case "$DL_SEL" in echo " 2) Copy the API Token shown" echo echo "Paste ONLY the API token value below:" - RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(read_masked_non_empty "Enter Real-Debrid API Token")" + RIVEN_DOWNLOADERS_REAL_DEBRID_API_KEY="$(require_non_empty "Enter Real-Debrid API Token")" ;; 2) RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED=true @@ -500,7 +500,7 @@ case "$DL_SEL" in echo " 2) Generate or copy an existing key" echo echo "Paste ONLY the API key value below:" - RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(read_masked_non_empty "Enter All-Debrid API Key")" + RIVEN_DOWNLOADERS_ALL_DEBRID_API_KEY="$(require_non_empty "Enter All-Debrid API Key")" ;; 3) RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED=true @@ -512,7 +512,7 @@ case "$DL_SEL" in echo " 2) Copy your API key" echo echo "Paste ONLY the API key value below:" - RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(read_masked_non_empty "Enter Debrid-Link API Key")" + RIVEN_DOWNLOADERS_DEBRID_LINK_API_KEY="$(require_non_empty "Enter Debrid-Link API Key")" ;; *) fail "Downloader selection REQUIRED" From 7b30a964a739abcb445637ab01a0cb0f944761a6 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:18:17 -0500 Subject: [PATCH 124/154] Update install.sh --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 74147fc..001d8f3 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.5" +INSTALL_VERSION="v0.5.6" ############################################ # HELPERS From 0e7835fc5ab2bed44f5f2e8661da215e0eac92fb Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:28:40 -0500 Subject: [PATCH 125/154] Update install.sh --- ubuntu/install.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 001d8f3..51735f1 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -170,13 +170,27 @@ log "Logging initialized" log "Log file: $LOG_FILE" ############################################ -# TIMEZONE +# TIMEZONE (INSTALLER SAFE) ############################################ banner "Timezone" -TZ_DETECTED="$(timedatectl show --property=Timezone --value || echo UTC)" + +detect_timezone() { + timedatectl show --property=Timezone --value 2>/dev/null \ + || cat /etc/timezone 2>/dev/null \ + || echo UTC +} + +TZ_DETECTED="$(detect_timezone)" read -rp "Timezone [$TZ_DETECTED]: " TZ_INPUT TZ_SELECTED="${TZ_INPUT:-$TZ_DETECTED}" -timedatectl set-timezone "$TZ_SELECTED" + +if [[ ! -f "/usr/share/zoneinfo/$TZ_SELECTED" ]]; then + fail "Invalid timezone: $TZ_SELECTED" +fi + +ln -sf "/usr/share/zoneinfo/$TZ_SELECTED" /etc/localtime +echo "$TZ_SELECTED" > /etc/timezone + ok "Timezone set: $TZ_SELECTED" ############################################ From 890681adcd2ab4f4137ec63d1a6c3fe7162bfdfb Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:28:48 -0500 Subject: [PATCH 126/154] Update install.sh --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 51735f1..5e9d2c9 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.6" +INSTALL_VERSION="v0.5.7" ############################################ # HELPERS From 67a65b27cbcd744270741db8b42fa4c3f777dd74 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 11 Jan 2026 19:34:51 -0500 Subject: [PATCH 127/154] Update install.sh --- ubuntu/install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 5e9d2c9..deee32f 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.7" +INSTALL_VERSION="v0.5.8" ############################################ # HELPERS @@ -291,6 +291,8 @@ setup_docker_group() { fi } +setup_docker_group + ############################################ # FILESYSTEM ############################################ @@ -648,7 +650,11 @@ AUTH_SECRET="$(openssl rand -base64 32)" # ------------------------------------------ # PART 1: Generate API key # ------------------------------------------ +# Generate backend API key safely under pipefail +set +o pipefail BACKEND_API_KEY="$(tr -dc 'A-Za-z0-9' Date: Mon, 12 Jan 2026 22:52:22 -0500 Subject: [PATCH 128/154] Create riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 142 ++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 ubuntu/riven-remount-cycle.sh diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh new file mode 100644 index 0000000..7ff52c5 --- /dev/null +++ b/ubuntu/riven-remount-cycle.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -euo pipefail + +############################################ +# REQUIRE ROOT +############################################ +if [[ "$EUID" -ne 0 ]]; then + echo + echo "[✖] This script must be run with sudo" + echo " Example:" + echo " sudo curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh | sudo bash" + echo + exit 1 +fi + +############################################ +# DEFAULTS +############################################ +RIVEN_CONTAINER="riven" +DEFAULT_MOUNT="/mnt/riven/mount" + +UNMOUNT_RETRIES=6 +WAIT_BETWEEN=2 +MOUNT_WAIT=30 + +############################################ +# HELPERS +############################################ +log() { echo -e "\n[+] $1"; } +warn() { echo -e "[!] $1"; } +fail() { echo -e "\n[✖] $1"; exit 1; } + +is_mounted() { + findmnt -T "$MOUNT_PATH" >/dev/null 2>&1 +} + +############################################ +# MOUNT PATH PROMPT +############################################ +echo +read -rp "Enter mount path [${DEFAULT_MOUNT}]: " MOUNT_PATH +MOUNT_PATH="${MOUNT_PATH:-$DEFAULT_MOUNT}" + +log "Using mount path: $MOUNT_PATH" + +############################################ +# MEDIA SERVER SELECTION +############################################ +echo +echo "Select your media server:" +echo "1) Plex" +echo "2) Jellyfin" +echo "3) Emby" +echo "4) Custom container name" + +read -rp "Enter choice [1-4]: " MEDIA_CHOICE + +case "$MEDIA_CHOICE" in + 1) MEDIA_CONTAINER="plex" ;; + 2) MEDIA_CONTAINER="jellyfin" ;; + 3) MEDIA_CONTAINER="emby" ;; + 4) + read -rp "Enter Docker container name: " MEDIA_CONTAINER + ;; + *) + fail "Invalid selection" + ;; +esac + +log "Using media container: $MEDIA_CONTAINER" + +############################################ +# 1. STOP CONTAINERS +############################################ +log "Stopping Riven container" +docker stop "$RIVEN_CONTAINER" || true + +log "Stopping media container" +docker stop "$MEDIA_CONTAINER" || true + +############################################ +# 2. UNMOUNT LOOP (STRICT) +############################################ +log "Ensuring mount is fully released" + +for attempt in $(seq 1 $UNMOUNT_RETRIES); do + if ! is_mounted; then + log "Mount is gone" + break + fi + + warn "Unmount attempt $attempt" + umount "$MOUNT_PATH" || true + sleep "$WAIT_BETWEEN" +done + +############################################ +# 3. FINAL UNMOUNT CHECK +############################################ +if is_mounted; then + fail "Mount still present after ${UNMOUNT_RETRIES} attempts" +fi + +log "Unmount verified" + +############################################ +# 4. START RIVEN +############################################ +log "Starting Riven container" +docker start "$RIVEN_CONTAINER" + +############################################ +# 5. WAIT FOR REMOUNT +############################################ +log "Waiting for Riven mount" + +for i in $(seq 1 $MOUNT_WAIT); do + if is_mounted; then + log "Mount active" + break + fi + sleep 1 +done + +if ! is_mounted; then + fail "Riven did not remount" +fi + +############################################ +# 6. START MEDIA +############################################ +log "Starting media container" +docker start "$MEDIA_CONTAINER" + +############################################ +# 7. RESTART MEDIA (POST-MOUNT SAFETY) +############################################ +log "Restarting media container after mount stabilization" +sleep 5 +docker restart "$MEDIA_CONTAINER" + +log "Riven remount cycle complete ✔" \ No newline at end of file From 0aa78e4538f182a49c68b3ddcfeb0d4fc114372e Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 12 Jan 2026 22:54:18 -0500 Subject: [PATCH 129/154] Update readme.md --- ubuntu/readme.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 4ba0c46..0e71e98 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -8,7 +8,7 @@ Run this command on Ubuntu: ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/install.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh)" ``` # 🔁 Riven Ubuntu Installer @@ -150,7 +150,7 @@ This command **completely removes Riven and all related components** installed b ### ▶️ Run this command on Ubuntu ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-uninstall.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-uninstall.sh)" ``` ### ⚠️ What this removes @@ -172,7 +172,7 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/d ### ▶️ Run this command on Ubuntu ```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/distributables/main/ubuntu/riven-update.sh)" +sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-update.sh)" ``` This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. From fb61906f3c5f8db377c82e16d7afce119f9b5d02 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 12 Jan 2026 22:54:58 -0500 Subject: [PATCH 130/154] Update riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 7ff52c5..3ab7199 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -8,7 +8,7 @@ if [[ "$EUID" -ne 0 ]]; then echo echo "[✖] This script must be run with sudo" echo " Example:" - echo " sudo curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh | sudo bash" + echo " https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh" echo exit 1 fi From 436922c4c1070eb008e2ee2bd75a8f0687741b01 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:15:31 -0500 Subject: [PATCH 131/154] Update readme.md --- ubuntu/readme.md | 121 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/ubuntu/readme.md b/ubuntu/readme.md index 0e71e98..f7efb11 100644 --- a/ubuntu/readme.md +++ b/ubuntu/readme.md @@ -1,17 +1,20 @@ ## 📚 Table of Contents - [Install Riven](#installer) +- [Recover Riven Mount & Restart Media](#remount) - [Uninstall Riven](#uninstaller) - [Update Riven](#updater) +--- + ## ▶️ How to run the installer (Ubuntu Script) Run this command on Ubuntu: -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh)" -``` -# 🔁 Riven Ubuntu Installer + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/install.sh)" + + +# 🔁 Riven Ubuntu Installer # Riven Ubuntu Installer This installer deploys **Riven** on Ubuntu using Docker and Docker Compose with a fully interactive guided setup. @@ -102,6 +105,7 @@ After installation completes, the script prints: http://:3000 Or http:// + --- ## IMPORTANT PATHS @@ -131,14 +135,94 @@ View backend logs: --- -## INSTALL COMPLETE + +## 🔁 Riven Mount Recovery & Media Restart Tool + +This utility safely **resets the Riven mount and restarts media services** without reinstalling or reconfiguring anything. + +It is designed for situations where: +- The Riven mount becomes stale +- Media servers see empty libraries +- FUSE/bind mounts fail to release cleanly +- You need to safely cycle storage without rebooting + +--- + +### ▶️ Run this command on Ubuntu + + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh)" + +--- + +### WHAT THIS SCRIPT DOES + +- Stops the **Riven Docker container** +- Stops the selected **media server container** +- Actively unmounts the Riven mount path +- Verifies the mount is **fully released** +- Re-attempts unmounting until the kernel confirms it is gone +- Restarts the Riven container +- Waits for the mount to become available +- Starts the media server +- Restarts the media server **after mount stabilization** + +--- + +### INTERACTIVE PROMPTS + +During execution, you will be asked to: + +- Confirm or change the mount path + - Default: `/mnt/riven/mount` +- Select your media server: + - Plex + - Jellyfin + - Emby + - Custom container name + +No configuration files are modified. + +--- + +### IMPORTANT NOTES + +- This script **must be run with sudo** +- Safe to run multiple times +- Does **not** remove data +- Does **not** change `.env` or settings +- Does **not** reinstall containers +- Designed for production systems + +--- + +### WHEN TO USE THIS + +Use this tool if: +- Media libraries disappear unexpectedly +- Riven appears running but media sees no files +- Mounts do not release after stopping containers +- You want a clean mount reset without rebooting + +--- + +### WHEN NOT TO USE THIS + +Do **not** use this script to: +- Install Riven +- Update Riven +- Change configuration +- Replace the installer or updater + +--- + +## ✔️ SAFE RECOVERY COMPLETE -If containers are running and no errors are shown: -- Riven is installed -- Media servers are connected -- Scraping is active -- No further setup is required +If the script completes without errors: +- The mount is healthy +- Media servers are correctly attached +- No further action is required +--- ## 🗑️ Riven Ubuntu Uninstaller @@ -149,9 +233,10 @@ This command **completely removes Riven and all related components** installed b ### ▶️ Run this command on Ubuntu -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-uninstall.sh)" -``` + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-uninstall.sh)" + +--- + ### ⚠️ What this removes - Riven containers @@ -166,14 +251,14 @@ sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/r > Docker itself is **preserved by default** (you will be prompted). +--- + ## 🔁 Riven Ubuntu Updater ### ▶️ Run this command on Ubuntu -```bash -sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-update.sh)" -``` + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-update.sh)" This command updates **Riven** to the latest available Docker images and optionally updates the configured **media server**. @@ -184,6 +269,4 @@ The updater is **safe by default** and does **not** remove: - `.env` - Media libraries ---- - - +--- \ No newline at end of file From 7611dd0c1dcd95dde43275134beed85a9ac7f758 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:15:41 -0500 Subject: [PATCH 132/154] Update riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 120 ++++++++++++++++------------------ 1 file changed, 57 insertions(+), 63 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 3ab7199..94cda3b 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -5,11 +5,7 @@ set -euo pipefail # REQUIRE ROOT ############################################ if [[ "$EUID" -ne 0 ]]; then - echo - echo "[✖] This script must be run with sudo" - echo " Example:" - echo " https://raw.githubusercontent.com/AquaHorizonGaming/riven-scripts/main/ubuntu/riven-remount-cycle.sh" - echo + echo "❌ Must be run as root" exit 1 fi @@ -19,16 +15,17 @@ fi RIVEN_CONTAINER="riven" DEFAULT_MOUNT="/mnt/riven/mount" -UNMOUNT_RETRIES=6 +UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 -MOUNT_WAIT=30 +REMOUNT_WAIT=5 ############################################ -# HELPERS +# OUTPUT HELPERS ############################################ -log() { echo -e "\n[+] $1"; } -warn() { echo -e "[!] $1"; } -fail() { echo -e "\n[✖] $1"; exit 1; } +section() { echo -e "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"; echo "▶ $1"; } +ok() { echo "✔ $1"; } +warn() { echo "⚠ $1"; } +fail() { echo "✖ $1"; exit 1; } is_mounted() { findmnt -T "$MOUNT_PATH" >/dev/null 2>&1 @@ -37,55 +34,49 @@ is_mounted() { ############################################ # MOUNT PATH PROMPT ############################################ -echo -read -rp "Enter mount path [${DEFAULT_MOUNT}]: " MOUNT_PATH +section "Mount Configuration" +read -rp "Mount path [${DEFAULT_MOUNT}]: " MOUNT_PATH MOUNT_PATH="${MOUNT_PATH:-$DEFAULT_MOUNT}" - -log "Using mount path: $MOUNT_PATH" +ok "Using mount path: $MOUNT_PATH" ############################################ # MEDIA SERVER SELECTION ############################################ -echo -echo "Select your media server:" +section "Media Server Selection" echo "1) Plex" echo "2) Jellyfin" echo "3) Emby" echo "4) Custom container name" - -read -rp "Enter choice [1-4]: " MEDIA_CHOICE +read -rp "Choice [1-4]: " MEDIA_CHOICE case "$MEDIA_CHOICE" in 1) MEDIA_CONTAINER="plex" ;; 2) MEDIA_CONTAINER="jellyfin" ;; 3) MEDIA_CONTAINER="emby" ;; - 4) - read -rp "Enter Docker container name: " MEDIA_CONTAINER - ;; - *) - fail "Invalid selection" - ;; + 4) read -rp "Container name: " MEDIA_CONTAINER ;; + *) fail "Invalid selection" ;; esac -log "Using media container: $MEDIA_CONTAINER" +ok "Using media container: $MEDIA_CONTAINER" ############################################ -# 1. STOP CONTAINERS +# STOP CONTAINERS ############################################ -log "Stopping Riven container" -docker stop "$RIVEN_CONTAINER" || true +section "Stopping Containers" +docker stop "$RIVEN_CONTAINER" >/dev/null 2>&1 || true +ok "Riven stopped" -log "Stopping media container" -docker stop "$MEDIA_CONTAINER" || true +docker stop "$MEDIA_CONTAINER" >/dev/null 2>&1 || true +ok "Media server stopped" ############################################ -# 2. UNMOUNT LOOP (STRICT) +# UNMOUNT ############################################ -log "Ensuring mount is fully released" +section "Unmounting Mount Path" for attempt in $(seq 1 $UNMOUNT_RETRIES); do if ! is_mounted; then - log "Mount is gone" + ok "Mount is fully unmounted" break fi @@ -94,49 +85,52 @@ for attempt in $(seq 1 $UNMOUNT_RETRIES); do sleep "$WAIT_BETWEEN" done -############################################ -# 3. FINAL UNMOUNT CHECK -############################################ if is_mounted; then - fail "Mount still present after ${UNMOUNT_RETRIES} attempts" + fail "Mount still present after retries" fi -log "Unmount verified" - ############################################ -# 4. START RIVEN +# REMOUNT (BIND + RSHARED) ############################################ -log "Starting Riven container" -docker start "$RIVEN_CONTAINER" +section "Re-establishing Mount" + +mount --bind "$MOUNT_PATH" "$MOUNT_PATH" +ok "Bind mount created" + +mount --make-rshared "$MOUNT_PATH" +ok "Mount marked as rshared" ############################################ -# 5. WAIT FOR REMOUNT +# VERIFY PROPAGATION ############################################ -log "Waiting for Riven mount" +section "Verifying Propagation" -for i in $(seq 1 $MOUNT_WAIT); do - if is_mounted; then - log "Mount active" - break - fi - sleep 1 -done +findmnt -T "$MOUNT_PATH" -o TARGET,PROPAGATION -if ! is_mounted; then - fail "Riven did not remount" +PROP=$(findmnt -T "$MOUNT_PATH" -o PROPAGATION -n) +if [[ "$PROP" != "shared" && "$PROP" != "rshared" ]]; then + fail "Propagation incorrect: $PROP" fi -############################################ -# 6. START MEDIA -############################################ -log "Starting media container" -docker start "$MEDIA_CONTAINER" +ok "Propagation verified: $PROP" ############################################ -# 7. RESTART MEDIA (POST-MOUNT SAFETY) +# START CONTAINERS ############################################ -log "Restarting media container after mount stabilization" +section "Starting Containers" + +docker start "$RIVEN_CONTAINER" >/dev/null +ok "Riven started" + +docker start "$MEDIA_CONTAINER" >/dev/null +ok "Media server started" + sleep 5 -docker restart "$MEDIA_CONTAINER" +docker restart "$MEDIA_CONTAINER" >/dev/null +ok "Media server restarted (post-mount)" -log "Riven remount cycle complete ✔" \ No newline at end of file +############################################ +# DONE +############################################ +section "Complete" +ok "Riven bind-remount cycle finished successfully" From 434a9400e30681a97bc9999043677052facbe09c Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:17:02 -0500 Subject: [PATCH 133/154] Update riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 94cda3b..988d335 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -76,13 +76,18 @@ section "Unmounting Mount Path" for attempt in $(seq 1 $UNMOUNT_RETRIES); do if ! is_mounted; then - ok "Mount is fully unmounted" + ok "Mount is already unmounted" break fi warn "Unmount attempt $attempt" umount "$MOUNT_PATH" || true sleep "$WAIT_BETWEEN" + + if ! is_mounted; then + ok "Mount successfully unmounted" + break + fi done if is_mounted; then From 0f619b4b89d2d50dbf93ed457f1a81c2918dbed8 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:22:27 -0500 Subject: [PATCH 134/154] Update riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 988d335..c96a511 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -18,6 +18,7 @@ DEFAULT_MOUNT="/mnt/riven/mount" UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 REMOUNT_WAIT=5 +VERSION=1.0 ############################################ # OUTPUT HELPERS @@ -31,6 +32,15 @@ is_mounted() { findmnt -T "$MOUNT_PATH" >/dev/null 2>&1 } +############################################ +# VERSION MODULE +############################################ +show_version() { + section "Riven Bind-Remount Cycle" + ok "Version: v$VERSION" +} + + ############################################ # MOUNT PATH PROMPT ############################################ From 0729193b0ddce44805dc25d6ad456d28e8b02265 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:24:37 -0500 Subject: [PATCH 135/154] Update riven-remount-cycle.sh --- ubuntu/riven-remount-cycle.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index c96a511..34eb4d7 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -18,7 +18,7 @@ DEFAULT_MOUNT="/mnt/riven/mount" UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 REMOUNT_WAIT=5 -VERSION=1.0 +VERSION=1.2 ############################################ # OUTPUT HELPERS @@ -91,7 +91,12 @@ for attempt in $(seq 1 $UNMOUNT_RETRIES); do fi warn "Unmount attempt $attempt" - umount "$MOUNT_PATH" || true + + if umount "$MOUNT_PATH" 2>&1 | grep -q "not mounted"; then + ok "Mount was already unmounted" + break + fi + sleep "$WAIT_BETWEEN" if ! is_mounted; then @@ -100,10 +105,6 @@ for attempt in $(seq 1 $UNMOUNT_RETRIES); do fi done -if is_mounted; then - fail "Mount still present after retries" -fi - ############################################ # REMOUNT (BIND + RSHARED) ############################################ From 12da4ca0cf58bb00107217c120dc97357d4a860f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:34:05 -0500 Subject: [PATCH 136/154] feat: add safe remount cycle with Docker/systemd media support - Add robust bind remount cycle with rshared propagation - Handle Docker and systemd media servers (Plex, Jellyfin, Emby) - Fix unmount loop to exit cleanly when already unmounted - Add version banner and consistent output helpers - Improve safety under set -euo pipefail --- ubuntu/riven-remount-cycle.sh | 98 +++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 22 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 34eb4d7..477af31 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -40,6 +40,7 @@ show_version() { ok "Version: v$VERSION" } +show_version ############################################ # MOUNT PATH PROMPT @@ -49,6 +50,28 @@ read -rp "Mount path [${DEFAULT_MOUNT}]: " MOUNT_PATH MOUNT_PATH="${MOUNT_PATH:-$DEFAULT_MOUNT}" ok "Using mount path: $MOUNT_PATH" +############################################ +# MEDIA RUNTIME SELECTION +############################################ +section "Media Server Runtime" +echo "1) Docker" +echo "2) Systemd service" +read -rp "Choice [1-2]: " MEDIA_RUNTIME + +case "$MEDIA_RUNTIME" in + 1) + MEDIA_MODE="docker" + ok "Media server will be controlled via Docker" + ;; + 2) + MEDIA_MODE="systemd" + ok "Media server will be controlled via systemd" + ;; + *) + fail "Invalid runtime selection" + ;; +esac + ############################################ # MEDIA SERVER SELECTION ############################################ @@ -56,28 +79,52 @@ section "Media Server Selection" echo "1) Plex" echo "2) Jellyfin" echo "3) Emby" -echo "4) Custom container name" +echo "4) Custom name" read -rp "Choice [1-4]: " MEDIA_CHOICE case "$MEDIA_CHOICE" in - 1) MEDIA_CONTAINER="plex" ;; - 2) MEDIA_CONTAINER="jellyfin" ;; - 3) MEDIA_CONTAINER="emby" ;; - 4) read -rp "Container name: " MEDIA_CONTAINER ;; + 1) MEDIA_NAME="plex" ;; + 2) MEDIA_NAME="jellyfin" ;; + 3) MEDIA_NAME="emby" ;; + 4) read -rp "Enter media name: " MEDIA_NAME ;; *) fail "Invalid selection" ;; esac -ok "Using media container: $MEDIA_CONTAINER" +ok "Selected media server: $MEDIA_NAME" ############################################ -# STOP CONTAINERS +# MEDIA TARGET RESOLUTION ############################################ -section "Stopping Containers" -docker stop "$RIVEN_CONTAINER" >/dev/null 2>&1 || true -ok "Riven stopped" +if [[ "$MEDIA_MODE" == "docker" ]]; then + MEDIA_CONTAINER="$MEDIA_NAME" + ok "Using Docker container: $MEDIA_CONTAINER" +else + case "$MEDIA_NAME" in + plex) MEDIA_SERVICE="plexmediaserver" ;; + jellyfin) MEDIA_SERVICE="jellyfin" ;; + emby) MEDIA_SERVICE="emby-server" ;; + *) + read -rp "Enter systemd service name: " MEDIA_SERVICE + ;; + esac + ok "Using systemd service: $MEDIA_SERVICE" +fi -docker stop "$MEDIA_CONTAINER" >/dev/null 2>&1 || true -ok "Media server stopped" +############################################ +# STOP SERVICES +############################################ +section "Stopping Services" + +docker stop "$RIVEN_CONTAINER" >/dev/null 2>&1 || true +ok "Riven container stopped" + +if [[ "$MEDIA_MODE" == "docker" ]]; then + docker stop "$MEDIA_CONTAINER" >/dev/null 2>&1 || true + ok "Media container stopped" +else + systemctl stop "$MEDIA_SERVICE" + ok "Media service stopped" +fi ############################################ # UNMOUNT @@ -131,19 +178,26 @@ fi ok "Propagation verified: $PROP" ############################################ -# START CONTAINERS +# START SERVICES ############################################ -section "Starting Containers" +section "Starting Services" docker start "$RIVEN_CONTAINER" >/dev/null -ok "Riven started" - -docker start "$MEDIA_CONTAINER" >/dev/null -ok "Media server started" - -sleep 5 -docker restart "$MEDIA_CONTAINER" >/dev/null -ok "Media server restarted (post-mount)" +ok "Riven container started" + +if [[ "$MEDIA_MODE" == "docker" ]]; then + docker start "$MEDIA_CONTAINER" >/dev/null + ok "Media container started" + sleep 5 + docker restart "$MEDIA_CONTAINER" >/dev/null + ok "Media container restarted (post-mount)" +else + systemctl start "$MEDIA_SERVICE" + ok "Media service started" + sleep 5 + systemctl restart "$MEDIA_SERVICE" + ok "Media service restarted (post-mount)" +fi ############################################ # DONE From b3e2c5f0f9205055a8d653a930ebfc5af49b23eb Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:35:53 -0500 Subject: [PATCH 137/154] chore: bump riven-remount-cycle version to v1.3 Increment script version to v1.3 after remount, propagation, and media runtime improvements. --- ubuntu/riven-remount-cycle.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 477af31..346fb4a 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -18,7 +18,7 @@ DEFAULT_MOUNT="/mnt/riven/mount" UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 REMOUNT_WAIT=5 -VERSION=1.2 +VERSION=1.3 ############################################ # OUTPUT HELPERS From 7ea56adf048c0f789d5d2b85411439fad579d1f1 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:55:33 -0500 Subject: [PATCH 138/154] fix: correct media/riven startup order and bump version to v1.4 - Start media service before Riven to ensure mount readiness - Restart media after Riven for reliable mount propagation - Bump riven-remount-cycle version to v1.4 --- ubuntu/riven-remount-cycle.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 346fb4a..855f209 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -18,7 +18,8 @@ DEFAULT_MOUNT="/mnt/riven/mount" UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 REMOUNT_WAIT=5 -VERSION=1.3 +WAIT_TIME=5 +VERSION=1.4 ############################################ # OUTPUT HELPERS @@ -182,23 +183,35 @@ ok "Propagation verified: $PROP" ############################################ section "Starting Services" +# 1. Start media server +if [[ "$MEDIA_MODE" == "docker" ]]; then + docker start "$MEDIA_CONTAINER" >/dev/null + ok "Media container started" +else + systemctl start "$MEDIA_SERVICE" + ok "Media service started" +fi + +# 2. Wait before starting Riven +sleep 5 + +# 3. Start Riven docker start "$RIVEN_CONTAINER" >/dev/null ok "Riven container started" +# 4. Wait for mount propagation +sleep 5 + +# 5. Restart media server (post-mount) if [[ "$MEDIA_MODE" == "docker" ]]; then - docker start "$MEDIA_CONTAINER" >/dev/null - ok "Media container started" - sleep 5 docker restart "$MEDIA_CONTAINER" >/dev/null ok "Media container restarted (post-mount)" else - systemctl start "$MEDIA_SERVICE" - ok "Media service started" - sleep 5 systemctl restart "$MEDIA_SERVICE" ok "Media service restarted (post-mount)" fi + ############################################ # DONE ############################################ From 74296f2419e583681e0a315705f04f871b4506b0 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:57:45 -0500 Subject: [PATCH 139/154] fix: increase startup wait to allow Riven to initialize (v1.5) Increase wait time before restarting media server to ensure Riven is fully initialized and mounts are ready. --- ubuntu/riven-remount-cycle.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 855f209..cf420a5 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -19,7 +19,7 @@ UNMOUNT_RETRIES=5 WAIT_BETWEEN=2 REMOUNT_WAIT=5 WAIT_TIME=5 -VERSION=1.4 +VERSION=1.5 ############################################ # OUTPUT HELPERS @@ -200,7 +200,7 @@ docker start "$RIVEN_CONTAINER" >/dev/null ok "Riven container started" # 4. Wait for mount propagation -sleep 5 +sleep 15 # 5. Restart media server (post-mount) if [[ "$MEDIA_MODE" == "docker" ]]; then From fc06f7ef11544a7485ff904dcb037381254cfd2f Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:13:07 -0500 Subject: [PATCH 140/154] chore: update riven-remount-cycle.sh to v2.0 - Reduced mount retry attempts to 3 - Bumped script version to v2.0 - Minor reliability tweaks to remount flow --- ubuntu/riven-remount-cycle.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index cf420a5..286ad67 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -15,11 +15,11 @@ fi RIVEN_CONTAINER="riven" DEFAULT_MOUNT="/mnt/riven/mount" -UNMOUNT_RETRIES=5 +UNMOUNT_RETRIES=3 WAIT_BETWEEN=2 REMOUNT_WAIT=5 WAIT_TIME=5 -VERSION=1.5 +VERSION=2.0 ############################################ # OUTPUT HELPERS From 7c8f7ef69c4a84268a913259321e5083a4c2777d Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 14 Jan 2026 15:11:53 -0500 Subject: [PATCH 141/154] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f587014..d5af92a 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Full Ubuntu documentation: ## Repository Structure ```text -distributables/ +Riven-Scripts/ ├── proxmox/ # Proxmox VE LXC installer + docs ├── ubuntu/ # Ubuntu installer + docs └── README.md # This file From f01d50970476958567768fd83d9aa059dd38eabb Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:35:37 -0500 Subject: [PATCH 142/154] fix: stderr for URL validation Prevents validation messages from being captured during command substitution by redirecting warnings to stderr, ensuring ORIGIN is always a valid URL. --- ubuntu/install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index deee32f..7131b4b 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.5.8" +INSTALL_VERSION="v0.6" ############################################ # HELPERS @@ -85,7 +85,7 @@ require_url() { IFS= read -r -p "$prompt: " val val="$(printf '%s' "$val" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" [[ "$val" =~ ^https?:// ]] && { printf '%s' "$val"; return; } - warn "Must include http:// or https://" + warn "Must include http:// or https://" >&2 done } From 5e5b8e8abe0b7db184fd7c1a6a0ec53b22ef31e8 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:44:03 -0500 Subject: [PATCH 143/154] feat: multi-select scraper configuration Update scraper selection to support multiple backends in a single install. Accepts space- or comma-separated input and configures each scraper sequentially with per-scraper prompts and validation. --- ubuntu/install.sh | 167 ++++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 74 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 7131b4b..05c9e73 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -536,20 +536,24 @@ case "$DL_SEL" in esac ############################################ -# SCRAPER SELECTION (REQUIRED) +# SCRAPER SELECTION (MULTI-SELECT, REQUIRED) ############################################ banner "Scraper Selection (REQUIRED)" -echo "Choose ONE scraping backend:" -echo +echo "Select one or more scraping backends." +echo "Enter numbers separated by spaces or commas (example: 1 3 5 or 1,3,5)" +echo "" echo "1) Torrentio (No config required)" echo "2) Prowlarr (Local instance only)" echo "3) Comet (Public or self-hosted)" echo "4) Jackett (Local instance only)" echo "5) Zilean (Public or self-hosted)" -echo +echo "" + +read -rp "Select one or more: " SCR_SEL_RAW -read -rp "Select ONE: " SCR_SEL +# Normalize commas → spaces, collapse whitespace +SCR_SEL="$(echo "$SCR_SEL_RAW" | tr ',' ' ' | xargs)" # Reset all flags RIVEN_SCRAPING_TORRENTIO_ENABLED=false @@ -558,82 +562,97 @@ RIVEN_SCRAPING_COMET_ENABLED=false RIVEN_SCRAPING_JACKETT_ENABLED=false RIVEN_SCRAPING_ZILEAN_ENABLED=false +# Reset config values RIVEN_SCRAPING_PROWLARR_URL="" RIVEN_SCRAPING_PROWLARR_API_KEY="" - RIVEN_SCRAPING_COMET_URL="" - RIVEN_SCRAPING_JACKETT_URL="" RIVEN_SCRAPING_JACKETT_API_KEY="" - RIVEN_SCRAPING_ZILEAN_URL="" -case "$SCR_SEL" in - 1) - RIVEN_SCRAPING_TORRENTIO_ENABLED=true - echo - echo "Torrentio selected." - echo "• Uses public Torrentio endpoint" - echo "• No configuration required" - ;; - 2) - RIVEN_SCRAPING_PROWLARR_ENABLED=true - echo - echo "Prowlarr selected." - echo - echo "Example:" - echo " • http://localhost:9696" - echo - echo "API Key location:" - echo " Settings → General → API Key" - echo - RIVEN_SCRAPING_PROWLARR_URL="$(require_url "Enter Prowlarr URL")" - RIVEN_SCRAPING_PROWLARR_API_KEY="$(read_masked_non_empty "Enter Prowlarr API Key")" - ;; - 3) - RIVEN_SCRAPING_COMET_ENABLED=true - echo - echo "Comet selected." - echo - echo "Examples:" - echo " • Public: https://cometfortheweebs.midnightignite.me" - echo " • Local: http://localhost:" - echo - echo "No API key is required." - echo - RIVEN_SCRAPING_COMET_URL="$(require_url "Enter Comet base URL")" - ;; - 4) - RIVEN_SCRAPING_JACKETT_ENABLED=true - echo - echo "Jackett selected." - echo - echo "Example:" - echo " • http://localhost:9117" - echo - echo "API Key location:" - echo " Jackett Web UI → Top-right corner" - echo - RIVEN_SCRAPING_JACKETT_URL="$(require_url "Enter Jackett URL")" - RIVEN_SCRAPING_JACKETT_API_KEY="$(read_masked_non_empty "Enter Jackett API Key")" - ;; - 5) - RIVEN_SCRAPING_ZILEAN_ENABLED=true - echo - echo "Zilean selected." - echo - echo "Examples:" - echo " • Public: https://zilean.example.com" - echo " • Local: http://localhost:" - echo - echo "No API key is required." - echo - RIVEN_SCRAPING_ZILEAN_URL="$(require_url "Enter Zilean base URL")" - ;; - *) - fail "Scraper selection REQUIRED" - ;; -esac +VALID_SELECTION=false + +for sel in $SCR_SEL; do + case "$sel" in + 1) + RIVEN_SCRAPING_TORRENTIO_ENABLED=true + VALID_SELECTION=true + log "Torrentio enabled" + ;; + 2) + RIVEN_SCRAPING_PROWLARR_ENABLED=true + VALID_SELECTION=true + + echo "" + echo "Prowlarr configuration" + echo "Example: http://localhost:9696" + echo "API Key: Settings → General → API Key" + + RIVEN_SCRAPING_PROWLARR_URL="$(require_url "Enter Prowlarr URL")" + RIVEN_SCRAPING_PROWLARR_API_KEY="$(read_masked_non_empty "Enter Prowlarr API Key")" + + log "Prowlarr enabled" + ;; + 3) + RIVEN_SCRAPING_COMET_ENABLED=true + VALID_SELECTION=true + + echo "" + echo "Comet configuration" + echo "Examples:" + echo " • https://cometfortheweebs.midnightignite.me" + echo " • http://localhost:" + + RIVEN_SCRAPING_COMET_URL="$(require_url "Enter Comet base URL")" + + log "Comet enabled" + ;; + 4) + RIVEN_SCRAPING_JACKETT_ENABLED=true + VALID_SELECTION=true + + echo "" + echo "Jackett configuration" + echo "Example: http://localhost:9117" + echo "API Key: Jackett Web UI → Top-right corner" + + RIVEN_SCRAPING_JACKETT_URL="$(require_url "Enter Jackett URL")" + RIVEN_SCRAPING_JACKETT_API_KEY="$(read_masked_non_empty "Enter Jackett API Key")" + + log "Jackett enabled" + ;; + 5) + RIVEN_SCRAPING_ZILEAN_ENABLED=true + VALID_SELECTION=true + + echo "" + echo "Zilean configuration" + echo "Examples:" + echo " • https://zilean.example.com" + echo " • http://localhost:" + + RIVEN_SCRAPING_ZILEAN_URL="$(require_url "Enter Zilean base URL")" + + log "Zilean enabled" + ;; + *) + warn "Invalid scraper option ignored: $sel" + ;; + esac +done + +if [[ "$VALID_SELECTION" != "true" ]]; then + fail "At least one scraper must be selected" +fi + +echo "" +echo "Enabled scrapers:" +[[ "$RIVEN_SCRAPING_TORRENTIO_ENABLED" == "true" ]] && echo " • Torrentio" +[[ "$RIVEN_SCRAPING_PROWLARR_ENABLED" == "true" ]] && echo " • Prowlarr" +[[ "$RIVEN_SCRAPING_COMET_ENABLED" == "true" ]] && echo " • Comet" +[[ "$RIVEN_SCRAPING_JACKETT_ENABLED" == "true" ]] && echo " • Jackett" +[[ "$RIVEN_SCRAPING_ZILEAN_ENABLED" == "true" ]] && echo " • Zilean" +echo "" ############################################ # SECRETS From 5532411df6d7fdb2e47c5f8ab794ccbc69db22b2 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 24 Jan 2026 23:53:15 -0500 Subject: [PATCH 144/154] feat: add configuration validation summary Display a full configuration summary before install and require confirmation. Reduces misconfiguration and failed installs. Adds v0.6.7. --- ubuntu/install.sh | 73 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 05c9e73..c96d3fe 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.6" +INSTALL_VERSION="v0.6.7" ############################################ # HELPERS @@ -730,6 +730,77 @@ case "$MEDIA_PROFILE" in ;; esac +############################################ +# VALIDATION SUMMARY (CONFIRM BEFORE CONTINUE) +############################################ +banner "Configuration Summary" + +echo "Please review your configuration below:" +echo "" + +echo "🕒 Timezone" +echo " • $TZ_SELECTED" +echo "" + +echo "🌍 Frontend Origin" +echo " • $ORIGIN" +echo "" + +echo "🎬 Media Server" +echo " • Selected: $MEDIA_PROFILE" +echo " • URL: http://$SERVER_IP:$MEDIA_PORT" +echo "" + +echo "⬇️ Downloader" +if [[ "$RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED" == "true" ]]; then + echo " • Real-Debrid" +elif [[ "$RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED" == "true" ]]; then + echo " • All-Debrid" +elif [[ "$RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED" == "true" ]]; then + echo " • Debrid-Link" +else + echo " • NONE (invalid)" +fi +echo "" + +echo "🔍 Scrapers Enabled" +SCRAPER_COUNT=0 + +[[ "$RIVEN_SCRAPING_TORRENTIO_ENABLED" == "true" ]] && { echo " • Torrentio"; ((SCRAPER_COUNT++)); } +[[ "$RIVEN_SCRAPING_PROWLARR_ENABLED" == "true" ]] && { echo " • Prowlarr ($RIVEN_SCRAPING_PROWLARR_URL)"; ((SCRAPER_COUNT++)); } +[[ "$RIVEN_SCRAPING_COMET_ENABLED" == "true" ]] && { echo " • Comet ($RIVEN_SCRAPING_COMET_URL)"; ((SCRAPER_COUNT++)); } +[[ "$RIVEN_SCRAPING_JACKETT_ENABLED" == "true" ]] && { echo " • Jackett ($RIVEN_SCRAPING_JACKETT_URL)"; ((SCRAPER_COUNT++)); } +[[ "$RIVEN_SCRAPING_ZILEAN_ENABLED" == "true" ]] && { echo " • Zilean ($RIVEN_SCRAPING_ZILEAN_URL)"; ((SCRAPER_COUNT++)); } + +if [[ "$SCRAPER_COUNT" -eq 0 ]]; then + echo " • NONE (invalid)" +fi +echo "" + +echo "📁 Paths" +echo " • Install Dir: $INSTALL_DIR" +echo " • Backend Path: $BACKEND_PATH" +echo " • Mount Path: $MOUNT_PATH" +echo "" + +echo "👤 Ownership" +echo " • UID:GID $TARGET_UID:$TARGET_GID" +echo "" + +echo "⚠️ IMPORTANT" +echo " • This will write configuration files" +echo " • Docker containers will be started" +echo " • Secrets will be generated automatically" +echo "" + +read -rp "Continue with this configuration? [y/N]: " CONFIRM +if [[ ! "${CONFIRM,,}" =~ ^y$ ]]; then + echo "" + echo "Installation aborted by user." + exit 1 +fi + +ok "Configuration confirmed" ############################################ # WRITE .env (ONCE, NO SED) From 731e5b4ef36f54944bca445671386cee8fb291b5 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:19:15 -0500 Subject: [PATCH 145/154] Fix: installer crash when evaluating disabled scrapers Resolved an installer crash caused by using inline [[ condition ]] && blocks while running with `set -euo pipefail`. Disabled scrapers were returning non-zero exit codes, which caused the script to terminate unexpectedly during the configuration summary phase. All scraper checks have been converted to explicit `if [[ ... ]]; then` blocks, making the logic safe under `set -e` and preventing premature exits when a scraper is disabled or unset. This ensures the configuration summary always completes and accurately reports enabled scrapers. --- ubuntu/install.sh | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index c96d3fe..7e180eb 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -766,17 +766,36 @@ echo "" echo "🔍 Scrapers Enabled" SCRAPER_COUNT=0 -[[ "$RIVEN_SCRAPING_TORRENTIO_ENABLED" == "true" ]] && { echo " • Torrentio"; ((SCRAPER_COUNT++)); } -[[ "$RIVEN_SCRAPING_PROWLARR_ENABLED" == "true" ]] && { echo " • Prowlarr ($RIVEN_SCRAPING_PROWLARR_URL)"; ((SCRAPER_COUNT++)); } -[[ "$RIVEN_SCRAPING_COMET_ENABLED" == "true" ]] && { echo " • Comet ($RIVEN_SCRAPING_COMET_URL)"; ((SCRAPER_COUNT++)); } -[[ "$RIVEN_SCRAPING_JACKETT_ENABLED" == "true" ]] && { echo " • Jackett ($RIVEN_SCRAPING_JACKETT_URL)"; ((SCRAPER_COUNT++)); } -[[ "$RIVEN_SCRAPING_ZILEAN_ENABLED" == "true" ]] && { echo " • Zilean ($RIVEN_SCRAPING_ZILEAN_URL)"; ((SCRAPER_COUNT++)); } +if [[ "${RIVEN_SCRAPING_TORRENTIO_ENABLED:-false}" == "true" ]]; then + echo " • Torrentio" + ((SCRAPER_COUNT++)) +fi + +if [[ "${RIVEN_SCRAPING_PROWLARR_ENABLED:-false}" == "true" ]]; then + echo " • Prowlarr" + ((SCRAPER_COUNT++)) +fi + +if [[ "${RIVEN_SCRAPING_COMET_ENABLED:-false}" == "true" ]]; then + echo " • Comet" + ((SCRAPER_COUNT++)) +fi + +if [[ "${RIVEN_SCRAPING_JACKETT_ENABLED:-false}" == "true" ]]; then + echo " • Jackett" + ((SCRAPER_COUNT++)) +fi + +if [[ "${RIVEN_SCRAPING_ZILEAN_ENABLED:-false}" == "true" ]]; then + echo " • Zilean" + ((SCRAPER_COUNT++)) +fi if [[ "$SCRAPER_COUNT" -eq 0 ]]; then echo " • NONE (invalid)" fi -echo "" +echo "" echo "📁 Paths" echo " • Install Dir: $INSTALL_DIR" echo " • Backend Path: $BACKEND_PATH" From 7b45b18b2360e28db5b73893b7e0c4c6b1983516 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:20:00 -0500 Subject: [PATCH 146/154] Bump installer version to v0.6.8 Bumped the installer version to v0.6.8. This release includes internal refinements and stability improvements made since v0.6.7, with no changes required from end users. Version metadata and related references have been updated accordingly to reflect the new release. --- ubuntu/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 7e180eb..114aece 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -14,7 +14,7 @@ RIVEN_COMPOSE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/distribut DEFAULT_ORIGIN="http://localhost:3000" -INSTALL_VERSION="v0.6.7" +INSTALL_VERSION="v0.6.8" ############################################ # HELPERS From 9037c1ec08ddf7749dbb10c1502ef378a81fdd0a Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Wed, 28 Jan 2026 10:47:38 -0500 Subject: [PATCH 147/154] Delete install.sh --- proxmox/install.sh | 291 --------------------------------------------- 1 file changed, 291 deletions(-) delete mode 100644 proxmox/install.sh diff --git a/proxmox/install.sh b/proxmox/install.sh deleted file mode 100644 index 9b0e055..0000000 --- a/proxmox/install.sh +++ /dev/null @@ -1,291 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -############################################################################### -# Riven Proxmox LXC Installer (Docker-based) -# Run on Proxmox host as root -# -# One-liner supported: -# bash -c "$(wget -qLO - https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/install.sh)" -############################################################################### - -[[ "$EUID" -ne 0 ]] && { echo "ERROR: Run as root on Proxmox host"; exit 1; } - -############################################################################### -# Helpers -############################################################################### -require_cmd() { - command -v "$1" >/dev/null 2>&1 || { - echo "ERROR: Missing required command: $1" - exit 1 - } -} - -apt_install_if_missing() { - local bin="$1" - local pkg="$2" - if ! command -v "$bin" >/dev/null 2>&1; then - echo "📦 Installing: ${pkg} (required for UI prompts)" - apt-get update -y - apt-get install -y "$pkg" - fi -} - -# Prefer bridges on Proxmox, fallback to usable interfaces -list_bridges() { - # Linux bridge devices (e.g., vmbr0) - ip -o link show type bridge 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*$//' || true -} - -list_interfaces() { - ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*$//' \ - | grep -Ev '^(lo|docker|veth|virbr|br-|tun|tap)' -} - -whiptail_menu_select() { - local title="$1" - local prompt="$2" - - local OPTIONS=() - while read -r item; do - [[ -z "$item" ]] && continue - OPTIONS+=("$item" "") - done - - if [[ ${#OPTIONS[@]} -eq 0 ]]; then - return 1 - fi - - whiptail --title "$title" \ - --menu "$prompt" \ - 20 70 10 \ - "${OPTIONS[@]}" \ - 3>&1 1>&2 2>&3 -} - -select_network_target_ui() { - # Ensure whiptail exists - apt_install_if_missing whiptail whiptail - - local bridges ifaces - bridges="$(list_bridges)" - - if [[ -n "$bridges" ]]; then - BRIDGE="$(printf '%s\n' "$bridges" | whiptail_menu_select "Network Bridge" "Select the Proxmox bridge to attach the LXC to:")" - if [[ -z "$BRIDGE" ]]; then - echo "❌ No bridge selected. Exiting." - exit 1 - fi - echo "✔ Selected bridge: $BRIDGE" - return 0 - fi - - # Fallback: raw interfaces (not ideal for Proxmox). We still let them pick - # so we can print a helpful error instead of asking them to type names. - ifaces="$(list_interfaces || true)" - if [[ -z "$ifaces" ]]; then - echo "❌ No usable network interfaces detected." - exit 1 - fi - - IFACE="$(printf '%s\n' "$ifaces" | whiptail_menu_select "Network Interface" "No bridges detected. Select a network interface:")" - if [[ -z "$IFACE" ]]; then - echo "❌ No interface selected. Exiting." - exit 1 - fi - - echo "⚠ Selected interface (no bridge found): $IFACE" -} - -############################################################################### -# Requirements -############################################################################### -require_cmd pveversion -require_cmd pct -require_cmd pvesm -require_cmd curl - -############################################################################### -# Proxmox version check (8.1+) -############################################################################### -PVE_VER_RAW="$(pveversion | head -n1)" -PVE_VER="$(echo "$PVE_VER_RAW" | sed -nE 's/.*pve-manager\/([0-9]+)\.([0-9]+).*/\1.\2/p')" - -if [[ -z "$PVE_VER" ]]; then - echo "WARNING: Could not parse Proxmox version from: $PVE_VER_RAW" -else - MAJ="${PVE_VER%.*}" - MIN="${PVE_VER#*.}" - if (( MAJ < 8 )) || (( MAJ == 8 && MIN < 1 )); then - echo "ERROR: Proxmox 8.1+ required. Detected: $PVE_VER_RAW" - exit 1 - fi -fi - -echo "== Riven Proxmox LXC (Docker) Installer ==" - -############################################################################### -# Download LXC installer components from GitHub -############################################################################### -BASE_URL="https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/proxmox/lxc" -WORKDIR="/tmp/riven-proxmox-lxc" - -echo "📥 Downloading installer components from GitHub..." -mkdir -p "$WORKDIR" -cd "$WORKDIR" - -fetch() { - local file="$1" - echo " - $file" - curl -fsSL "$BASE_URL/$file" -o "$file" -} - -fetch lxc-create.sh -fetch lxc-bootstrap.sh -fetch riven-install.sh -fetch docker-compose.yml -fetch upgrade.sh - -chmod +x lxc-create.sh lxc-bootstrap.sh riven-install.sh upgrade.sh - -############################################################################### -# Defaults -############################################################################### -DEFAULT_CTID="" -while :; do - CANDIDATE=$(( (RANDOM % 900) + 100 )) - if ! pct status "$CANDIDATE" >/dev/null 2>&1; then - DEFAULT_CTID="$CANDIDATE" - break - fi -done - -read -rp "Container ID (CTID) [${DEFAULT_CTID}]: " CTID -CTID="${CTID:-$DEFAULT_CTID}" - -if pct status "$CTID" >/dev/null 2>&1; then - echo "ERROR: CTID $CTID already exists." - exit 1 -fi - -read -rp "Hostname [riven]: " HOSTNAME -HOSTNAME="${HOSTNAME:-riven}" - -read -rp "Rootfs disk size (GB) [16]: " DISK_GB -DISK_GB="${DISK_GB:-16}" - -read -rp "Memory (MB) [4096]: " MEM_MB -MEM_MB="${MEM_MB:-4096}" - -read -rp "CPU cores [4]: " CORES -CORES="${CORES:-4}" - -DEFAULT_STORAGE="$(pvesm status | awk 'NR>1{print $1}' | head -n1)" -DEFAULT_STORAGE="${DEFAULT_STORAGE:-local}" -read -rp "Storage for rootfs/template [${DEFAULT_STORAGE}]: " STORAGE -STORAGE="${STORAGE:-$DEFAULT_STORAGE}" - -############################################################################### -# Network selection (UI) -############################################################################### -BRIDGE="" -IFACE="" -select_network_target_ui - -############################################################################### -# IP config -############################################################################### -read -rp "Network config (dhcp or static like 192.168.1.50/24,gw=192.168.1.1) [dhcp]: " NETCFG -NETCFG="${NETCFG:-dhcp}" - -# Default LXC NIC name remains eth0 inside the CT. -if [[ -n "$BRIDGE" ]]; then - if [[ "$NETCFG" == "dhcp" ]]; then - NET0="name=eth0,bridge=${BRIDGE},ip=dhcp" - else - NET0="name=eth0,bridge=${BRIDGE},ip=${NETCFG}" - fi -else - echo "ERROR: No Proxmox bridges detected. Create a vmbr bridge first (e.g., vmbr0) and rerun." - exit 1 -fi - -############################################################################### -# Host mount (single unified root) -############################################################################### -echo -echo "Host storage bind-mount into the LXC (recommended)" -echo "This will be mounted inside the CT at: /srv/riven" -echo "Suggested host path: /srv/riven" -read -rp "Host path to mount [ /srv/riven ] (blank to skip): " HOST_RIVEN_PATH -HOST_RIVEN_PATH="${HOST_RIVEN_PATH:-/srv/riven}" - -############################################################################### -# GPU passthrough -############################################################################### -GPU_ENABLE="no" -if [[ -d /dev/dri ]]; then - read -rp "Detected /dev/dri. Enable GPU passthrough? [Y/n]: " GPU_ANS - GPU_ANS="${GPU_ANS:-Y}" - [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" -else - read -rp "No /dev/dri detected. Force GPU passthrough anyway? [y/N]: " GPU_ANS - GPU_ANS="${GPU_ANS:-N}" - [[ "$GPU_ANS" =~ ^[Yy]$ ]] && GPU_ENABLE="yes" -fi - -############################################################################### -# Create LXC -############################################################################### -bash "$WORKDIR/lxc-create.sh" \ - --ctid "$CTID" \ - --hostname "$HOSTNAME" \ - --storage "$STORAGE" \ - --disk-gb "$DISK_GB" \ - --mem-mb "$MEM_MB" \ - --cores "$CORES" \ - --net0 "$NET0" \ - --host-riven-path "$HOST_RIVEN_PATH" \ - --gpu "$GPU_ENABLE" - -############################################################################### -# Push files into CT -############################################################################### -echo "📦 Pushing installer files into CT $CTID..." -pct push "$CTID" "$WORKDIR/lxc-bootstrap.sh" /root/lxc-bootstrap.sh -perms 0755 -pct push "$CTID" "$WORKDIR/riven-install.sh" /root/riven-install.sh -perms 0755 -pct push "$CTID" "$WORKDIR/docker-compose.yml" /root/docker-compose.yml -perms 0644 -pct push "$CTID" "$WORKDIR/upgrade.sh" /root/upgrade.sh -perms 0755 - -############################################################################### -# Bootstrap Docker -############################################################################### -echo "🐳 Installing Docker inside CT..." -pct exec "$CTID" -- bash /root/lxc-bootstrap.sh - -############################################################################### -# Install Riven -############################################################################### -echo "🚀 Deploying Riven stack..." -pct exec "$CTID" -- bash /root/riven-install.sh - -############################################################################### -# Final info -############################################################################### -echo -echo "✅ Installation complete." -echo "Find CT IP:" -echo " pct exec $CTID -- hostname -I" -echo -echo "Backend : http://:8080" -echo "Frontend : http://:3000" -echo -echo "Volumes inside CT (unified):" -echo " /srv/riven/app (docker-compose + .env)" -echo " /srv/riven/backend (Riven backend persistence)" -echo " /srv/riven/mount (FUSE/VFS mount)" -echo " /srv/riven/media/* (media server configs)" -echo -echo "Logs:" -echo " pct exec $CTID -- docker logs -f riven" -echo " pct exec $CTID -- docker logs -f riven-db" From 63b6ad29f461c2370070822e259e3329e7116afa Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:07:42 -0500 Subject: [PATCH 148/154] Adjust remount wait time and version to 3.0 Increased remount wait time and updated version number. --- ubuntu/riven-remount-cycle.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ubuntu/riven-remount-cycle.sh b/ubuntu/riven-remount-cycle.sh index 286ad67..7db9c1d 100644 --- a/ubuntu/riven-remount-cycle.sh +++ b/ubuntu/riven-remount-cycle.sh @@ -17,9 +17,9 @@ DEFAULT_MOUNT="/mnt/riven/mount" UNMOUNT_RETRIES=3 WAIT_BETWEEN=2 -REMOUNT_WAIT=5 +REMOUNT_WAIT=30 WAIT_TIME=5 -VERSION=2.0 +VERSION=3.0 ############################################ # OUTPUT HELPERS @@ -200,7 +200,7 @@ docker start "$RIVEN_CONTAINER" >/dev/null ok "Riven container started" # 4. Wait for mount propagation -sleep 15 +sleep 30 # 5. Restart media server (post-mount) if [[ "$MEDIA_MODE" == "docker" ]]; then From bec7731ecf8b4a053b8f7358e82f70ff2f74cd14 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:21:50 -0500 Subject: [PATCH 149/154] Add Riven DB maintenance and show state management script Adds an interactive maintenance script for managing Riven show, season, and episode states. Features: - Safe database backup before changes - Search shows by TVDB ID or title - Delete episodes by state (Indexed, Unknown, Failed, Scraped) - Mark episodes or seasons as Unreleased - Reset episodes/seasons back to Indexed - Update season and show-level states - Final verification summary after execution Intended for advanced maintenance and recovery scenarios. --- db-tools/riven-db-maintenance.sh | 520 +++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 db-tools/riven-db-maintenance.sh diff --git a/db-tools/riven-db-maintenance.sh b/db-tools/riven-db-maintenance.sh new file mode 100644 index 0000000..f8294aa --- /dev/null +++ b/db-tools/riven-db-maintenance.sh @@ -0,0 +1,520 @@ +#!/bin/bash + +# ============================================================ +# Riven Show Cleanup & Management Script (Enhanced Version) +# ============================================================ +# This script helps you manage show states in the Riven database +# including deleting episodes, marking as unreleased, and +# updating states for episodes, seasons, and shows. +# ============================================================ + +# Function to check if the Docker container is running +check_container_status() { + docker ps | grep -q "riven-db" + if [ $? -ne 0 ]; then + echo "╔════════════════════════════════════════════════════════════╗" + echo "║ ERROR: The 'riven-db' container is not running! ║" + echo "║ Please start the container and try again. ║" + echo "╚════════════════════════════════════════════════════════════╝" + exit 1 + fi +} + +# Function to create a backup of the database +backup_database() { + echo "" + echo "╔════════════════════════════════════════════════════════════╗" + echo "║ DATABASE BACKUP ║" + echo "╚════════════════════════════════════════════════════════════╝" + echo "" + echo "RECOMMENDED: Creating a backup allows you to restore your" + echo "database if something goes wrong. The backup will be stored" + echo "at '/tmp/riven_backup.sql' on your host machine." + echo "" + read -p "Do you want to create a backup? (y/n): " backup_choice + if [[ "$backup_choice" =~ ^[Yy]$ ]]; then + echo "" + echo "Creating backup of the database..." + # Backup inside the container at /tmp/riven_backup.sql + docker exec riven-db pg_dump -U postgres -d riven -f /tmp/riven_backup.sql + if [ $? -eq 0 ]; then + echo "✓ Backup successful inside the container at /tmp/riven_backup.sql." + # Copy the backup file from the container to the host + docker cp riven-db:/tmp/riven_backup.sql /tmp/riven_backup.sql + if [ $? -eq 0 ]; then + echo "✓ Backup file successfully copied to the host at /tmp/riven_backup.sql." + echo "" + else + echo "✗ Error: Failed to copy the backup file from the container to the host." + echo "Exiting script for safety." + exit 1 + fi + else + echo "✗ Error: Backup failed inside the container." + echo "Exiting script for safety." + exit 1 + fi + else + echo "⚠ Skipping backup. Proceeding without backup protection." + echo "" + fi +} + +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ Riven Show Cleanup & Management Script ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" + +# Step 0: Check if the Docker container is running +check_container_status + +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ FIND YOUR SHOW ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "Search for the show by:" +echo " 1) TVDB ID (exact match - fastest if you know the ID)" +echo " 2) Show Name (partial match - searches titles containing your text)" +echo "" +read -p "Enter choice (1 or 2): " search_choice + +if [ "$search_choice" == "1" ]; then + read -p "Enter TVDB ID: " tvdb_id + search_condition="mi_show.tvdb_id = '$tvdb_id'" +elif [ "$search_choice" == "2" ]; then + read -p "Enter show name (partial match works): " show_name + search_condition="mi_show.title ILIKE '%$show_name%'" +else + echo "✗ Invalid choice. Exiting." + exit 1 +fi + +# Find the parent show(s) +result=$(docker exec riven-db psql -U postgres -d riven -t -A -F',' -c " +SELECT mi_show.id, mi_show.title +FROM \"MediaItem\" mi_show +WHERE mi_show.type = 'show' +AND $search_condition;") + +show_count=$(echo "$result" | grep -v '^$' | wc -l) + +if [ "$show_count" -eq 0 ]; then + echo "✗ No shows found. Exiting." + exit 0 +elif [ "$show_count" -gt 1 ]; then + echo "" + echo "Multiple shows found. Please choose the correct one:" + echo "──────────────────────────────────────────────────" + echo "$result" | while IFS=',' read -r id title; do + echo " ID: $id | Title: $title" + done + echo "" + read -p "Enter the ID of the show you want to manage: " show_id + show_title=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT title FROM \"MediaItem\" WHERE id = $show_id;" | xargs) +else + show_id=$(echo "$result" | cut -d',' -f1) + show_title=$(echo "$result" | cut -d',' -f2) + echo "" + echo "✓ Found: $show_title (ID: $show_id)" + echo "" + read -p "Proceed with this show? (y/n): " confirm + [[ ! "$confirm" =~ ^[Yy](es)?$ ]] && exit 0 +fi + +final_condition="parent.id = $show_id" + +# Create backup before making any changes +backup_database + +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ CURRENT STATE SUMMARY ║" +echo "║ Show: $show_title" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +docker exec riven-db psql -U postgres -d riven -c " +SELECT mi.last_state AS state, COUNT(*) AS count +FROM \"MediaItem\" mi +INNER JOIN \"Episode\" e ON mi.id = e.id +INNER JOIN \"Season\" s ON e.parent_id = s.id +INNER JOIN \"Show\" sh ON s.parent_id = sh.id +INNER JOIN \"MediaItem\" parent ON sh.id = parent.id +WHERE $final_condition +GROUP BY mi.last_state;" + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ STEP 1: DELETE EPISODES ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "⚠ WHAT THIS DOES:" +echo " • Permanently REMOVES episodes from the database" +echo " • Riven will FORGET about these episodes and STOP trying to find them" +echo " • Deleted episodes will NOT appear in your library or search results" +echo " • To restore deleted episodes, you must do a FULL SHOW RESET/RE-ADD" +echo "" +echo "WHEN TO USE THIS:" +echo " • Remove episodes that don't exist (specials, bonus content, etc.)" +echo " • Clean up incorrectly indexed episodes" +echo " • Remove episodes you never want Riven to search for" +echo "" +echo "Available states you might want to delete:" +echo " • Indexed - Episodes that were found but not yet scraped" +echo " • Unknown - Episodes that Riven couldn't find streams for" +echo " • Failed - Episodes that had errors during processing" +echo " • Scraped - Episodes that have been scraped but not downloaded" +echo "" +read -p "Enter state name to DELETE those episodes (or press Enter to skip): " raw_target_state + +# Format input to Title Case (e.g., indexed -> Indexed) +target_state=$(echo "${raw_target_state,,}" | sed 's/./\u&/') + +deleted_count=0 +affected_seasons="" + +if [ ! -z "$target_state" ]; then + echo "" + echo "⚠ WARNING: You are about to DELETE all episodes with state: $target_state" + read -p "Are you absolutely sure? Type 'DELETE' to confirm: " delete_confirm + + if [ "$delete_confirm" == "DELETE" ]; then + affected_seasons=$(docker exec riven-db psql -U postgres -d riven -t -c " + SELECT DISTINCT s.number + FROM \"MediaItem\" mi + INNER JOIN \"Episode\" e ON mi.id = e.id + INNER JOIN \"Season\" s ON e.parent_id = s.id + INNER JOIN \"Show\" sh ON s.parent_id = sh.id + INNER JOIN \"MediaItem\" parent ON sh.id = parent.id + WHERE $final_condition AND mi.last_state = '$target_state' + ORDER BY s.number;" | xargs | sed 's/ /,/g') + + if [ ! -z "$affected_seasons" ]; then + docker exec riven-db psql -U postgres -d riven -c "DELETE FROM \"MediaItem\" WHERE id IN (SELECT mi.id FROM \"MediaItem\" mi INNER JOIN \"Episode\" e ON mi.id = e.id INNER JOIN \"Season\" s ON e.parent_id = s.id INNER JOIN \"Show\" sh ON s.parent_id = sh.id INNER JOIN \"MediaItem\" parent ON sh.id = parent.id WHERE $final_condition AND mi.last_state = '$target_state');" + echo "✓ Episodes deleted in Season(s): $affected_seasons" + deleted_count=1 + else + echo "ℹ No episodes found with state '$target_state'" + fi + else + echo "⚠ Deletion cancelled. No episodes were deleted." + fi +else + echo "ℹ Skipping deletion step." +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ STEP 2: MARK EPISODES AS UNRELEASED ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "ℹ WHAT THIS DOES:" +echo " • Changes episode state to 'Unreleased'" +echo " • Tells Riven these episodes are not yet available/aired" +echo " • Riven will SKIP these episodes during scraping" +echo " • Episodes remain in the database (not deleted)" +echo "" +echo "WHEN TO USE THIS:" +echo " • Mark future episodes that haven't aired yet" +echo " • Temporarily disable specific episodes from being searched" +echo " • Useful for shows with irregular release schedules" +echo "" +read -p "Do you want to mark specific episodes as 'Unreleased'? (y/n): " do_unreleased + +if [[ "$do_unreleased" =~ ^[Yy](es)?$ ]]; then + read -p "Enter Season Number: " target_season + echo "" + echo "Enter Episode Number(s):" + echo " • For specific episodes: 1,2,3" + echo " • For ALL episodes in the season: type 'A' or 'All'" + echo "" + read -p "Episode Number(s): " target_eps + + # Handle 'A', 'a', or 'All' + if [[ "${target_eps,,}" =~ ^a(ll)?$ ]]; then + ep_condition="s.number = $target_season" + echo "" + echo "Marking ALL episodes in Season $target_season as Unreleased..." + else + ep_condition="s.number = $target_season AND e.number IN ($target_eps)" + echo "" + echo "Marking episodes $target_eps in Season $target_season as Unreleased..." + fi + + docker exec riven-db psql -U postgres -d riven -c " + UPDATE \"MediaItem\" SET last_state = 'Unreleased' + WHERE id IN ( + SELECT mi.id FROM \"MediaItem\" mi + INNER JOIN \"Episode\" e ON mi.id = e.id + INNER JOIN \"Season\" s ON e.parent_id = s.id + WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) + AND $ep_condition + );" + echo "✓ Episodes set to Unreleased." +else + echo "ℹ Skipping unreleased marking step." +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ STEP 3: RESET SEASON(S) TO INDEXED ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "ℹ WHAT THIS DOES:" +echo " • Resets ALL episodes in selected season(s) to 'Indexed' state" +echo " • Also resets the season itself to 'Indexed'" +echo " • Clears scraping history (scraped_at, scraped_times, active_stream)" +echo " • Tells Riven to start fresh and re-scrape these episodes" +echo "" +echo "WHEN TO USE THIS:" +echo " • Force Riven to re-search for better quality streams" +echo " • Reset after changing scraper settings" +echo " • Clear 'Unknown' or 'Failed' states and try again" +echo " • Start over with specific seasons" +echo "" +read -p "Do you want to reset entire season(s) to 'Indexed'? (y/n): " do_reset + +if [[ "$do_reset" =~ ^[Yy](es)?$ ]]; then + echo "" + echo "Enter Season Number(s):" + echo " • For specific seasons: 1,2,3" + echo " • For ALL seasons: type 'A' or 'All'" + echo "" + read -p "Season Number(s): " reset_seasons + + # Handle 'A', 'a', or 'All' + if [[ "${reset_seasons,,}" =~ ^a(ll)?$ ]]; then + echo "" + echo "Resetting ALL episodes and seasons to 'Indexed'..." + + # Reset all episodes to Indexed + docker exec riven-db psql -U postgres -d riven -c " + UPDATE \"MediaItem\" + SET last_state = 'Indexed', + scraped_at = NULL, + scraped_times = 0, + active_stream = NULL + WHERE id IN ( + SELECT mi.id FROM \"MediaItem\" mi + INNER JOIN \"Episode\" e ON mi.id = e.id + INNER JOIN \"Season\" s ON e.parent_id = s.id + WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) + );" + + # Reset all seasons to Indexed + docker exec riven-db psql -U postgres -d riven -c " + UPDATE \"MediaItem\" + SET last_state = 'Indexed', + scraped_at = NULL, + scraped_times = 0, + active_stream = NULL + WHERE type = 'season' AND id IN ( + SELECT s.id FROM \"Season\" s + WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) + );" + + echo "✓ All episodes and seasons reset to 'Indexed'." + else + echo "" + echo "Resetting Season(s) $reset_seasons to 'Indexed'..." + + # Reset episodes in specified seasons to Indexed + docker exec riven-db psql -U postgres -d riven -c " + UPDATE \"MediaItem\" + SET last_state = 'Indexed', + scraped_at = NULL, + scraped_times = 0, + active_stream = NULL + WHERE id IN ( + SELECT mi.id FROM \"MediaItem\" mi + INNER JOIN \"Episode\" e ON mi.id = e.id + INNER JOIN \"Season\" s ON e.parent_id = s.id + WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) + AND s.number IN ($reset_seasons) + );" + + # Reset specified seasons to Indexed + docker exec riven-db psql -U postgres -d riven -c " + UPDATE \"MediaItem\" + SET last_state = 'Indexed', + scraped_at = NULL, + scraped_times = 0, + active_stream = NULL + WHERE type = 'season' AND id IN ( + SELECT s.id FROM \"Season\" s + WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) + AND s.number IN ($reset_seasons) + );" + + echo "✓ Season(s) $reset_seasons and their episodes reset to 'Indexed'." + fi +else + echo "ℹ Skipping season reset step." +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ STEP 4: UPDATE SEASON STATES ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "ℹ WHAT THIS DOES:" +echo " • Sets the state for entire season(s)" +echo " • Affects how Riven treats the season during updates" +echo "" +echo "STATE MEANINGS:" +echo " • Completed: All episodes are done, stop checking for new ones" +echo " • Ongoing: Season is airing, keep checking for new episodes" +echo " • Unreleased: Season hasn't aired yet, skip for now" +echo " • PartiallyCompleted: Some episodes done, still working on others" +echo " • Indexed: Ready to be scraped/processed" +echo " • Scraped: Has been scraped, ready for download" +echo " • Paused: Temporarily stop processing this season" +echo "" + +if [ $deleted_count -gt 0 ]; then + echo "You deleted episodes in Season(s): $affected_seasons" + echo "Choose a state for these affected seasons:" + echo "" + echo " 1) Completed" + echo " 2) Ongoing" + echo " 3) Unreleased" + echo " 4) PartiallyCompleted" + echo " 5) Indexed" + echo " 6) Scraped" + echo " 7) Paused" + echo "" + read -p "Choice (1-7): " del_ms_choice + case $del_ms_choice in + 1) ms_state="Completed" ;; + 2) ms_state="Ongoing" ;; + 3) ms_state="Unreleased" ;; + 4) ms_state="PartiallyCompleted" ;; + 5) ms_state="Indexed" ;; + 6) ms_state="Scraped" ;; + 7) ms_state="Paused" ;; + esac + if [ ! -z "$ms_state" ]; then + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = '$ms_state' WHERE type = 'season' AND id IN (SELECT s.id FROM \"Season\" s WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) AND s.number IN ($affected_seasons));" + echo "✓ Seasons $affected_seasons updated to $ms_state." + fi +else + read -p "Update states for any seasons? Enter season numbers (e.g. 1,2) or press Enter to skip: " manual_seasons + if [ ! -z "$manual_seasons" ]; then + echo "" + echo "Choose state for Season(s) $manual_seasons:" + echo "" + echo " 1) Completed" + echo " 2) Ongoing" + echo " 3) Unreleased" + echo " 4) PartiallyCompleted" + echo " 5) Indexed" + echo " 6) Scraped" + echo " 7) Paused" + echo "" + read -p "Choice (1-7): " ms_choice + case $ms_choice in + 1) ms_state="Completed" ;; + 2) ms_state="Ongoing" ;; + 3) ms_state="Unreleased" ;; + 4) ms_state="PartiallyCompleted" ;; + 5) ms_state="Indexed" ;; + 6) ms_state="Scraped" ;; + 7) ms_state="Paused" ;; + esac + if [ ! -z "$ms_state" ]; then + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = '$ms_state' WHERE type = 'season' AND id IN (SELECT s.id FROM \"Season\" s WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) AND s.number IN ($manual_seasons));" + echo "✓ Seasons $manual_seasons updated to $ms_state." + fi + else + echo "ℹ Skipping manual season state update." + fi +fi + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ STEP 5: UPDATE SHOW STATE ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "ℹ WHAT THIS DOES:" +echo " • Sets the state for THE ENTIRE SHOW" +echo " • Affects how Riven monitors and updates this show" +echo "" +echo "STATE MEANINGS:" +echo " • Completed: Show is finished, no new episodes expected" +echo " • Ongoing: Show is actively airing, check for new episodes" +echo " • Indexed: Show is ready to be processed/scraped" +echo " • Unreleased: Show hasn't premiered yet" +echo " • PartiallyCompleted: Some seasons done, others ongoing" +echo "" +echo "Choose state for THE ENTIRE SHOW: $show_title" +echo "" +echo " 1) Completed" +echo " 2) Ongoing" +echo " 3) Indexed" +echo " 4) Unreleased" +echo " 5) PartiallyCompleted" +echo "" +read -p "Choice (1-5 or press Enter to skip): " show_choice + +case $show_choice in + 1) + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = 'Completed' WHERE id = $show_id;" + echo "✓ Show set to 'Completed'." + ;; + 2) + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = 'Ongoing' WHERE id = $show_id;" + echo "✓ Show set to 'Ongoing'." + ;; + 3) + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = 'Indexed' WHERE id = $show_id;" + echo "✓ Show set to 'Indexed'." + ;; + 4) + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = 'Unreleased' WHERE id = $show_id;" + echo "✓ Show set to 'Unreleased'." + ;; + 5) + docker exec riven-db psql -U postgres -d riven -c "UPDATE \"MediaItem\" SET last_state = 'PartiallyCompleted' WHERE id = $show_id;" + echo "✓ Show set to 'PartiallyCompleted'." + ;; + *) + echo "ℹ Skipping show state update." + ;; +esac + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ FINAL VERIFICATION ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +echo "Show State:" +echo "───────────" +docker exec riven-db psql -U postgres -d riven -c "SELECT title, last_state FROM \"MediaItem\" WHERE id = $show_id;" + +echo "" +echo "Season States:" +echo "──────────────" +docker exec riven-db psql -U postgres -d riven -c "SELECT s.number as season, mi.last_state FROM \"Season\" s INNER JOIN \"MediaItem\" mi ON s.id = mi.id WHERE s.parent_id = (SELECT id FROM \"Show\" WHERE id = $show_id) ORDER BY s.number;" + +echo "" +echo "Episode State Summary:" +echo "──────────────────────" +docker exec riven-db psql -U postgres -d riven -c " +SELECT mi.last_state AS state, COUNT(*) AS count +FROM \"MediaItem\" mi +INNER JOIN \"Episode\" e ON mi.id = e.id +INNER JOIN \"Season\" s ON e.parent_id = s.id +INNER JOIN \"Show\" sh ON s.parent_id = sh.id +INNER JOIN \"MediaItem\" parent ON sh.id = parent.id +WHERE $final_condition +GROUP BY mi.last_state +ORDER BY mi.last_state;" + +echo "" +echo "╔════════════════════════════════════════════════════════════╗" +echo "║ SCRIPT COMPLETED! ║" +echo "╚════════════════════════════════════════════════════════════╝" +echo "" +if [[ "$backup_choice" =~ ^[Yy]$ ]]; then + echo "ℹ Your database backup is located at: /tmp/riven_backup.sql" + echo "" +fi +echo "Done." \ No newline at end of file From 0a949869db955ff4edfab560e9580b731428ea85 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:38:34 -0500 Subject: [PATCH 150/154] docs: add README for Riven database maintenance tool Adds documentation for the riven-db-maintenance.sh script, including: - Usage instructions - Table of contents - Safety guarantees and warnings - Supported environments (Ubuntu / Proxmox LXC) - Backup-first maintenance workflow Keeps documentation consistent with existing Riven installer READMEs. --- db-tools/readme.md | 183 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 db-tools/readme.md diff --git a/db-tools/readme.md b/db-tools/readme.md new file mode 100644 index 0000000..e58ea94 --- /dev/null +++ b/db-tools/readme.md @@ -0,0 +1,183 @@ +# 🗄️ Riven Database Maintenance Tool (PostgreSQL) + +This tool provides **safe, interactive maintenance and recovery operations** for the **Riven PostgreSQL database** used in Docker-based Riven deployments. + +It is designed to be: +- ✅ Production-safe +- 🧭 Beginner-friendly +- 🔁 Repeatable +- 🛡️ Confirmation-guarded for destructive actions + +Works on: +- Ubuntu installs +- Proxmox LXC (unprivileged) +- Any Docker-based Riven setup + +--- + +## 📚 Table of Contents + +- [Run the Database Maintenance Tool](#run-the-database-maintenance-tool) +- [What This Tool Does](#what-this-tool-does) +- [Available Operations](#available-operations) + - [Backup Database](#backup-database) + - [Vacuum & Analyze](#vacuum--analyze) + - [Clean Stale / Orphaned Data](#clean-stale--orphaned-data) + - [Reset Database (Destructive)](#reset-database-destructive) +- [Recommended Usage](#recommended-usage) +- [Restart Services After Maintenance](#restart-services-after-maintenance) +- [Backup Files](#backup-files) +- [Safety Guarantees](#safety-guarantees) +- [When NOT to Use This Tool](#when-not-to-use-this-tool) +- [Summary](#summary) + +--- + +## ▶️ Run the Database Maintenance Tool + +Run this **directly on the system where Riven is installed**: + + sudo bash -c "$(curl -fsSL https://raw.githubusercontent.com/AquaHorizonGaming/Riven-Scripts/main/db-tools/riven-db-maintenance.sh | sed 's/\r$//')" + +This command automatically fixes Windows CRLF line-ending issues before execution. + +--- + +## 🧠 What This Tool Does + +This script provides an **interactive menu** for managing the **Riven PostgreSQL database container**. + +It automatically: +- Detects the Riven database container +- Verifies PostgreSQL connectivity +- Prevents unsafe or accidental operations +- Prompts before any destructive action + +--- + +## 🛠 Available Operations + +### 🗂️ Backup Database + +Creates a timestamped PostgreSQL dump: +- Safe to run at any time +- Stored locally on the host +- Strongly recommended before any cleanup or reset + +--- + +### 🧹 Vacuum & Analyze + +Performs standard PostgreSQL maintenance: +- Reclaims unused space +- Improves query performance +- Updates planner statistics + +Safe for routine maintenance. + +--- + +### 🧽 Clean Stale / Orphaned Data + +Removes broken or unused records caused by: +- Failed scrapes +- Interrupted downloads +- Partial imports + +⚠️ Requires confirmation before execution. + +--- + +### 🔄 Reset Database (Destructive) + +🚨 **THIS WILL DELETE ALL RIVEN DATABASE DATA** 🚨 + +Includes: +- Scrape history +- Index state +- Download records +- Cached metadata + +Use only if: +- The database is corrupted +- Riven cannot recover normally +- You plan to rescrape everything + +A backup is **strongly recommended first**. + +--- + +## 🔁 Recommended Usage + +### Routine maintenance +1. Backup +2. Vacuum & Analyze +3. Exit + +### If Riven behaves incorrectly +1. Backup +2. Clean stale/orphaned data +3. Restart Riven + +### Last-resort recovery +1. Backup +2. Reset database +3. Restart Riven +4. Reconfigure and rescrape + +--- + +## 🔄 Restart Services After Maintenance + +After completing any operation: + + docker restart riven + +If you use a media server, restart only what applies: + + docker restart jellyfin + docker restart plex + docker restart emby + +--- + +## 📂 Backup Files + +Backups are created with timestamps: + + riven-db-backup-YYYY-MM-DD_HH-MM-SS.sql + +Store these somewhere safe before performing destructive actions. + +--- + +## 🛡️ Safety Guarantees + +This tool: +- ❌ Does NOT modify `.env` +- ❌ Does NOT touch media files +- ❌ Does NOT modify mounts +- ❌ Does NOT uninstall containers +- ✅ Uses confirmations for destructive actions +- ✅ Is safe to run multiple times + +--- + +## 🚫 When NOT to Use This Tool + +Do **not** use this script to: +- Install Riven +- Update containers +- Fix mount issues +- Replace the installer or updater + +Use the appropriate **installer**, **updater**, or **remount-cycle script** instead. + +--- + +## ✅ Summary + +- Interactive PostgreSQL maintenance for Riven +- Backup-first, safety-focused workflow +- Handles common performance and corruption issues +- Works across Ubuntu and Proxmox LXC deployments From 93267194f890679d1051ca6e3fb36352e2c892f4 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:41:27 -0500 Subject: [PATCH 151/154] docs: fix README table of contents anchors Adds explicit anchor tags so the table of contents links work correctly on GitHub. Improves navigation and keeps documentation consistent with other Riven READMEs. --- db-tools/readme.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/db-tools/readme.md b/db-tools/readme.md index e58ea94..8f75262 100644 --- a/db-tools/readme.md +++ b/db-tools/readme.md @@ -33,6 +33,7 @@ Works on: --- + ## ▶️ Run the Database Maintenance Tool Run this **directly on the system where Riven is installed**: @@ -43,6 +44,7 @@ This command automatically fixes Windows CRLF line-ending issues before executio --- + ## 🧠 What This Tool Does This script provides an **interactive menu** for managing the **Riven PostgreSQL database container**. @@ -55,8 +57,10 @@ It automatically: --- + ## 🛠 Available Operations + ### 🗂️ Backup Database Creates a timestamped PostgreSQL dump: @@ -66,6 +70,7 @@ Creates a timestamped PostgreSQL dump: --- + ### 🧹 Vacuum & Analyze Performs standard PostgreSQL maintenance: @@ -77,6 +82,7 @@ Safe for routine maintenance. --- + ### 🧽 Clean Stale / Orphaned Data Removes broken or unused records caused by: @@ -88,6 +94,7 @@ Removes broken or unused records caused by: --- + ### 🔄 Reset Database (Destructive) 🚨 **THIS WILL DELETE ALL RIVEN DATABASE DATA** 🚨 @@ -107,6 +114,7 @@ A backup is **strongly recommended first**. --- + ## 🔁 Recommended Usage ### Routine maintenance @@ -127,6 +135,7 @@ A backup is **strongly recommended first**. --- + ## 🔄 Restart Services After Maintenance After completing any operation: @@ -141,6 +150,7 @@ If you use a media server, restart only what applies: --- + ## 📂 Backup Files Backups are created with timestamps: @@ -151,6 +161,7 @@ Store these somewhere safe before performing destructive actions. --- + ## 🛡️ Safety Guarantees This tool: @@ -163,6 +174,7 @@ This tool: --- + ## 🚫 When NOT to Use This Tool Do **not** use this script to: @@ -175,6 +187,7 @@ Use the appropriate **installer**, **updater**, or **remount-cycle script** inst --- + ## ✅ Summary - Interactive PostgreSQL maintenance for Riven From e25155d91ac13aac64efb71a551ad274bd4ffd43 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:04:06 -0500 Subject: [PATCH 152/154] Add db_pegger_9000.sh for MediaItem state management This script provides functionality to manage MediaItem states in a Dockerized PostgreSQL database. It includes backup creation, state item fetching, resetting states, and displaying current counts. --- db-tools/db_pegger_9000.sh | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 db-tools/db_pegger_9000.sh diff --git a/db-tools/db_pegger_9000.sh b/db-tools/db_pegger_9000.sh new file mode 100644 index 0000000..2926f98 --- /dev/null +++ b/db-tools/db_pegger_9000.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# Function to check if the Docker container is running +check_container_status() { + docker ps | grep -q "riven-db" + if [ $? -ne 0 ]; then + echo "Error: The 'riven-db' container is not running. Please start the container and try again." + exit 1 + fi +} + +# Function to perform a backup +backup_database() { + local timestamp=$(date +"%Y-%m-%d_%H-%M-%S") + local backup_file="/tmp/riven_backup_${timestamp}.sql" + + echo "Do you want to create a backup of the database before proceeding?" + read -p "Type 'y' to back up or 'n' to skip: " backup_choice + + if [[ "$backup_choice" =~ ^[Yy]$ ]]; then + echo "Creating a backup of the database..." + + # Use docker exec to dump the database + docker exec riven-db pg_dump -U postgres -d riven -f "$backup_file" + + if [ $? -eq 0 ]; then + echo "Backup created successfully: $backup_file" + else + echo "Error: Backup failed." + exit 1 + fi + else + echo "No backup created. Proceeding with the reset." + fi +} + +# Function to fetch and display state items with confirmation +fetch_state_items() { + local state=$1 + echo "Do you want to see the items in the '$state' state?" + read -p "Type 'y' to view them or 'n' to skip: " view_choice + if [[ "$view_choice" =~ ^[Yy]$ ]]; then + echo "Fetching items in the '$state' state..." + docker exec riven-db psql -U postgres -d riven -c "SELECT id, title, last_state, scraped_times FROM \"MediaItem\" WHERE last_state = '$state';" + else + echo "Skipping viewing '$state' items." + fi +} + +# Function to reset state items (Unknown, Paused, Failed) +reset_state_items() { + local state=$1 + echo "You are about to reset the '$state' items to 'Indexed'." + echo "This will reset the following attributes:" + echo " - 'scraped_times' to 0" + echo " - 'scraped_at' to NULL" + echo " - 'active_stream' to NULL" + read -p "Press Enter to confirm or CTRL+C to cancel..." + + # Perform the reset in the database + echo "Resetting '$state' items..." + docker exec riven-db psql -U postgres -d riven -c " + BEGIN; + UPDATE \"MediaItem\" + SET last_state = 'Indexed', + scraped_at = NULL, + scraped_times = 0, + active_stream = NULL + WHERE last_state = '$state'; + COMMIT; + " + if [ $? -eq 0 ]; then + echo "'$state' items successfully reset to 'Indexed'." + else + echo "Error: Database update failed for '$state'. Rolling back changes." + docker exec riven-db psql -U postgres -d riven -c "ROLLBACK;" + exit 1 + fi +} + +# Function to ask the user which states they want to reset +ask_reset_states() { + echo "Select which states you want to reset (you can choose multiple states):" + echo "1) Unknown" + echo "2) Paused" + echo "3) Failed" + read -p "Enter your choices (e.g., '1 3' for Unknown and Failed, '2 3' for Paused and Failed, etc.): " -a choices + + # Loop through the choices and perform actions for each selected state + for choice in "${choices[@]}"; do + case $choice in + 1) + fetch_state_items "Unknown" + reset_state_items "Unknown" + ;; + 2) + fetch_state_items "Paused" + reset_state_items "Paused" + ;; + 3) + fetch_state_items "Failed" + reset_state_items "Failed" + ;; + *) + echo "Invalid choice: $choice. Skipping." + ;; + esac + done +} + +# Function to display current state counts after reset +show_current_counts() { + echo "Fetching current counts of MediaItem states..." + + indexed_count=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT count(*) FROM \"MediaItem\" WHERE last_state = 'Indexed';") + paused_count=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT count(*) FROM \"MediaItem\" WHERE last_state = 'Paused';") + unknown_count=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT count(*) FROM \"MediaItem\" WHERE last_state = 'Unknown';") + failed_count=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT count(*) FROM \"MediaItem\" WHERE last_state = 'Failed';") + completed_count=$(docker exec riven-db psql -U postgres -d riven -t -c "SELECT count(*) FROM \"MediaItem\" WHERE last_state = 'Completed';") + + echo "Current MediaItem States:" + echo " - Indexed: $indexed_count" + echo " - Paused: $paused_count" + echo " - Unknown: $unknown_count" + echo " - Failed: $failed_count" + echo " - Completed: $completed_count" +} + +# Main script execution +echo "Starting the reset process for MediaItem states..." + +# Step 1: Check if the Docker container is running +check_container_status + +# Step 2: Ask the user if they want to create a backup +backup_database + +# Step 3: Ask the user which states they want to reset (can select multiple) +ask_reset_states + +# Step 4: Show the current counts after the reset +show_current_counts + +echo "Script completed successfully." From 3692574141d87e75728966f707ad613311e34f11 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:31:54 -0500 Subject: [PATCH 153/154] Simplify compose startup to use up --pull always --- ubuntu/install.sh | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 114aece..21692b2 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -24,6 +24,23 @@ ok() { printf "✔ %s\n" "$1"; } warn() { printf "⚠ %s\n" "$1"; } fail() { printf "✖ %s\n" "$1"; exit 1; } +# Capture whether the original stdout is a terminal before logging redirection. +exec 3>&1 +ORIGINAL_STDOUT_IS_TTY=false +[[ -t 3 ]] && ORIGINAL_STDOUT_IS_TTY=true + +run_docker_compose_up_detached() { + local -a compose_cmd=("$@") + + if [[ "$ORIGINAL_STDOUT_IS_TTY" == "true" ]]; then + # Preserve TTY behavior so pull progress updates in-place. + "${compose_cmd[@]}" up -d --pull always 1>&3 2>&3 + else + # Keep normal output in CI / non-interactive shells. + "${compose_cmd[@]}" up -d --pull always + fi +} + ############################################ # REQUIRED NON-EMPTY (SILENT) # (keep for non-secret values if needed) @@ -392,7 +409,7 @@ esac # START MEDIA SERVER ############################################ banner "Starting Media Server" -docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" up -d +run_docker_compose_up_detached docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" ok "Media server started" SERVER_IP="$(hostname -I | awk '{print $1}')" @@ -768,27 +785,27 @@ SCRAPER_COUNT=0 if [[ "${RIVEN_SCRAPING_TORRENTIO_ENABLED:-false}" == "true" ]]; then echo " • Torrentio" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_PROWLARR_ENABLED:-false}" == "true" ]]; then echo " • Prowlarr" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_COMET_ENABLED:-false}" == "true" ]]; then echo " • Comet" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_JACKETT_ENABLED:-false}" == "true" ]]; then echo " • Jackett" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_ZILEAN_ENABLED:-false}" == "true" ]]; then echo " • Zilean" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "$SCRAPER_COUNT" -eq 0 ]]; then @@ -921,7 +938,7 @@ ok ".env repaired and sanitized" # START RIVEN ############################################ banner "Starting Riven" -docker compose up -d +run_docker_compose_up_detached docker compose ok "Riven started" banner "INSTALL COMPLETE" @@ -1011,4 +1028,3 @@ echo " • Riven started after config complete" echo ok "Riven is ready 🚀" - From 48b8128b83893d586f7e83c23851db4395b1e973 Mon Sep 17 00:00:00 2001 From: AquaHorizonGaming <48632521+AquaHorizonGaming@users.noreply.github.com> Date: Tue, 10 Feb 2026 20:37:35 -0500 Subject: [PATCH 154/154] Add --debug mode and improve installer diagnostics --- ubuntu/install.sh | 80 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/ubuntu/install.sh b/ubuntu/install.sh index 114aece..cca12e2 100644 --- a/ubuntu/install.sh +++ b/ubuntu/install.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash set -euo pipefail +DEBUG_MODE=false +if [[ "${1:-}" == "--debug" ]]; then + DEBUG_MODE=true + shift +fi + ############################################ # CONSTANTS ############################################ @@ -24,6 +30,49 @@ ok() { printf "✔ %s\n" "$1"; } warn() { printf "⚠ %s\n" "$1"; } fail() { printf "✖ %s\n" "$1"; exit 1; } +debug() { + [[ "$DEBUG_MODE" == "true" ]] || return 0 + local source_name="${BASH_SOURCE[1]##*/}" + local line_no="${BASH_LINENO[0]:-0}" + local func_name="${FUNCNAME[1]:-main}" + printf "[DEBUG][%s:%s][%s] %s\n" "$source_name" "$line_no" "$func_name" "$*" +} + +debug_rc() { + local rc="$1" + shift || true + debug "exit_code=${rc} command=$*" + return "$rc" +} + +debug_step() { + debug "step=$*" +} + +# Capture whether the original stdout is a terminal before logging redirection. +exec 3>&1 +ORIGINAL_STDOUT_IS_TTY=false +[[ -t 3 ]] && ORIGINAL_STDOUT_IS_TTY=true + +run_docker_compose_up_detached() { + local -a compose_cmd=("$@") + local rc=0 + + debug_step "docker compose up -d --pull always" + debug "compose_cmd=${compose_cmd[*]}" + + if [[ "$ORIGINAL_STDOUT_IS_TTY" == "true" ]]; then + # Preserve TTY behavior so pull progress updates in-place. + "${compose_cmd[@]}" up -d --pull always 1>&3 2>&3 || rc=$? + else + # Keep normal output in CI / non-interactive shells. + "${compose_cmd[@]}" up -d --pull always || rc=$? + fi + + debug_rc "$rc" "${compose_cmd[*]} up -d --pull always" + return "$rc" +} + ############################################ # REQUIRED NON-EMPTY (SILENT) # (keep for non-secret values if needed) @@ -164,7 +213,14 @@ log_warn() { echo "[WARN] $*"; } log_error() { echo "[ERROR] $*"; } log_section(){ echo -e "\n========== $* ==========\n"; } -trap 'log_error "Installer exited unexpectedly at line $LINENO"' ERR +if [[ "$DEBUG_MODE" == "true" ]]; then + export PS4='+ [${BASH_SOURCE##*/}:${LINENO}:${FUNCNAME[0]:-main}] ' + set -x + debug "Debug mode enabled" + debug "original_stdout_is_tty=$ORIGINAL_STDOUT_IS_TTY" +fi + +trap 'rc=$?; cmd=${BASH_COMMAND:-unknown}; log_error "Installer exited unexpectedly at line $LINENO (rc=$rc, cmd: $cmd)"; debug "failure_context rc=$rc cmd=$cmd"; exit $rc' ERR log "Logging initialized" log "Log file: $LOG_FILE" @@ -173,6 +229,7 @@ log "Log file: $LOG_FILE" # TIMEZONE (INSTALLER SAFE) ############################################ banner "Timezone" +debug_step "timezone detection and configuration" detect_timezone() { timedatectl show --property=Timezone --value 2>/dev/null \ @@ -197,6 +254,7 @@ ok "Timezone set: $TZ_SELECTED" # SYSTEM DEPS ############################################ banner "System Dependencies" +debug_step "system dependency verification" dpkg -s ca-certificates curl gnupg lsb-release openssl fuse3 >/dev/null 2>&1 \ && ok "System dependencies already installed" \ @@ -244,6 +302,7 @@ ok "Detected user ownership: UID=$TARGET_UID GID=$TARGET_GID" # DOCKER ############################################ banner "Docker" +debug_step "docker installation check" if command -v docker >/dev/null 2>&1; then ok "Docker already installed" @@ -392,7 +451,7 @@ esac # START MEDIA SERVER ############################################ banner "Starting Media Server" -docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" up -d +run_docker_compose_up_detached docker compose -f docker-compose.media.yml --profile "$MEDIA_PROFILE" ok "Media server started" SERVER_IP="$(hostname -I | awk '{print $1}')" @@ -734,9 +793,13 @@ esac # VALIDATION SUMMARY (CONFIRM BEFORE CONTINUE) ############################################ banner "Configuration Summary" +debug_step "print configuration summary and collect confirmation" echo "Please review your configuration below:" echo "" +debug "summary timezone=$TZ_SELECTED origin=$ORIGIN media_profile=$MEDIA_PROFILE media_url=http://$SERVER_IP:$MEDIA_PORT" +debug "summary downloader_flags rd=${RIVEN_DOWNLOADERS_REAL_DEBRID_ENABLED:-unset} ad=${RIVEN_DOWNLOADERS_ALL_DEBRID_ENABLED:-unset} dl=${RIVEN_DOWNLOADERS_DEBRID_LINK_ENABLED:-unset}" +debug "summary secrets_present media_api_key_set=$([[ -n "${MEDIA_API_KEY:-}" ]] && echo true || echo false) backend_api_key_set=$([[ -n "${BACKEND_API_KEY:-}" ]] && echo true || echo false) auth_secret_set=$([[ -n "${AUTH_SECRET:-}" ]] && echo true || echo false)" echo "🕒 Timezone" echo " • $TZ_SELECTED" @@ -768,27 +831,27 @@ SCRAPER_COUNT=0 if [[ "${RIVEN_SCRAPING_TORRENTIO_ENABLED:-false}" == "true" ]]; then echo " • Torrentio" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_PROWLARR_ENABLED:-false}" == "true" ]]; then echo " • Prowlarr" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_COMET_ENABLED:-false}" == "true" ]]; then echo " • Comet" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_JACKETT_ENABLED:-false}" == "true" ]]; then echo " • Jackett" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "${RIVEN_SCRAPING_ZILEAN_ENABLED:-false}" == "true" ]]; then echo " • Zilean" - ((SCRAPER_COUNT++)) + ((++SCRAPER_COUNT)) fi if [[ "$SCRAPER_COUNT" -eq 0 ]]; then @@ -921,7 +984,7 @@ ok ".env repaired and sanitized" # START RIVEN ############################################ banner "Starting Riven" -docker compose up -d +run_docker_compose_up_detached docker compose ok "Riven started" banner "INSTALL COMPLETE" @@ -1011,4 +1074,3 @@ echo " • Riven started after config complete" echo ok "Riven is ready 🚀" -