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

282
main.go
View File

@@ -1,13 +1,16 @@
package main
import (
"bytes"
"fmt"
"io"
"io/fs"
"log"
"math/rand"
"net/url"
"os"
"strings"
"sync"
"git.maid.zone/stuff/soundcloak/lib/misc"
"github.com/gofiber/fiber/v3"
@@ -23,8 +26,9 @@ import (
proxystreams "git.maid.zone/stuff/soundcloak/lib/proxy_streams"
"git.maid.zone/stuff/soundcloak/lib/restream"
"git.maid.zone/stuff/soundcloak/lib/sc"
static_files "git.maid.zone/stuff/soundcloak/static"
"git.maid.zone/stuff/soundcloak/templates"
static_files "git.maid.zone/stuff/soundcloak/static"
)
func boolean(b bool) string {
@@ -34,77 +38,213 @@ func boolean(b bool) string {
return "Disabled"
}
type osfs struct{}
func (osfs) Open(name string) (fs.File, error) {
misc.Log("osfs:", name)
if strings.HasPrefix(name, "js/") {
return os.Open("static/external/" + name[3:])
}
f, err := os.Open("static/instance/" + name)
if err == nil {
return f, nil
}
f, err = os.Open("static/assets/" + name)
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")
}
type compressionMap = map[string][][]byte
func parseCompressionMap(path string, filesystem fs.FS) compressionMap {
f, err := filesystem.Open(path + "/.compression")
if err != nil {
return nil
}
defer f.Close()
data, err := io.ReadAll(f)
if err != nil {
return nil
}
sp := bytes.Split(data, []byte("\n"))
cm := make(compressionMap, len(sp))
for _, h := range sp {
sp2 := bytes.Split(h, []byte("|"))
if len(sp2) != 2 || string(sp2[0]) == ".gitkeep" {
continue
}
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() {
app := fiber.New(fiber.Config{
//Prefork: cfg.Prefork, // moved to ListenConfig in v3
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
@@ -112,6 +252,10 @@ func main() {
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
app.Use(recover.New())
}
@@ -159,10 +303,14 @@ func main() {
if cfg.EmbedFiles {
misc.Log("using embedded files")
ServeFromFS(app, staticfs{})
ServeFS(app, static_files.All)
} else {
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

View File

@@ -4,11 +4,5 @@ package static
import "embed"
//go:embed assets/*
var Assets embed.FS
//go:embed instance/*
var Instance embed.FS
//go:embed external/*
var External embed.FS
//go:embed */*
var All 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 }/>
if needPlayer {
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"/>
<title>soundcloak</title>
if *prefs.Player == cfg.HLSPlayer && stream != "" {
<script src="/_/static/js/hls.light.js"></script>
<script src="/_/static/external/hls.light.min.js"></script>
}
</head>
<body>