mirror of
https://git.maid.zone/stuff/soundcloak.git
synced 2025-12-10 05:39:38 +05:00
table
This commit is contained in:
@@ -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 && \
|
||||||
|
|||||||
@@ -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
276
main.go
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user