initial commit
This commit is contained in:
commit
3473119419
40
.containerignore
Normal file
40
.containerignore
Normal 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
59
Containerfile
Normal 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
255
PODMAN-SETUP.md
Normal 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
9
README.md
Normal 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
24
entrypoint-app.sh
Normal 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
26
entrypoint-licserver.sh
Normal 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
183
example.py
Normal 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)
|
||||
15
o11.cfg
Normal file
15
o11.cfg
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"EpgUrl": "",
|
||||
"Server": "",
|
||||
"Users": [
|
||||
{
|
||||
"Username": "admin",
|
||||
"Password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||
"Network": "",
|
||||
"IsAdmin": true,
|
||||
"HasWebAccess": true,
|
||||
"ProviderIds": []
|
||||
}
|
||||
],
|
||||
"Servers": null
|
||||
}
|
||||
96
pod-start.ps1
Normal file
96
pod-start.ps1
Normal 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
103
pod-start.sh
Normal 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
37
pod-stop.ps1
Normal 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
43
pod-stop.sh
Normal 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
29
run.sh
Normal 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
112
server.js
Normal 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();
|
||||
Loading…
Reference in New Issue
Block a user