|
| 1 | +package api |
| 2 | + |
| 3 | +import ( |
| 4 | + "time" |
| 5 | + |
| 6 | + "bridgerton.audius.co/api/dbv1" |
| 7 | + "github.com/gofiber/fiber/v2" |
| 8 | + "github.com/jackc/pgx/v5" |
| 9 | +) |
| 10 | + |
| 11 | +func (app *ApiServer) v1UsersLibraryPlaylists(c *fiber.Ctx) error { |
| 12 | + |
| 13 | + playlistType := "playlist" |
| 14 | + if c.Params("playlistType") == "albums" { |
| 15 | + playlistType = "album" |
| 16 | + } |
| 17 | + |
| 18 | + sortField := "item_created_at" |
| 19 | + switch c.Query("sort_method") { |
| 20 | + case "reposts": |
| 21 | + sortField = "aggregate_playlist.repost_count" |
| 22 | + case "saves": |
| 23 | + sortField = "aggregate_playlist.save_count" |
| 24 | + } |
| 25 | + |
| 26 | + sortDirection := "DESC" |
| 27 | + if c.Query("sort_direction") == "asc" { |
| 28 | + sortDirection = "ASC" |
| 29 | + } |
| 30 | + |
| 31 | + sql := ` |
| 32 | + WITH playlist_actions AS ( |
| 33 | + -- include "own" playlists |
| 34 | + SELECT |
| 35 | + playlist_id as item_id, |
| 36 | + created_at as item_created_at, |
| 37 | + false as is_purchase |
| 38 | + FROM playlists |
| 39 | + WHERE playlist_owner_id = @userId |
| 40 | + AND is_album = (@playlistType = 'album') |
| 41 | + AND is_delete = false |
| 42 | + AND @actionType in ('favorite', 'all') |
| 43 | +
|
| 44 | + UNION ALL |
| 45 | +
|
| 46 | + SELECT |
| 47 | + save_item_id as item_id, |
| 48 | + created_at as item_created_at, |
| 49 | + false as is_purchase |
| 50 | + FROM saves |
| 51 | + WHERE save_type != 'track' |
| 52 | + AND user_id = @userId |
| 53 | + AND is_delete = false |
| 54 | + AND @actionType in ('favorite', 'all') |
| 55 | +
|
| 56 | + UNION ALL |
| 57 | +
|
| 58 | + SELECT |
| 59 | + repost_item_id as item_id, |
| 60 | + created_at as item_created_at, |
| 61 | + false as is_purchase |
| 62 | + FROM reposts |
| 63 | + WHERE repost_type != 'track' |
| 64 | + AND user_id = @userId |
| 65 | + AND is_delete = false |
| 66 | + AND @actionType in ('repost', 'all') |
| 67 | +
|
| 68 | + UNION ALL |
| 69 | +
|
| 70 | + SELECT |
| 71 | + content_id as item_id, |
| 72 | + created_at as item_created_at, |
| 73 | + true as is_purchase |
| 74 | + FROM usdc_purchases |
| 75 | + WHERE content_type = @playlistType::usdc_purchase_content_type |
| 76 | + AND buyer_user_id = @userId |
| 77 | + AND @actionType in ('purchase', 'all') |
| 78 | +
|
| 79 | + ), |
| 80 | + deduped as ( |
| 81 | + SELECT |
| 82 | + item_id, |
| 83 | + max(item_created_at) as item_created_at, |
| 84 | + bool_or(is_purchase) as is_purchase |
| 85 | + FROM playlist_actions |
| 86 | + GROUP BY item_id |
| 87 | + ) |
| 88 | + SELECT deduped.* |
| 89 | + FROM deduped |
| 90 | + JOIN playlists ON playlist_id = item_id |
| 91 | + LEFT JOIN aggregate_playlist USING (playlist_id) |
| 92 | + WHERE playlists.is_album = (@playlistType = 'album') |
| 93 | + ORDER BY ` + sortField + ` ` + sortDirection + `, item_id desc |
| 94 | + LIMIT @limit |
| 95 | + OFFSET @offset |
| 96 | + ` |
| 97 | + |
| 98 | + rows, err := app.pool.Query(c.Context(), sql, pgx.NamedArgs{ |
| 99 | + "playlistType": playlistType, |
| 100 | + "userId": c.Locals("userId"), |
| 101 | + "actionType": c.Query("type", "all"), |
| 102 | + "limit": c.Query("limit", "50"), |
| 103 | + "offset": c.Query("offset", "0"), |
| 104 | + }) |
| 105 | + if err != nil { |
| 106 | + return err |
| 107 | + } |
| 108 | + |
| 109 | + type Activity struct { |
| 110 | + // Class string `json:"class"` |
| 111 | + ItemID int32 `json:"item_id"` |
| 112 | + ItemCreatedAt time.Time `json:"timestamp"` |
| 113 | + IsPurchase bool `json:"-"` |
| 114 | + |
| 115 | + Item any `db:"-" json:"item"` |
| 116 | + } |
| 117 | + |
| 118 | + items, err := pgx.CollectRows(rows, pgx.RowToStructByName[Activity]) |
| 119 | + if err != nil { |
| 120 | + return err |
| 121 | + } |
| 122 | + |
| 123 | + // get ids |
| 124 | + ids := []int32{} |
| 125 | + for _, i := range items { |
| 126 | + ids = append(ids, i.ItemID) |
| 127 | + } |
| 128 | + |
| 129 | + // get playlists |
| 130 | + playlists, err := app.queries.FullPlaylistsKeyed(c.Context(), dbv1.GetPlaylistsParams{ |
| 131 | + Ids: ids, |
| 132 | + MyID: app.getMyId(c), |
| 133 | + }) |
| 134 | + |
| 135 | + // attach |
| 136 | + for idx, item := range items { |
| 137 | + if p, ok := playlists[item.ItemID]; ok { |
| 138 | + // todo: python code does: exclude playlists with only hidden tracks and empty playlists |
| 139 | + |
| 140 | + // python API doesn't attach tracks??? |
| 141 | + p.Tracks = nil |
| 142 | + |
| 143 | + item.Item = p |
| 144 | + items[idx] = item |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + return c.JSON(fiber.Map{ |
| 149 | + "data": items, |
| 150 | + }) |
| 151 | +} |
0 commit comments