diff --git a/api/dbv1/tracks.go b/api/dbv1/tracks.go index fecb0542..94847b18 100644 --- a/api/dbv1/tracks.go +++ b/api/dbv1/tracks.go @@ -12,6 +12,11 @@ type TracksParams struct { GetTracksParams } +// IncludeID3TagsCtxKey is the request-context key used to opt in to ID3 tag +// query params on generated stream/preview URLs. The id3 middleware sets it +// from the `?id3=true` query param. +const IncludeID3TagsCtxKey = "includeID3Tags" + // Track is the standard track type containing all track data type Track struct { GetTracksRow @@ -128,9 +133,12 @@ func (q *Queries) TracksKeyed(ctx context.Context, arg TracksParams) (map[int32] // Get access from the bulk access map access := accessMap[rawTrack.TrackID] - id3Tags := &Id3Tags{ - Title: rawTrack.Title.String, - Artist: user.Name.String, + var id3Tags *Id3Tags + if includeID3Tags, _ := ctx.Value(IncludeID3TagsCtxKey).(bool); includeID3Tags { + id3Tags = &Id3Tags{ + Title: rawTrack.Title.String, + Artist: user.Name.String, + } } var stream *MediaLink diff --git a/api/id3_middleware.go b/api/id3_middleware.go new file mode 100644 index 00000000..1f7c9b85 --- /dev/null +++ b/api/id3_middleware.go @@ -0,0 +1,20 @@ +package api + +import ( + "api.audius.co/api/dbv1" + "github.com/gofiber/fiber/v2" +) + +// id3Middleware reads the optional `?id3=true` query param and stashes the +// resulting bool on the request context so dbv1.TracksKeyed can decide +// whether to embed ID3 tag query params on generated stream/preview URLs. +// +// ID3 tags are opt-in: when omitted, validator nodes can redirect straight to +// presigned blob-storage URLs; when requested, the validator must proxy the +// bytes so it can prepend the ID3 tag. +func (app *ApiServer) id3Middleware(c *fiber.Ctx) error { + if c.QueryBool("id3") { + c.Locals(dbv1.IncludeID3TagsCtxKey, true) + } + return c.Next() +} diff --git a/api/server.go b/api/server.go index 474b11d2..15bab7f9 100644 --- a/api/server.go +++ b/api/server.go @@ -390,6 +390,7 @@ func NewApiServer(config config.Config) *ApiServer { app.Use(app.resolveMyIdMiddleware) app.Use(app.authMiddleware) app.Use(app.solanaWalletMiddleware) + app.Use(app.id3Middleware) v1 := app.Group("/v1") v1Full := app.Group("/v1/full") diff --git a/api/v1_track_stream_test.go b/api/v1_track_stream_test.go index 1641e114..bfe29bc1 100644 --- a/api/v1_track_stream_test.go +++ b/api/v1_track_stream_test.go @@ -12,5 +12,22 @@ func TestGetTrackStream(t *testing.T) { req := httptest.NewRequest("GET", "/v1/tracks/eYJyn/stream", nil) res, err := app.Test(req, -1) assert.NoError(t, err) - assert.Contains(t, res.Header.Get("Location"), "tracks/cidstream/?id3=true&id3_artist=&id3_title=Culca+Canyon&signature=%7B%22data%22%3A%22%7B%5C%22cid%5C%22%3A%5C%22%5C%22%2C%5C%22timestamp%5C%22%3") + location := res.Header.Get("Location") + assert.Contains(t, location, "tracks/cidstream/") + assert.Contains(t, location, "signature=") + // ID3 tags are opt-in via ?id3=true; verify they are NOT present by default. + assert.NotContains(t, location, "id3=true") + assert.NotContains(t, location, "id3_artist=") + assert.NotContains(t, location, "id3_title=") +} + +func TestGetTrackStreamWithID3(t *testing.T) { + app := testAppWithFixtures(t) + req := httptest.NewRequest("GET", "/v1/tracks/eYJyn/stream?id3=true", nil) + res, err := app.Test(req, -1) + assert.NoError(t, err) + location := res.Header.Get("Location") + assert.Contains(t, location, "tracks/cidstream/") + assert.Contains(t, location, "id3=true") + assert.Contains(t, location, "id3_title=Culca+Canyon") }