mirror of
https://git.maid.zone/stuff/soundcloak.git
synced 2025-12-10 05:39:38 +05:00
preference to dynamically load comments and small fixes
This commit is contained in:
@@ -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"}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
41
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 {
|
||||
|
||||
25
static/assets/comments.js
Normal file
25
static/assets/comments.js
Normal 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();
|
||||
}
|
||||
@@ -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{
|
||||
|
||||
@@ -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,25 +197,48 @@ 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>
|
||||
<div>
|
||||
for _, c := range comments.Collection {
|
||||
<div class="listing">
|
||||
if c.Author.Avatar != "" {
|
||||
<img src={ c.Author.Avatar }/>
|
||||
} else {
|
||||
<img src="/_/static/placeholder.jpg"/>
|
||||
}
|
||||
<div class="comment">
|
||||
<h3 class="link"><a href={ templ.SafeURL("/" + c.Author.Permalink) }>{ c.Author.Username }</a></h3>
|
||||
<p>{ c.Body }</p>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
}
|
||||
</div>
|
||||
} 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 {
|
||||
<a class="btn" href="?pagination=%3Flimit%3D20%26threaded%3D1">load comments</a>
|
||||
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 != "" {
|
||||
<img src={ c.Author.Avatar }/>
|
||||
} else {
|
||||
<img src="/_/static/placeholder.jpg"/>
|
||||
}
|
||||
<div class="comment">
|
||||
<h3 class="link"><a href={ templ.SafeURL("/" + c.Author.Permalink) }>{ c.Author.Username }</a></h3>
|
||||
<p>{ c.Body }</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user