From 0c655b0b1c2a61dabfab4c532bd48fa2ec99081b Mon Sep 17 00:00:00 2001 From: Pavel Pikta Date: Thu, 2 Jul 2026 00:33:37 +0300 Subject: [PATCH] feat(settings): introduce StreamWA feature flag for unauthenticated streaming - Added TS_STREAMWA environment variable to .env.example and docker-compose.yml. - Updated docker-entrypoint.sh to handle StreamWA flag. - Modified server settings and initialization to include StreamWA. - Enhanced play and stream API endpoints to allow streaming without authentication when StreamWA is enabled. Signed-off-by: Pavel Pikta --- .env.example | 1 + docker-compose.yml | 1 + docker-entrypoint.sh | 1 + server/cmd/main.go | 5 +++++ server/server.go | 2 +- server/settings/args.go | 1 + server/settings/settings.go | 4 +++- server/web/api/play.go | 14 +++++++++++--- server/web/api/stream.go | 14 +++++++++++--- 9 files changed, 35 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index 57f8c4f94..782c794a3 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,7 @@ TS_TORR_DIR=/opt/ts/torrents # Feature flags — set to 1 to enable TS_HTTPAUTH=0 +TS_STREAMWA=0 TS_RDB=0 TS_DONTKILL=0 diff --git a/docker-compose.yml b/docker-compose.yml index d3342450d..e4fd60aef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,6 +40,7 @@ services: # Feature flags — set to "1" to enable (see docker-entrypoint.sh) TS_HTTPAUTH: ${TS_HTTPAUTH:-0} # basic auth; place auth file in TS_CONF_PATH + TS_STREAMWA: ${TS_STREAMWA:-0} # allow /stream play and /play without auth TS_RDB: ${TS_RDB:-0} # remote debug (`--rdb`) TS_DONTKILL: ${TS_DONTKILL:-0} # keep torrents after last client disconnects diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 8450a9f02..f30132c13 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -3,6 +3,7 @@ FLAGS="--path $TS_CONF_PATH --logpath $TS_LOG_PATH --port $TS_PORT --torrentsdir $TS_TORR_DIR" if [ -n "$TS_IP" ]; then FLAGS="${FLAGS} -i ${TS_IP}"; fi if [ "$TS_HTTPAUTH" = "1" ]; then FLAGS="${FLAGS} --httpauth"; fi +if [ "$TS_STREAMWA" = "1" ]; then FLAGS="${FLAGS} --streamwa"; fi if [ "$TS_RDB" = "1" ]; then FLAGS="${FLAGS} --rdb"; fi if [ "$TS_DONTKILL" = "1" ]; then FLAGS="${FLAGS} --dontkill"; fi if [ "$TS_EN_SSL" = "1" ]; then FLAGS="${FLAGS} --ssl"; fi diff --git a/server/cmd/main.go b/server/cmd/main.go index 587221ba4..2107b646d 100644 --- a/server/cmd/main.go +++ b/server/cmd/main.go @@ -45,6 +45,7 @@ type args struct { PubIPv4 string `arg:"-4" help:"set public IPv4 addr"` PubIPv6 string `arg:"-6" help:"set public IPv6 addr"` SearchWA bool `arg:"-s" help:"search without auth"` + StreamWA bool `arg:"--streamwa" help:"stream play and m3u without auth (auto-add torrents for external players)"` MaxSize string `arg:"-m" help:"max allowed stream size (in Bytes)"` TGToken string `arg:"-T" help:"telegram bot token"` FusePath string `arg:"-f" help:"fuse mount path"` @@ -82,6 +83,9 @@ func main() { if params.HttpAuth { log.TLogln("Use HTTP Auth file", settings.Path+"/accs.db") } + if params.StreamWA { + log.TLogln("Stream play/m3u allowed without auth (streamwa)") + } if params.RDB { log.TLogln("Running in Read-only DB mode!") } @@ -167,6 +171,7 @@ func main() { PubIPv4: params.PubIPv4, PubIPv6: params.PubIPv6, SearchWA: params.SearchWA, + StreamWA: params.StreamWA, MaxSize: params.MaxSize, TGToken: params.TGToken, FusePath: params.FusePath, diff --git a/server/server.go b/server/server.go index 2c487a307..d3802a65d 100644 --- a/server/server.go +++ b/server/server.go @@ -16,7 +16,7 @@ import ( ) func Start() { - settings.InitSets(settings.Args.RDB, settings.Args.SearchWA) + settings.InitSets(settings.Args.RDB, settings.Args.SearchWA, settings.Args.StreamWA) // https checks if settings.Args.Ssl { // set settings ssl enabled diff --git a/server/settings/args.go b/server/settings/args.go index eea82775f..f4cbb8c80 100644 --- a/server/settings/args.go +++ b/server/settings/args.go @@ -19,6 +19,7 @@ type ExecArgs struct { PubIPv4 string PubIPv6 string SearchWA bool + StreamWA bool MaxSize string TGToken string FusePath string diff --git a/server/settings/settings.go b/server/settings/settings.go index 982c9ea5d..395968c2a 100644 --- a/server/settings/settings.go +++ b/server/settings/settings.go @@ -31,15 +31,17 @@ var ( ReadOnly bool HttpAuth bool SearchWA bool + StreamWA bool PubIPv4 string PubIPv6 string TorAddr string MaxSize int64 ) -func InitSets(readOnly, searchWA bool) { +func InitSets(readOnly, searchWA, streamWA bool) { ReadOnly = readOnly SearchWA = searchWA + StreamWA = streamWA bboltDB := NewTDB() if bboltDB == nil { diff --git a/server/web/api/play.go b/server/web/api/play.go index 153a0485f..256d825b2 100644 --- a/server/web/api/play.go +++ b/server/web/api/play.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" + sets "server/settings" "server/torr" "server/torr/state" "server/web/api/utils" @@ -43,9 +44,16 @@ func play(c *gin.Context) { tor := torr.GetTorrent(spec.InfoHash.HexString()) if tor == nil && notAuth { - c.Header("WWW-Authenticate", "Basic realm=Authorization Required") - c.AbortWithStatus(http.StatusUnauthorized) - return + if !sets.StreamWA { + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + tor, err = torr.AddTorrent(spec, "", "", "", "") + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } } if tor == nil { diff --git a/server/web/api/stream.go b/server/web/api/stream.go index 14d9f7b2f..4fe666dc5 100644 --- a/server/web/api/stream.go +++ b/server/web/api/stream.go @@ -10,6 +10,7 @@ import ( "server/torr" "server/torr/state" + sets "server/settings" utils2 "server/utils" "server/web/api/utils" @@ -250,9 +251,16 @@ func streamNoAuth(c *gin.Context) { tor := torr.GetTorrent(spec.InfoHash.HexString()) if tor == nil { - c.Header("WWW-Authenticate", "Basic realm=Authorization Required") - c.AbortWithStatus(http.StatusUnauthorized) - return + if !sets.StreamWA { + c.Header("WWW-Authenticate", "Basic realm=Authorization Required") + c.AbortWithStatus(http.StatusUnauthorized) + return + } + tor, err = torr.AddTorrent(spec, title, poster, "", category) + if err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } } if title == "" {