diff --git a/api/dbv1/full_tracks.go b/api/dbv1/full_tracks.go index 39bebe5d..49c87017 100644 --- a/api/dbv1/full_tracks.go +++ b/api/dbv1/full_tracks.go @@ -46,7 +46,7 @@ func (q *Queries) FullTracks(ctx context.Context, arg GetTracksParams) ([]FullTr userMap[user.UserID] = user } - fullTracks := make([]FullTrack, 0, len(rawTracks)) + trackMap := map[int32]FullTrack{} for _, track := range rawTracks { track.ID, _ = trashid.EncodeHashId(int(track.TrackID)) user, ok := userMap[track.UserID] @@ -75,14 +75,24 @@ func (q *Queries) FullTracks(ctx context.Context, arg GetTracksParams) ([]FullTr } } - fullTracks = append(fullTracks, FullTrack{ + fullTrack := FullTrack{ GetTracksRow: track, Artwork: squareImageStruct(track.CoverArtSizes, track.CoverArt), User: user, UserID: user.ID, FolloweeFavorites: followeeFavorites, FolloweeReposts: followeeReposts, - }) + } + trackMap[track.TrackID] = fullTrack + } + + // return in same order as input list of ids + // some ids may be not found... + fullTracks := []FullTrack{} + for _, id := range arg.Ids { + if t, found := trackMap[id]; found { + fullTracks = append(fullTracks, t) + } } return fullTracks, nil diff --git a/api/dbv1/get_tracks.sql.go b/api/dbv1/get_tracks.sql.go index 1fc92595..4ce34737 100644 --- a/api/dbv1/get_tracks.sql.go +++ b/api/dbv1/get_tracks.sql.go @@ -172,19 +172,13 @@ LEFT JOIN aggregate_plays on play_item_id = t.track_id LEFT JOIN track_routes on t.track_id = track_routes.track_id and track_routes.is_current = true WHERE is_available = true AND (is_unlisted = false OR t.owner_id = $1) - AND ( - t.track_id = $2 - OR t.owner_id = $3 - OR t.track_id = ANY($4::int[]) - ) + AND t.track_id = ANY($2::int[]) ORDER BY t.track_id ` type GetTracksParams struct { - MyID interface{} `json:"my_id"` - TrackID int32 `json:"track_id"` - OwnerID int32 `json:"owner_id"` - Ids []int32 `json:"ids"` + MyID interface{} `json:"my_id"` + Ids []int32 `json:"ids"` } type GetTracksRow struct { @@ -259,12 +253,7 @@ type GetTracksRow struct { } func (q *Queries) GetTracks(ctx context.Context, arg GetTracksParams) ([]GetTracksRow, error) { - rows, err := q.db.Query(ctx, getTracks, - arg.MyID, - arg.TrackID, - arg.OwnerID, - arg.Ids, - ) + rows, err := q.db.Query(ctx, getTracks, arg.MyID, arg.Ids) if err != nil { return nil, err } diff --git a/api/dbv1/queries/get_tracks.sql b/api/dbv1/queries/get_tracks.sql index 7aa13078..25c8a027 100644 --- a/api/dbv1/queries/get_tracks.sql +++ b/api/dbv1/queries/get_tracks.sql @@ -158,10 +158,6 @@ LEFT JOIN aggregate_plays on play_item_id = t.track_id LEFT JOIN track_routes on t.track_id = track_routes.track_id and track_routes.is_current = true WHERE is_available = true AND (is_unlisted = false OR t.owner_id = @my_id) - AND ( - t.track_id = @track_id - OR t.owner_id = @owner_id - OR t.track_id = ANY(@ids::int[]) - ) + AND t.track_id = ANY(@ids::int[]) ORDER BY t.track_id ; diff --git a/api/server.go b/api/server.go index 26e1a473..45dd4815 100644 --- a/api/server.go +++ b/api/server.go @@ -130,6 +130,8 @@ func NewApiServer(config Config) *ApiServer { app.Get("/v1/full/users/:userId/mutuals", Full(app.v1UsersMutuals)) app.Get("/v1/full/users/:userId/supporting", Full(app.v1UsersSupporting)) + app.Get("/v1/full/users/handle/:handle/tracks", Full(app.v1UserTracks)) + app.Get("/v1/full/tracks", Full(app.v1Tracks)) app.Get("/v1/full/tracks/:trackId/reposts", Full(app.v1TrackReposts)) app.Get("/v1/full/tracks/:trackId/favorites", Full(app.v1TrackFavorites)) diff --git a/api/v1_tracks_test.go b/api/v1_tracks_test.go deleted file mode 100644 index 9ddf3f96..00000000 --- a/api/v1_tracks_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package api - -import ( - "testing" - - "bridgerton.audius.co/api/dbv1" - "github.com/stretchr/testify/assert" -) - -func TestGetTracks(t *testing.T) { - // someone else can only see public tracks - { - tracks, err := app.queries.FullTracks(t.Context(), dbv1.GetTracksParams{ - MyID: 1, - OwnerID: 2, - }) - assert.NoError(t, err) - assert.Len(t, tracks, 1) - assert.True(t, tracks[0].HasCurrentUserReposted) - } - - // I can see all my tracks - { - tracks, err := app.queries.FullTracks(t.Context(), dbv1.GetTracksParams{ - MyID: 2, - OwnerID: 2, - }) - assert.NoError(t, err) - assert.Len(t, tracks, 2) - } - - { - tracks, err := app.queries.FullTracks(t.Context(), dbv1.GetTracksParams{ - MyID: 2, - Ids: []int32{301}, - }) - assert.NoError(t, err) - track := tracks[0] - assert.Equal(t, 135.0, track.DownloadConditions.UsdcPurchase.Price) - - } -} diff --git a/api/v1_users.go b/api/v1_users.go index 4a1cdf6a..e236614a 100644 --- a/api/v1_users.go +++ b/api/v1_users.go @@ -131,6 +131,68 @@ func (app *ApiServer) v1UsersMutuals(c *fiber.Ctx, minResponse bool) error { }, minResponse) } +func (app *ApiServer) v1UserTracks(c *fiber.Ctx, minResponse bool) error { + myId, _ := trashid.DecodeHashId(c.Query("user_id")) + + sortDir := "DESC" + if c.Query("sort_direction") == "asc" { + sortDir = "ASC" + } + + sortField := "coalesce(t.release_date, t.created_at)" + switch c.Query("sort") { + case "reposts": + sortField = "repost_count" + case "saves": + sortField = "save_count" + } + + sql := ` + SELECT track_id + FROM tracks t + JOIN aggregate_track USING (track_id) + JOIN users u ON owner_id = u.user_id + JOIN aggregate_plays ON track_id = play_item_id + WHERE u.handle_lc = LOWER(@handle) + AND u.is_deactivated = false + AND t.is_delete = false + AND t.is_available = true + AND t.is_unlisted = false + AND t.stem_of is null + ORDER BY ` + sortField + ` ` + sortDir + ` + LIMIT @limit + OFFSET @offset + ` + + args := pgx.NamedArgs{ + "handle": c.Params("handle"), + } + args["limit"] = c.Query("limit", "20") + args["offset"] = c.Query("offset", "0") + + rows, err := app.pool.Query(c.Context(), sql, args) + if err != nil { + return err + } + + ids, err := pgx.CollectRows(rows, pgx.RowTo[int32]) + if err != nil { + return err + } + + tracks, err := app.queries.FullTracks(c.Context(), dbv1.GetTracksParams{ + Ids: ids, + MyID: myId, + }) + if err != nil { + return err + } + + return c.JSON(fiber.Map{ + "data": tracks, + }) +} + func (app *ApiServer) v1UsersSupporting(c *fiber.Ctx, minResponse bool) error { myId, _ := trashid.DecodeHashId(c.Query("user_id")) diff --git a/apidiff2.ts b/apidiff2.ts index 9af59d5e..8f23d3f2 100644 --- a/apidiff2.ts +++ b/apidiff2.ts @@ -50,6 +50,9 @@ const testPaths = [ "/v1/playlists/P5abMZp/favorites?limit=15&offset=0&user_id=aNzoj", "/v1/developer_apps/7d7b6b7a97d1deefe3a1ccc5a13c48e8f055e0b6", + + "/v1/full/users/handle/sammiezonana/tracks?filter_tracks=all&limit=10&offset=0&sort=date&user_id=aNzoj", + "/v1/full/users/handle/stereosteve/tracks?filter_tracks=all&limit=10&offset=0&sort=date&user_id=7eP5n", ]; import { html } from "https://deno.land/x/html/mod.ts";