diff --git a/README.md b/README.md index 8456e8d..b4bc0fb 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,54 @@ wip opensource ui for soundcloud # [official public instance](https://sc.maid.zone) there is no image and audio proxy for now so beware + +# Setting it up +## Prerequisites: +1. [node.js + npm](https://nodejs.org) (any recent enough version should do, it's just used for getting hls.js builds) +2. [golang](https://go.dev) (1.21 or higher was tested, others might work too) +3. [git](https://git-scm.com) + +1. Clone this repository: +```sh +git clone https://github.com/maid-zone/soundcloak +``` + +2. Go into the cloned repository: +```sh +cd soundcloak +``` + +3. Download hls.js: +```sh +npm i +``` + +4. Download templ: +```sh +go install github.com/a-h/templ/cmd/templ@latest +``` + +5. Download other required go modules: +```sh +go get +``` + +6. *Optional.* Edit config: +You can change some values in `lib/cfg/init.go` if you want. Keep in mind that you need to rebuild the binary each time you want to update the config. + +7. Generate code from templates & build binary: +*You might need to add go binaries to your PATH (add this line to your .bashrc / .zshrc / whatever)* +```sh +export PATH=${PATH}:`go env GOPATH`/bin +``` + +```sh +templ generate && go build main.go +``` + +8. Run the binary: +```sh +./main +``` + +This will run soundcloak on localhost, port 4664. \ No newline at end of file diff --git a/assets/index.html b/assets/index.html index 5f41545..59d7085 100644 --- a/assets/index.html +++ b/assets/index.html @@ -8,6 +8,11 @@
diff --git a/lib/cfg/init.go b/lib/cfg/init.go index f9c5df9..97930c9 100644 --- a/lib/cfg/init.go +++ b/lib/cfg/init.go @@ -22,4 +22,7 @@ const UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 // time-to-live for dns cache const DNSCacheTTL = 10 * time.Minute +// run soundcloak on this address (localhost:4664 by default) +const Addr = ":4664" + var JSON = jsoniter.ConfigFastest diff --git a/lib/sc/init.go b/lib/sc/init.go index 1df6a31..c7598b1 100644 --- a/lib/sc/init.go +++ b/lib/sc/init.go @@ -36,6 +36,8 @@ var httpc = fasthttp.HostClient{ var usersCache = map[string]cached[User]{} var tracksCache = map[string]cached[Track]{} +var userSearchCache = map[string]cached[Paginated[User]]{} +var trackSearchCache = map[string]cached[Paginated[Track]]{} var verRegex = regexp.MustCompile(`(?m)^$`) var scriptsRegex = regexp.MustCompile(`(?m)^$`) @@ -308,3 +310,33 @@ func (t Track) GetStream() (string, error) { return s.URL, nil } + +func SearchTracks(args string) (*Paginated[Track], error) { + cid, err := GetClientID() + if err != nil { + return nil, err + } + + p := Paginated[Track]{Next: "https://api-v2.soundcloud.com/search/tracks" + args + "&client_id=" + cid} + err = p.Proceed() + if err != nil { + return nil, err + } + + return &p, nil +} + +func SearchUsers(args string) (*Paginated[User], error) { + cid, err := GetClientID() + if err != nil { + return nil, err + } + + p := Paginated[User]{Next: "https://api-v2.soundcloud.com/search/users" + args + "&client_id=" + cid} + err = p.Proceed() + if err != nil { + return nil, err + } + + return &p, nil +} diff --git a/lib/sc/structs.go b/lib/sc/structs.go index 3fa3346..df3d765 100644 --- a/lib/sc/structs.go +++ b/lib/sc/structs.go @@ -73,6 +73,7 @@ type Track struct { type Paginated[T any] struct { Collection []T `json:"collection"` + Total int `json:"total_results"` Next string `json:"next_href"` } diff --git a/main.go b/main.go index 0e12592..990a686 100644 --- a/main.go +++ b/main.go @@ -5,12 +5,14 @@ import ( _ "embed" "fmt" "log" + "net/url" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/earlydata" "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/maid-zone/soundcloak/lib/cfg" "github.com/maid-zone/soundcloak/lib/sc" "github.com/maid-zone/soundcloak/templates" ) @@ -24,11 +26,43 @@ func main() { app.Static("/", "assets", fiber.Static{Compress: true, MaxAge: 3600}) app.Static("/js/hls.js/", "node_modules/hls.js/dist", fiber.Static{Compress: true, MaxAge: 3600}) + app.Get("/search", func(c *fiber.Ctx) error { + q := c.Query("q") + if q == "" { + return c.SendStatus(404) + } + + t := c.Query("type") + switch t { + case "tracks": + p, err := sc.SearchTracks("?q=" + url.QueryEscape(q)) + if err != nil { + fmt.Printf("error getting tracks for %s: %s\n", q, err) + return err + } + + c.Set("Content-Type", "text/html") + return templates.Base("tracks: "+q, templates.SearchTracks(p)).Render(context.Background(), c) + + case "users": + p, err := sc.SearchUsers("?q=" + url.QueryEscape(q)) + if err != nil { + fmt.Printf("error getting users for %s: %s\n", q, err) + return err + } + + c.Set("Content-Type", "text/html") + return templates.Base("users: "+q, templates.SearchUsers(p)).Render(context.Background(), c) + } + + return c.SendStatus(404) + }) + app.Get("/:user/:track", func(c *fiber.Ctx) error { track, err := sc.GetTrack(c.Params("user") + "/" + c.Params("track")) if err != nil { fmt.Printf("error getting %s from %s: %s\n", c.Params("track"), c.Params("user"), err) - return c.SendStatus(404) + return err } stream, err := track.GetStream() @@ -45,7 +79,7 @@ func main() { usr, err := sc.GetUser(c.Params("user")) if err != nil { fmt.Printf("error getting %s: %s\n", c.Params("user"), err) - return c.SendStatus(404) + return err } //fmt.Println("getuser", time.Since(h)) @@ -53,7 +87,7 @@ func main() { p, err := usr.GetTracks(c.Query("pagination", "?limit=20")) if err != nil { fmt.Printf("error getting %s tracks: %s\n", c.Params("user"), err) - return c.SendStatus(404) + return err } //fmt.Println("gettracks", time.Since(h)) @@ -61,5 +95,5 @@ func main() { return templates.Base(usr.Username, templates.User(usr, p)).Render(context.Background(), c) }) - log.Fatal(app.Listen(":4664")) + log.Fatal(app.Listen(cfg.Addr)) } diff --git a/templates/searchTracks.templ b/templates/searchTracks.templ new file mode 100644 index 0000000..0e16177 --- /dev/null +++ b/templates/searchTracks.templ @@ -0,0 +1,9 @@ +package templates + +import "github.com/maid-zone/soundcloak/lib/sc" + +templ SearchTracks(p *sc.Paginated[sc.Track]) { + for _, track := range p.Collection { +