diff --git a/package/secubox/secubox-app-qbittorrent/Makefile b/package/secubox/secubox-app-qbittorrent/Makefile new file mode 100644 index 00000000..5788c842 --- /dev/null +++ b/package/secubox/secubox-app-qbittorrent/Makefile @@ -0,0 +1,31 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-qbittorrent +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-qbittorrent + SECTION:=secubox + CATEGORY:=SecuBox + TITLE:=qBittorrent - BitTorrent Client + DEPENDS:=+lxc +curl +tar +xz + PKGARCH:=all +endef + +define Package/secubox-app-qbittorrent/description + qBittorrent torrent client in Debian LXC container. + Features web UI, RSS, search plugins, and API. +endef + +define Package/secubox-app-qbittorrent/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/etc/config/qbittorrent $(1)/etc/config/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/etc/init.d/qbittorrent $(1)/etc/init.d/ + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/usr/sbin/qbittorrentctl $(1)/usr/sbin/ +endef + +$(eval $(call BuildPackage,secubox-app-qbittorrent)) diff --git a/package/secubox/secubox-app-qbittorrent/files/etc/config/qbittorrent b/package/secubox/secubox-app-qbittorrent/files/etc/config/qbittorrent new file mode 100644 index 00000000..da736b2b --- /dev/null +++ b/package/secubox/secubox-app-qbittorrent/files/etc/config/qbittorrent @@ -0,0 +1,10 @@ +config qbittorrent 'main' + option enabled '1' + option port '8090' + option data_dir '/srv/qbittorrent' + option download_dir '/srv/downloads/torrents' + option memory '536870912' + +config exposure 'exposure' + option domain 'torrent.gk2.secubox.in' + option ssl '1' diff --git a/package/secubox/secubox-app-qbittorrent/files/etc/init.d/qbittorrent b/package/secubox/secubox-app-qbittorrent/files/etc/init.d/qbittorrent new file mode 100755 index 00000000..cf1362e1 --- /dev/null +++ b/package/secubox/secubox-app-qbittorrent/files/etc/init.d/qbittorrent @@ -0,0 +1,22 @@ +#!/bin/sh /etc/rc.common + +START=95 +STOP=10 +USE_PROCD=1 + +start_service() { + [ "$(uci -q get qbittorrent.main.enabled)" = "1" ] || return 0 + qbittorrentctl start +} + +stop_service() { + qbittorrentctl stop +} + +restart() { + qbittorrentctl restart +} + +status() { + qbittorrentctl status +} diff --git a/package/secubox/secubox-app-qbittorrent/files/usr/sbin/qbittorrentctl b/package/secubox/secubox-app-qbittorrent/files/usr/sbin/qbittorrentctl new file mode 100755 index 00000000..182e2a4c --- /dev/null +++ b/package/secubox/secubox-app-qbittorrent/files/usr/sbin/qbittorrentctl @@ -0,0 +1,383 @@ +#!/bin/sh +# ═══════════════════════════════════════════════════════════════════════════════ +# qBittorrent Controller - BitTorrent Client +# LXC container with Debian rootfs (no Docker/Podman) +# ═══════════════════════════════════════════════════════════════════════════════ + +CONTAINER_NAME="qbittorrent" +CONTAINER_DIR="/srv/lxc/$CONTAINER_NAME" +DATA_DIR="/srv/qbittorrent" +DOWNLOAD_DIR="/srv/downloads/torrents" +CONFIG="qbittorrent" + +# Debian LXC rootfs URL (arm64) +DEBIAN_LXC_MIRROR="https://images.linuxcontainers.org/images/debian/bookworm/arm64/default" + +# Logging +log_info() { logger -t qbittorrent -p user.info "$*"; echo "[INFO] $*"; } +log_error() { logger -t qbittorrent -p user.error "$*"; echo "[ERROR] $*" >&2; } +log_ok() { echo "[OK] $*"; } + +# UCI helpers +uci_get() { uci -q get "$CONFIG.$1"; } + +# ───────────────────────────────────────────────────────────────────────────────── +# Install container with Debian rootfs +# ───────────────────────────────────────────────────────────────────────────────── +cmd_install() { + log_info "Installing qBittorrent container (Debian LXC)..." + + # Create directories + mkdir -p "$CONTAINER_DIR/rootfs" + mkdir -p "$DATA_DIR/config" + mkdir -p "$DOWNLOAD_DIR"/{complete,incomplete,watch} + + local rootfs="$CONTAINER_DIR/rootfs" + + # Download Debian LXC rootfs if not exists + if [ ! -f "$rootfs/bin/bash" ]; then + log_info "Downloading Debian LXC rootfs..." + + # Get latest build date from LXC mirror + local latest=$(curl -sL "$DEBIAN_LXC_MIRROR/" | grep -oE '[0-9]{8}_[0-9]{2}:[0-9]{2}' | sort -r | head -1) + [ -z "$latest" ] && latest="20240301_05:24" + + local rootfs_url="${DEBIAN_LXC_MIRROR}/${latest}/rootfs.tar.xz" + local tarball="/tmp/debian-rootfs-qbt.tar.xz" + + log_info "Fetching: $rootfs_url" + curl -L -o "$tarball" "$rootfs_url" || { + log_error "Failed to download Debian rootfs" + return 1 + } + + log_info "Extracting rootfs..." + xz -d -c "$tarball" | tar -xf - -C "$rootfs" || { + log_error "Failed to extract rootfs" + rm -f "$tarball" + return 1 + } + rm -f "$tarball" + fi + + # Configure DNS + echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf" + echo "nameserver 1.1.1.1" >> "$rootfs/etc/resolv.conf" + + # Create startup script + cat > "$rootfs/start-qbittorrent.sh" <<'STARTEOF' +#!/bin/bash +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +export HOME="/root" +exec > /config/startup.log 2>&1 + +echo "=== Starting qBittorrent $(date) ===" + +# Install qBittorrent if not present +if ! command -v qbittorrent-nox >/dev/null 2>&1; then + echo "Installing qBittorrent..." + + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + qbittorrent-nox \ + ca-certificates || { echo "apt install failed"; exit 1; } + + apt-get clean + rm -rf /var/lib/apt/lists/* +fi + +echo "=== Starting qBittorrent server ===" +mkdir -p /config /downloads +cd /config + +# Run qBittorrent-nox +exec qbittorrent-nox --webui-port=8090 --profile=/config +STARTEOF + chmod +x "$rootfs/start-qbittorrent.sh" + + # Create LXC config + local memory=$(uci_get main.memory) + [ -z "$memory" ] && memory="536870912" # 512MB in bytes + + cat > "$CONTAINER_DIR/config" </dev/null | grep -q "RUNNING"; then + log_info "qBittorrent already running" + return 0 + fi + + log_info "Starting qBittorrent..." + lxc-start -n "$CONTAINER_NAME" -f "$CONTAINER_DIR/config" + + # Wait for startup + sleep 5 + if lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_ok "qBittorrent started on http://192.168.255.42:8090/" + log_info "Default login: admin / adminadmin" + else + log_error "Failed to start qBittorrent" + lxc-info -n "$CONTAINER_NAME" 2>&1 + return 1 + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Stop container +# ───────────────────────────────────────────────────────────────────────────────── +cmd_stop() { + if ! lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_info "qBittorrent not running" + return 0 + fi + + log_info "Stopping qBittorrent..." + lxc-stop -n "$CONTAINER_NAME" -t 30 + log_ok "qBittorrent stopped" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Restart container +# ───────────────────────────────────────────────────────────────────────────────── +cmd_restart() { + cmd_stop + sleep 2 + cmd_start +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Status +# ───────────────────────────────────────────────────────────────────────────────── +cmd_status() { + echo "=== qBittorrent Status ===" + + # Container state + if lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + echo "Container: RUNNING" + echo "IP: 192.168.255.42" + else + echo "Container: STOPPED" + return 0 + fi + + # API status + local info=$(curl -s "http://192.168.255.42:8090/api/v2/app/version" 2>/dev/null) + if [ -n "$info" ]; then + echo "Version: $info" + fi + + local transfer=$(curl -s "http://192.168.255.42:8090/api/v2/transfer/info" 2>/dev/null) + if [ -n "$transfer" ]; then + local dl_speed=$(echo "$transfer" | jsonfilter -e '@.dl_info_speed' 2>/dev/null) + local up_speed=$(echo "$transfer" | jsonfilter -e '@.up_info_speed' 2>/dev/null) + [ -n "$dl_speed" ] && echo "Download: $(($dl_speed / 1024)) KB/s" + [ -n "$up_speed" ] && echo "Upload: $(($up_speed / 1024)) KB/s" + fi + + echo "Web UI: http://192.168.255.42:8090/" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Logs +# ───────────────────────────────────────────────────────────────────────────────── +cmd_logs() { + local lines="${1:-50}" + if [ -f "$DATA_DIR/config/startup.log" ]; then + tail -n "$lines" "$DATA_DIR/config/startup.log" + else + log_info "No logs yet." + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Shell access +# ───────────────────────────────────────────────────────────────────────────────── +cmd_shell() { + if ! lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_error "Container not running" + return 1 + fi + lxc-attach -n "$CONTAINER_NAME" -- /bin/bash +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Add torrent +# ───────────────────────────────────────────────────────────────────────────────── +cmd_add() { + local url="$1" + [ -z "$url" ] && { log_error "Usage: qbittorrentctl add "; return 1; } + + local result=$(curl -s -X POST "http://192.168.255.42:8090/api/v2/torrents/add" \ + -d "urls=$url" 2>/dev/null) + + if [ "$result" = "Ok." ]; then + log_ok "Torrent added" + else + log_error "Failed to add torrent: $result" + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# List torrents +# ───────────────────────────────────────────────────────────────────────────────── +cmd_list() { + local torrents=$(curl -s "http://192.168.255.42:8090/api/v2/torrents/info" 2>/dev/null) + + if [ -n "$torrents" ]; then + echo "$torrents" | python3 -c " +import sys, json +try: + data = json.load(sys.stdin) + for i, t in enumerate(data[:20], 1): + name = t.get('name', 'N/A')[:50] + progress = t.get('progress', 0) * 100 + state = t.get('state', 'N/A') + print(f'{i}. [{state}] {name} ({progress:.1f}%)') +except: + pass +" 2>/dev/null + else + log_info "No torrents or API not accessible" + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Configure HAProxy exposure +# ───────────────────────────────────────────────────────────────────────────────── +cmd_configure_haproxy() { + local domain=$(uci_get exposure.domain) + [ -z "$domain" ] && domain="torrent.gk2.secubox.in" + + log_info "Configuring HAProxy for $domain" + + # Create backend + uci set haproxy.qbittorrent_web=backend + uci set haproxy.qbittorrent_web.name='qbittorrent_web' + uci set haproxy.qbittorrent_web.mode='http' + uci set haproxy.qbittorrent_web.server="qbittorrent 192.168.255.42:8090 weight 100 check" + + # Create vhost + local vhost_id=$(echo "$domain" | tr '.' '_') + uci set "haproxy.$vhost_id=vhost" + uci set "haproxy.$vhost_id.domain=$domain" + uci set "haproxy.$vhost_id.backend=mitmproxy_inspector" + uci set "haproxy.$vhost_id.original_backend=qbittorrent_web" + uci set "haproxy.$vhost_id.ssl=1" + uci set "haproxy.$vhost_id.ssl_redirect=1" + uci set "haproxy.$vhost_id.acme=1" + uci commit haproxy + + # Add mitmproxy route + if [ -f /srv/mitmproxy/haproxy-routes.json ]; then + python3 -c " +import json +with open('/srv/mitmproxy/haproxy-routes.json') as f: + routes = json.load(f) +routes['$domain'] = ['192.168.255.42', 8090] +with open('/srv/mitmproxy/haproxy-routes.json', 'w') as f: + json.dump(routes, f, indent=2) +" 2>/dev/null + fi + + # Reload + haproxyctl reload 2>/dev/null || true + /etc/init.d/mitmproxy restart 2>/dev/null || true + + log_ok "HAProxy configured: https://$domain/" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Uninstall +# ───────────────────────────────────────────────────────────────────────────────── +cmd_uninstall() { + log_info "Uninstalling qBittorrent..." + + cmd_stop 2>/dev/null + + rm -rf "$CONTAINER_DIR" + log_info "Container removed. Data preserved in $DATA_DIR" + + log_ok "qBittorrent uninstalled" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Main +# ───────────────────────────────────────────────────────────────────────────────── +case "$1" in + install) cmd_install ;; + start) cmd_start ;; + stop) cmd_stop ;; + restart) cmd_restart ;; + status) cmd_status ;; + logs) shift; cmd_logs "$@" ;; + shell) cmd_shell ;; + add) shift; cmd_add "$@" ;; + list) cmd_list ;; + configure-haproxy) cmd_configure_haproxy ;; + uninstall) cmd_uninstall ;; + *) + echo "qBittorrent Controller - BitTorrent Client" + echo "" + echo "Usage: qbittorrentctl " + echo "" + echo "Commands:" + echo " install Install Debian LXC container with qBittorrent" + echo " start Start qBittorrent" + echo " stop Stop qBittorrent" + echo " restart Restart qBittorrent" + echo " status Show status and transfer info" + echo " logs [n] Show last n log lines (default 50)" + echo " shell Interactive shell in container" + echo " add Add torrent by magnet/URL" + echo " list List active torrents" + echo " configure-haproxy Setup HAProxy reverse proxy" + echo " uninstall Remove container (keeps data)" + ;; +esac diff --git a/package/secubox/secubox-app-webtorrent/Makefile b/package/secubox/secubox-app-webtorrent/Makefile new file mode 100644 index 00000000..672c9f46 --- /dev/null +++ b/package/secubox/secubox-app-webtorrent/Makefile @@ -0,0 +1,31 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-webtorrent +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-webtorrent + SECTION:=secubox + CATEGORY:=SecuBox + TITLE:=WebTorrent - Browser Torrent Streaming + DEPENDS:=+lxc +curl +tar +xz + PKGARCH:=all +endef + +define Package/secubox-app-webtorrent/description + WebTorrent streaming server in Debian LXC container. + Stream torrents directly in the browser via WebRTC. +endef + +define Package/secubox-app-webtorrent/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/etc/config/webtorrent $(1)/etc/config/ + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/etc/init.d/webtorrent $(1)/etc/init.d/ + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/usr/sbin/webtorrentctl $(1)/usr/sbin/ +endef + +$(eval $(call BuildPackage,secubox-app-webtorrent)) diff --git a/package/secubox/secubox-app-webtorrent/files/etc/config/webtorrent b/package/secubox/secubox-app-webtorrent/files/etc/config/webtorrent new file mode 100644 index 00000000..82317084 --- /dev/null +++ b/package/secubox/secubox-app-webtorrent/files/etc/config/webtorrent @@ -0,0 +1,10 @@ +config webtorrent 'main' + option enabled '1' + option port '8095' + option data_dir '/srv/webtorrent' + option download_dir '/srv/downloads/webtorrent' + option memory '268435456' + +config exposure 'exposure' + option domain 'stream.gk2.secubox.in' + option ssl '1' diff --git a/package/secubox/secubox-app-webtorrent/files/etc/init.d/webtorrent b/package/secubox/secubox-app-webtorrent/files/etc/init.d/webtorrent new file mode 100755 index 00000000..5104aea1 --- /dev/null +++ b/package/secubox/secubox-app-webtorrent/files/etc/init.d/webtorrent @@ -0,0 +1,22 @@ +#!/bin/sh /etc/rc.common + +START=95 +STOP=10 +USE_PROCD=1 + +start_service() { + [ "$(uci -q get webtorrent.main.enabled)" = "1" ] || return 0 + webtorrentctl start +} + +stop_service() { + webtorrentctl stop +} + +restart() { + webtorrentctl restart +} + +status() { + webtorrentctl status +} diff --git a/package/secubox/secubox-app-webtorrent/files/usr/sbin/webtorrentctl b/package/secubox/secubox-app-webtorrent/files/usr/sbin/webtorrentctl new file mode 100755 index 00000000..50cfd78c --- /dev/null +++ b/package/secubox/secubox-app-webtorrent/files/usr/sbin/webtorrentctl @@ -0,0 +1,579 @@ +#!/bin/sh +# ═══════════════════════════════════════════════════════════════════════════════ +# WebTorrent Controller - Browser Torrent Streaming +# LXC container with Debian rootfs (no Docker/Podman) +# ═══════════════════════════════════════════════════════════════════════════════ + +CONTAINER_NAME="webtorrent" +CONTAINER_DIR="/srv/lxc/$CONTAINER_NAME" +DATA_DIR="/srv/webtorrent" +DOWNLOAD_DIR="/srv/downloads/webtorrent" +CONFIG="webtorrent" + +# Debian LXC rootfs URL (arm64) +DEBIAN_LXC_MIRROR="https://images.linuxcontainers.org/images/debian/bookworm/arm64/default" + +# Logging +log_info() { logger -t webtorrent -p user.info "$*"; echo "[INFO] $*"; } +log_error() { logger -t webtorrent -p user.error "$*"; echo "[ERROR] $*" >&2; } +log_ok() { echo "[OK] $*"; } + +# UCI helpers +uci_get() { uci -q get "$CONFIG.$1"; } + +# ───────────────────────────────────────────────────────────────────────────────── +# Install container with Debian rootfs +# ───────────────────────────────────────────────────────────────────────────────── +cmd_install() { + log_info "Installing WebTorrent container (Debian LXC)..." + + # Create directories + mkdir -p "$CONTAINER_DIR/rootfs" + mkdir -p "$DATA_DIR/config" + mkdir -p "$DOWNLOAD_DIR" + + local rootfs="$CONTAINER_DIR/rootfs" + + # Download Debian LXC rootfs if not exists + if [ ! -f "$rootfs/bin/bash" ]; then + log_info "Downloading Debian LXC rootfs..." + + # Get latest build date from LXC mirror + local latest=$(curl -sL "$DEBIAN_LXC_MIRROR/" | grep -oE '[0-9]{8}_[0-9]{2}:[0-9]{2}' | sort -r | head -1) + [ -z "$latest" ] && latest="20240301_05:24" + + local rootfs_url="${DEBIAN_LXC_MIRROR}/${latest}/rootfs.tar.xz" + local tarball="/tmp/debian-rootfs-webtorrent.tar.xz" + + log_info "Fetching: $rootfs_url" + curl -L -o "$tarball" "$rootfs_url" || { + log_error "Failed to download Debian rootfs" + return 1 + } + + log_info "Extracting rootfs..." + xz -d -c "$tarball" | tar -xf - -C "$rootfs" || { + log_error "Failed to extract rootfs" + rm -f "$tarball" + return 1 + } + rm -f "$tarball" + fi + + # Configure DNS + echo "nameserver 8.8.8.8" > "$rootfs/etc/resolv.conf" + echo "nameserver 1.1.1.1" >> "$rootfs/etc/resolv.conf" + + # Create startup script + cat > "$rootfs/start-webtorrent.sh" <<'STARTEOF' +#!/bin/bash +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +export HOME="/root" +exec > /config/startup.log 2>&1 + +echo "=== Starting WebTorrent $(date) ===" + +# Install Node.js and WebTorrent if not present +if ! command -v node >/dev/null 2>&1; then + echo "Installing Node.js..." + + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + nodejs \ + npm \ + ca-certificates || { echo "apt install failed"; exit 1; } + + apt-get clean + rm -rf /var/lib/apt/lists/* +fi + +# Install webtorrent-hybrid and instant.io server +if [ ! -d "/opt/webtorrent/node_modules" ]; then + echo "Installing WebTorrent packages..." + mkdir -p /opt/webtorrent + cd /opt/webtorrent + + # Create package.json + cat > package.json << 'PKGEOF' +{ + "name": "webtorrent-server", + "version": "1.0.0", + "dependencies": { + "webtorrent": "^2.0.0", + "express": "^4.18.0", + "cors": "^2.8.5" + } +} +PKGEOF + + npm install --production || { echo "npm install failed"; exit 1; } + + # Create server script + cat > server.js << 'SRVEOF' +const WebTorrent = require('webtorrent'); +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const fs = require('fs'); + +const app = express(); +const client = new WebTorrent(); +const PORT = 8095; +const DOWNLOAD_PATH = '/downloads'; + +app.use(cors()); +app.use(express.json()); +app.use('/downloads', express.static(DOWNLOAD_PATH)); + +// Serve web UI +app.get('/', (req, res) => { + res.send(` + + + WebTorrent Streaming + + + + +
+

🌊 WebTorrent Streaming

+
+ + +
+
+
+ + +`); +}); + +// API: List torrents +app.get('/api/torrents', (req, res) => { + const torrents = client.torrents.map(t => ({ + infoHash: t.infoHash, + name: t.name, + length: t.length, + downloaded: t.downloaded, + uploaded: t.uploaded, + downloadSpeed: t.downloadSpeed, + uploadSpeed: t.uploadSpeed, + progress: t.progress, + numPeers: t.numPeers, + files: t.files.map(f => ({ name: f.name, path: f.path, length: f.length })) + })); + res.json(torrents); +}); + +// API: Add torrent +app.post('/api/add', (req, res) => { + const { magnet } = req.body; + if (!magnet) return res.status(400).json({ error: 'Magnet required' }); + + client.add(magnet, { path: DOWNLOAD_PATH }, torrent => { + console.log('Added:', torrent.name); + }); + res.json({ success: true }); +}); + +// Stream file +app.get('/stream/:infoHash/*', (req, res) => { + const torrent = client.get(req.params.infoHash); + if (!torrent) return res.status(404).send('Torrent not found'); + + const filePath = req.params[0]; + const file = torrent.files.find(f => f.path === filePath); + if (!file) return res.status(404).send('File not found'); + + const range = req.headers.range; + if (range) { + const positions = range.replace(/bytes=/, '').split('-'); + const start = parseInt(positions[0], 10); + const end = positions[1] ? parseInt(positions[1], 10) : file.length - 1; + const chunksize = (end - start) + 1; + + res.writeHead(206, { + 'Content-Range': `bytes ${start}-${end}/${file.length}`, + 'Accept-Ranges': 'bytes', + 'Content-Length': chunksize, + 'Content-Type': 'video/mp4' + }); + file.createReadStream({ start, end }).pipe(res); + } else { + res.writeHead(200, { + 'Content-Length': file.length, + 'Content-Type': 'video/mp4' + }); + file.createReadStream().pipe(res); + } +}); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`WebTorrent server running on http://0.0.0.0:${PORT}`); +}); +SRVEOF +fi + +echo "=== Starting WebTorrent server ===" +mkdir -p /config /downloads +cd /opt/webtorrent +exec node server.js +STARTEOF + chmod +x "$rootfs/start-webtorrent.sh" + + # Create LXC config + local memory=$(uci_get main.memory) + [ -z "$memory" ] && memory="268435456" # 256MB in bytes + + cat > "$CONTAINER_DIR/config" </dev/null | grep -q "RUNNING"; then + log_info "WebTorrent already running" + return 0 + fi + + log_info "Starting WebTorrent..." + lxc-start -n "$CONTAINER_NAME" -f "$CONTAINER_DIR/config" + + # Wait for startup + sleep 5 + if lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_ok "WebTorrent started on http://192.168.255.43:8095/" + else + log_error "Failed to start WebTorrent" + lxc-info -n "$CONTAINER_NAME" 2>&1 + return 1 + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Stop container +# ───────────────────────────────────────────────────────────────────────────────── +cmd_stop() { + if ! lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_info "WebTorrent not running" + return 0 + fi + + log_info "Stopping WebTorrent..." + lxc-stop -n "$CONTAINER_NAME" -t 30 + log_ok "WebTorrent stopped" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Restart container +# ───────────────────────────────────────────────────────────────────────────────── +cmd_restart() { + cmd_stop + sleep 2 + cmd_start +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Status +# ───────────────────────────────────────────────────────────────────────────────── +cmd_status() { + echo "=== WebTorrent Status ===" + + # Container state + if lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + echo "Container: RUNNING" + echo "IP: 192.168.255.43" + else + echo "Container: STOPPED" + return 0 + fi + + # API status + local torrents=$(curl -s "http://192.168.255.43:8095/api/torrents" 2>/dev/null) + if [ -n "$torrents" ]; then + local count=$(echo "$torrents" | python3 -c "import sys,json; print(len(json.load(sys.stdin)))" 2>/dev/null) + echo "Active Torrents: ${count:-0}" + fi + + echo "Web UI: http://192.168.255.43:8095/" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Logs +# ───────────────────────────────────────────────────────────────────────────────── +cmd_logs() { + local lines="${1:-50}" + if [ -f "$DATA_DIR/config/startup.log" ]; then + tail -n "$lines" "$DATA_DIR/config/startup.log" + else + log_info "No logs yet." + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Shell access +# ───────────────────────────────────────────────────────────────────────────────── +cmd_shell() { + if ! lxc-info -n "$CONTAINER_NAME" 2>/dev/null | grep -q "RUNNING"; then + log_error "Container not running" + return 1 + fi + lxc-attach -n "$CONTAINER_NAME" -- /bin/bash +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Add torrent +# ───────────────────────────────────────────────────────────────────────────────── +cmd_add() { + local magnet="$1" + [ -z "$magnet" ] && { log_error "Usage: webtorrentctl add "; return 1; } + + local result=$(curl -s -X POST "http://192.168.255.43:8095/api/add" \ + -H "Content-Type: application/json" \ + -d "{\"magnet\":\"$magnet\"}" 2>/dev/null) + + if echo "$result" | grep -q "success"; then + log_ok "Torrent added" + else + log_error "Failed to add torrent" + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# List torrents +# ───────────────────────────────────────────────────────────────────────────────── +cmd_list() { + local torrents=$(curl -s "http://192.168.255.43:8095/api/torrents" 2>/dev/null) + + if [ -n "$torrents" ]; then + echo "$torrents" | python3 -c " +import sys, json +try: + data = json.load(sys.stdin) + if not data: + print('No torrents') + for i, t in enumerate(data, 1): + name = t.get('name', 'N/A')[:50] + progress = t.get('progress', 0) * 100 + peers = t.get('numPeers', 0) + print(f'{i}. {name} ({progress:.1f}%) - {peers} peers') +except: + pass +" 2>/dev/null + else + log_info "No torrents or server not running" + fi +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Configure HAProxy exposure +# ───────────────────────────────────────────────────────────────────────────────── +cmd_configure_haproxy() { + local domain=$(uci_get exposure.domain) + [ -z "$domain" ] && domain="stream.gk2.secubox.in" + + log_info "Configuring HAProxy for $domain" + + # Create backend + uci set haproxy.webtorrent_web=backend + uci set haproxy.webtorrent_web.name='webtorrent_web' + uci set haproxy.webtorrent_web.mode='http' + uci set haproxy.webtorrent_web.server="webtorrent 192.168.255.43:8095 weight 100 check" + + # Create vhost + local vhost_id=$(echo "$domain" | tr '.' '_') + uci set "haproxy.$vhost_id=vhost" + uci set "haproxy.$vhost_id.domain=$domain" + uci set "haproxy.$vhost_id.backend=mitmproxy_inspector" + uci set "haproxy.$vhost_id.original_backend=webtorrent_web" + uci set "haproxy.$vhost_id.ssl=1" + uci set "haproxy.$vhost_id.ssl_redirect=1" + uci set "haproxy.$vhost_id.acme=1" + uci commit haproxy + + # Add mitmproxy route + if [ -f /srv/mitmproxy/haproxy-routes.json ]; then + python3 -c " +import json +with open('/srv/mitmproxy/haproxy-routes.json') as f: + routes = json.load(f) +routes['$domain'] = ['192.168.255.43', 8095] +with open('/srv/mitmproxy/haproxy-routes.json', 'w') as f: + json.dump(routes, f, indent=2) +" 2>/dev/null + fi + + # Reload + haproxyctl reload 2>/dev/null || true + /etc/init.d/mitmproxy restart 2>/dev/null || true + + log_ok "HAProxy configured: https://$domain/" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Uninstall +# ───────────────────────────────────────────────────────────────────────────────── +cmd_uninstall() { + log_info "Uninstalling WebTorrent..." + + cmd_stop 2>/dev/null + + rm -rf "$CONTAINER_DIR" + log_info "Container removed. Data preserved in $DATA_DIR" + + log_ok "WebTorrent uninstalled" +} + +# ───────────────────────────────────────────────────────────────────────────────── +# Main +# ───────────────────────────────────────────────────────────────────────────────── +case "$1" in + install) cmd_install ;; + start) cmd_start ;; + stop) cmd_stop ;; + restart) cmd_restart ;; + status) cmd_status ;; + logs) shift; cmd_logs "$@" ;; + shell) cmd_shell ;; + add) shift; cmd_add "$@" ;; + list) cmd_list ;; + configure-haproxy) cmd_configure_haproxy ;; + uninstall) cmd_uninstall ;; + *) + echo "WebTorrent Controller - Browser Torrent Streaming" + echo "" + echo "Usage: webtorrentctl " + echo "" + echo "Commands:" + echo " install Install Debian LXC container with WebTorrent" + echo " start Start WebTorrent" + echo " stop Stop WebTorrent" + echo " restart Restart WebTorrent" + echo " status Show status" + echo " logs [n] Show last n log lines (default 50)" + echo " shell Interactive shell in container" + echo " add Add torrent by magnet link" + echo " list List active torrents" + echo " configure-haproxy Setup HAProxy reverse proxy" + echo " uninstall Remove container (keeps data)" + ;; +esac