This commit is contained in:
Laptop
2025-03-24 21:09:25 +02:00
parent 8fd150ff96
commit 3b63615014
5 changed files with 223 additions and 87 deletions

View File

@@ -18,7 +18,7 @@ RUN soundcloakctl js download
RUN templ generate RUN templ generate
RUN go generate ./lib/* RUN go generate ./lib/*
RUN soundcloakctl config codegen RUN soundcloakctl config codegen
RUN soundcloakctl -nozstd -notable precompress RUN soundcloakctl -nozstd precompress
RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} GOOS=${TARGETOS} go build -ldflags "-s -w -extldflags '-static'" -o ./app RUN CGO_ENABLED=0 GOARCH=${TARGETARCH} GOOS=${TARGETOS} go build -ldflags "-s -w -extldflags '-static'" -o ./app
RUN echo "soundcloak:x:5000:5000:Soundcloak user:/:/sbin/nologin" > /etc/minimal-passwd && \ RUN echo "soundcloak:x:5000:5000:Soundcloak user:/:/sbin/nologin" > /etc/minimal-passwd && \

View File

@@ -157,10 +157,9 @@ func GetClientID() (string, error) {
if ver == "" && bytes.HasPrefix(l, sc_version) { if ver == "" && bytes.HasPrefix(l, sc_version) {
ver = cfg.B2s(l[len(sc_version) : len(l)-len(`"</script>`)]) ver = cfg.B2s(l[len(sc_version) : len(l)-len(`"</script>`)])
misc.Log("found ver:", ver) misc.Log("found ver:", ver)
// if scriptUrl != nil { if ClientIDCache.Version != "" && ver == ClientIDCache.Version {
// misc.Log("we early (1)") goto verCacheHit
// break }
// }
} else if bytes.HasPrefix(l, script0) { } else if bytes.HasPrefix(l, script0) {
scriptUrl = l[len(`<script crossorigin src="`) : len(l)-len(`"></script>`)] scriptUrl = l[len(`<script crossorigin src="`) : len(l)-len(`"></script>`)]
misc.Log("found scriptUrl:", string(scriptUrl)) misc.Log("found scriptUrl:", string(scriptUrl))
@@ -168,10 +167,6 @@ func GetClientID() (string, error) {
} }
} }
if ver == ClientIDCache.Version {
goto verCacheHit
}
if ver == "" { if ver == "" {
return "", ErrVersionNotFound return "", ErrVersionNotFound
} }
@@ -212,15 +207,14 @@ func GetClientID() (string, error) {
for l := range bytes.SplitSeq(data, newline) { for l := range bytes.SplitSeq(data, newline) {
if ver == "" && bytes.HasPrefix(l, sc_version) { if ver == "" && bytes.HasPrefix(l, sc_version) {
ver = cfg.B2s(l[len(sc_version) : len(l)-len(`"</script>`)]) ver = cfg.B2s(l[len(sc_version) : len(l)-len(`"</script>`)])
if ver == ClientIDCache.Version {
goto verCacheHit
}
} else if bytes.HasPrefix(l, script) { } else if bytes.HasPrefix(l, script) {
scriptUrls = append(scriptUrls, l[len(`<script crossorigin src="`):len(l)-len(`"></script>`)]) scriptUrls = append(scriptUrls, l[len(`<script crossorigin src="`):len(l)-len(`"></script>`)])
} }
} }
if ver == ClientIDCache.Version {
goto verCacheHit
}
if ver == "" { if ver == "" {
return "", ErrVersionNotFound return "", ErrVersionNotFound
} }

276
main.go
View File

@@ -1,13 +1,16 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"io"
"io/fs" "io/fs"
"log" "log"
"math/rand" "math/rand"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"sync"
"git.maid.zone/stuff/soundcloak/lib/misc" "git.maid.zone/stuff/soundcloak/lib/misc"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
@@ -23,8 +26,9 @@ import (
proxystreams "git.maid.zone/stuff/soundcloak/lib/proxy_streams" proxystreams "git.maid.zone/stuff/soundcloak/lib/proxy_streams"
"git.maid.zone/stuff/soundcloak/lib/restream" "git.maid.zone/stuff/soundcloak/lib/restream"
"git.maid.zone/stuff/soundcloak/lib/sc" "git.maid.zone/stuff/soundcloak/lib/sc"
static_files "git.maid.zone/stuff/soundcloak/static"
"git.maid.zone/stuff/soundcloak/templates" "git.maid.zone/stuff/soundcloak/templates"
static_files "git.maid.zone/stuff/soundcloak/static"
) )
func boolean(b bool) string { func boolean(b bool) string {
@@ -34,77 +38,213 @@ func boolean(b bool) string {
return "Disabled" return "Disabled"
} }
type osfs struct{} type compressionMap = map[string][][]byte
func (osfs) Open(name string) (fs.File, error) { func parseCompressionMap(path string, filesystem fs.FS) compressionMap {
misc.Log("osfs:", name) f, err := filesystem.Open(path + "/.compression")
if err != nil {
return nil
}
defer f.Close()
if strings.HasPrefix(name, "js/") { data, err := io.ReadAll(f)
return os.Open("static/external/" + name[3:]) if err != nil {
return nil
} }
f, err := os.Open("static/instance/" + name) sp := bytes.Split(data, []byte("\n"))
if err == nil { cm := make(compressionMap, len(sp))
return f, nil for _, h := range sp {
} sp2 := bytes.Split(h, []byte("|"))
if len(sp2) != 2 || string(sp2[0]) == ".gitkeep" {
f, err = os.Open("static/assets/" + name) continue
return f, err
}
type staticfs struct{}
func (staticfs) Open(name string) (fs.File, error) {
misc.Log("staticfs:", name)
if strings.HasPrefix(name, "js/") {
return static_files.External.Open("external/" + name[3:])
}
f, err := static_files.Instance.Open("instance/" + name)
if err == nil {
return f, nil
}
f, err = static_files.Assets.Open("assets/" + name)
return f, err
}
// stubby implementation of static middleware
// why? mainly because of the pathrewrite
// i hate seeing it trying to get root directory
func ServeFromFS(r *fiber.App, filesystem fs.FS) {
const path = "/_/static"
const l = len(path)
fs := fasthttp.FS{
FS: filesystem,
PathRewrite: func(ctx *fasthttp.RequestCtx) []byte {
return ctx.Path()[l:]
},
Compress: true,
CompressBrotli: true,
CompressedFileSuffixes: map[string]string{
"gzip": ".gzip",
"br": ".br",
"zstd": ".zstd",
},
}
handler := fs.NewRequestHandler()
r.Use(path, func(c fiber.Ctx) error {
handler(c.RequestCtx())
if c.RequestCtx().Response.StatusCode() == 200 {
c.Set("Cache-Control", "public, max-age=28800")
} }
return nil h := bytes.Split(sp2[1], []byte(","))
}) if len(h) == 0 || len(h[0]) == 0 {
h = nil
}
cm[cfg.B2s(sp2[0])] = h
}
return cm
}
func parseCompressionMaps(filesystem fs.FS) compressionMap {
var (
external = parseCompressionMap("external", filesystem)
assets = parseCompressionMap("assets", filesystem)
instance = parseCompressionMap("instance", filesystem)
)
res := make(compressionMap, len(external)+len(assets)+len(instance))
for k, v := range external {
res["external/"+k] = v
}
for k, v := range assets {
res["assets/"+k] = v
}
for k, v := range instance {
res["instance/"+k] = v
}
return res
}
type pooledReader struct {
handle io.ReadSeeker
p *sync.Pool
}
func (pr *pooledReader) Read(data []byte) (int, error) {
return pr.handle.Read(data)
}
func (pr *pooledReader) Close() error {
pr.handle.Seek(0, io.SeekStart)
pr.p.Put(pr)
return nil
}
type pooledFs struct {
filesystem fs.FS
pools map[string]*sync.Pool
}
func (p *pooledFs) Open(name string) (*pooledReader, error) {
pool := p.pools[name]
if pool == nil {
misc.Log("pool is nil for", name)
pool = &sync.Pool{}
p.pools[name] = pool
goto new
}
if pr := pool.Get(); pr != nil {
return pr.(*pooledReader), nil
}
new:
h, err := p.filesystem.Open(name)
if err != nil {
return nil, err
}
return &pooledReader{h.(io.ReadSeeker), pool}, nil
}
func ServeFS(r *fiber.App, filesystem fs.FS) {
cm := parseCompressionMaps(filesystem)
misc.Log(cm)
pfs := &pooledFs{filesystem, make(map[string]*sync.Pool)}
const path = "/_/static/"
if len(cm) == 0 {
r.Use(path, func(c fiber.Ctx) error {
// start := time.Now()
// defer func() { fmt.Println("it took", time.Since(start)) }()
fp := cfg.B2s(c.RequestCtx().Path()[len(path):])
if strings.HasSuffix(fp, ".css") {
c.Response().Header.SetContentType("text/css")
} else if strings.HasSuffix(fp, ".js") {
c.Response().Header.SetContentType("text/javascript")
} else if strings.HasSuffix(fp, ".jpg") {
c.Response().Header.SetContentType("image/jpeg")
} else if strings.HasSuffix(fp, ".ttf") {
c.Response().Header.SetContentType("font/ttf")
}
var (
f *pooledReader
err error
)
if !strings.HasPrefix(fp, "external/") {
f, err = pfs.Open("instance/" + fp)
if err != nil {
f, err = pfs.Open("assets/" + fp)
}
} else {
f, err = pfs.Open(fp)
}
if err != nil {
return err
}
c.Set("Cache-Control", "public, max-age=28800")
return c.SendStream(f)
})
} else {
r.Use(path, func(c fiber.Ctx) error {
// start := time.Now()
// defer func() { fmt.Println("it took", time.Since(start)) }()
fp := cfg.B2s(c.RequestCtx().Path()[len(path):])
var (
encs [][]byte
ok bool
)
if strings.HasPrefix(fp, "external/") {
encs, ok = cm[fp]
} else {
encs, ok = cm["instance/"+fp]
if ok {
fp = "instance/" + fp
} else {
fp = "assets/" + fp
encs, ok = cm[fp]
}
}
if !ok {
return fiber.ErrNotFound
}
if strings.HasSuffix(fp, ".css") {
c.Response().Header.SetContentType("text/css")
} else if strings.HasSuffix(fp, ".js") {
c.Response().Header.SetContentType("text/javascript")
} else if strings.HasSuffix(fp, ".jpg") {
c.Response().Header.SetContentType("image/jpeg")
} else if strings.HasSuffix(fp, ".ttf") {
c.Response().Header.SetContentType("font/ttf")
}
if len(encs) != 0 {
ae := c.Request().Header.Peek("Accept-Encoding")
if len(ae) == 1 && ae[0] == '*' {
c.Response().Header.SetContentEncodingBytes(encs[0])
fp += "." + cfg.B2s(encs[0])
} else {
for _, enc := range encs {
if bytes.Index(ae, enc) != -1 {
c.Response().Header.SetContentEncodingBytes(enc)
fp += "." + cfg.B2s(enc)
break
}
}
}
}
f, err := pfs.Open(fp)
if err != nil {
return err
}
c.Set("Cache-Control", "public, max-age=28800")
return c.SendStream(f)
})
}
} }
func main() { func main() {
app := fiber.New(fiber.Config{ app := fiber.New(fiber.Config{
//Prefork: cfg.Prefork, // moved to ListenConfig in v3
JSONEncoder: json.Marshal, JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal, JSONDecoder: json.Unmarshal,
@@ -112,6 +252,10 @@ func main() {
TrustProxyConfig: fiber.TrustProxyConfig{Proxies: cfg.TrustedProxies}, TrustProxyConfig: fiber.TrustProxyConfig{Proxies: cfg.TrustedProxies},
}) })
if cfg.Debug {
app.Server().Logger = fasthttp.Logger(log.New(os.Stdout, "", log.LstdFlags))
}
if !cfg.Debug { // you wanna catch any possible panics as soon as possible if !cfg.Debug { // you wanna catch any possible panics as soon as possible
app.Use(recover.New()) app.Use(recover.New())
} }
@@ -159,10 +303,14 @@ func main() {
if cfg.EmbedFiles { if cfg.EmbedFiles {
misc.Log("using embedded files") misc.Log("using embedded files")
ServeFromFS(app, staticfs{}) ServeFS(app, static_files.All)
} else { } else {
misc.Log("loading files dynamically") misc.Log("loading files dynamically")
ServeFromFS(app, osfs{}) r, err := os.OpenRoot("static")
if err != nil {
panic(err)
}
ServeFS(app, r.FS())
} }
// why? because when you load a page without link rel="icon" the browser will // why? because when you load a page without link rel="icon" the browser will

View File

@@ -4,11 +4,5 @@ package static
import "embed" import "embed"
//go:embed assets/* //go:embed */*
var Assets embed.FS var All embed.FS
//go:embed instance/*
var Instance embed.FS
//go:embed external/*
var External embed.FS

View File

@@ -49,7 +49,7 @@ templ TrackHeader(prefs cfg.Preferences, t sc.Track, needPlayer bool) {
<link rel="icon" type="image/x-icon" href={ t.Artwork }/> <link rel="icon" type="image/x-icon" href={ t.Artwork }/>
if needPlayer { if needPlayer {
if *prefs.Player == cfg.HLSPlayer { if *prefs.Player == cfg.HLSPlayer {
<script src="/_/static/js/hls.light.min.js"></script> <script src="/_/static/external/hls.light.min.js"></script>
} }
} }
} }
@@ -277,7 +277,7 @@ templ TrackEmbed(prefs cfg.Preferences, t sc.Track, stream string, displayErr st
<link rel="stylesheet" href="/_/static/global.css"/> <link rel="stylesheet" href="/_/static/global.css"/>
<title>soundcloak</title> <title>soundcloak</title>
if *prefs.Player == cfg.HLSPlayer && stream != "" { if *prefs.Player == cfg.HLSPlayer && stream != "" {
<script src="/_/static/js/hls.light.js"></script> <script src="/_/static/external/hls.light.min.js"></script>
} }
</head> </head>
<body> <body>