initial commit

This commit is contained in:
spongebob 2026-02-22 17:02:56 -05:00
commit 3473119419
16 changed files with 1031 additions and 0 deletions

40
.containerignore Normal file
View File

@ -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

59
Containerfile Normal file
View File

@ -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

255
PODMAN-SETUP.md Normal file
View File

@ -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.

9
README.md Normal file
View File

@ -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.

24
entrypoint-app.sh Normal file
View File

@ -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

26
entrypoint-licserver.sh Normal file
View File

@ -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

183
example.py Normal file
View File

@ -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)

BIN
lic.cr Normal file

Binary file not shown.

15
o11.cfg Normal file
View File

@ -0,0 +1,15 @@
{
"EpgUrl": "",
"Server": "",
"Users": [
{
"Username": "admin",
"Password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
"Network": "",
"IsAdmin": true,
"HasWebAccess": true,
"ProviderIds": []
}
],
"Servers": null
}

BIN
o11v4 Normal file

Binary file not shown.

96
pod-start.ps1 Normal file
View File

@ -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 ""

103
pod-start.sh Normal file
View File

@ -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 ""

37
pod-stop.ps1 Normal file
View File

@ -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 ""

43
pod-stop.sh Normal file
View File

@ -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 ""

29
run.sh Normal file
View File

@ -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 <<EOL >> /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

112
server.js Normal file
View File

@ -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();