blenders commited on
Commit
5afa8d3
·
1 Parent(s): 383b262
Files changed (7) hide show
  1. Dockerfile +88 -0
  2. init.sh +119 -0
  3. mime.types +73 -0
  4. nginx.conf +106 -0
  5. stat.xsl +0 -0
  6. www/index.html +20 -0
  7. www/script.js +41 -0
Dockerfile ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04
2
+
3
+ # Build args (change if upstream playit repo changes)
4
+ ARG PLAYIT_GPG_URL="https://playit-cloud.github.io/ppa/key.gpg"
5
+ ARG PLAYIT_APT_LINE="deb [signed-by=/etc/apt/trusted.gpg.d/playit.gpg] https://playit-cloud.github.io/ppa/data ./"
6
+
7
+
8
+ ENV DEBIAN_FRONTEND=noninteractive \
9
+ TZ=Etc/UTC \
10
+ LANG=C.UTF-8
11
+
12
+ USER root
13
+ # Install dependencies
14
+ RUN apt-get update && apt-get install -y --no-install-recommends \
15
+ build-essential \
16
+ libpcre3 libpcre3-dev \
17
+ libssl-dev \
18
+ zlib1g-dev \
19
+ wget \
20
+ nginx \
21
+ ffmpeg \
22
+ git \
23
+ build-essential \
24
+ libpcre3 libpcre3-dev \
25
+ libssl-dev \
26
+ wget \
27
+ curl \
28
+ unzip \
29
+ openssl \
30
+ apt-transport-https \
31
+ ca-certificates \
32
+ gnupg \
33
+ dirmngr \
34
+ tzdata \
35
+ gettext-base \
36
+ ; \
37
+ # ensure tzdata doesn't prompt (already set TZ env); configure timezone non-interactively
38
+ ln -fs /usr/share/zoneinfo/$TZ /etc/localtime; \
39
+ dpkg-reconfigure --frontend noninteractive tzdata || true; \
40
+ # prepare apt trusted key location
41
+ mkdir -p /etc/apt/trusted.gpg.d; \
42
+ # fetch playit GPG key and dearmor it for apt
43
+ curl -fsSL "${PLAYIT_GPG_URL}" -o /tmp/playit.key.gpg; \
44
+ gpg --dearmor --batch --yes -o /etc/apt/trusted.gpg.d/playit.gpg /tmp/playit.key.gpg; \
45
+ rm -f /tmp/playit.key.gpg; \
46
+ # add apt source list
47
+ echo "${PLAYIT_APT_LINE}" > /etc/apt/sources.list.d/playit-cloud.list; \
48
+ apt-get update; \
49
+ # Install playit, telling dpkg to accept default config answers if asked
50
+ apt-get install -y --no-install-recommends -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" playit; \
51
+ # cleanup apt caches
52
+ apt-get clean; \
53
+ rm -rf /var/lib/apt/lists/* /tmp/*
54
+
55
+
56
+
57
+ # Install Nginx with RTMP module
58
+ RUN mkdir -p /opt/nginx && \
59
+ cd /opt/nginx && \
60
+ git clone https://github.com/arut/nginx-rtmp-module.git && \
61
+ wget http://nginx.org/download/nginx-1.25.0.tar.gz && \
62
+ tar -zxvf nginx-1.25.0.tar.gz && \
63
+ cd nginx-1.25.0 && \
64
+ ./configure --with-http_ssl_module --add-module=../nginx-rtmp-module && \
65
+ make && make install
66
+
67
+ RUN mkdir -p /tmp/nginx /tmp/nginx/logs /tmp/nginx/proxy_temp /tmp/nginx/html /tmp/nginx/html/media/hls /tmp/nginx/html/media/rhls /tmp/nginx/html/media/recordings \
68
+ && chmod -R 777 /tmp/nginx /tmp/nginx/html
69
+ RUN chmod -R 755 /tmp/nginx/html/media
70
+ # Copy configs and web UI
71
+ COPY nginx.conf /tmp/nginx/nginx.conf
72
+ COPY www/ /tmp/nginx/html/
73
+ COPY mime.types /tmp/nginx/mime.types
74
+
75
+ # Copy entrypoint
76
+ COPY init.sh /tmp/nginx/entrypoint.sh
77
+ RUN chmod +x /tmp/nginx/entrypoint.sh
78
+ RUN chmod +x /tmp/nginx/logs
79
+
80
+
81
+ # Expose only HF Spaces allowed port
82
+ EXPOSE 7860/tcp
83
+
84
+ # Workdir
85
+ WORKDIR /tmp/app
86
+ USER root
87
+ ENTRYPOINT ["/tmp/nginx/entrypoint.sh"]
88
+ CMD []
init.sh ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ LOG_PREFIX="[playit-entrypoint]"
5
+ PLAYIT_ENV_BIN="${PLAYIT_BIN:-}"
6
+ CANDIDATE_PATHS=(
7
+ "/usr/bin/playit"
8
+ "/usr/local/bin/playit"
9
+ "/opt/playit/playit"
10
+ )
11
+ PLAYIT_LOG="/tmp/playit.log"
12
+ PLAYIT_SECRET_VAR="${PLAYIT_SECRET:-}"
13
+ # cp "/usr/local/nginx/sbin/nginx" "/tmp/nginx/sbin/nginx"
14
+ # NGINX_BIN="/tmp/nginx/sbin/nginx"
15
+ NGINX_CONF="/tmp/nginx/nginx.conf"
16
+ NGINX_ORIG_CONF_DIR="/usr/local/nginx/conf"
17
+ LOG_DIR="/tmp/nginx/logs"
18
+ PROXY_TEMP="/tmp/nginx/proxy_temp"
19
+ if [ -x "/usr/local/nginx/sbin/nginx" ]; then
20
+ mkdir -p /tmp/nginx/sbin
21
+
22
+ # Copy mime.types and stat.xsl for nginx http block
23
+ cp "${NGINX_ORIG_CONF_DIR}/mime.types" /tmp/nginx/mime.types
24
+ cp "${NGINX_ORIG_CONF_DIR}/stat.xsl" /tmp/nginx/stat.xsl
25
+ # Then copy the binary
26
+ cp /usr/local/nginx/sbin/nginx /tmp/nginx/sbin/nginx
27
+ chmod +x /tmp/nginx/sbin/nginx
28
+ NGINX_BIN="/tmp/nginx/sbin/nginx"
29
+ else
30
+ echo "$LOG_PREFIX nginx binary not found at /usr/local/nginx/sbin/nginx" >&2
31
+ exit 1
32
+ fi
33
+
34
+
35
+ echo "$LOG_PREFIX starting entrypoint..."
36
+
37
+ determine_playit_bin() {
38
+ if [ -n "${PLAYIT_ENV_BIN:-}" ]; then
39
+ if [ -x "$PLAYIT_ENV_BIN" ]; then
40
+ echo "$PLAYIT_ENV_BIN"
41
+ return 0
42
+ else
43
+ echo "$LOG_PREFIX PLAYIT_BIN is set but not executable: $PLAYIT_ENV_BIN" >&2
44
+ return 1
45
+ fi
46
+ fi
47
+
48
+ if command -v playit >/dev/null 2>&1; then
49
+ command -v playit
50
+ return 0
51
+ fi
52
+
53
+ for p in "${CANDIDATE_PATHS[@]}"; do
54
+ if [ -x "$p" ]; then
55
+ echo "$p"
56
+ return 0
57
+ fi
58
+ done
59
+
60
+ return 1
61
+ }
62
+
63
+ # Find binary
64
+ if PLAYIT_BIN_PATH="$(determine_playit_bin)"; then
65
+ echo "$LOG_PREFIX playit binary found: $PLAYIT_BIN_PATH"
66
+ else
67
+ echo "$LOG_PREFIX playit binary NOT found. Will continue but playit won't run." >&2
68
+ PLAYIT_BIN_PATH=""
69
+ fi
70
+
71
+ # Prepare playit log
72
+ rm -f "$PLAYIT_LOG" || true
73
+ touch "$PLAYIT_LOG"
74
+ chmod 600 "$PLAYIT_LOG" || true
75
+
76
+ PLAYIT_PID=0
77
+
78
+ start_playit() {
79
+ if [ -n "${PLAYIT_BIN_PATH}" ] && [ -n "${PLAYIT_SECRET_VAR}" ]; then
80
+ echo "$LOG_PREFIX starting playit agent..."
81
+ # quote secret to avoid word-splitting; use nohup so it doesn't die when shell exits
82
+ nohup "$PLAYIT_BIN_PATH" --secret "$PLAYIT_SECRET_VAR" start >>"$PLAYIT_LOG" 2>&1 &
83
+ PLAYIT_PID=$!
84
+ echo "$LOG_PREFIX Playit agent started with PID=$PLAYIT_PID"
85
+ else
86
+ if [ -z "${PLAYIT_BIN_PATH}" ]; then
87
+ echo "$LOG_PREFIX playit binary not available; skipping playit startup"
88
+ else
89
+ echo "$LOG_PREFIX PLAYIT_SECRET not provided; skipping playit startup"
90
+ fi
91
+ fi
92
+ }
93
+
94
+ stop_playit() {
95
+ if [ "$PLAYIT_PID" -ne 0 ] && kill -0 "$PLAYIT_PID" >/dev/null 2>&1; then
96
+ echo "$LOG_PREFIX stopping playit (pid=$PLAYIT_PID)"
97
+ kill "$PLAYIT_PID" || true
98
+ sleep 1
99
+ kill -9 "$PLAYIT_PID" 2>/dev/null || true
100
+ fi
101
+ }
102
+
103
+ # Trap termination to stop playit cleanly
104
+ trap 'echo "$LOG_PREFIX received termination signal"; stop_playit; exit 0' TERM INT
105
+
106
+ # Start playit (if configured)
107
+ start_playit
108
+
109
+ echo "$LOG_PREFIX starting nginx..."
110
+
111
+ mkdir -p /tmp/nginx/{logs,proxy_temp,fastcgi_temp,uwsgi_temp,scgi_temp,sbin,html}
112
+ # chmod 1777 /tmp/nginx/logs /tmp/nginx/proxy_temp
113
+ # chmod 1777 /tmp/nginx/fastcgi_temp /tmp/nginx/uwsgi_temp /tmp/nginx/scgi_temp
114
+ #chmod 1777 /tmp/nginx /tmp/media
115
+
116
+ NGINX_PREFIX="/tmp/nginx"
117
+ exec "$NGINX_BIN" -c "$NGINX_CONF" -p "$NGINX_PREFIX" -g "daemon off;"
118
+ # exec "$NGINX_BIN" -c "$NGINX_CONF" -g "daemon off;"
119
+ echo "$LOG_PREFIX nginx started"
mime.types ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Basic mime.types for Nginx
2
+ types {
3
+ text/html html htm shtml;
4
+ text/css css;
5
+ text/xml xml rss;
6
+ image/gif gif;
7
+ image/jpeg jpeg jpg;
8
+ application/javascript js;
9
+ application/atom+xml atom;
10
+ text/mathml mml;
11
+ text/plain txt;
12
+ text/vnd.sun.j2me.app-descriptor jad;
13
+ text/vnd.wap.wml wml;
14
+ text/x-component htc;
15
+ image/png png;
16
+ image/tiff tif tiff;
17
+ image/vnd.wap.wbmp wbmp;
18
+ image/x-icon ico;
19
+ image/x-jng jng;
20
+ image/x-ms-bmp bmp;
21
+ image/svg+xml svg svgz;
22
+ image/webp webp;
23
+ application/font-woff woff;
24
+ application/java-archive jar war ear;
25
+ application/json json;
26
+ application/mac-binhex40 hqx;
27
+ application/msword doc;
28
+ application/pdf pdf;
29
+ application/postscript ps eps ai;
30
+ application/rtf rtf;
31
+ application/vnd.ms-excel xls;
32
+ application/vnd.ms-powerpoint ppt;
33
+ application/vnd.wap.wmlc wmlc;
34
+ application/vnd.google-earth.kml+xml kml;
35
+ application/vnd.google-earth.kmz kmz;
36
+ application/x-7z-compressed 7z;
37
+ application/x-cocoa cco;
38
+ application/x-java-archive-diff jardiff;
39
+ application/x-java-jnlp-file jnlp;
40
+ application/x-makeself run;
41
+ application/x-perl pl pm;
42
+ application/x-pilot prc pdb;
43
+ application/x-rar-compressed rar;
44
+ application/x-redhat-package-manager rpm;
45
+ application/x-sea sea;
46
+ application/x-shockwave-flash swf;
47
+ application/x-stuffit sit;
48
+ application/x-tcl tcl tk;
49
+ application/x-x509-ca-cert der pem crt;
50
+ application/x-xpinstall xpi;
51
+ application/xhtml+xml xhtml;
52
+ application/zip zip;
53
+ application/octet-stream bin exe dll;
54
+ application/octet-stream deb;
55
+ application/octet-stream dmg;
56
+ application/octet-stream iso img;
57
+ application/octet-stream msi msp msm;
58
+ audio/midi mid midi kar;
59
+ audio/mpeg mp3;
60
+ audio/ogg ogg;
61
+ audio/x-m4a m4a;
62
+ audio/x-realaudio ra;
63
+ video/3gpp 3gpp 3gp;
64
+ video/mp2t ts;
65
+ video/mp4 mp4;
66
+ video/mpeg mpeg mpg;
67
+ video/quicktime mov;
68
+ video/webm webm;
69
+ video/x-flv flv;
70
+ video/x-m4v m4v;
71
+ video/x-ms-wmv wmv;
72
+ video/x-msvideo avi;
73
+ }
nginx.conf ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ worker_processes 1;
2
+ pid /tmp/nginx/nginx.pid;
3
+
4
+ events {
5
+ worker_connections 1024;
6
+ }
7
+
8
+ rtmp {
9
+ server {
10
+ listen 1935;
11
+ chunk_size 4096;
12
+
13
+ application live {
14
+ live on;
15
+ }
16
+
17
+ application hls {
18
+ live on;
19
+ hls on;
20
+ hls_type live;
21
+ #hls_nested on;
22
+ hls_path /tmp/nginx/html/media/hls;
23
+ hls_fragment 3;
24
+ hls_playlist_length 9;
25
+ hls_variant _subsd BANDWIDTH=400000;
26
+ hls_variant _sd BANDWIDTH=1000000;
27
+ hls_variant _hd BANDWIDTH=5000000;
28
+ allow publish all;
29
+ allow play all;
30
+ }
31
+
32
+ application rhls {
33
+ live on;
34
+ hls on;
35
+ #hls_nested on;
36
+ hls_type live;
37
+ hls_path /tmp/nginx/html/media/rhls;
38
+ hls_fragment 3;
39
+ hls_playlist_length 9;
40
+ record all;
41
+ record_path /tmp/nginx/html/media/recordings;
42
+ record_unique on;
43
+ record_append off;
44
+ hls_variant _subsd BANDWIDTH=400000;
45
+ hls_variant _sd BANDWIDTH=1000000;
46
+ hls_variant _hd BANDWIDTH=5000000;
47
+
48
+ allow publish all;
49
+ allow play all;
50
+ }
51
+ }
52
+ }
53
+
54
+ http {
55
+ include /tmp/nginx/mime.types;
56
+ default_type application/octet-stream;
57
+ sendfile on;
58
+ keepalive_timeout 65;
59
+
60
+ # Writable logs
61
+ error_log /tmp/nginx/error.log info;
62
+ access_log /tmp/nginx/access.log;
63
+
64
+ # Temp path must be inside http block
65
+ #client_body_temp_path /tmp/nginx/proxy_temp;
66
+
67
+ server {
68
+ listen 7860;
69
+ # Serve HLS from /tmp/media/hls
70
+
71
+ location / {
72
+ root /tmp/nginx/html;
73
+ index index.html;
74
+ }
75
+
76
+ location /hls/ {
77
+ root /tmp/nginx/html/media/hls/;
78
+ types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; }
79
+ add_header Cache-Control no-cache;
80
+ add_header Access-Control-Allow-Origin *;
81
+ autoindex on;
82
+ }
83
+
84
+ location /rhls/ {
85
+ root /tmp/nginx/html/media/rhls/;
86
+ types { application/vnd.apple.mpegurl m3u8; video/mp2t ts; }
87
+ add_header Cache-Control no-cache;
88
+ add_header Access-Control-Allow-Origin *;
89
+ autoindex on;
90
+ }
91
+
92
+ location /recordings/ {
93
+ root /tmp/nginx/html/media/recordings/;
94
+ add_header Cache-Control no-cache;
95
+ autoindex on;
96
+ }
97
+
98
+
99
+
100
+
101
+ location /stat {
102
+ rtmp_stat all;
103
+ rtmp_stat_stylesheet stat.xsl;
104
+ }
105
+ }
106
+ }
stat.xsl ADDED
File without changes
www/index.html ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>RTMP Live Dashboard</title>
7
+ <style>
8
+ body { font-family: sans-serif; margin: 0; padding: 0; background: #111; color: #fff; }
9
+ h1 { text-align: center; padding: 10px; }
10
+ #grid { display: grid; gap: 10px; padding: 10px; }
11
+ video { width: 100%; height: auto; background: #000; }
12
+ button { margin-top: 5px; padding: 5px; }
13
+ </style>
14
+ </head>
15
+ <body>
16
+ <h1>Live Streams</h1>
17
+ <div id="grid"></div>
18
+ <script src="scripts.js"></script>
19
+ </body>
20
+ </html>
www/script.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const grid = document.getElementById("grid");
2
+
3
+ async function fetchStreams() {
4
+ try {
5
+ const res = await fetch("/stat"); // You may need a JSON stat endpoint
6
+ const text = await res.text();
7
+ // Simple parsing to get stream keys from the rtmp_stat XML
8
+ const parser = new DOMParser();
9
+ const xml = parser.parseFromString(text, "text/xml");
10
+ const streams = Array.from(xml.getElementsByTagName("stream")).map(s => s.getAttribute("name"));
11
+ renderStreams(streams);
12
+ } catch (e) {
13
+ console.error(e);
14
+ }
15
+ }
16
+
17
+ function renderStreams(streams) {
18
+ grid.innerHTML = "";
19
+ const columns = Math.ceil(Math.sqrt(streams.length));
20
+ grid.style.gridTemplateColumns = `repeat(${columns}, 1fr)`;
21
+ streams.forEach(stream => {
22
+ const container = document.createElement("div");
23
+ const video = document.createElement("video");
24
+ video.src = `/live/${stream}.flv`; // Use HLS/FLV player if needed
25
+ video.controls = true;
26
+ video.autoplay = true;
27
+ video.muted = false;
28
+
29
+ const btn = document.createElement("button");
30
+ btn.textContent = "Play/Pause";
31
+ btn.onclick = () => video.paused ? video.play() : video.pause();
32
+
33
+ container.appendChild(video);
34
+ container.appendChild(btn);
35
+ grid.appendChild(container);
36
+ });
37
+ }
38
+
39
+ // Refresh every 5 seconds
40
+ setInterval(fetchStreams, 5000);
41
+ fetchStreams();