Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ func NewApiServer(config config.Config) *ApiServer {
g.Get("/playlists/unclaimed-id", app.v1PlaylistsUnclaimedId)
g.Get("/playlists/trending", app.v1PlaylistsTrending)
g.Get("/playlists/top", app.v1PlaylistsTop)
g.Get("/playlists/new-releases", app.v1PlaylistsNewReleases)
g.Get("/playlists/by_permalink/:handle/:slug", app.v1PlaylistByPermalink)
g.Get("/playlists/by-permalink/:handle/:slug", app.v1PlaylistByPermalink)

Expand Down
45 changes: 45 additions & 0 deletions api/swagger/swagger-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,51 @@ paths:
"500":
description: Server error
content: {}
/playlists/new-releases:
get:
tags:
- playlists
description: Returns recently released playlists or albums
operationId: Get Playlists New Releases
security:
- {}
- OAuth2:
- read
parameters:
- name: offset
in: query
description:
The number of items to skip. Useful for pagination (page number
* limit)
schema:
type: integer
- name: limit
in: query
description: The number of items to fetch
schema:
type: integer
- name: type
in: query
description: The type of content to filter by
schema:
type: string
default: playlist
enum:
- playlist
- album
responses:
"200":
description: Success
content:
application/json:
schema:
$ref: "#/components/schemas/trending_playlists_response"
"400":
description: Bad request
content: {}
"500":
description: Server error
content: {}
/playlists/trending:
get:
tags:
Expand Down
64 changes: 64 additions & 0 deletions api/v1_playlists_new_releases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package api

import (
"api.audius.co/api/dbv1"
"github.com/gofiber/fiber/v2"
"github.com/jackc/pgx/v5"
)

type GetPlaylistsNewReleasesParams struct {
Limit int `query:"limit" default:"10" validate:"min=1,max=100"`
Offset int `query:"offset" default:"0" validate:"min=0"`
Type string `query:"type" default:"playlist" validate:"oneof=playlist album"`
}

func (app *ApiServer) v1PlaylistsNewReleases(c *fiber.Ctx) error {
params := GetPlaylistsNewReleasesParams{}
if err := app.ParseAndValidateQueryParams(c, &params); err != nil {
return err
}

isAlbum := params.Type == "album"

sql := `
SELECT playlist_id
FROM playlists
WHERE is_delete = false
AND is_current = true
AND is_private = false
AND is_album = @is_album
AND COALESCE(release_date, created_at) <= NOW()
AND COALESCE(release_date, created_at) > NOW() - INTERVAL '90 days'
ORDER BY COALESCE(release_date, created_at) DESC, playlist_id DESC
LIMIT @limit
OFFSET @offset
`

rows, err := app.pool.Query(c.Context(), sql, pgx.NamedArgs{
"is_album": isAlbum,
"limit": params.Limit,
"offset": params.Offset,
})
if err != nil {
return err
}

ids, err := pgx.CollectRows(rows, pgx.RowTo[int32])
if err != nil {
return err
}

playlists, err := app.queries.Playlists(c.Context(), dbv1.PlaylistsParams{
GetPlaylistsParams: dbv1.GetPlaylistsParams{
Ids: ids,
MyID: app.getMyId(c),
},
OmitTracks: true,
AuthedWallet: app.tryGetAuthedWallet(c),
})
if err != nil {
return err
}

return v1PlaylistsResponse(c, playlists)
}
94 changes: 94 additions & 0 deletions api/v1_playlists_new_releases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package api

import (
"testing"
"time"

"api.audius.co/api/dbv1"
"api.audius.co/database"
"api.audius.co/trashid"
"github.com/stretchr/testify/assert"
)

func TestGetPlaylistsNewReleases_Albums(t *testing.T) {
app := emptyTestApp(t)
now := time.Now()
fixtures := database.FixtureMap{
"users": {{"user_id": 1, "handle_lc": "one"}},
"playlists": {
// newest album
{
"playlist_id": 1,
"playlist_owner_id": 1,
"is_album": true,
"is_private": false,
"release_date": now.AddDate(0, 0, -1),
"created_at": now.AddDate(0, 0, -1),
"playlist_name": "one",
},
// older album
{
"playlist_id": 2,
"playlist_owner_id": 1,
"is_album": true,
"is_private": false,
"release_date": now.AddDate(0, 0, -10),
"created_at": now.AddDate(0, 0, -10),
"playlist_name": "two",
},
// non-album playlist (excluded when type=album)
{
"playlist_id": 3,
"playlist_owner_id": 1,
"is_album": false,
"is_private": false,
"release_date": now,
"created_at": now,
"playlist_name": "three",
},
// private album (excluded)
{
"playlist_id": 4,
"playlist_owner_id": 1,
"is_album": true,
"is_private": true,
"release_date": now,
"created_at": now,
"playlist_name": "four",
},
// outside of 90-day window (excluded)
{
"playlist_id": 5,
"playlist_owner_id": 1,
"is_album": true,
"is_private": false,
"release_date": now.AddDate(0, 0, -120),
"created_at": now.AddDate(0, 0, -120),
"playlist_name": "five",
},
// future release (excluded until release_date has passed)
{
"playlist_id": 6,
"playlist_owner_id": 1,
"is_album": true,
"is_private": false,
"release_date": now.AddDate(0, 0, 10),
"created_at": now,
"playlist_name": "six",
},
},
}
database.Seed(app.pool.Replicas[0], fixtures)

var resp struct {
Data []dbv1.Playlist
}

status, body := testGet(t, app, "/v1/playlists/new-releases?type=album", &resp)
assert.Equal(t, 200, status)
jsonAssert(t, body, map[string]any{
"data.0.id": trashid.MustEncodeHashID(1),
"data.1.id": trashid.MustEncodeHashID(2),
})
assert.Len(t, resp.Data, 2)
}
Loading