diff --git a/assets/global.css b/assets/global.css index b765933..6ab5b6d 100644 --- a/assets/global.css +++ b/assets/global.css @@ -133,14 +133,6 @@ details > summary:hover { width: fit-content; } -footer { - margin-top: 5rem; - gap: 1rem; - display: grid; - grid-template: auto / auto auto auto; - justify-content: center; -} - select { background: var(--secondary); border-color: var(--0); diff --git a/assets/index.css b/assets/index.css new file mode 100644 index 0000000..5d9893e --- /dev/null +++ b/assets/index.css @@ -0,0 +1,26 @@ +#search-suggestions { + list-style: none; + position: absolute; + background-color: var(--secondary); + margin: 0; + padding: 1rem; + padding-bottom: 0; + /* god damn this shit i hate webdev */ + width: 100%; + width: -moz-available; + width: -webkit-fill-available; + width: stretch; + width: fill-available; +} + +#search-suggestions > li { + padding-bottom: 1rem; +} + +footer { + margin-top: 5rem; + gap: 1rem; + display: grid; + grid-template: auto / auto auto auto; + justify-content: center; +} \ No newline at end of file diff --git a/assets/index.html b/assets/index.html deleted file mode 100644 index 1f6661f..0000000 --- a/assets/index.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - soundcloak - - - - - -

soundcloak

-
-
- - - -
- - -
- - - - \ No newline at end of file diff --git a/assets/index.js b/assets/index.js new file mode 100644 index 0000000..3acda8a --- /dev/null +++ b/assets/index.js @@ -0,0 +1,55 @@ +var searchSuggestions = document.getElementById('search-suggestions'); +var input = document.getElementById('q'); +var timeout; + +function getSuggestions() { + if (input.value == 0) { + searchSuggestions.style.display = 'none'; + return; + } + + var xhr = new XMLHttpRequest(); + xhr.open('GET', '/_/searchSuggestions?q=' + encodeURIComponent(input.value), true); + xhr.onload = function () { + try { + var cloned = searchSuggestions.cloneNode(false); + + var data = JSON.parse(xhr.responseText); + if (data.length == 0) { + searchSuggestions.style.display = 'none'; + return; + } + + for (var i = 0; i < data.length; i++) { + var e = document.createElement('li'); + e.textContent = data[i]; + e.onclick = function () { + input.value = this.textContent; + searchSuggestions.style.display = 'none'; + } + cloned.appendChild(e); + } + + searchSuggestions.parentNode.replaceChild(cloned, searchSuggestions); + searchSuggestions = cloned; + searchSuggestions.style.display = 'block'; + } catch { + searchSuggestions.style.display = 'none'; + } + } + + xhr.onerror = function () { + searchSuggestions.style.display = 'none'; + } + + xhr.send(); +} + +input.addEventListener('input', function () { + if (!timeout) { + timeout = setTimeout(getSuggestions, 250); + } else { + clearTimeout(timeout); + timeout = setTimeout(getSuggestions, 250); + } +}); \ No newline at end of file diff --git a/lib/cfg/init.go b/lib/cfg/init.go index be9a88f..8b31e02 100644 --- a/lib/cfg/init.go +++ b/lib/cfg/init.go @@ -99,6 +99,10 @@ var CodegenConfig = false // ParseDescriptions: true // AutoplayNextTrack: false // DefaultAutoplayMode: AutoplayNormal +// HLSAudio: AudioMP3 +// RestreamAudio: AudioMP3 +// DownloadAudio: AudioMP3 +// ShowAudio: false func defaultPreferences() { var p string if Restream { @@ -126,6 +130,8 @@ func defaultPreferences() { DefaultPreferences.DownloadAudio = &p3 DefaultPreferences.ShowAudio = &False + + DefaultPreferences.SearchSuggestions = &False } func loadDefaultPreferences(loaded Preferences) { @@ -202,6 +208,12 @@ func loadDefaultPreferences(loaded Preferences) { } else { DefaultPreferences.ShowAudio = &False } + + if loaded.SearchSuggestions != nil { + DefaultPreferences.SearchSuggestions = loaded.SearchSuggestions + } else { + DefaultPreferences.SearchSuggestions = &False + } } func boolean(in string) bool { diff --git a/lib/cfg/misc.go b/lib/cfg/misc.go index dfbf3bd..b75b697 100644 --- a/lib/cfg/misc.go +++ b/lib/cfg/misc.go @@ -58,11 +58,13 @@ type Preferences struct { DefaultAutoplayMode *string // "normal" or "random" - // Check line 38 for constants + // Check above for more info // Probably best to keep all at "mpeg" by default for compatibility HLSAudio *string // Please don't use "opus" or "best". hls.js doesn't work with ogg/opus RestreamAudio *string // You can actually use anything here DownloadAudio *string // "aac" may not play well with some players ShowAudio *bool // display audio (aac, opus, mpeg etc) under track player + + SearchSuggestions *bool // load search suggestions on main page } diff --git a/lib/preferences/init.go b/lib/preferences/init.go index 61fe159..65914d0 100644 --- a/lib/preferences/init.go +++ b/lib/preferences/init.go @@ -55,6 +55,10 @@ func Defaults(dst *cfg.Preferences) { if dst.ShowAudio == nil { dst.ShowAudio = cfg.DefaultPreferences.ShowAudio } + + if dst.SearchSuggestions == nil { + dst.SearchSuggestions = cfg.DefaultPreferences.SearchSuggestions + } } func Get(c *fiber.Ctx) (cfg.Preferences, error) { @@ -82,6 +86,7 @@ type PrefsForm struct { RestreamAudio string DownloadAudio string ShowAudio string + SearchSuggestions string } type Export struct { @@ -167,6 +172,12 @@ func Load(r fiber.Router) { old.ParseDescriptions = &cfg.False } + if p.SearchSuggestions == "on" { + old.SearchSuggestions = &cfg.True + } else { + old.SearchSuggestions = &cfg.False + } + old.Player = &p.Player data, err := json.Marshal(old) diff --git a/lib/sc/init.go b/lib/sc/init.go index 75be33a..fe0210b 100644 --- a/lib/sc/init.go +++ b/lib/sc/init.go @@ -408,6 +408,25 @@ func TagListParser(taglist string) (res []string) { return } +type SearchSuggestion struct { + Query string `json:"query"` +} + +func GetSearchSuggestions(cid string, query string) ([]string, error) { + p := Paginated[SearchSuggestion]{Next: "https://" + api + "/search/queries?limit=10&q=" + url.QueryEscape(query)} + err := p.Proceed(cid, false) + if err != nil { + return nil, err + } + + l := make([]string, len(p.Collection)) // just so the response is a bit smaller, why not + for i, r := range p.Collection { + l[i] = r.Query + } + + return l, nil +} + // could probably make a generic function, whatever func init() { go func() { diff --git a/main.go b/main.go index 5207e79..2d98b28 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "context" "log" "math/rand" "net/url" @@ -65,6 +64,21 @@ func main() { }) } + { + mainPageHandler := func(c *fiber.Ctx) error { + prefs, err := preferences.Get(c) + if err != nil { + return err + } + + c.Set("Content-Type", "text/html") + return templates.Base("", templates.MainPage(prefs), templates.MainPageHead()).Render(c.Context(), c) + } + + app.Get("/", mainPageHandler) + app.Get("/index.html", mainPageHandler) + } + app.Get("/search", func(c *fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { @@ -82,7 +96,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("tracks: "+q, templates.SearchTracks(p), nil).Render(context.Background(), c) + return templates.Base("tracks: "+q, templates.SearchTracks(p), nil).Render(c.Context(), c) case "users": p, err := sc.SearchUsers("", prefs, c.Query("pagination", "?q="+url.QueryEscape(q))) @@ -92,7 +106,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("users: "+q, templates.SearchUsers(p), nil).Render(context.Background(), c) + return templates.Base("users: "+q, templates.SearchUsers(p), nil).Render(c.Context(), c) case "playlists": p, err := sc.SearchPlaylists("", prefs, c.Query("pagination", "?q="+url.QueryEscape(q))) @@ -102,7 +116,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("playlists: "+q, templates.SearchPlaylists(p), nil).Render(context.Background(), c) + return templates.Base("playlists: "+q, templates.SearchPlaylists(p), nil).Render(c.Context(), c) } return c.SendStatus(404) @@ -197,7 +211,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.TrackEmbed(prefs, track, stream, displayErr).Render(context.Background(), c) + return templates.TrackEmbed(prefs, track, stream, displayErr).Render(c.Context(), c) }) app.Get("/tags/:tag", func(c *fiber.Ctx) error { @@ -219,7 +233,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("Recent tracks tagged "+tag, templates.RecentTracks(tag, p), nil).Render(context.Background(), c) + return templates.Base("Recent tracks tagged "+tag, templates.RecentTracks(tag, p), nil).Render(c.Context(), c) }) app.Get("/tags/:tag/popular-tracks", func(c *fiber.Ctx) error { @@ -241,7 +255,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("Popular tracks tagged "+tag, templates.PopularTracks(tag, p), nil).Render(context.Background(), c) + return templates.Base("Popular tracks tagged "+tag, templates.PopularTracks(tag, p), nil).Render(c.Context(), c) }) app.Get("/tags/:tag/playlists", func(c *fiber.Ctx) error { @@ -264,7 +278,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("Playlists tagged "+tag, templates.TaggedPlaylists(tag, p), nil).Render(context.Background(), c) + return templates.Base("Playlists tagged "+tag, templates.TaggedPlaylists(tag, p), nil).Render(c.Context(), c) }) app.Get("/_/featured", func(c *fiber.Ctx) error { @@ -280,7 +294,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("Featured Tracks", templates.FeaturedTracks(tracks), nil).Render(context.Background(), c) + return templates.Base("Featured Tracks", templates.FeaturedTracks(tracks), nil).Render(c.Context(), c) }) app.Get("/discover", func(c *fiber.Ctx) error { @@ -296,7 +310,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base("Discover", templates.Discover(selections), nil).Render(context.Background(), c) + return templates.Base("Discover", templates.Discover(selections), nil).Render(c.Context(), c) }) if cfg.ProxyImages { @@ -337,6 +351,20 @@ func main() { preferences.Load(app) + app.Get("/_/searchSuggestions", func(c *fiber.Ctx) error { + q := c.Query("q") + if q == "" { + return fiber.ErrBadRequest + } + + s, err := sc.GetSearchSuggestions("", q) + if err != nil { + return err + } + + return c.JSON(s) + }) + // Currently, /:user is the tracks page app.Get("/:user/tracks", func(c *fiber.Ctx) error { return c.Redirect("/" + c.Params("user")) @@ -367,7 +395,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserPlaylists(prefs, user, pl), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserPlaylists(prefs, user, pl), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/albums", func(c *fiber.Ctx) error { @@ -395,7 +423,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserAlbums(prefs, user, pl), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserAlbums(prefs, user, pl), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/reposts", func(c *fiber.Ctx) error { @@ -423,7 +451,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserReposts(prefs, user, p), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserReposts(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/likes", func(c *fiber.Ctx) error { @@ -451,7 +479,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserLikes(prefs, user, p), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserLikes(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/popular-tracks", func(c *fiber.Ctx) error { @@ -479,7 +507,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserTopTracks(prefs, user, p), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserTopTracks(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/followers", func(c *fiber.Ctx) error { @@ -507,7 +535,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserFollowers(prefs, user, p), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserFollowers(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/following", func(c *fiber.Ctx) error { @@ -535,7 +563,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserFollowing(prefs, user, p), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserFollowing(prefs, user, p), templates.UserHeader(user)).Render(c.Context(), c) }) app.Get("/:user/:track", func(c *fiber.Ctx) error { @@ -629,7 +657,7 @@ func main() { } 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), templates.TrackHeader(prefs, track, true)).Render(context.Background(), c) + 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), templates.TrackHeader(prefs, track, true)).Render(c.Context(), c) }) app.Get("/:user", func(c *fiber.Ctx) error { @@ -657,7 +685,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(usr.Username, templates.User(prefs, usr, p), templates.UserHeader(usr)).Render(context.Background(), c) + return templates.Base(usr.Username, templates.User(prefs, usr, p), templates.UserHeader(usr)).Render(c.Context(), c) }) app.Get("/:user/sets/:playlist", func(c *fiber.Ctx) error { @@ -697,7 +725,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(playlist.Title+" by "+playlist.Author.Username, templates.Playlist(prefs, playlist), templates.PlaylistHeader(playlist)).Render(context.Background(), c) + return templates.Base(playlist.Title+" by "+playlist.Author.Username, templates.Playlist(prefs, playlist), templates.PlaylistHeader(playlist)).Render(c.Context(), c) }) app.Get("/:user/_/related", func(c *fiber.Ctx) error { @@ -725,7 +753,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(user.Username, templates.UserRelated(prefs, user, r), templates.UserHeader(user)).Render(context.Background(), c) + return templates.Base(user.Username, templates.UserRelated(prefs, user, r), templates.UserHeader(user)).Render(c.Context(), c) }) // I'd like to make this "related" but keeping it "recommended" to have the same url as soundcloud @@ -754,7 +782,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(track.Title+" by "+track.Author.Username, templates.RelatedTracks(track, r), templates.TrackHeader(prefs, track, false)).Render(context.Background(), c) + return templates.Base(track.Title+" by "+track.Author.Username, templates.RelatedTracks(track, r), templates.TrackHeader(prefs, track, false)).Render(c.Context(), c) }) app.Get("/:user/:track/sets", func(c *fiber.Ctx) error { @@ -782,7 +810,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(track.Title+" by "+track.Author.Username, templates.TrackInPlaylists(track, p), templates.TrackHeader(prefs, track, false)).Render(context.Background(), c) + return templates.Base(track.Title+" by "+track.Author.Username, templates.TrackInPlaylists(track, p), templates.TrackHeader(prefs, track, false)).Render(c.Context(), c) }) app.Get("/:user/:track/albums", func(c *fiber.Ctx) error { @@ -810,7 +838,7 @@ func main() { } c.Set("Content-Type", "text/html") - return templates.Base(track.Title+" by "+track.Author.Username, templates.TrackInAlbums(track, p), templates.TrackHeader(prefs, track, false)).Render(context.Background(), c) + return templates.Base(track.Title+" by "+track.Author.Username, templates.TrackInAlbums(track, p), templates.TrackHeader(prefs, track, false)).Render(c.Context(), c) }) if cfg.CodegenConfig { diff --git a/templates/base.templ b/templates/base.templ index 30d9fdf..a25337c 100644 --- a/templates/base.templ +++ b/templates/base.templ @@ -1,9 +1,6 @@ package templates -import ( - "git.maid.zone/stuff/soundcloak/lib/cfg" - "git.maid.zone/stuff/soundcloak/lib/textparsing" -) +import "git.maid.zone/stuff/soundcloak/lib/cfg" templ Base(title string, content templ.Component, head templ.Component) { @@ -32,22 +29,35 @@ templ Base(title string, content templ.Component, head templ.Component) { } -templ Description(prefs cfg.Preferences, text string, injected templ.Component) { - if text != "" || injected != nil { -
- Toggle description -

- if text != "" { - if *prefs.ParseDescriptions { - @templ.Raw(textparsing.Format(text)) - } else { - { text } - } - } - if injected != nil { - @injected - } -

-
- } +templ MainPageHead() { + +} + +templ MainPage(p cfg.Preferences) { +
+
+
+ + +
+ if *p.SearchSuggestions { + + + } +
+ +
+ + } diff --git a/templates/misc.templ b/templates/misc.templ new file mode 100644 index 0000000..0ed9d18 --- /dev/null +++ b/templates/misc.templ @@ -0,0 +1,26 @@ +package templates + +import ( + "git.maid.zone/stuff/soundcloak/lib/cfg" + "git.maid.zone/stuff/soundcloak/lib/textparsing" +) + +templ Description(prefs cfg.Preferences, text string, injected templ.Component) { + if text != "" || injected != nil { +
+ Toggle description +

+ if text != "" { + if *prefs.ParseDescriptions { + @templ.Raw(textparsing.Format(text)) + } else { + { text } + } + } + if injected != nil { + @injected + } +

+
+ } +} diff --git a/templates/preferences.templ b/templates/preferences.templ index 79d40a5..98c5653 100644 --- a/templates/preferences.templ +++ b/templates/preferences.templ @@ -76,6 +76,11 @@ templ Preferences(prefs cfg.Preferences) { }, *prefs.DefaultAutoplayMode) } +