preference to dynamically load comments and small fixes

This commit is contained in:
Laptop
2025-01-18 01:51:11 +02:00
parent bedb149cce
commit 71012be90e
11 changed files with 171 additions and 51 deletions

View File

@@ -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"}
}

View File

@@ -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))
}

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

41
main.go
View File

@@ -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 {

25
static/assets/comments.js Normal file
View File

@@ -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();
}

View File

@@ -65,7 +65,7 @@ templ Preferences(prefs cfg.Preferences) {
<label>
Autoplay next track in playlists:
@checkbox("AutoplayNextTrack", *prefs.AutoplayNextTrack)
(requires JS)
(requires JS; you need to allow autoplay from this domain!!)
</label>
if *prefs.AutoplayNextTrack {
<label>
@@ -81,6 +81,11 @@ templ Preferences(prefs cfg.Preferences) {
@checkbox("SearchSuggestions", *prefs.SearchSuggestions)
(requires JS)
</label>
<label>
Dynamically load comments:
@checkbox("DynamicLoadComments", *prefs.DynamicLoadComments)
(requires JS)
</label>
<label>
Player:
@sel("Player", []option{

View File

@@ -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 != "" {
<img src={ t.Artwork } width="300px"/>
}
@@ -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 {
<div style="display: flex; margin-bottom: 1rem;">
<a class="btn" href={ templ.SafeURL("/_/restream" + t.Href() + "?metadata=true") } download={ t.Permalink + "." + toExt(audio) }>download</a>
<a class="btn" href={ templ.SafeURL("/_/restream" + t.Href() + "?metadata=true") } download={ t.Permalink + "." + toExt(*downloadAudio) }>download</a>
</div>
}
if t.Genre != "" {
@@ -197,9 +197,36 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string,
if t.TagList != "" {
<p>Tags: { strings.Join(sc.TagListParser(t.TagList), ", ") }</p>
}
if comments != nil {
<h1>Comments</h1>
if *prefs.DynamicLoadComments {
if comments != nil {
<div id="comments">
@Comments(comments)
</div>
<script async src="/_/static/comments.js"></script>
if comments.Next != "" {
<a class="btn" href={ templ.SafeURL("?pagination=" + url.QueryEscape(strings.Split(comments.Next, "/comments")[1])) } rel="noreferrer" onclick="event.preventDefault(); comments(this)" data-id={ string(t.ID) }>more comments</a>
}
} else {
<div id="comments"></div>
<script async src="/_/static/comments.js"></script>
<a class="btn" href="?pagination=%3Flimit%3D20%26threaded%3D1" data-id={ string(t.ID) } onclick="event.preventDefault(); comments(this)">load comments</a>
}
} else {
if comments != nil {
<div>
@Comments(comments)
</div>
if comments.Next != "" {
<a class="btn" href={ templ.SafeURL("?pagination=" + url.QueryEscape(strings.Split(comments.Next, "/comments")[1])) } rel="noreferrer">more comments</a>
}
} else {
<a class="btn" href="?pagination=%3Flimit%3D20%26threaded%3D1">load comments</a>
}
}
}
templ Comments(comments *sc.Paginated[*sc.Comment]) {
for _, c := range comments.Collection {
<div class="listing">
if c.Author.Avatar != "" {
@@ -213,10 +240,6 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string,
</div>
</div>
}
</div>
} else {
<a class="btn" href="?pagination=%3Flimit%3D20%26threaded%3D1">load comments</a>
}
}
templ TrackEmbed(prefs cfg.Preferences, t sc.Track, stream string, displayErr string) {