diff --git a/lib/cfg/init.go b/lib/cfg/init.go index 27c1503..2be5987 100644 --- a/lib/cfg/init.go +++ b/lib/cfg/init.go @@ -134,6 +134,7 @@ func defaultPreferences() { DefaultPreferences.ShowAudio = &False DefaultPreferences.SearchSuggestions = &False + DefaultPreferences.DynamicLoadComments = &False } func loadDefaultPreferences(loaded Preferences) { @@ -216,6 +217,12 @@ func loadDefaultPreferences(loaded Preferences) { } else { DefaultPreferences.SearchSuggestions = &False } + + if loaded.DynamicLoadComments != nil { + DefaultPreferences.DynamicLoadComments = loaded.DynamicLoadComments + } else { + DefaultPreferences.DynamicLoadComments = &False + } } func boolean(in string) bool { @@ -240,7 +247,7 @@ func fromEnv() error { env = os.Getenv("DEFAULT_PREFERENCES") if env != "" { var p Preferences - err := json.Unmarshal([]byte(env), &p) + err := json.Unmarshal(S2b(env), &p) if err != nil { return wrappedError{err, "DEFAULT_PREFERENCES"} } @@ -383,7 +390,7 @@ func fromEnv() error { env = os.Getenv("TRUSTED_PROXIES") if env != "" { var p []string - err := json.Unmarshal([]byte(env), &p) + err := json.Unmarshal(S2b(env), &p) if err != nil { return wrappedError{err, "TRUSTED_PROXIES"} } diff --git a/lib/cfg/misc.go b/lib/cfg/misc.go index b75b697..3b7a6f8 100644 --- a/lib/cfg/misc.go +++ b/lib/cfg/misc.go @@ -1,5 +1,7 @@ package cfg +import "unsafe" + // seems soundcloud has 4 of these (i1, i2, i3, i4) // they point to the same ip from my observations, and they all serve the same files const ImageCDN = "i1.sndcdn.com" @@ -67,4 +69,14 @@ type Preferences struct { ShowAudio *bool // display audio (aac, opus, mpeg etc) under track player SearchSuggestions *bool // load search suggestions on main page + + DynamicLoadComments *bool // dynamic comments loader without leaving track page +} + +func B2s(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} + +func S2b(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) } diff --git a/lib/preferences/init.go b/lib/preferences/init.go index 2f491d9..46641f1 100644 --- a/lib/preferences/init.go +++ b/lib/preferences/init.go @@ -59,13 +59,17 @@ func Defaults(dst *cfg.Preferences) { if dst.SearchSuggestions == nil { dst.SearchSuggestions = cfg.DefaultPreferences.SearchSuggestions } + + if dst.DynamicLoadComments == nil { + dst.DynamicLoadComments = cfg.DefaultPreferences.DynamicLoadComments + } } func Get(c fiber.Ctx) (cfg.Preferences, error) { rawprefs := c.Cookies("prefs", "{}") var p cfg.Preferences - err := json.Unmarshal([]byte(rawprefs), &p) + err := json.Unmarshal(cfg.S2b(rawprefs), &p) if err != nil { return p, err } @@ -87,6 +91,7 @@ type PrefsForm struct { DownloadAudio string ShowAudio string SearchSuggestions string + DynamicLoadComments string } type Export struct { @@ -178,6 +183,12 @@ func Load(r *fiber.App) { old.SearchSuggestions = &cfg.False } + if p.DynamicLoadComments == "on" { + old.DynamicLoadComments = &cfg.True + } else { + old.DynamicLoadComments = &cfg.False + } + old.Player = &p.Player data, err := json.Marshal(old) @@ -187,7 +198,7 @@ func Load(r *fiber.App) { c.Cookie(&fiber.Cookie{ Name: "prefs", - Value: string(data), + Value: cfg.B2s(data), Expires: time.Now().Add(400 * 24 * time.Hour), HTTPOnly: true, SameSite: "strict", @@ -251,7 +262,7 @@ func Load(r *fiber.App) { c.Cookie(&fiber.Cookie{ Name: "prefs", - Value: string(data), + Value: cfg.B2s(data), Expires: time.Now().Add(400 * 24 * time.Hour), HTTPOnly: true, SameSite: "strict", diff --git a/lib/proxy_images/init.go b/lib/proxy_images/init.go index bc3f705..f7eef3e 100644 --- a/lib/proxy_images/init.go +++ b/lib/proxy_images/init.go @@ -24,15 +24,15 @@ func Load(r *fiber.App) { } r.Get("/_/proxy/images", func(c fiber.Ctx) error { - url := c.Query("url") - if url == "" { + url := c.RequestCtx().QueryArgs().Peek("url") + if len(url) == 0 { return fiber.ErrBadRequest } parsed := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(parsed) - err := parsed.Parse(nil, []byte(url)) + err := parsed.Parse(nil, url) if err != nil { return err } diff --git a/lib/proxy_streams/init.go b/lib/proxy_streams/init.go index 1671227..1008518 100644 --- a/lib/proxy_streams/init.go +++ b/lib/proxy_streams/init.go @@ -13,15 +13,15 @@ import ( func Load(r *fiber.App) { r.Get("/_/proxy/streams", func(c fiber.Ctx) error { - ur := c.Query("url") - if ur == "" { + ur := c.RequestCtx().QueryArgs().Peek("url") + if len(ur) == 0 { return fiber.ErrBadRequest } parsed := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(parsed) - err := parsed.Parse(nil, []byte(ur)) + err := parsed.Parse(nil, ur) if err != nil { return err } @@ -52,15 +52,15 @@ func Load(r *fiber.App) { }) r.Get("/_/proxy/streams/aac", func(c fiber.Ctx) error { - ur := c.Query("url") - if ur == "" { + ur := c.RequestCtx().QueryArgs().Peek("url") + if len(ur) == 0 { return fiber.ErrBadRequest } parsed := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(parsed) - err := parsed.Parse(nil, []byte(ur)) + err := parsed.Parse(nil, ur) if err != nil { return err } @@ -90,15 +90,15 @@ func Load(r *fiber.App) { }) r.Get("/_/proxy/streams/playlist", func(c fiber.Ctx) error { - ur := c.Query("url") - if ur == "" { + ur := c.RequestCtx().QueryArgs().Peek("url") + if len(ur) == 0 { return fiber.ErrBadRequest } parsed := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(parsed) - err := parsed.Parse(nil, []byte(ur)) + err := parsed.Parse(nil, ur) if err != nil { return err } @@ -127,13 +127,13 @@ func Load(r *fiber.App) { data = resp.Body() } - var sp = bytes.Split(data, []byte("\n")) + var sp = bytes.Split(data, []byte{'\n'}) for i, l := range sp { if len(l) == 0 || l[0] == '#' { continue } - l = []byte("/_/proxy/streams?url=" + url.QueryEscape(string(l))) + l = []byte("/_/proxy/streams?url=" + url.QueryEscape(cfg.B2s(l))) sp[i] = l } @@ -141,15 +141,15 @@ func Load(r *fiber.App) { }) r.Get("/_/proxy/streams/playlist/aac", func(c fiber.Ctx) error { - ur := c.Query("url") - if ur == "" { + ur := c.RequestCtx().QueryArgs().Peek("url") + if len(ur) == 0 { return fiber.ErrBadRequest } parsed := fasthttp.AcquireURI() defer fasthttp.ReleaseURI(parsed) - err := parsed.Parse(nil, []byte(ur)) + err := parsed.Parse(nil, ur) if err != nil { return err } @@ -186,14 +186,14 @@ func Load(r *fiber.App) { if l[0] == '#' { if bytes.HasPrefix(l, []byte(`#EXT-X-MAP:URI="`)) { - l = []byte(`#EXT-X-MAP:URI="/_/proxy/streams/aac?url=` + url.QueryEscape(string(l[16:len(l)-1])) + `"`) + l = []byte(`#EXT-X-MAP:URI="/_/proxy/streams/aac?url=` + url.QueryEscape(cfg.B2s(l[16:len(l)-1])) + `"`) sp[i] = l } continue } - l = []byte("/_/proxy/streams/aac?url=" + url.QueryEscape(string(l))) + l = []byte("/_/proxy/streams/aac?url=" + url.QueryEscape(cfg.B2s(l))) sp[i] = l } diff --git a/lib/restream/init.go b/lib/restream/init.go index 6c87514..5e1cbc7 100644 --- a/lib/restream/init.go +++ b/lib/restream/init.go @@ -209,7 +209,7 @@ func Load(r *fiber.App) { return err } - var isDownload = c.Query("metadata") == "true" + var isDownload = string(c.RequestCtx().QueryArgs().Peek("metadata")) == "true" var quality *string if isDownload { quality = p.DownloadAudio diff --git a/lib/sc/init.go b/lib/sc/init.go index cc013ca..2a9a077 100644 --- a/lib/sc/init.go +++ b/lib/sc/init.go @@ -142,7 +142,7 @@ func GetClientID() (string, error) { data = resp.Body() } - m, _ := verRegex.FindStringMatch(string(data)) + m, _ := verRegex.FindStringMatch(cfg.B2s(data)) if m == nil { return "", ErrVersionNotFound } @@ -160,7 +160,7 @@ func GetClientID() (string, error) { } if experimental_GetClientID { - m, _ = scriptRegex.FindStringMatch(string(data)) + m, _ = scriptRegex.FindStringMatch(cfg.B2s(data)) if m != nil { g = m.GroupByNumber(1) if g != nil { @@ -175,7 +175,7 @@ func GetClientID() (string, error) { data = resp.Body() } - m, _ = clientIdRegex.FindStringMatch(string(data)) + m, _ = clientIdRegex.FindStringMatch(cfg.B2s(data)) if m != nil { g = m.GroupByNumber(1) if g != nil { @@ -192,7 +192,7 @@ func GetClientID() (string, error) { ch := make(chan string, 1) wg := &sync.WaitGroup{} done := false - m, _ = scriptsRegex.FindStringMatch(string(data)) + m, _ = scriptsRegex.FindStringMatch(cfg.B2s(data)) for m != nil { g = m.GroupByNumber(1) if g != nil { diff --git a/main.go b/main.go index 77a6564..aa1acc3 100644 --- a/main.go +++ b/main.go @@ -260,7 +260,7 @@ Disallow: /`) //fmt.Println(c.Hostname(), c.Protocol(), c.IPs()) - u, err := url.Parse(string(loc)) + u, err := url.Parse(cfg.B2s(loc)) if err != nil { return err } @@ -771,8 +771,45 @@ Disallow: /`) } } + var downloadAudio *string + if cfg.Restream { + _, audio := track.Media.SelectCompatible(*prefs.DownloadAudio, true) + downloadAudio = &audio + } + c.Set("Content-Type", "text/html") - return templates.Base(track.Title+" by "+track.Author.Username, templates.Track(prefs, track, stream, displayErr, c.Query("autoplay") == "true", playlist, nextTrack, c.Query("volume"), mode, audio, comments), templates.TrackHeader(prefs, track, true)).Render(c.RequestCtx(), c) + return templates.Base(track.Title+" by "+track.Author.Username, templates.Track(prefs, track, stream, displayErr, string(c.RequestCtx().QueryArgs().Peek("autoplay")) == "true", playlist, nextTrack, c.Query("volume"), mode, audio, downloadAudio, comments), templates.TrackHeader(prefs, track, true)).Render(c.RequestCtx(), c) + }) + + app.Get("/_/partials/comments/:id", func(c fiber.Ctx) error { + id := c.Params("id") + if id == "" { + return fiber.ErrBadRequest + } + + pagination := c.RequestCtx().QueryArgs().Peek("pagination") + if len(pagination) == 0 { + return fiber.ErrBadRequest + } + + prefs, err := preferences.Get(c) + if err != nil { + return err + } + + t := sc.Track{ID: json.Number(id)} + comm, err := t.GetComments("", prefs, cfg.B2s(pagination)) + if err != nil { + return err + } + + if comm.Next != "" { + c.Set("next", "?pagination="+url.QueryEscape(strings.Split(comm.Next, "/comments")[1])) + } else { + c.Set("next", "done") + } + + return templates.Comments(comm).Render(c.RequestCtx(), c) }) app.Get("/:user", func(c fiber.Ctx) error { diff --git a/static/assets/comments.js b/static/assets/comments.js new file mode 100644 index 0000000..4d83438 --- /dev/null +++ b/static/assets/comments.js @@ -0,0 +1,25 @@ +var comm = document.getElementById('comments'); +function comments(self) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', '/_/partials/comments/'+self.getAttribute('data-id')+self.getAttribute('href'), true); + xhr.onerror = function(e) { + alert('Something went wrong. Check console'); + console.error(e); + } + xhr.onload = function() { + if (xhr.status != 200) { + alert(xhr.responseText); + return; + } + + comm.innerHTML += xhr.responseText; + var next = xhr.getResponseHeader('next'); + if (next == 'done') { + self.remove(); + } else { + self.setAttribute('href', next); + } + self.textContent = 'more comments'; + } + xhr.send(); +} \ No newline at end of file diff --git a/templates/preferences.templ b/templates/preferences.templ index 98c5653..197edfe 100644 --- a/templates/preferences.templ +++ b/templates/preferences.templ @@ -65,7 +65,7 @@ templ Preferences(prefs cfg.Preferences) { Autoplay next track in playlists: @checkbox("AutoplayNextTrack", *prefs.AutoplayNextTrack) - (requires JS) + (requires JS; you need to allow autoplay from this domain!!) if *prefs.AutoplayNextTrack { @@ -81,6 +81,11 @@ templ Preferences(prefs cfg.Preferences) { @checkbox("SearchSuggestions", *prefs.SearchSuggestions) (requires JS) + + Dynamically load comments: + @checkbox("DynamicLoadComments", *prefs.DynamicLoadComments) + (requires JS) + Player: @sel("Player", []option{ diff --git a/templates/track.templ b/templates/track.templ index 90890e1..8576f49 100644 --- a/templates/track.templ +++ b/templates/track.templ @@ -145,7 +145,7 @@ templ TrackItem(track *sc.Track, showUsername bool, overrideHref string) { } } -templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string, autoplay bool, playlist *sc.Playlist, nextTrack *sc.Track, volume string, mode string, audio string, comments *sc.Paginated[*sc.Comment]) { +templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string, autoplay bool, playlist *sc.Playlist, nextTrack *sc.Track, volume string, mode string, audio string, downloadAudio *string, comments *sc.Paginated[*sc.Comment]) { if t.Artwork != "" { } @@ -153,7 +153,7 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string, @TrackPlayer(prefs, t, stream, displayErr, autoplay, nextTrack, playlist, volume, mode, audio) if cfg.Restream { - download + download } if t.Genre != "" { @@ -197,25 +197,48 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string, if t.TagList != "" { Tags: { strings.Join(sc.TagListParser(t.TagList), ", ") } } - if comments != nil { - Comments - - for _, c := range comments.Collection { - - if c.Author.Avatar != "" { - - } else { - - } - - { c.Author.Username } - { c.Body } - - + Comments + if *prefs.DynamicLoadComments { + if comments != nil { + + @Comments(comments) + + + if comments.Next != "" { + more comments } - + } else { + + + load comments + } } else { - load comments + if comments != nil { + + @Comments(comments) + + if comments.Next != "" { + more comments + } + } else { + load comments + } + } +} + +templ Comments(comments *sc.Paginated[*sc.Comment]) { + for _, c := range comments.Collection { + + if c.Author.Avatar != "" { + + } else { + + } + + { c.Author.Username } + { c.Body } + + } }
Tags: { strings.Join(sc.TagListParser(t.TagList), ", ") }
{ c.Body }