feat(torrent): Add qBittorrent and WebTorrent packages
qBittorrent (secubox-app-qbittorrent): - Full-featured BitTorrent client with web UI - Container IP: 192.168.255.42:8090 - qbittorrent-nox from Debian repos - API commands: add, list, status WebTorrent (secubox-app-webtorrent): - Browser-based torrent streaming via WebRTC - Container IP: 192.168.255.43:8095 - Node.js server with webtorrent library - Stream video files directly in browser - Beautiful dark theme web UI Both use Debian LXC containers (no Docker/Podman) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d308b22070
commit
0ec28266c5
31
package/secubox/secubox-app-qbittorrent/Makefile
Normal file
31
package/secubox/secubox-app-qbittorrent/Makefile
Normal file
@ -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))
|
||||
@ -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'
|
||||
22
package/secubox/secubox-app-qbittorrent/files/etc/init.d/qbittorrent
Executable file
22
package/secubox/secubox-app-qbittorrent/files/etc/init.d/qbittorrent
Executable file
@ -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
|
||||
}
|
||||
383
package/secubox/secubox-app-qbittorrent/files/usr/sbin/qbittorrentctl
Executable file
383
package/secubox/secubox-app-qbittorrent/files/usr/sbin/qbittorrentctl
Executable file
@ -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" <<EOF
|
||||
lxc.uts.name = $CONTAINER_NAME
|
||||
lxc.rootfs.path = dir:$CONTAINER_DIR/rootfs
|
||||
lxc.init.cmd = /start-qbittorrent.sh
|
||||
|
||||
# Network - use veth with bridge
|
||||
lxc.net.0.type = veth
|
||||
lxc.net.0.link = br-lan
|
||||
lxc.net.0.flags = up
|
||||
lxc.net.0.ipv4.address = 192.168.255.42/24
|
||||
lxc.net.0.ipv4.gateway = 192.168.255.1
|
||||
|
||||
# Mounts
|
||||
lxc.mount.auto = proc:mixed sys:ro
|
||||
lxc.mount.entry = $DATA_DIR/config config none bind,create=dir 0 0
|
||||
lxc.mount.entry = $DOWNLOAD_DIR downloads none bind,create=dir 0 0
|
||||
lxc.mount.entry = tmpfs tmp tmpfs defaults 0 0
|
||||
lxc.mount.entry = tmpfs run tmpfs defaults 0 0
|
||||
|
||||
# Capabilities
|
||||
lxc.cap.drop = sys_admin sys_boot sys_module sys_rawio sys_time
|
||||
|
||||
# Memory limit
|
||||
lxc.cgroup2.memory.max = $memory
|
||||
|
||||
# TTY
|
||||
lxc.tty.max = 0
|
||||
lxc.pty.max = 256
|
||||
|
||||
# Seccomp (disabled for compatibility)
|
||||
lxc.seccomp.profile =
|
||||
|
||||
# Auto-start
|
||||
lxc.start.auto = 1
|
||||
EOF
|
||||
|
||||
log_ok "qBittorrent container installed"
|
||||
log_info "Container IP: 192.168.255.42"
|
||||
log_info "Run: qbittorrentctl start"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
# Start container
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
cmd_start() {
|
||||
if ! [ -d "$CONTAINER_DIR/rootfs" ]; then
|
||||
log_error "Container not installed. Run: qbittorrentctl install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if lxc-info -n "$CONTAINER_NAME" 2>/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 <magnet/url>"; 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 <command>"
|
||||
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 <url> Add torrent by magnet/URL"
|
||||
echo " list List active torrents"
|
||||
echo " configure-haproxy Setup HAProxy reverse proxy"
|
||||
echo " uninstall Remove container (keeps data)"
|
||||
;;
|
||||
esac
|
||||
31
package/secubox/secubox-app-webtorrent/Makefile
Normal file
31
package/secubox/secubox-app-webtorrent/Makefile
Normal file
@ -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))
|
||||
@ -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'
|
||||
22
package/secubox/secubox-app-webtorrent/files/etc/init.d/webtorrent
Executable file
22
package/secubox/secubox-app-webtorrent/files/etc/init.d/webtorrent
Executable file
@ -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
|
||||
}
|
||||
579
package/secubox/secubox-app-webtorrent/files/usr/sbin/webtorrentctl
Executable file
579
package/secubox/secubox-app-webtorrent/files/usr/sbin/webtorrentctl
Executable file
@ -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(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebTorrent Streaming</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
body { font-family: system-ui; background: #0a0a12; color: #e0e0e0; min-height: 100vh; }
|
||||
.container { max-width: 900px; margin: 0 auto; padding: 20px; }
|
||||
h1 { color: #00d4ff; margin-bottom: 20px; }
|
||||
.add-form { display: flex; gap: 10px; margin-bottom: 30px; }
|
||||
input[type="text"] { flex: 1; padding: 12px; background: #1a1a24; border: 1px solid #3a3a4e; border-radius: 8px; color: #e0e0e0; font-size: 16px; }
|
||||
button { padding: 12px 24px; background: linear-gradient(135deg, #00d4ff, #7c3aed); border: none; border-radius: 8px; color: #fff; font-weight: 600; cursor: pointer; }
|
||||
button:hover { opacity: 0.9; }
|
||||
.torrent { background: #1a1a24; border: 1px solid #2a2a3e; border-radius: 12px; padding: 20px; margin-bottom: 15px; }
|
||||
.torrent:hover { border-color: #00d4ff; }
|
||||
.torrent-name { font-weight: 600; color: #00d4ff; margin-bottom: 10px; word-break: break-all; }
|
||||
.torrent-info { color: #888; font-size: 14px; }
|
||||
.progress-bar { height: 8px; background: #2a2a3e; border-radius: 4px; margin: 10px 0; overflow: hidden; }
|
||||
.progress-fill { height: 100%; background: linear-gradient(90deg, #00d4ff, #7c3aed); transition: width 0.3s; }
|
||||
.files { margin-top: 15px; }
|
||||
.file { padding: 8px 12px; background: #12121a; border-radius: 6px; margin: 5px 0; display: flex; justify-content: space-between; align-items: center; }
|
||||
.file a { color: #00d4ff; text-decoration: none; }
|
||||
.file a:hover { text-decoration: underline; }
|
||||
.empty { text-align: center; color: #666; padding: 60px; }
|
||||
video { width: 100%; border-radius: 8px; margin-top: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🌊 WebTorrent Streaming</h1>
|
||||
<div class="add-form">
|
||||
<input type="text" id="magnet" placeholder="Paste magnet link or info hash...">
|
||||
<button onclick="addTorrent()">Add</button>
|
||||
</div>
|
||||
<div id="torrents"></div>
|
||||
</div>
|
||||
<script>
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024;
|
||||
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
async function addTorrent() {
|
||||
const magnet = document.getElementById('magnet').value.trim();
|
||||
if (!magnet) return;
|
||||
const res = await fetch('/api/add', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ magnet })
|
||||
});
|
||||
document.getElementById('magnet').value = '';
|
||||
loadTorrents();
|
||||
}
|
||||
|
||||
async function loadTorrents() {
|
||||
const res = await fetch('/api/torrents');
|
||||
const data = await res.json();
|
||||
const container = document.getElementById('torrents');
|
||||
if (data.length === 0) {
|
||||
container.innerHTML = '<div class="empty">No torrents. Add a magnet link above.</div>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = data.map(t => \`
|
||||
<div class="torrent">
|
||||
<div class="torrent-name">\${t.name}</div>
|
||||
<div class="torrent-info">
|
||||
\${formatBytes(t.downloaded)} / \${formatBytes(t.length)}
|
||||
(\${(t.progress * 100).toFixed(1)}%)
|
||||
↓ \${formatBytes(t.downloadSpeed)}/s
|
||||
↑ \${formatBytes(t.uploadSpeed)}/s
|
||||
Peers: \${t.numPeers}
|
||||
</div>
|
||||
<div class="progress-bar"><div class="progress-fill" style="width: \${t.progress * 100}%"></div></div>
|
||||
<div class="files">
|
||||
\${t.files.map(f => \`
|
||||
<div class="file">
|
||||
<span>\${f.name}</span>
|
||||
<a href="/stream/\${t.infoHash}/\${encodeURIComponent(f.path)}" target="_blank">Stream</a>
|
||||
</div>
|
||||
\`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
\`).join('');
|
||||
}
|
||||
|
||||
setInterval(loadTorrents, 2000);
|
||||
loadTorrents();
|
||||
</script>
|
||||
</body>
|
||||
</html>`);
|
||||
});
|
||||
|
||||
// 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" <<EOF
|
||||
lxc.uts.name = $CONTAINER_NAME
|
||||
lxc.rootfs.path = dir:$CONTAINER_DIR/rootfs
|
||||
lxc.init.cmd = /start-webtorrent.sh
|
||||
|
||||
# Network - use veth with bridge
|
||||
lxc.net.0.type = veth
|
||||
lxc.net.0.link = br-lan
|
||||
lxc.net.0.flags = up
|
||||
lxc.net.0.ipv4.address = 192.168.255.43/24
|
||||
lxc.net.0.ipv4.gateway = 192.168.255.1
|
||||
|
||||
# Mounts
|
||||
lxc.mount.auto = proc:mixed sys:ro
|
||||
lxc.mount.entry = $DATA_DIR/config config none bind,create=dir 0 0
|
||||
lxc.mount.entry = $DOWNLOAD_DIR downloads none bind,create=dir 0 0
|
||||
lxc.mount.entry = tmpfs tmp tmpfs defaults 0 0
|
||||
lxc.mount.entry = tmpfs run tmpfs defaults 0 0
|
||||
|
||||
# Capabilities
|
||||
lxc.cap.drop = sys_admin sys_boot sys_module sys_rawio sys_time
|
||||
|
||||
# Memory limit
|
||||
lxc.cgroup2.memory.max = $memory
|
||||
|
||||
# TTY
|
||||
lxc.tty.max = 0
|
||||
lxc.pty.max = 256
|
||||
|
||||
# Seccomp (disabled for compatibility)
|
||||
lxc.seccomp.profile =
|
||||
|
||||
# Auto-start
|
||||
lxc.start.auto = 1
|
||||
EOF
|
||||
|
||||
log_ok "WebTorrent container installed"
|
||||
log_info "Container IP: 192.168.255.43"
|
||||
log_info "Run: webtorrentctl start"
|
||||
}
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
# Start container
|
||||
# ─────────────────────────────────────────────────────────────────────────────────
|
||||
cmd_start() {
|
||||
if ! [ -d "$CONTAINER_DIR/rootfs" ]; then
|
||||
log_error "Container not installed. Run: webtorrentctl install"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if lxc-info -n "$CONTAINER_NAME" 2>/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 <magnet>"; 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 <command>"
|
||||
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 <magnet> Add torrent by magnet link"
|
||||
echo " list List active torrents"
|
||||
echo " configure-haproxy Setup HAProxy reverse proxy"
|
||||
echo " uninstall Remove container (keeps data)"
|
||||
;;
|
||||
esac
|
||||
Loading…
Reference in New Issue
Block a user