commit 34731194199b637813ae476098524855cf2bd895 Author: spongebob <130098791+skieop@users.noreply.github.com> Date: Sun Feb 22 17:02:56 2026 -0500 initial commit diff --git a/.containerignore b/.containerignore new file mode 100644 index 0000000..4c3a3fb --- /dev/null +++ b/.containerignore @@ -0,0 +1,40 @@ +# Ignore git metadata +.git +.gitignore + +# Ignore container/build files themselves +Containerfile +Dockerfile +.containerignore +.dockerignore + +# Compose (no longer used) +podman-compose.yml +docker-compose.yml + +# Scripts and docs +pod-start.ps1 +pod-stop.ps1 +PODMAN-SETUP.md +README.md + +# IDE / OS artifacts +.vscode +.idea +*.swp +*.swo +*~ +Thumbs.db +.DS_Store + +# Node modules will be installed fresh in the container +node_modules +package-lock.json + +# Python caches +__pycache__ +*.pyc + +# Generated at runtime +certs/ +*.log diff --git a/Containerfile b/Containerfile new file mode 100644 index 0000000..8a6bc35 --- /dev/null +++ b/Containerfile @@ -0,0 +1,59 @@ +############################################################################### +# o11v4 Containerfile — Podman Pods +# +# Single image used by BOTH containers in the pod: +# - licserver (runs server.js via PM2) +# - app (runs o11v4 binary watchdog) +# +# Build: podman build -t o11v4 . +# Run: .\pod-start.ps1 (see PODMAN-SETUP.md) +############################################################################### + +FROM docker.io/library/node:20-slim + +# ── System packages ───────────────────────────────────────────────────────── +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv \ + ffmpeg \ + openssl \ + procps \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# ── Python venv + dependencies (used by example.py) ───────────────────────── +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" +RUN pip install --no-cache-dir requests PyJWT + +# ── PM2 (Node process manager) ────────────────────────────────────────────── +RUN npm install -g pm2 + +# ── Application directory ─────────────────────────────────────────────────── +RUN mkdir -p /home/o11/hls /home/o11/dl /home/o11/certs /home/o11/www + +WORKDIR /home/o11 + +# ── Copy project files ────────────────────────────────────────────────────── +COPY server.js o11.cfg lic.cr run.sh ./ + +# If you have a www/ directory with static assets, uncomment: +# COPY www/ ./www/ + +# ── Install Express locally ───────────────────────────────────────────────── +RUN npm init -y > /dev/null 2>&1 && npm install express + +# ── Copy binary + entrypoints ─────────────────────────────────────────────── +COPY o11v4 ./o11v4 +COPY entrypoint-licserver.sh /entrypoint-licserver.sh +COPY entrypoint-app.sh /entrypoint-app.sh + +RUN chmod +x /home/o11/o11v4 \ + /entrypoint-licserver.sh \ + /entrypoint-app.sh + +# ── Expose ports ──────────────────────────────────────────────────────────── +# 80 - License server HTTP +# 443 - License server HTTPS +# 5454 - License server HTTP alt +# 8484 - o11v4 main application +EXPOSE 80 443 5454 8484 diff --git a/PODMAN-SETUP.md b/PODMAN-SETUP.md new file mode 100644 index 0000000..6038d00 --- /dev/null +++ b/PODMAN-SETUP.md @@ -0,0 +1,255 @@ +# o11v4 — Podman Setup + +Two containers in one pod. Podman handles everything. + +--- + +## 1. Install Podman + +### Windows + +Download and install from https://podman.io/ + +Close and reopen your terminal, then verify: + +```powershell +podman --version +``` + +Initialize the machine (once): + +```powershell +podman machine init +podman machine start +``` + +If PowerShell blocks scripts, run this first: + +```powershell +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser +``` + +### macOS + +Install via Homebrew: + +```bash +brew install podman +``` + +Initialize the machine (once): + +```bash +podman machine init +podman machine start +``` + +### Linux + +Install from your package manager: + +```bash +# Debian / Ubuntu +sudo apt install -y podman + +# Fedora +sudo dnf install -y podman + +# Arch +sudo pacman -S podman +``` + +No machine init needed — Podman runs natively on Linux. + +--- + +## 2. Build & Run + +### Windows + +```powershell +.\pod-start.ps1 +``` + +### macOS / Linux + +```bash +chmod +x pod-start.sh pod-stop.sh +./pod-start.sh +``` + +Open `http://localhost:8484` — login: `admin` / `admin` + +--- + +## 3. What the Script Does + +The start script performs these steps: + +1. Builds the container image from the Containerfile +2. Creates a pod with shared networking and `/etc/hosts` entries +3. Starts the license server container +4. Starts the o11v4 app container + +You can run the commands manually if you prefer: + +```bash +# 1. Build the image +podman build -t o11v4 . + +# 2. Create the pod +podman pod create --name o11v4-pod \ + --add-host "lic.cryptolive.one:127.0.0.1" \ + --add-host "lic.bitmaster.cc:127.0.0.1" \ + -p 8484:8484 -p 8080:80 -p 8443:443 -p 5454:5454 + +# 3. Run the license server container +podman run -d --pod o11v4-pod --name licserver \ + -e SERVER_IP=127.0.0.1 o11v4 /entrypoint-licserver.sh + +# 4. Run the app container +podman run -d --pod o11v4-pod --name o11v4-app \ + -e SERVER_IP=127.0.0.1 \ + --tmpfs /home/o11/hls:size=2g,mode=1777 \ + --tmpfs /home/o11/dl:size=2g,mode=1777 \ + o11v4 /entrypoint-app.sh +``` + +--- + +## 4. Managing the Pod + +### Windows + +| Action | Command | +|--------|---------| +| Start | `.\pod-start.ps1` | +| Stop | `.\pod-stop.ps1` | +| Full rebuild | `.\pod-stop.ps1 -RemoveImage` then `.\pod-start.ps1` | + +### macOS / Linux + +| Action | Command | +|--------|---------| +| Start | `./pod-start.sh` | +| Stop | `./pod-stop.sh` | +| Full rebuild | `./pod-stop.sh --remove-image` then `./pod-start.sh` | + +### Common Commands (all platforms) + +| Action | Command | +|--------|---------| +| Pod status | `podman pod ps` | +| All containers | `podman ps --pod` | +| Restart pod | `podman pod restart o11v4-pod` | +| App logs | `podman logs -f o11v4-app` | +| Licserver logs | `podman logs -f licserver` | +| Shell into app | `podman exec -it o11v4-app bash` | +| Shell into licserver | `podman exec -it licserver bash` | + +--- + +## 5. Configuration + +### o11.cfg (optional, edit before building) + +You can change the admin password by generating a SHA-256 hash and putting it in the `Password` field in `o11.cfg`. + +**Windows (PowerShell):** + +```powershell +$p = "yourpassword" +[BitConverter]::ToString( + [Security.Cryptography.SHA256]::Create().ComputeHash( + [Text.Encoding]::UTF8.GetBytes($p) + ) +).Replace("-","").ToLower() +``` + +**macOS / Linux:** + +```bash +echo -n "yourpassword" | sha256sum | awk '{print $1}' +``` + +### tmpfs sizes + +**Windows:** + +```powershell +.\pod-start.ps1 -HlsSize 4g -DlSize 4g +``` + +**macOS / Linux:** + +```bash +./pod-start.sh --hls-size 4g --dl-size 4g +``` + +--- + +## 6. Troubleshooting + +**"Cannot connect to Podman" (Windows / macOS):** +```bash +podman machine start +``` + +**o11v4 crashes:** +```bash +podman logs -f o11v4-app +``` + +**Can't reach localhost:8484:** +```bash +podman pod ps # pod running? +podman port o11v4-pod # ports mapped? +``` + +**License server issues:** +```bash +podman exec licserver pm2 logs licserver --lines 50 +podman exec o11v4-app cat /etc/hosts +``` + +**Full reset (Windows):** +```powershell +.\pod-stop.ps1 -RemoveImage +.\pod-start.ps1 +``` + +**Full reset (macOS / Linux):** +```bash +./pod-stop.sh --remove-image +./pod-start.sh +``` + +--- + +## 7. How It Works + +``` + o11v4-pod (shared network namespace) + ┌──────────────────────────────────────────────────┐ + │ │ + │ licserver container o11v4-app container │ + │ ┌──────────────┐ ┌──────────────────┐ │ + │ │ server.js │ │ o11v4 binary │ │ + │ │ PM2 │◄────────│ watchdog loop │ │ + │ │ :80 :443 │ license │ :8484 main app │ │ + │ │ :5454 │ check │ FFmpeg │ │ + │ │ lic.cr │ via │ tmpfs: hls/, dl/ │ │ + │ └──────────────┘ localhost└──────────────────┘ │ + │ │ + │ /etc/hosts: │ + │ 127.0.0.1 lic.cryptolive.one │ + │ 127.0.0.1 lic.bitmaster.cc │ + └──────────────────────────────────────────────────┘ + │ + └── localhost:8484 → your browser +``` + +Both containers share `localhost`. The pod's `--add-host` entries +redirect licensing domains to `127.0.0.1`, so o11v4's license check +hits the licserver container. The licserver serves `lic.cr` and the +check passes. diff --git a/README.md b/README.md new file mode 100644 index 0000000..72c0f6b --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# o11v4-podman + +Run o11v4 in a containerized environment using Podman. + +This project packages o11v4 inside a Podman pod, letting you run it on devices that aren't really supported, such as **Windows** and **macOS**, while keeping everything isolated and secure. The container handles all Linux dependencies, so you don't need to set up a Linux environment yourself using something like WSL. + +## Getting Started + +See [PODMAN-SETUP.md](PODMAN-SETUP.md) for full instructions on how to install Podman and run o11v4 on your system. diff --git a/entrypoint-app.sh b/entrypoint-app.sh new file mode 100644 index 0000000..59faa39 --- /dev/null +++ b/entrypoint-app.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +WORK="/home/o11" +cd "$WORK" + +echo "[app] Waiting for license server to be ready..." +sleep 5 + +echo "[app] Starting o11v4 watchdog..." +while true; do + if ! pgrep -x "o11v4" > /dev/null 2>&1; then + echo "[app] Starting o11v4 on port 8484..." + "$WORK/o11v4" \ + -p 8484 \ + -noramfs \ + -f /usr/bin/ffmpeg \ + -path "$WORK/" \ + -noautostart \ + -plstreamname "%s [%p]" & + sleep 10 + fi + sleep 20 +done diff --git a/entrypoint-licserver.sh b/entrypoint-licserver.sh new file mode 100644 index 0000000..6022cdd --- /dev/null +++ b/entrypoint-licserver.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -e + +WORK="/home/o11" +cd "$WORK" + +IP="${SERVER_IP:-127.0.0.1}" +echo "[licserver] Using IP address: $IP" + +# ── Patch server.js with the correct IP ───────────────────────────────────── +sed -i "s|const ipAddress = '';|const ipAddress = '${IP}';|" "$WORK/server.js" + +# ── Generate self-signed TLS certs (if missing) ───────────────────────────── +if [ ! -f "$WORK/certs/key.pem" ] || [ ! -f "$WORK/certs/cert.pem" ]; then + echo "[licserver] Generating self-signed TLS certificates..." + openssl req -x509 -newkey rsa:2048 \ + -keyout "$WORK/certs/key.pem" \ + -out "$WORK/certs/cert.pem" \ + -days 365 -nodes \ + -subj "/CN=localhost" 2>/dev/null + echo "[licserver] Certificates generated." +fi + +# ── Start the license server (foreground — keeps container alive) ──────────── +echo "[licserver] Starting license server on ports 80, 443, 5454..." +exec pm2-runtime start "$WORK/server.js" --name licserver diff --git a/example.py b/example.py new file mode 100644 index 0000000..68bb278 --- /dev/null +++ b/example.py @@ -0,0 +1,183 @@ +#!/usr/bin/python3 +import sys +import os +import base64 +import json +import requests +import jwt +import logging +from logging.handlers import RotatingFileHandler # Import RotatingFileHandler +from typing import Any, Dict, Optional + +# Utility function for parameter parsing +def parse_params(params: list[str], name: str, default: str = "") -> str: + return next((p.split('=')[1] for p in params if p.startswith(f"{name}=")), default) + +# Fetch parameters from sys.argv +user = parse_params(sys.argv, 'user') +password = parse_params(sys.argv, 'password') +id = parse_params(sys.argv, 'id') +action = parse_params(sys.argv, 'action') +pssh = parse_params(sys.argv, 'pssh') + +# Dynamically set the log filename based on the 'id' parameter + +log_filename = f"{id}.log" if id else f"default.log" +# Setup logging to a dynamically named file +logging.basicConfig( + level=logging.DEBUG, # Capture all log messages + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + RotatingFileHandler(log_filename, maxBytes=5*1024*1024, backupCount=3), + logging.StreamHandler() + ] +) + +# Constants for URLs +CHANNELS_API_URL = 'https://cbd46b77.cdn.cms.movetv.com/cms/publish3/domain/summary/ums/1.json' +PLAYBACK_API_URL = 'https://cbd46b77.cdn.cms.movetv.com/playermetadata/sling/v1/api/channels/{id}/schedule/now/playback_info.qvt' + +# API headers +api_headers = {"ccdodlad-api-key": "bluechip_studio_696223755_api_and_extension_unlimited"} + +# CDM process +def do_cdm(pssh: str, id: str) -> Optional[Dict[str, Any]]: + try: + # Use f-string for URL formatting + sling_key_api = f"id={id}&pssh={pssh}" + + # Make the request + response = requests.get(sling_key_api) + response.raise_for_status() # Raise an error for bad HTTP status codes + + try: + key_data = response.json() # Attempt to parse JSON + except ValueError: + logging.error(f"Invalid JSON response: {response.content}") + return None + + # Check if 'status' is present in the response and handle 'false' status + if key_data.get('status') == 'false': + logging.error("Failed to Grab Key: Status is false") + return None + + # Return the 'keys' field from the response if present + keys = key_data.get('keys', None) + if keys is None or not keys: # Check if keys is None or an empty list + logging.error("Keys are empty or not present") + return None + + list = "\n".join(keys) + + print(list) + + except requests.exceptions.RequestException as e: + logging.error(f"RequestException: {e}") + except Exception as e: + logging.error(f"Unexpected error in do_cdm: {e}") + + return None + +# Function to process channels and fetch playback manifest +def process_channel(id: str) -> Optional[str]: + try: + response = requests.get(PLAYBACK_API_URL.format(id=id)) + response.raise_for_status() + + data = response.json() + dash_manifest_url = data.get('playback_info', {}).get('dash_manifest_url') + + if dash_manifest_url: + return dash_manifest_url + else: + logging.error("dash_manifest_url not found in the response") + return None + + except requests.exceptions.RequestException as e: + logging.error(f"API request error: {e}") + except KeyError as e: + logging.error(f"KeyError: {e}") + except Exception as e: + logging.error(f"Unexpected error: {e}") + + return None + +# Function to handle the channels action +def handle_channels() -> None: + output = {"Channels": []} + + try: + response = requests.get(CHANNELS_API_URL).json() + channels = response.get('channels', []) + + for channel_data in channels: + channelid = str(channel_data['channel_guid']) + + channel_info = { + "Name": channel_data['metadata']['channel_name'], + "Mode": "live", + "SessionManifest": True, + "ManifestScript": f'id={channelid}', + "CdmType": "widevine", + "UseCdm": True, + "Cdm": f'id={channelid}', + "Video": "best", + "OnDemand": True, + "SpeedUp": True, + "CdmMode": "external" + } + + output['Channels'].append(channel_info) + + print(json.dumps(output, indent=2)) + + except (KeyError, ValueError, requests.RequestException) as e: + logging.error(f"Error while handling channels: {e}") + +# Function to handle the manifest action +def handle_manifest(id: str) -> None: + try: + manifest_url = process_channel(id) + output = { + "ManifestUrl": manifest_url, + "Headers": { + "Manifest": { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + }, + "Media": { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' + } + }, + "Heartbeat": { + "Url": '', + "Params": '', + "PeriodMs": 5 * 60 * 1000 + } + } + print(json.dumps(output, indent=4)) + + except Exception as e: + logging.error(f"Error while handling manifest: {e}") + + +def fix_base64(encoded_str): + # Add necessary padding if needed + missing_padding = len(encoded_str) % 4 + if missing_padding: + encoded_str += '=' * (4 - missing_padding) # Add '=' padding + + return encoded_str + +# Main control flow +logging.error("Running with action : " + action) +if action == "cdm": + logging.error("Requesting keys") + do_cdm(pssh, id) + +elif action == "channels": + logging.error("Reloading channels") + handle_channels() + +elif action == "manifest": + logging.error("Requesting manifest") + handle_manifest(id) diff --git a/lic.cr b/lic.cr new file mode 100644 index 0000000..d68a417 Binary files /dev/null and b/lic.cr differ diff --git a/o11.cfg b/o11.cfg new file mode 100644 index 0000000..397adce --- /dev/null +++ b/o11.cfg @@ -0,0 +1,15 @@ +{ + "EpgUrl": "", + "Server": "", + "Users": [ + { + "Username": "admin", + "Password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918", + "Network": "", + "IsAdmin": true, + "HasWebAccess": true, + "ProviderIds": [] + } + ], + "Servers": null +} diff --git a/o11v4 b/o11v4 new file mode 100644 index 0000000..0ed5139 Binary files /dev/null and b/o11v4 differ diff --git a/pod-start.ps1 b/pod-start.ps1 new file mode 100644 index 0000000..c3f33f9 --- /dev/null +++ b/pod-start.ps1 @@ -0,0 +1,96 @@ +############################################################################### +# pod-start.ps1 — Build image, create pod, start containers +# +# Usage: .\pod-start.ps1 +# .\pod-start.ps1 -ServerIP 192.168.1.50 +# .\pod-start.ps1 -HlsSize 4g -DlSize 4g +# +# Requires: Podman installed, Podman Machine running +############################################################################### + +param( + [string]$ServerIP = "127.0.0.1", + [string]$HlsSize = "2g", + [string]$DlSize = "2g", + [string]$ImageName = "o11v4", + [string]$PodName = "o11v4-pod" +) + +$ErrorActionPreference = "Stop" + +Write-Host "" +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " o11v4 Podman Pod Setup" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" + +# ── Clean up any existing pod ──────────────────────────────────────────────── +podman pod exists $PodName 2>$null +if ($LASTEXITCODE -eq 0) { + Write-Host "Removing existing pod '$PodName'..." -ForegroundColor Yellow + podman pod stop $PodName 2>$null + podman pod rm -f $PodName 2>$null +} + +# ── Build the image ───────────────────────────────────────────────────────── +Write-Host "" +Write-Host "[1/4] Building image '$ImageName'..." -ForegroundColor Green +podman build -t $ImageName . +Write-Host " Image built successfully." -ForegroundColor Green + +# ── Create the pod ────────────────────────────────────────────────────────── +# All containers in the pod share a network namespace (localhost). +# --add-host redirects licensing domains to the license server. +Write-Host "" +Write-Host "[2/4] Creating pod '$PodName'..." -ForegroundColor Green +podman pod create ` + --name $PodName ` + --add-host "lic.cryptolive.one:$ServerIP" ` + --add-host "lic.bitmaster.cc:$ServerIP" ` + -p 8484:8484 ` + -p 8080:80 ` + -p 8443:443 ` + -p 5454:5454 +Write-Host " Pod created." -ForegroundColor Green + +# ── Start the license server container ────────────────────────────────────── +Write-Host "" +Write-Host "[3/4] Starting license server container..." -ForegroundColor Green +podman run -d ` + --pod $PodName ` + --name licserver ` + -e "SERVER_IP=$ServerIP" ` + $ImageName ` + /entrypoint-licserver.sh +Write-Host " License server started." -ForegroundColor Green + +# ── Start the o11v4 app container ─────────────────────────────────────────── +Write-Host "" +Write-Host "[4/4] Starting o11v4 app container..." -ForegroundColor Green +podman run -d ` + --pod $PodName ` + --name o11v4-app ` + -e "SERVER_IP=$ServerIP" ` + --tmpfs "/home/o11/hls:size=$HlsSize,mode=1777" ` + --tmpfs "/home/o11/dl:size=$DlSize,mode=1777" ` + $ImageName ` + /entrypoint-app.sh +Write-Host " o11v4 app started." -ForegroundColor Green + +# ── Done ──────────────────────────────────────────────────────────────────── +Write-Host "" +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " All running!" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan +Write-Host "" +Write-Host " App: http://localhost:8484" -ForegroundColor White +Write-Host " Login: admin / admin" -ForegroundColor White +Write-Host "" +Write-Host " Useful commands:" -ForegroundColor Gray +Write-Host " podman pod ps # pod status" -ForegroundColor Gray +Write-Host " podman ps --pod # all containers" -ForegroundColor Gray +Write-Host " podman logs -f licserver # license server logs" -ForegroundColor Gray +Write-Host " podman logs -f o11v4-app # app logs" -ForegroundColor Gray +Write-Host " podman exec -it o11v4-app bash # shell into app" -ForegroundColor Gray +Write-Host " .\pod-stop.ps1 # stop everything" -ForegroundColor Gray +Write-Host "" diff --git a/pod-start.sh b/pod-start.sh new file mode 100644 index 0000000..8294a25 --- /dev/null +++ b/pod-start.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +############################################################################### +# pod-start.sh — Build image, create pod, start containers +# +# Usage: ./pod-start.sh +# ./pod-start.sh --server-ip 192.168.1.50 +# ./pod-start.sh --hls-size 4g --dl-size 4g +# +# Requires: Podman installed and running +############################################################################### + +set -euo pipefail + +SERVER_IP="127.0.0.1" +HLS_SIZE="2g" +DL_SIZE="2g" +IMAGE_NAME="o11v4" +POD_NAME="o11v4-pod" + +while [[ $# -gt 0 ]]; do + case "$1" in + --server-ip) SERVER_IP="$2"; shift 2 ;; + --hls-size) HLS_SIZE="$2"; shift 2 ;; + --dl-size) DL_SIZE="$2"; shift 2 ;; + --image) IMAGE_NAME="$2"; shift 2 ;; + --pod) POD_NAME="$2"; shift 2 ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +echo "" +echo "========================================" +echo " o11v4 Podman Pod Setup" +echo "========================================" +echo "" + +# ── Clean up any existing pod ──────────────────────────────────────────────── +if podman pod exists "$POD_NAME" 2>/dev/null; then + echo "Removing existing pod '$POD_NAME'..." + podman pod stop "$POD_NAME" 2>/dev/null || true + podman pod rm -f "$POD_NAME" 2>/dev/null || true +fi + +# ── Build the image ───────────────────────────────────────────────────────── +echo "" +echo "[1/4] Building image '$IMAGE_NAME'..." +podman build -t "$IMAGE_NAME" . +echo " Image built successfully." + +# ── Create the pod ────────────────────────────────────────────────────────── +echo "" +echo "[2/4] Creating pod '$POD_NAME'..." +podman pod create \ + --name "$POD_NAME" \ + --add-host "lic.cryptolive.one:$SERVER_IP" \ + --add-host "lic.bitmaster.cc:$SERVER_IP" \ + -p 8484:8484 \ + -p 8080:80 \ + -p 8443:443 \ + -p 5454:5454 +echo " Pod created." + +# ── Start the license server container ────────────────────────────────────── +echo "" +echo "[3/4] Starting license server container..." +podman run -d \ + --pod "$POD_NAME" \ + --name licserver \ + -e "SERVER_IP=$SERVER_IP" \ + "$IMAGE_NAME" \ + /entrypoint-licserver.sh +echo " License server started." + +# ── Start the o11v4 app container ─────────────────────────────────────────── +echo "" +echo "[4/4] Starting o11v4 app container..." +podman run -d \ + --pod "$POD_NAME" \ + --name o11v4-app \ + -e "SERVER_IP=$SERVER_IP" \ + --tmpfs "/home/o11/hls:size=$HLS_SIZE,mode=1777" \ + --tmpfs "/home/o11/dl:size=$DL_SIZE,mode=1777" \ + "$IMAGE_NAME" \ + /entrypoint-app.sh +echo " o11v4 app started." + +# ── Done ──────────────────────────────────────────────────────────────────── +echo "" +echo "========================================" +echo " All running!" +echo "========================================" +echo "" +echo " App: http://localhost:8484" +echo " Login: admin / admin" +echo "" +echo " Useful commands:" +echo " podman pod ps # pod status" +echo " podman ps --pod # all containers" +echo " podman logs -f licserver # license server logs" +echo " podman logs -f o11v4-app # app logs" +echo " podman exec -it o11v4-app bash # shell into app" +echo " ./pod-stop.sh # stop everything" +echo "" diff --git a/pod-stop.ps1 b/pod-stop.ps1 new file mode 100644 index 0000000..ab0b822 --- /dev/null +++ b/pod-stop.ps1 @@ -0,0 +1,37 @@ +############################################################################### +# pod-stop.ps1 — Stop and remove the o11v4 pod and all its containers +# +# Usage: .\pod-stop.ps1 +# .\pod-stop.ps1 -PodName my-pod +# .\pod-stop.ps1 -RemoveImage # also delete the built image +############################################################################### + +param( + [string]$PodName = "o11v4-pod", + [string]$ImageName = "o11v4", + [switch]$RemoveImage +) + +$ErrorActionPreference = "SilentlyContinue" + +Write-Host "" +Write-Host "Stopping pod '$PodName'..." -ForegroundColor Yellow + +# ── Stop and remove the pod (takes all containers with it) ────────────────── +podman pod stop $PodName 2>$null +podman pod rm -f $PodName 2>$null + +if ($LASTEXITCODE -eq 0 -or $true) { + Write-Host "Pod stopped and removed." -ForegroundColor Green +} + +# ── Optionally remove the image ───────────────────────────────────────────── +if ($RemoveImage) { + Write-Host "Removing image '$ImageName'..." -ForegroundColor Yellow + podman rmi $ImageName 2>$null + Write-Host "Image removed." -ForegroundColor Green +} + +Write-Host "" +Write-Host "Done. To start again: .\pod-start.ps1" -ForegroundColor Cyan +Write-Host "" diff --git a/pod-stop.sh b/pod-stop.sh new file mode 100644 index 0000000..4065cfa --- /dev/null +++ b/pod-stop.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +############################################################################### +# pod-stop.sh — Stop and remove the o11v4 pod and all its containers +# +# Usage: ./pod-stop.sh +# ./pod-stop.sh --pod my-pod +# ./pod-stop.sh --remove-image # also delete the built image +############################################################################### + +set -euo pipefail + +POD_NAME="o11v4-pod" +IMAGE_NAME="o11v4" +REMOVE_IMAGE=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --pod) POD_NAME="$2"; shift 2 ;; + --image) IMAGE_NAME="$2"; shift 2 ;; + --remove-image) REMOVE_IMAGE=true; shift ;; + *) echo "Unknown option: $1"; exit 1 ;; + esac +done + +echo "" +echo "Stopping pod '$POD_NAME'..." + +# ── Stop and remove the pod (takes all containers with it) ────────────────── +podman pod stop "$POD_NAME" 2>/dev/null || true +podman pod rm -f "$POD_NAME" 2>/dev/null || true + +echo "Pod stopped and removed." + +# ── Optionally remove the image ───────────────────────────────────────────── +if [ "$REMOVE_IMAGE" = true ]; then + echo "Removing image '$IMAGE_NAME'..." + podman rmi "$IMAGE_NAME" 2>/dev/null || true + echo "Image removed." +fi + +echo "" +echo "Done. To start again: ./pod-start.sh" +echo "" diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..de56b4d --- /dev/null +++ b/run.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Remove old /home/o11 entries from /etc/fstab +sed -i '/home\/o11/d' /etc/fstab +sleep 2 + +# Append new tmpfs entries to /etc/fstab +cat <> /etc/fstab + +tmpfs /home/o11/hls tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=70% 0 0 +tmpfs /home/o11/dl tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777,size=70% 0 0 +EOL + +# Mount all entries in /etc/fstab +mount -av + +# Infinite loop to check if o11 is running +while true; do + if ! pgrep "o11v4" > /dev/null; then + # Start the o11 process + /home/o11/o11v4 -p 8484 -noramfs -f /usr/local/bin/ffmpeg -path "/home/o11/" -noautostart -plstreamname "%s [%p]" & + + # Wait before checking again to give the process time to start + sleep 10 + fi + + # Wait before checking again + sleep 20 +done diff --git a/server.js b/server.js new file mode 100644 index 0000000..2a18463 --- /dev/null +++ b/server.js @@ -0,0 +1,112 @@ +const https = require('https'); +const http = require('http'); +const express = require('express'); +const fs = require('fs'); +const path = require('path'); +const { exec } = require('child_process'); + +const hostspath = '/etc/hosts'; +const domain = 'lic.cryptolive.one'; +const domain2 = 'lic.bitmaster.cc'; +const ipAddress = ''; +const portHttp = 80; +const portHttps = 443; + +// Start Express Server +const app = express(); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +app.use((req, res, next) => { + if (req.method === 'POST') { + console.log(`Received POST request to ${req.url}`); + console.log('Headers:', req.headers); + console.log('Body:', req.body); + } + next(); +}); + +app.use('/', express.static('www')); + +// Handle POST /lic requests +app.post('/lic', (req, res) => { + console.log('Received POST /lic request'); + + // Define the file path + const filePath = path.join(__dirname, 'lic.cr'); + + // Check if the file exists + if (!fs.existsSync(filePath)) { + console.error('File not found:', filePath); + return res.status(404).send('File not found'); + } + + // Send the file as a response + res.sendFile(filePath, (err) => { + if (err) { + console.error('Error sending file:', err); + res.status(500).send('Error sending file'); + } + }); +}); + + +// Generate self-signed certificates if they don't exist +const certPath = './certs'; +const keyFile = `${certPath}/key.pem`; +const certFile = `${certPath}/cert.pem`; + +if (!fs.existsSync(certPath)) { + fs.mkdirSync(certPath, { recursive: true }); // Ensure the directory exists +} + +if (!fs.existsSync(keyFile) || !fs.existsSync(certFile)) { + exec( + `openssl req -x509 -newkey rsa:2048 -keyout ${keyFile} -out ${certFile} -days 365 -nodes -subj "/CN=localhost"`, + { stdio: 'inherit' } // Show output in console + ); +} + +const options = { + key: fs.readFileSync(keyFile), + cert: fs.readFileSync(certFile) +}; + +// Start HTTP and HTTPS servers +[80, 5454].forEach(port => { + http.createServer(app).listen(port, () => { + console.log(`HTTP server running on port ${port}`); + }); +}); + +https.createServer(options, app).listen(portHttps, (err) => { + if (err) { + console.error(`Error starting HTTPS server: ${err.message}`); + } else{ + console.log(`HTTPS server running on port ${portHttps}`); + } +}); + +// Function to update the /etc/hosts file +function updateHosts() { + try { + let content = fs.readFileSync(hostspath, 'utf8').split('\n'); + + content = content.filter(line => !line.includes(domain)); + content.push(`${ipAddress} ${domain}`); + fs.writeFileSync(hostspath, content.join('\n')); + console.log(`Successfully mapped ${domain} to ${ipAddress} in ${hostspath}`); + + content = fs.readFileSync(hostspath, 'utf8').split('\n'); + content = content.filter(line => !line.includes(domain2)); + content.push(`${ipAddress} ${domain2}`); + fs.writeFileSync(hostspath, content.join('\n')); + console.log(`Successfully mapped ${domain2} to ${ipAddress} in ${hostspath}`); + + } catch (error) { + console.error(`Error updating hosts file: ${error.message}`); + } +} + +// Run tasks +updateHosts();