diff --git a/README.md b/README.md index f2f9803..c9d8ba0 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,40 @@ --- +## Install (one-liner) + +```bash +# With curl (requires root) +sudo sh -c "$(curl -fsSL https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" + +# With wget (requires root) +sudo sh -c "$(wget -O- https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" + +# With fetch / BSD (requires root) +sudo sh -c "$(fetch -o - https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" +``` + +### Install options (via environment variables) + +| Variable | Description | Default | +| --------------- | ----------------------------- | -------------- | +| `O11PRO_DIR` | Install directory | `/root/o11pro-unpacked` | +| `O11PRO_PORT` | Server port | `1337` | +| `O11PRO_VERBOSE`| Log level (0–5) | `2` | +| `O11PRO_USER` | Admin username | auto-generated | +| `O11PRO_PASS` | Admin password | auto-generated | +| `O11PRO_BRANCH` | Git branch to clone | `main` | +| `O11PRO_YES` | Skip confirm prompts | `0` | + +Example with custom options: + +```bash +O11PRO_PORT=8080 O11PRO_YES=1 \ + sudo sh -c "$(curl -fsSL https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" +``` + +--- + ## Further Reading | Document | Description | diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..1d44f9a --- /dev/null +++ b/install.sh @@ -0,0 +1,207 @@ +#!/bin/sh +# install.sh – One-command installer for o11pro-unpacked +# +# Usage: +# sh -c "$(curl -fsSL https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" +# sh -c "$(wget -O- https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" +# sh -c "$(fetch -o - https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked/main/install.sh)" +# +# Environment: +# O11PRO_DIR Install directory (default: /root/o11pro-unpacked, root required) +# O11PRO_PORT Server port (default: 1337) +# O11PRO_VERBOSE Log level 0-5 (default: 2) +# O11PRO_USER Admin username +# O11PRO_PASS Admin password +# O11PRO_YES Skip confirmation prompts (set to 1) +# O11PRO_BRANCH Git branch to clone (default: main) +# +# Options: +# --yes, -y Non-interactive mode (auto-confirm all prompts) +# --help, -h Show this help message + +set -eu + +REPO_URL="https://github.com/Ap0dexMe0/o11pro-unpacked.git" +REPO_RAW="https://raw.githubusercontent.com/Ap0dexMe0/o11pro-unpacked" +BRANCH="${O11PRO_BRANCH:-main}" + +# ─── Parse flags ───────────────────────────────────────────────────────── +FORCE=0 +for arg in "$@"; do + case "$arg" in + --yes|-y) FORCE=1 ;; + --help|-h) + sed -n '2,/^$/p' "$0" | sed 's/^# \?//' + exit 0 + ;; + esac +done +[ "${O11PRO_YES:-0}" = "1" ] && FORCE=1 + +# ─── Colors ────────────────────────────────────────────────────────────── +RST='\033[0m' +BLD='\033[1m' +DIM='\033[2m' +RED='\033[0;31m' +GRN='\033[0;32m' +YLW='\033[0;33m' +BLU='\033[0;34m' +CYN='\033[0;36m' + +ok() { printf " ${GRN}\xE2\x9C\x94${RST} ${BLD}%s${RST} %s\n" "$1" "${2-}"; } +info() { printf " ${CYN}\xE2\x96\xB6${RST} ${DIM}%s${RST}\n" "$1"; } +warn() { printf " ${YLW}\xE2\x9A\xA0${RST} ${BLD}%s${RST} %s\n" "$1" "${2-}"; } +fail() { printf " ${RED}\xE2\x9C\x98${RST} ${RED}%s${RST}\n" "$1"; exit 1; } +sep() { printf " ${DIM}----------------------------------------${RST}\n"; } +header(){ printf "\n ${BLD}%s${RST}\n ${DIM}%s${RST}\n\n" "$1" "${2-}"; } + +# ─── Check prerequisites ───────────────────────────────────────────────── +header "o11pro Installer" "One-command setup for o11pro-unpacked" + +has_cmd() { command -v "$1" >/dev/null 2>&1; } + +info "Checking prerequisites..." + +if ! has_cmd git; then + fail "git is required but not installed.\n Install it with your package manager (apt install git, yum install git, etc.)" +fi +ok "git" "$(git --version 2>/dev/null)" + +if ! has_cmd python3; then + fail "python3 is required but not installed." +fi +ok "python3" "$(python3 --version 2>/dev/null)" + +FETCH_CMD="" +DOWNLOAD_CMD="" +DOWNLOAD_PIPE="" +if has_cmd curl; then + FETCH_CMD="curl -fsSL" + DOWNLOAD_CMD="curl -fsSL -o" + DOWNLOAD_PIPE="curl -fsSL" + ok "curl" "available" +elif has_cmd wget; then + FETCH_CMD="wget -O-" + DOWNLOAD_CMD="wget -O" + DOWNLOAD_PIPE="wget -O-" + ok "wget" "available" +elif has_cmd fetch; then + FETCH_CMD="fetch -o -" + DOWNLOAD_CMD="fetch -o" + DOWNLOAD_PIPE="fetch -o -" + ok "fetch" "available" +else + fail "curl, wget, or fetch is required to download files." +fi + +# ─── Determine install directory ───────────────────────────────────────── +INSTALL_DIR="${O11PRO_DIR:-/root/o11pro-unpacked}" + +# ─── Root check ────────────────────────────────────────────────────────── +if [ "$(id -u)" -ne 0 ]; then + fail "This installer must be run as root.\n Install directory ${INSTALL_DIR} requires root privileges.\n Try: sudo sh -c \"\$(curl ...)\"" +fi +ok "root" "privileges confirmed" + +if [ "$FORCE" = "0" ] && [ -d "$INSTALL_DIR" ]; then + printf " Directory %s already exists.\n" "$INSTALL_DIR" + printf " Overwrite? [y/N] "; read -r REPLY + case "$REPLY" in + [yY]|[yY][eE][sS]) ;; + *) fail "Installation cancelled." ;; + esac +fi + +# ─── Install ───────────────────────────────────────────────────────────── +sep + +info "Installing to ${BLD}$INSTALL_DIR${RST}..." + +# Ensure parent directory exists +PARENT_DIR=$(dirname "$INSTALL_DIR") +mkdir -p "$PARENT_DIR" + +# Clone or update the repository +if [ -d "$INSTALL_DIR/.git" ]; then + info "Updating existing installation..." + cd "$INSTALL_DIR" + git pull --ff-only origin "$BRANCH" 2>/dev/null || { + warn "git pull failed, re-cloning..." + cd / + rm -rf "$INSTALL_DIR" + git clone --depth 1 -b "$BRANCH" "$REPO_URL" "$INSTALL_DIR" + } +else + rm -rf "$INSTALL_DIR" + git clone --depth 1 -b "$BRANCH" "$REPO_URL" "$INSTALL_DIR" +fi +ok "repository" "cloned to ${DIM}$INSTALL_DIR${RST}" + +cd "$INSTALL_DIR" + +# ─── Python virtual environment ────────────────────────────────────────── +VENV_DIR="$INSTALL_DIR/src/venv" +if [ ! -d "$VENV_DIR" ]; then + info "Creating Python virtual environment..." + python3 -m venv "$VENV_DIR" +fi +ok "venv" "ready" + +# ─── Install Python dependencies ───────────────────────────────────────── +if [ -f "requirements.txt" ]; then + info "Installing Python dependencies..." + "$VENV_DIR/bin/pip" install --upgrade pip -q 2>/dev/null || true + "$VENV_DIR/bin/pip" install -r requirements.txt -q 2>/dev/null || warn "Some pip packages failed to install" + ok "dependencies" "installed" +fi + +# ─── Make binary executable ────────────────────────────────────────────── +BINARY="" +for b in src/o11pro src/o11; do + [ -f "$b" ] && BINARY="$b" && break +done + +if [ -n "$BINARY" ]; then + chmod +x "$BINARY" + ok "binary" "ready (${DIM}$BINARY${RST})" +else + warn "binary" "not found in src/ — check your download" +fi + +# Make launcher executable +[ -f "src/RunMe.sh" ] && chmod +x "src/RunMe.sh" +ok "launcher" "ready" + +# ─── Check for FFmpeg ──────────────────────────────────────────────────── +if has_cmd ffmpeg; then + ok "ffmpeg" "$(ffmpeg -version 2>&1 | head -1 | sed 's/ffmpeg version //' | sed 's/ Copyright.*//')" +else + warn "ffmpeg not found" "install it for transcoding support" +fi + +# ─── Create runtime directories ────────────────────────────────────────── +mkdir -p "$INSTALL_DIR/src/hls/live" +mkdir -p "$INSTALL_DIR/src/keys" +mkdir -p "$INSTALL_DIR/src/epg" +mkdir -p "$INSTALL_DIR/src/dl" +mkdir -p "$INSTALL_DIR/src/manifests" +mkdir -p "$INSTALL_DIR/src/offair" +mkdir -p "$INSTALL_DIR/src/overlay" +mkdir -p "$INSTALL_DIR/src/logos" +mkdir -p "$INSTALL_DIR/src/fonts" +mkdir -p "$INSTALL_DIR/src/rec" +mkdir -p "$INSTALL_DIR/src/scripts" +mkdir -p "$INSTALL_DIR/src/logs" +mkdir -p "$INSTALL_DIR/providers" +mkdir -p "$INSTALL_DIR/cache" +ok "directories" "created" + +# ─── Done ──────────────────────────────────────────────────────────────── +sep +echo +printf " ${GRN}\xE2\x9C\x94${RST} ${BLD}o11pro installed successfully${RST}\n" +echo + +printf " ${DIM}Launch:${RST}\n" +printf " cd ${INSTALL_DIR}/src && ./RunMe.sh ${O11PRO_PORT:-1337} ${O11PRO_VERBOSE:-2}\n" +echo diff --git a/setup.sh b/setup.sh deleted file mode 100644 index ea97bd0..0000000 --- a/setup.sh +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env bash -# setup.sh – Fully automatic environment setup + launcher -# -# Creates venv, installs dependencies, verifies structure, then launches. -# All arguments are forwarded to src/RunMe.sh. -# -# Usage: -# ./setup.sh # default port 1337 -# ./setup.sh 8080 # custom port -# ./setup.sh 8080 4 # custom port + verbose=4 -# -# Environment variables (pass before or alongside): -# MONITOR=true Enable security monitoring proxy (port 1339) -# HLS_PROXY=false Disable HLS rewrite proxy (default: true) -# GOMEMLIMIT=4G Override Go memory limit (default: 2GiB) -# MAX_STREAMS=8 Limit concurrent streams -# ADMIN_USER=admin Static admin username (default: admin) -# ADMIN_PASS=secret Static admin password (default: admin1337) - -set -euo pipefail - -SETUP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SRC_DIR="$SETUP_DIR/src" - -# Switch to src/ early — venv, scripts, and binary all live here -cd "$SRC_DIR" - -# ─── Colors ──────────────────────────────────────────────────────────── -BLD='\033[1m' -DIM='\033[2m' -RED='\033[0;31m' -GRN='\033[0;32m' -YLW='\033[0;33m' -BLU='\033[0;34m' -MAG='\033[0;35m' -CYN='\033[0;36m' -RST='\033[0m' -CHECK="${GRN}\xE2\x9C\x94${RST}" -CROSS="${RED}\xE2\x9C\x98${RST}" -ARROW="${CYN}\xE2\x96\xB6${RST}" - -# ─── Helpers ─────────────────────────────────────────────────────────── -ok() { echo -e " ${CHECK} ${BLD}$1${RST} $2"; } -info() { echo -e " ${ARROW} ${DIM}$1${RST}"; } -warn() { echo -e " ${YLW}\xE2\x9A\xA0${RST} ${BLD}$1${RST} $2"; } -fail() { echo -e " ${CROSS} ${RED}$1${RST}"; } -sep() { echo -e " ${DIM}──────────────────────────────────────────────${RST}"; } -box() { - local s="$1" w=56 - echo -e " ${DIM}\xE2\x95\x94$(printf '\xE2\x95\x90%.0s' $(seq 1 $w))\xE2\x95\x97${RST}" - echo -e " ${DIM}\xE2\x95\x91${RST} ${BLD}$s${RST}" - echo -e " ${DIM}\xE2\x95\x9A$(printf '\xE2\x95\x90%.0s' $(seq 1 $w))\xE2\x95\x9D${RST}" -} - -# ─── Header ──────────────────────────────────────────────────────────── -clear 2>/dev/null || true -echo -box "o11pro – Setup & Launch" -echo - -# ─── Phase 1: Python 3 ───────────────────────────────────────────────── -echo -e " ${BLU}\xE2\x96\xBC${RST} ${BLD}Phase 1${RST} \xE2\x80\x94 Python 3" -echo - -if ! command -v python3 &>/dev/null; then - warn "Python 3 not found" "attempting install..." - if command -v apt-get &>/dev/null; then - sudo apt-get update && sudo apt-get install -y python3 python3-venv python3-pip - elif command -v yum &>/dev/null; then - sudo yum install -y python3 python3-pip - else - fail "No supported package manager. Install Python 3 manually."; exit 1 - fi -fi -ok "Python" "$(python3 --version)" -echo - -# ─── Phase 2: Virtual Environment ────────────────────────────────────── -echo -e " ${BLU}\xE2\x96\xBC${RST} ${BLD}Phase 2${RST} \xE2\x80\x94 Virtual Environment" -echo - -VENV_DIR="$SRC_DIR/venv" -if [ ! -d "$VENV_DIR" ]; then - info "Creating venv at ${DIM}$VENV_DIR${RST}..." - python3 -m venv "$VENV_DIR" - ok "venv" "created" -else - ok "venv" "exists at ${DIM}$VENV_DIR${RST}" -fi -echo - -# ─── Phase 3: Dependencies ───────────────────────────────────────────── -echo -e " ${BLU}\xE2\x96\xBC${RST} ${BLD}Phase 3${RST} \xE2\x80\x94 Python Dependencies" -echo - -"$VENV_DIR/bin/pip" install --upgrade pip -q 2>/dev/null -if [ -f "$SETUP_DIR/requirements.txt" ]; then - REQ_COUNT=$(grep -cEv '^\s*(#|$)' "$SETUP_DIR/requirements.txt" || echo 0) - info "Installing ${REQ_COUNT} packages from ${DIM}requirements.txt${RST}..." - "$VENV_DIR/bin/pip" install -r "$SETUP_DIR/requirements.txt" -q 2>&1 | tail -1 - ok "dependencies" "ready" -else - warn "requirements.txt not found" "\xE2\x80\x94 skipping packages" -fi -echo - -# ─── Phase 4: Project Structure ──────────────────────────────────────── -echo -e " ${BLU}\xE2\x96\xBC${RST} ${BLD}Phase 4${RST} \xE2\x80\x94 Project Structure" -echo - -ok "root" "${DIM}$SETUP_DIR${RST}" - -# Launcher scripts -LAUNCHER_COUNT=$(ls -1 *.sh 2>/dev/null | wc -l) -ok "src/" "${LAUNCHER_COUNT} launcher(s)" - -# Binary -BINARY="" -for b in o11pro o11; do - [ -f "$b" ] && BINARY="$b" && break -done -if [ -n "$BINARY" ]; then - BIN_SIZE=$(stat -c%s "$BINARY" 2>/dev/null | numfmt --to=iec 2>/dev/null || echo "?") - ok "binary" "${DIM}$BINARY${RST} (${BIN_SIZE})" - [ -x "$BINARY" ] || chmod +x "$BINARY" && ok "binary" "made executable" -else - fail "No o11pro binary found in ${DIM}$SRC_DIR${RST}"; exit 1 -fi - -# RunMe.sh -if [ -f "RunMe.sh" ]; then - chmod +x "RunMe.sh" - ok "launcher" "${DIM}RunMe.sh${RST}" -else - fail "RunMe.sh not found in ${DIM}$SRC_DIR${RST}"; exit 1 -fi - -# Provider configs -PROV_DIR="$SETUP_DIR/providers" -PROV_COUNT=0 -if [ -d "$PROV_DIR" ]; then - PROV_COUNT=$(find "$PROV_DIR" -maxdepth 1 -name '*.cfg' 2>/dev/null | wc -l) -fi -if [ "$PROV_COUNT" -gt 0 ]; then - ok "providers/" "${PROV_COUNT} config(s)" -else - info "No provider configs yet — place .cfg files in ${DIM}providers/${RST}" -fi - -# HLS proxy maps -CACHE_DIR="$SETUP_DIR/cache" -ORIG_MAP="$CACHE_DIR/orig_urls.json" -if [ -f "$ORIG_MAP" ]; then - MAP_COUNT=$(python3 -c "import json; print(len(json.load(open('$ORIG_MAP'))))" 2>/dev/null || echo "?") - ok "HLS map" "${MAP_COUNT} channel(s) in ${DIM}cache/orig_urls.json${RST}" -else - info "HLS map will be auto-generated on launch" -fi -echo - -# ─── Phase 5: Launch ─────────────────────────────────────────────────── -echo -e " ${GRN}\xE2\x96\xB6${RST} ${BLD}Starting o11pro${RST} — delegating to src/RunMe.sh" -echo - -export PATH="$VENV_DIR/bin:$PATH" -exec ./RunMe.sh "$@" \ No newline at end of file