diff --git a/lib/sc/track.go b/lib/sc/track.go index b4dfcd5..372fe02 100644 --- a/lib/sc/track.go +++ b/lib/sc/track.go @@ -481,3 +481,57 @@ func RecentTracks(cid string, prefs cfg.Preferences, args string) (*Paginated[*T return &p, nil } + +func (t *Track) GetRelated(cid string, prefs cfg.Preferences, args string) (*Paginated[*Track], error) { + p := Paginated[*Track]{ + Next: "https://" + api + "/tracks/" + string(t.ID) + "/related" + args, + } + + err := p.Proceed(cid, true) + if err != nil { + return nil, err + } + + for _, t := range p.Collection { + t.Fix(false, false) + t.Postfix(prefs, false) + } + + return &p, nil +} + +func (t *Track) GetPlaylists(cid string, prefs cfg.Preferences, args string) (*Paginated[*Playlist], error) { + p := Paginated[*Playlist]{ + Next: "https://" + api + "/tracks/" + string(t.ID) + "/playlists_without_albums" + args, + } + + err := p.Proceed(cid, true) + if err != nil { + return nil, err + } + + for _, p := range p.Collection { + p.Fix("", false, false) + p.Postfix(prefs, false, false) + } + + return &p, nil +} + +func (t *Track) GetAlbums(cid string, prefs cfg.Preferences, args string) (*Paginated[*Playlist], error) { + p := Paginated[*Playlist]{ + Next: "https://" + api + "/tracks/" + string(t.ID) + "/albums" + args, + } + + err := p.Proceed(cid, true) + if err != nil { + return nil, err + } + + for _, p := range p.Collection { + p.Fix("", false, false) + p.Postfix(prefs, false, false) + } + + return &p, nil +} diff --git a/main.go b/main.go index e7a69f1..f481974 100644 --- a/main.go +++ b/main.go @@ -325,6 +325,11 @@ func main() { preferences.Load(app) + // Currently, /:user is the tracks page + app.Get("/:user/tracks", func(c *fiber.Ctx) error { + return c.Redirect("/" + c.Params("user")) + }) + app.Get("/:user/sets", func(c *fiber.Ctx) error { prefs, err := preferences.Get(c) if err != nil { @@ -612,7 +617,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)).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(context.Background(), c) }) app.Get("/:user", func(c *fiber.Ctx) error { @@ -711,5 +716,90 @@ func main() { return templates.Base(user.Username, templates.UserRelated(prefs, user, r), templates.UserHeader(user)).Render(context.Background(), c) }) + // I'd like to make this "related" but keeping it "recommended" to have the same url as soundcloud + app.Get("/:user/:track/recommended", func(c *fiber.Ctx) error { + prefs, err := preferences.Get(c) + if err != nil { + return err + } + + cid, err := sc.GetClientID() + if err != nil { + return err + } + + track, err := sc.GetTrack(cid, c.Params("user")+"/"+c.Params("track")) + if err != nil { + log.Printf("error getting %s from %s (related): %s\n", c.Params("track"), c.Params("user"), err) + return err + } + track.Postfix(prefs, true) + + r, err := track.GetRelated(cid, prefs, c.Query("pagination", "?limit=20")) + if err != nil { + log.Printf("error getting %s from %s related tracks: %s\n", c.Params("track"), c.Params("user"), err) + return err + } + + 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) + }) + + app.Get("/:user/:track/sets", func(c *fiber.Ctx) error { + prefs, err := preferences.Get(c) + if err != nil { + return err + } + + cid, err := sc.GetClientID() + if err != nil { + return err + } + + track, err := sc.GetTrack(cid, c.Params("user")+"/"+c.Params("track")) + if err != nil { + log.Printf("error getting %s from %s (sets): %s\n", c.Params("track"), c.Params("user"), err) + return err + } + track.Postfix(prefs, true) + + p, err := track.GetPlaylists(cid, prefs, c.Query("pagination", "?limit=20")) + if err != nil { + log.Printf("error getting %s from %s sets: %s\n", c.Params("track"), c.Params("user"), err) + return err + } + + 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) + }) + + app.Get("/:user/:track/albums", func(c *fiber.Ctx) error { + prefs, err := preferences.Get(c) + if err != nil { + return err + } + + cid, err := sc.GetClientID() + if err != nil { + return err + } + + track, err := sc.GetTrack(cid, c.Params("user")+"/"+c.Params("track")) + if err != nil { + log.Printf("error getting %s from %s (albums): %s\n", c.Params("track"), c.Params("user"), err) + return err + } + track.Postfix(prefs, true) + + p, err := track.GetAlbums(cid, prefs, c.Query("pagination", "?limit=20")) + if err != nil { + log.Printf("error getting %s from %s albums: %s\n", c.Params("track"), c.Params("user"), err) + return err + } + + 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) + }) + log.Fatal(app.Listen(cfg.Addr)) } diff --git a/templates/track.templ b/templates/track.templ index 6504254..fdec964 100644 --- a/templates/track.templ +++ b/templates/track.templ @@ -10,24 +10,40 @@ import ( func toExt(audio string) string { switch audio { - case cfg.AudioAAC: - return "m4a" - case cfg.AudioOpus: - return "ogg" - case cfg.AudioMP3: - return "mp3" + case cfg.AudioAAC: + return "m4a" + case cfg.AudioOpus: + return "ogg" + case cfg.AudioMP3: + return "mp3" } return "" } -templ TrackHeader(prefs cfg.Preferences, t sc.Track) { +templ TrackButtons(current string, track sc.Track) { +
+ for _, b := range [...]btn{{"related tracks", "/recommended", false},{"in albums", "/albums", false},{"in playlists", "/sets", false},{"view on soundcloud", "https://soundcloud.com"+track.Href(), true}} { + if b.text == current { + { b.text } + } else { + if b.external { + { b.text } + } else { + { b.text } + } + } + } +
+} + +templ TrackHeader(prefs cfg.Preferences, t sc.Track, needPlayer bool) { - if *prefs.Player == cfg.HLSPlayer { + if needPlayer && *prefs.Player == cfg.HLSPlayer { } } @@ -55,7 +71,7 @@ templ TrackPlayer(prefs cfg.Preferences, track sc.Track, stream string, displayE if cfg.Restream && *prefs.Player == cfg.RestreamPlayer { {{ audioPref = *prefs.RestreamAudio }} if nextTrack != nil { - + } else { @@ -86,7 +102,6 @@ templ TrackPlayer(prefs cfg.Preferences, track sc.Track, stream string, displayE

Only a 30-second snippet is available.

} - if *prefs.ShowAudio {
if audioPref == cfg.AudioBest { @@ -132,8 +147,13 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string, }

{ t.Title }

@TrackPlayer(prefs, t, stream, displayErr, autoplay, nextTrack, playlist, volume, mode, audio) + if cfg.Restream { +
+ download +
+ } if t.Genre != "" { -

{ t.Genre }

+

{ t.Genre }

} if playlist != nil {
@@ -143,22 +163,23 @@ templ Track(prefs cfg.Preferences, t sc.Track, stream string, displayErr string,

Next track:

@TrackItem(nextTrack, true, next(nextTrack, playlist, true, mode, volume))
- Stop playlist playback - if mode != cfg.AutoplayRandom { - Switch to random mode - } else { - Switch to normal mode - } + Stop playlist playback + if mode != cfg.AutoplayRandom { + Switch to random mode + } else { + Switch to normal mode + }
} @UserItem(&t.Author) -
- view on soundcloud - if cfg.Restream { - download - } -
+ @TrackButtons("", t) + //
+ // view on soundcloud + // if cfg.Restream { + // download + // } + //

@Description(prefs, t.Description, nil)

{ strconv.FormatInt(t.Likes, 10) } likes

@@ -212,3 +233,60 @@ templ SearchTracks(p *sc.Paginated[*sc.Track]) { } } } + +templ RelatedTracks(t sc.Track, p *sc.Paginated[*sc.Track]) { + if t.Artwork != "" { + + } +

{ t.Title }

+ @TrackButtons("related tracks", t) +
+ if len(p.Collection) == 0 { +

no more results

+ } else { + for _, track := range p.Collection { + @TrackItem(track, true, "") + } + if p.Next != "" { + more tracks + } + } +} + +templ TrackInAlbums(t sc.Track, p *sc.Paginated[*sc.Playlist]) { + if t.Artwork != "" { + + } +

{ t.Title }

+ @TrackButtons("in albums", t) +
+ if len(p.Collection) == 0 { +

no more albums

+ } else { + for _, playlist := range p.Collection { + @PlaylistItem(playlist, true) + } + if p.Next != "" { + more albums + } + } +} + +templ TrackInPlaylists(t sc.Track, p *sc.Paginated[*sc.Playlist]) { + if t.Artwork != "" { + + } +

{ t.Title }

+ @TrackButtons("in playlists", t) +
+ if len(p.Collection) == 0 { +

no more playlists

+ } else { + for _, playlist := range p.Collection { + @PlaylistItem(playlist, true) + } + if p.Next != "" { + more playlists + } + } +} diff --git a/templates/user.templ b/templates/user.templ index a1e576a..8201da2 100644 --- a/templates/user.templ +++ b/templates/user.templ @@ -83,7 +83,7 @@ type btn struct { templ UserButtons(current string, user string) {
// this part is the tedious one now, because formatting breaks if i space the list out with newlines - for _, b := range [7]btn{{"tracks", "", false},{"popular tracks", "/popular-tracks", false},{"playlists", "/sets",false},{"albums", "/albums", false},{"reposts","/reposts", false},{"related", "/_/related", false},{"view on soundcloud", "https://soundcloud.com/"+user, true}} { + for _, b := range [...]btn{{"tracks", "", false},{"popular tracks", "/popular-tracks", false},{"playlists", "/sets",false},{"albums", "/albums", false},{"reposts","/reposts", false},{"related", "/_/related", false},{"view on soundcloud", "https://soundcloud.com/"+user, true}} { if b.text == current { { b.text } } else {