From dbd51bd5fec5ab914aec7568bd887deda579ecb4 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:05:54 -0800 Subject: [PATCH 1/8] Add access_authorities filter --- api/dbv1/get_events.sql.go | 1 + api/dbv1/get_genres.sql.go | 1 + api/dbv1/get_track_ids_by_isrc.sql.go | 2 + api/dbv1/get_track_ids_by_permalink.sql.go | 2 + api/dbv1/get_tracks.sql.go | 1 + api/dbv1/models.go | 9 ++-- api/dbv1/queries/get_events.sql | 1 + api/dbv1/queries/get_genres.sql | 1 + api/dbv1/queries/get_track_ids_by_isrc.sql | 4 +- .../queries/get_track_ids_by_permalink.sql | 2 + api/dbv1/queries/get_tracks.sql | 1 + api/v1_events_test.go | 24 +++++++++ api/v1_metrics_genres_test.go | 52 +++++++++++++++++++ api/v1_resolve_test.go | 10 ++++ api/v1_tracks_test.go | 19 +++++++ ddl/functions/chat_blast_audience.sql | 4 +- ...racks_access_authorities_partial_index.sql | 9 ++++ esindexer/esindexer.go | 28 ++++++++++ esindexer/tracks_index.go | 1 + sql/01_schema.sql | 5 +- 20 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 ddl/migrations/0191_tracks_access_authorities_partial_index.sql diff --git a/api/dbv1/get_events.sql.go b/api/dbv1/get_events.sql.go index f93d710a..d4c36ace 100644 --- a/api/dbv1/get_events.sql.go +++ b/api/dbv1/get_events.sql.go @@ -26,6 +26,7 @@ SELECT e.event_data AS event_data FROM events e LEFT JOIN tracks t ON t.track_id = e.entity_id AND t.is_current = true AND e.entity_type = 'track' + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') WHERE ($1::int[] = '{}' OR e.entity_id = ANY($1::int[])) AND ($2::int[] = '{}' OR e.event_id = ANY($2::int[])) diff --git a/api/dbv1/get_genres.sql.go b/api/dbv1/get_genres.sql.go index 3dbea145..00a5990d 100644 --- a/api/dbv1/get_genres.sql.go +++ b/api/dbv1/get_genres.sql.go @@ -23,6 +23,7 @@ WHERE AND genre != '' AND is_current = TRUE AND created_at > $1 + AND (access_authorities IS NULL OR access_authorities = '{}') GROUP BY genre ORDER BY diff --git a/api/dbv1/get_track_ids_by_isrc.sql.go b/api/dbv1/get_track_ids_by_isrc.sql.go index 94438bec..ec74dbb8 100644 --- a/api/dbv1/get_track_ids_by_isrc.sql.go +++ b/api/dbv1/get_track_ids_by_isrc.sql.go @@ -13,6 +13,8 @@ const getTrackIdsByISRC = `-- name: GetTrackIdsByISRC :many SELECT track_id FROM tracks WHERE isrc = ANY($1::text[]) + AND is_current = true + AND (access_authorities IS NULL OR access_authorities = '{}') ` func (q *Queries) GetTrackIdsByISRC(ctx context.Context, isrcs []string) ([]int32, error) { diff --git a/api/dbv1/get_track_ids_by_permalink.sql.go b/api/dbv1/get_track_ids_by_permalink.sql.go index 968d59b7..f64f2d47 100644 --- a/api/dbv1/get_track_ids_by_permalink.sql.go +++ b/api/dbv1/get_track_ids_by_permalink.sql.go @@ -20,6 +20,8 @@ lower_permalinks AS ( ) SELECT tr.track_id FROM track_routes tr +JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/dbv1/get_tracks.sql.go b/api/dbv1/get_tracks.sql.go index c5553a5c..1b9d0dcc 100644 --- a/api/dbv1/get_tracks.sql.go +++ b/api/dbv1/get_tracks.sql.go @@ -224,6 +224,7 @@ 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_unlisted = false OR t.owner_id = $1 OR $2::bool = TRUE) AND t.track_id = ANY($3::int[]) + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') ORDER BY t.track_id ` diff --git a/api/dbv1/models.go b/api/dbv1/models.go index 7499836d..5e18b28b 100644 --- a/api/dbv1/models.go +++ b/api/dbv1/models.go @@ -2335,10 +2335,11 @@ type TrackRow struct { // Artist of the original song if this track is a cover CoverOriginalArtist pgtype.Text `json:"cover_original_artist"` // Indicates whether the track is owned by the user for publishing payouts - IsOwnedByUser bool `json:"is_owned_by_user"` - NoAiUse pgtype.Bool `json:"no_ai_use"` - ParentalWarning NullParentalWarningType `json:"parental_warning"` - TerritoryCodes []string `json:"territory_codes"` + IsOwnedByUser bool `json:"is_owned_by_user"` + NoAiUse pgtype.Bool `json:"no_ai_use"` + ParentalWarning NullParentalWarningType `json:"parental_warning"` + TerritoryCodes []string `json:"territory_codes"` + AccessAuthorities []string `json:"access_authorities"` } type TrackTrendingScore struct { diff --git a/api/dbv1/queries/get_events.sql b/api/dbv1/queries/get_events.sql index ac36d8f1..bef33629 100644 --- a/api/dbv1/queries/get_events.sql +++ b/api/dbv1/queries/get_events.sql @@ -12,6 +12,7 @@ SELECT e.event_data AS event_data FROM events e LEFT JOIN tracks t ON t.track_id = e.entity_id AND t.is_current = true AND e.entity_type = 'track' + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') WHERE (@entity_ids::int[] = '{}' OR e.entity_id = ANY(@entity_ids::int[])) AND (@event_ids::int[] = '{}' OR e.event_id = ANY(@event_ids::int[])) diff --git a/api/dbv1/queries/get_genres.sql b/api/dbv1/queries/get_genres.sql index c569ca4d..0cd51b9c 100644 --- a/api/dbv1/queries/get_genres.sql +++ b/api/dbv1/queries/get_genres.sql @@ -9,6 +9,7 @@ WHERE AND genre != '' AND is_current = TRUE AND created_at > @start_time + AND (access_authorities IS NULL OR access_authorities = '{}') GROUP BY genre ORDER BY diff --git a/api/dbv1/queries/get_track_ids_by_isrc.sql b/api/dbv1/queries/get_track_ids_by_isrc.sql index 39696ca7..0f647594 100644 --- a/api/dbv1/queries/get_track_ids_by_isrc.sql +++ b/api/dbv1/queries/get_track_ids_by_isrc.sql @@ -1,4 +1,6 @@ -- name: GetTrackIdsByISRC :many SELECT track_id FROM tracks -WHERE isrc = ANY(@isrcs::text[]); \ No newline at end of file +WHERE isrc = ANY(@isrcs::text[]) + AND is_current = true + AND (access_authorities IS NULL OR access_authorities = '{}'); \ No newline at end of file diff --git a/api/dbv1/queries/get_track_ids_by_permalink.sql b/api/dbv1/queries/get_track_ids_by_permalink.sql index 05f9d8eb..58c53817 100644 --- a/api/dbv1/queries/get_track_ids_by_permalink.sql +++ b/api/dbv1/queries/get_track_ids_by_permalink.sql @@ -9,6 +9,8 @@ lower_permalinks AS ( ) SELECT tr.track_id FROM track_routes tr +JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/dbv1/queries/get_tracks.sql b/api/dbv1/queries/get_tracks.sql index ce2b6169..d5091908 100644 --- a/api/dbv1/queries/get_tracks.sql +++ b/api/dbv1/queries/get_tracks.sql @@ -209,5 +209,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_unlisted = false OR t.owner_id = @my_id OR @include_unlisted::bool = TRUE) AND t.track_id = ANY(@ids::int[]) + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') ORDER BY t.track_id ; diff --git a/api/v1_events_test.go b/api/v1_events_test.go index 53665378..c0434358 100644 --- a/api/v1_events_test.go +++ b/api/v1_events_test.go @@ -86,3 +86,27 @@ func TestGetEventsExcludesDeletedTracks(t *testing.T) { "data.3.entity_id": trashid.MustEncodeHashID(101), }) } + +func TestGetEventsExcludesAccessAuthoritiesTracks(t *testing.T) { + app := testAppWithFixtures(t) + ctx := context.Background() + require.NotNil(t, app.writePool, "test requires write pool") + + // Set access_authorities on track 102 (which has event 6) so it is gated + _, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0x123']::text[] WHERE track_id = 102 AND is_current = true`) + require.NoError(t, err) + + var eventsResponse struct { + Data []dbv1.FullEvent + } + status, _ := testGet(t, app, "/v1/events", &eventsResponse) + assert.Equal(t, 200, status) + + // Event 6 is for entity_id 102; it must not appear when the track has access_authorities + entity102Hash := trashid.MustEncodeHashID(102) + for _, e := range eventsResponse.Data { + assert.NotEqual(t, entity102Hash, e.EntityId, "events for access_authorities track 102 must not be returned") + } + + assert.Len(t, eventsResponse.Data, 4, "expected 4 events after excluding event for access_authorities track") +} diff --git a/api/v1_metrics_genres_test.go b/api/v1_metrics_genres_test.go index 83b4faec..35b6fd80 100644 --- a/api/v1_metrics_genres_test.go +++ b/api/v1_metrics_genres_test.go @@ -1,11 +1,13 @@ package api import ( + "context" "fmt" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMetricsGenres(t *testing.T) { @@ -41,3 +43,53 @@ func TestMetricsGenres(t *testing.T) { } } + +func TestMetricsGenresExcludesAccessAuthoritiesTracks(t *testing.T) { + app := testAppWithFixtures(t) + ctx := context.Background() + require.NotNil(t, app.writePool, "test requires write pool") + + // Get baseline Electronic count + oneHourAgo := time.Now().Add(-1 * time.Hour).Unix() + url := fmt.Sprintf("/v1/metrics/genres?start_time=%d", oneHourAgo) + var before struct { + Data []struct { + Genre string `json:"genre"` + Count int `json:"count"` + } + } + status, _ := testGet(t, app, url, &before) + require.Equal(t, 200, status) + + var electronicCountBefore int + for _, g := range before.Data { + if g.Genre == "Electronic" { + electronicCountBefore = g.Count + break + } + } + require.Greater(t, electronicCountBefore, 0, "fixtures should have Electronic tracks") + + // Gate one Electronic track (track 100 is Electronic) + _, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0xgate']::text[] WHERE track_id = 100 AND is_current = true`) + require.NoError(t, err) + + var after struct { + Data []struct { + Genre string `json:"genre"` + Count int `json:"count"` + } + } + status, _ = testGet(t, app, url, &after) + require.Equal(t, 200, status) + + var electronicCountAfter int + for _, g := range after.Data { + if g.Genre == "Electronic" { + electronicCountAfter = g.Count + break + } + } + assert.Equal(t, electronicCountBefore-1, electronicCountAfter, "genre count must exclude access_authorities tracks") +} + diff --git a/api/v1_resolve_test.go b/api/v1_resolve_test.go index c5571b43..0183fa00 100644 --- a/api/v1_resolve_test.go +++ b/api/v1_resolve_test.go @@ -1,9 +1,11 @@ package api import ( + "context" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestResolveTrackURL(t *testing.T) { @@ -12,6 +14,14 @@ func TestResolveTrackURL(t *testing.T) { status, _ := testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") assert.Equal(t, 302, status) + // Test that track with access_authorities is not resolved (404) + app2 := testAppWithFixtures(t) + require.NotNil(t, app2.writePool, "test requires write pool") + _, err := app2.writePool.Exec(context.Background(), `UPDATE tracks SET access_authorities = ARRAY['0xabc']::text[] WHERE track_id = 500 AND is_current = true`) + require.NoError(t, err) + status, _ = testGet(t, app2, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") + assert.Equal(t, 404, status, "resolve must return 404 for track with access_authorities") + // Test failed track resolution status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/nonexistent/track") assert.Equal(t, 404, status) diff --git a/api/v1_tracks_test.go b/api/v1_tracks_test.go index 8352bb2c..39edc68d 100644 --- a/api/v1_tracks_test.go +++ b/api/v1_tracks_test.go @@ -1,10 +1,12 @@ package api import ( + "context" "testing" "api.audius.co/api/dbv1" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTracksEndpoint(t *testing.T) { @@ -33,3 +35,20 @@ func TestGetTracksByPermalink(t *testing.T) { "data.0.title": "track by permalink", }) } + +func TestGetTracksExcludesAccessAuthorities(t *testing.T) { + app := testAppWithFixtures(t) + ctx := context.Background() + require.NotNil(t, app.writePool, "test requires write pool") + + // Track 100 has title "T1" and is returned as id eYZmn. Set access_authorities so it is gated. + _, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0xgate']::text[] WHERE track_id = 100 AND is_current = true`) + require.NoError(t, err) + + var resp struct { + Data []dbv1.Track + } + status, _ := testGet(t, app, "/v1/full/tracks?id=eYZmn", &resp) + assert.Equal(t, 200, status) + assert.Len(t, resp.Data, 0, "tracks with access_authorities must not be returned") +} diff --git a/ddl/functions/chat_blast_audience.sql b/ddl/functions/chat_blast_audience.sql index 823ed27e..05b86593 100644 --- a/ddl/functions/chat_blast_audience.sql +++ b/ddl/functions/chat_blast_audience.sql @@ -26,7 +26,7 @@ BEGIN UNION - -- remixer_audience + -- remixer_audience (exclude tracks with access_authorities / programmable distribution) SELECT chat_blast.blast_id, t.owner_id AS to_user_id FROM tracks t JOIN remixes ON remixes.child_track_id = t.track_id @@ -35,6 +35,8 @@ BEGIN AND chat_blast.audience = 'remixer_audience' AND og.owner_id = chat_blast.from_user_id AND t.owner_id != chat_blast.from_user_id + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND (og.access_authorities IS NULL OR og.access_authorities = '{}') AND ( chat_blast.audience_content_id IS NULL OR ( diff --git a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql new file mode 100644 index 00000000..1b31a73a --- /dev/null +++ b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql @@ -0,0 +1,9 @@ +BEGIN; + +-- Partial index for API-visible tracks (no access_authorities set). +-- All track queries filter with (access_authorities IS NULL OR access_authorities = '{}'). +CREATE INDEX IF NOT EXISTS idx_tracks_access_authorities_null + ON tracks (track_id) + WHERE (access_authorities IS NULL OR access_authorities = '{}'); + +COMMIT; diff --git a/esindexer/esindexer.go b/esindexer/esindexer.go index bb5e1c92..edd5b332 100644 --- a/esindexer/esindexer.go +++ b/esindexer/esindexer.go @@ -113,6 +113,15 @@ func (indexer *EsIndexer) indexIds(collection string, ids ...int64) error { stringIds[idx] = strconv.Itoa(int(id)) } + // For tracks: delete requested IDs from the index first, then re-index only those + // that still pass the filter (e.g. no access_authorities). So when a track gets + // access_authorities set, the next listen cycle removes it from search. + if collection == "tracks" { + if err := indexer.deleteDocIds(cc.indexName, stringIds); err != nil { + return err + } + } + sql := fmt.Sprintf("%s AND %s IN (%s)", cc.sql, cc.idColumn, @@ -123,6 +132,25 @@ func (indexer *EsIndexer) indexIds(collection string, ids ...int64) error { return indexer.indexSql(cc.indexName, sql) } +// deleteDocIds removes documents by ID from an index (used for tracks so that +// when access_authorities is set, the document is removed from search). +func (indexer *EsIndexer) deleteDocIds(indexName string, docIds []string) error { + for _, id := range docIds { + resp, err := indexer.esc.Delete(indexName, id) + if err != nil { + return err + } + // 200 = deleted, 404 = not found (already missing) + if resp.IsError() && resp.StatusCode != 404 { + body, _ := io.ReadAll(resp.Body) + resp.Body.Close() + return fmt.Errorf("delete doc %s: %d %s", id, resp.StatusCode, body) + } + resp.Body.Close() + } + return nil +} + // runs a query + indexes documents // assumes query returns (id, json_doc) tuples func (indexer *EsIndexer) indexSql(indexName, sql string) error { diff --git a/esindexer/tracks_index.go b/esindexer/tracks_index.go index b26aeb7f..5a25d62d 100644 --- a/esindexer/tracks_index.go +++ b/esindexer/tracks_index.go @@ -53,5 +53,6 @@ var tracksConfig = collectionConfig{ AND users.is_available = true AND users.is_deactivated = false AND stem_of is null + AND (tracks.access_authorities IS NULL OR tracks.access_authorities = '{}') `, } diff --git a/sql/01_schema.sql b/sql/01_schema.sql index 8d259a98..29ab1fca 100644 --- a/sql/01_schema.sql +++ b/sql/01_schema.sql @@ -1293,7 +1293,7 @@ BEGIN UNION - -- remixer_audience + -- remixer_audience (exclude tracks with access_authorities / programmable distribution) SELECT chat_blast.blast_id, t.owner_id AS to_user_id FROM tracks t JOIN remixes ON remixes.child_track_id = t.track_id @@ -1302,6 +1302,8 @@ BEGIN AND chat_blast.audience = 'remixer_audience' AND og.owner_id = chat_blast.from_user_id AND t.owner_id != chat_blast.from_user_id + AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND (og.access_authorities IS NULL OR og.access_authorities = '{}') AND ( chat_blast.audience_content_id IS NULL OR ( @@ -5281,6 +5283,7 @@ CREATE TABLE public.tracks ( no_ai_use boolean DEFAULT false, parental_warning public.parental_warning_type, territory_codes text[], + access_authorities text[], CONSTRAINT check_territory_codes CHECK (public.validate_territory_codes(territory_codes)) ); From 8afaaf01659f03b4444c2649132708e70456ab31 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:16:27 -0800 Subject: [PATCH 2/8] drop '{}' check --- api/dbv1/get_events.sql.go | 2 +- api/dbv1/get_genres.sql.go | 2 +- api/dbv1/get_track_ids_by_isrc.sql.go | 2 +- api/dbv1/get_track_ids_by_permalink.sql.go | 2 +- api/dbv1/get_tracks.sql.go | 2 +- api/dbv1/queries/get_events.sql | 2 +- api/dbv1/queries/get_genres.sql | 2 +- api/dbv1/queries/get_track_ids_by_isrc.sql | 2 +- api/dbv1/queries/get_track_ids_by_permalink.sql | 2 +- api/dbv1/queries/get_tracks.sql | 2 +- ddl/functions/chat_blast_audience.sql | 4 ++-- .../0191_tracks_access_authorities_partial_index.sql | 6 +++--- esindexer/tracks_index.go | 2 +- sql/01_schema.sql | 4 ++-- 14 files changed, 18 insertions(+), 18 deletions(-) diff --git a/api/dbv1/get_events.sql.go b/api/dbv1/get_events.sql.go index d4c36ace..8f33c7ea 100644 --- a/api/dbv1/get_events.sql.go +++ b/api/dbv1/get_events.sql.go @@ -26,7 +26,7 @@ SELECT e.event_data AS event_data FROM events e LEFT JOIN tracks t ON t.track_id = e.entity_id AND t.is_current = true AND e.entity_type = 'track' - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL WHERE ($1::int[] = '{}' OR e.entity_id = ANY($1::int[])) AND ($2::int[] = '{}' OR e.event_id = ANY($2::int[])) diff --git a/api/dbv1/get_genres.sql.go b/api/dbv1/get_genres.sql.go index 00a5990d..eca10991 100644 --- a/api/dbv1/get_genres.sql.go +++ b/api/dbv1/get_genres.sql.go @@ -23,7 +23,7 @@ WHERE AND genre != '' AND is_current = TRUE AND created_at > $1 - AND (access_authorities IS NULL OR access_authorities = '{}') + AND access_authorities IS NULL GROUP BY genre ORDER BY diff --git a/api/dbv1/get_track_ids_by_isrc.sql.go b/api/dbv1/get_track_ids_by_isrc.sql.go index ec74dbb8..af5c757a 100644 --- a/api/dbv1/get_track_ids_by_isrc.sql.go +++ b/api/dbv1/get_track_ids_by_isrc.sql.go @@ -14,7 +14,7 @@ SELECT track_id FROM tracks WHERE isrc = ANY($1::text[]) AND is_current = true - AND (access_authorities IS NULL OR access_authorities = '{}') + AND access_authorities IS NULL ` func (q *Queries) GetTrackIdsByISRC(ctx context.Context, isrcs []string) ([]int32, error) { diff --git a/api/dbv1/get_track_ids_by_permalink.sql.go b/api/dbv1/get_track_ids_by_permalink.sql.go index f64f2d47..1cbd41f7 100644 --- a/api/dbv1/get_track_ids_by_permalink.sql.go +++ b/api/dbv1/get_track_ids_by_permalink.sql.go @@ -21,7 +21,7 @@ lower_permalinks AS ( SELECT tr.track_id FROM track_routes tr JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/dbv1/get_tracks.sql.go b/api/dbv1/get_tracks.sql.go index 1b9d0dcc..9794f5ec 100644 --- a/api/dbv1/get_tracks.sql.go +++ b/api/dbv1/get_tracks.sql.go @@ -224,7 +224,7 @@ 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_unlisted = false OR t.owner_id = $1 OR $2::bool = TRUE) AND t.track_id = ANY($3::int[]) - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL ORDER BY t.track_id ` diff --git a/api/dbv1/queries/get_events.sql b/api/dbv1/queries/get_events.sql index bef33629..557ffa0f 100644 --- a/api/dbv1/queries/get_events.sql +++ b/api/dbv1/queries/get_events.sql @@ -12,7 +12,7 @@ SELECT e.event_data AS event_data FROM events e LEFT JOIN tracks t ON t.track_id = e.entity_id AND t.is_current = true AND e.entity_type = 'track' - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL WHERE (@entity_ids::int[] = '{}' OR e.entity_id = ANY(@entity_ids::int[])) AND (@event_ids::int[] = '{}' OR e.event_id = ANY(@event_ids::int[])) diff --git a/api/dbv1/queries/get_genres.sql b/api/dbv1/queries/get_genres.sql index 0cd51b9c..bd761aab 100644 --- a/api/dbv1/queries/get_genres.sql +++ b/api/dbv1/queries/get_genres.sql @@ -9,7 +9,7 @@ WHERE AND genre != '' AND is_current = TRUE AND created_at > @start_time - AND (access_authorities IS NULL OR access_authorities = '{}') + AND access_authorities IS NULL GROUP BY genre ORDER BY diff --git a/api/dbv1/queries/get_track_ids_by_isrc.sql b/api/dbv1/queries/get_track_ids_by_isrc.sql index 0f647594..fbab337c 100644 --- a/api/dbv1/queries/get_track_ids_by_isrc.sql +++ b/api/dbv1/queries/get_track_ids_by_isrc.sql @@ -3,4 +3,4 @@ SELECT track_id FROM tracks WHERE isrc = ANY(@isrcs::text[]) AND is_current = true - AND (access_authorities IS NULL OR access_authorities = '{}'); \ No newline at end of file + AND access_authorities IS NULL; \ No newline at end of file diff --git a/api/dbv1/queries/get_track_ids_by_permalink.sql b/api/dbv1/queries/get_track_ids_by_permalink.sql index 58c53817..07f359af 100644 --- a/api/dbv1/queries/get_track_ids_by_permalink.sql +++ b/api/dbv1/queries/get_track_ids_by_permalink.sql @@ -10,7 +10,7 @@ lower_permalinks AS ( SELECT tr.track_id FROM track_routes tr JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/dbv1/queries/get_tracks.sql b/api/dbv1/queries/get_tracks.sql index d5091908..351b3190 100644 --- a/api/dbv1/queries/get_tracks.sql +++ b/api/dbv1/queries/get_tracks.sql @@ -209,6 +209,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_unlisted = false OR t.owner_id = @my_id OR @include_unlisted::bool = TRUE) AND t.track_id = ANY(@ids::int[]) - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') + AND t.access_authorities IS NULL ORDER BY t.track_id ; diff --git a/ddl/functions/chat_blast_audience.sql b/ddl/functions/chat_blast_audience.sql index 05b86593..54f9d19f 100644 --- a/ddl/functions/chat_blast_audience.sql +++ b/ddl/functions/chat_blast_audience.sql @@ -35,8 +35,8 @@ BEGIN AND chat_blast.audience = 'remixer_audience' AND og.owner_id = chat_blast.from_user_id AND t.owner_id != chat_blast.from_user_id - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') - AND (og.access_authorities IS NULL OR og.access_authorities = '{}') + AND t.access_authorities IS NULL + AND og.access_authorities IS NULL AND ( chat_blast.audience_content_id IS NULL OR ( diff --git a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql index 1b31a73a..39052ddb 100644 --- a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql +++ b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql @@ -1,9 +1,9 @@ BEGIN; --- Partial index for API-visible tracks (no access_authorities set). --- All track queries filter with (access_authorities IS NULL OR access_authorities = '{}'). +-- Partial index for API-visible tracks (access_authorities not set). +-- All track queries filter with access_authorities IS NULL. CREATE INDEX IF NOT EXISTS idx_tracks_access_authorities_null ON tracks (track_id) - WHERE (access_authorities IS NULL OR access_authorities = '{}'); + WHERE access_authorities IS NULL; COMMIT; diff --git a/esindexer/tracks_index.go b/esindexer/tracks_index.go index 5a25d62d..17d39e50 100644 --- a/esindexer/tracks_index.go +++ b/esindexer/tracks_index.go @@ -53,6 +53,6 @@ var tracksConfig = collectionConfig{ AND users.is_available = true AND users.is_deactivated = false AND stem_of is null - AND (tracks.access_authorities IS NULL OR tracks.access_authorities = '{}') + AND tracks.access_authorities IS NULL `, } diff --git a/sql/01_schema.sql b/sql/01_schema.sql index 29ab1fca..570ce3e0 100644 --- a/sql/01_schema.sql +++ b/sql/01_schema.sql @@ -1302,8 +1302,8 @@ BEGIN AND chat_blast.audience = 'remixer_audience' AND og.owner_id = chat_blast.from_user_id AND t.owner_id != chat_blast.from_user_id - AND (t.access_authorities IS NULL OR t.access_authorities = '{}') - AND (og.access_authorities IS NULL OR og.access_authorities = '{}') + AND t.access_authorities IS NULL + AND og.access_authorities IS NULL AND ( chat_blast.audience_content_id IS NULL OR ( From 80744373d98a597de16e7f734e5b07ef79d574ff Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:31:11 -0800 Subject: [PATCH 3/8] Fix tests, simplify indexer changes --- api/v1_metrics_genres_test.go | 23 +++++++++++------------ api/v1_resolve_test.go | 7 +++---- esindexer/esindexer.go | 28 ---------------------------- esindexer/tracks_index.go | 1 - 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/api/v1_metrics_genres_test.go b/api/v1_metrics_genres_test.go index 35b6fd80..2eb21119 100644 --- a/api/v1_metrics_genres_test.go +++ b/api/v1_metrics_genres_test.go @@ -49,26 +49,25 @@ func TestMetricsGenresExcludesAccessAuthoritiesTracks(t *testing.T) { ctx := context.Background() require.NotNil(t, app.writePool, "test requires write pool") - // Get baseline Electronic count - oneHourAgo := time.Now().Add(-1 * time.Hour).Unix() - url := fmt.Sprintf("/v1/metrics/genres?start_time=%d", oneHourAgo) + // Get baseline Electronic count (use epoch so all fixture tracks are included) + url := fmt.Sprintf("/v1/metrics/genres?start_time=%d", 0) var before struct { Data []struct { - Genre string `json:"genre"` - Count int `json:"count"` + Name string `json:"name"` + Count int64 `json:"count"` } } status, _ := testGet(t, app, url, &before) require.Equal(t, 200, status) - var electronicCountBefore int + var electronicCountBefore int64 for _, g := range before.Data { - if g.Genre == "Electronic" { + if g.Name == "Electronic" { electronicCountBefore = g.Count break } } - require.Greater(t, electronicCountBefore, 0, "fixtures should have Electronic tracks") + require.Greater(t, electronicCountBefore, int64(0), "fixtures should have Electronic tracks") // Gate one Electronic track (track 100 is Electronic) _, err := app.writePool.Exec(ctx, `UPDATE tracks SET access_authorities = ARRAY['0xgate']::text[] WHERE track_id = 100 AND is_current = true`) @@ -76,16 +75,16 @@ func TestMetricsGenresExcludesAccessAuthoritiesTracks(t *testing.T) { var after struct { Data []struct { - Genre string `json:"genre"` - Count int `json:"count"` + Name string `json:"name"` + Count int64 `json:"count"` } } status, _ = testGet(t, app, url, &after) require.Equal(t, 200, status) - var electronicCountAfter int + var electronicCountAfter int64 for _, g := range after.Data { - if g.Genre == "Electronic" { + if g.Name == "Electronic" { electronicCountAfter = g.Count break } diff --git a/api/v1_resolve_test.go b/api/v1_resolve_test.go index 0183fa00..4eb6cc20 100644 --- a/api/v1_resolve_test.go +++ b/api/v1_resolve_test.go @@ -15,11 +15,10 @@ func TestResolveTrackURL(t *testing.T) { assert.Equal(t, 302, status) // Test that track with access_authorities is not resolved (404) - app2 := testAppWithFixtures(t) - require.NotNil(t, app2.writePool, "test requires write pool") - _, err := app2.writePool.Exec(context.Background(), `UPDATE tracks SET access_authorities = ARRAY['0xabc']::text[] WHERE track_id = 500 AND is_current = true`) + require.NotNil(t, app.writePool, "test requires write pool") + _, err := app.writePool.Exec(context.Background(), `UPDATE tracks SET access_authorities = ARRAY['0xabc']::text[] WHERE track_id = 500 AND is_current = true`) require.NoError(t, err) - status, _ = testGet(t, app2, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") + status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") assert.Equal(t, 404, status, "resolve must return 404 for track with access_authorities") // Test failed track resolution diff --git a/esindexer/esindexer.go b/esindexer/esindexer.go index edd5b332..bb5e1c92 100644 --- a/esindexer/esindexer.go +++ b/esindexer/esindexer.go @@ -113,15 +113,6 @@ func (indexer *EsIndexer) indexIds(collection string, ids ...int64) error { stringIds[idx] = strconv.Itoa(int(id)) } - // For tracks: delete requested IDs from the index first, then re-index only those - // that still pass the filter (e.g. no access_authorities). So when a track gets - // access_authorities set, the next listen cycle removes it from search. - if collection == "tracks" { - if err := indexer.deleteDocIds(cc.indexName, stringIds); err != nil { - return err - } - } - sql := fmt.Sprintf("%s AND %s IN (%s)", cc.sql, cc.idColumn, @@ -132,25 +123,6 @@ func (indexer *EsIndexer) indexIds(collection string, ids ...int64) error { return indexer.indexSql(cc.indexName, sql) } -// deleteDocIds removes documents by ID from an index (used for tracks so that -// when access_authorities is set, the document is removed from search). -func (indexer *EsIndexer) deleteDocIds(indexName string, docIds []string) error { - for _, id := range docIds { - resp, err := indexer.esc.Delete(indexName, id) - if err != nil { - return err - } - // 200 = deleted, 404 = not found (already missing) - if resp.IsError() && resp.StatusCode != 404 { - body, _ := io.ReadAll(resp.Body) - resp.Body.Close() - return fmt.Errorf("delete doc %s: %d %s", id, resp.StatusCode, body) - } - resp.Body.Close() - } - return nil -} - // runs a query + indexes documents // assumes query returns (id, json_doc) tuples func (indexer *EsIndexer) indexSql(indexName, sql string) error { diff --git a/esindexer/tracks_index.go b/esindexer/tracks_index.go index 17d39e50..b26aeb7f 100644 --- a/esindexer/tracks_index.go +++ b/esindexer/tracks_index.go @@ -53,6 +53,5 @@ var tracksConfig = collectionConfig{ AND users.is_available = true AND users.is_deactivated = false AND stem_of is null - AND tracks.access_authorities IS NULL `, } From c8e7208a7b01ed7cfd4f9c157cff1219028b0f11 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:39:24 -0800 Subject: [PATCH 4/8] Address comments --- api/dbv1/get_track_ids_by_permalink.sql.go | 2 -- api/dbv1/queries/get_track_ids_by_permalink.sql | 2 -- api/v1_resolve_test.go | 5 +++-- .../0191_tracks_access_authorities_partial_index.sql | 4 +++- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/api/dbv1/get_track_ids_by_permalink.sql.go b/api/dbv1/get_track_ids_by_permalink.sql.go index 1cbd41f7..968d59b7 100644 --- a/api/dbv1/get_track_ids_by_permalink.sql.go +++ b/api/dbv1/get_track_ids_by_permalink.sql.go @@ -20,8 +20,6 @@ lower_permalinks AS ( ) SELECT tr.track_id FROM track_routes tr -JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true - AND t.access_authorities IS NULL JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/dbv1/queries/get_track_ids_by_permalink.sql b/api/dbv1/queries/get_track_ids_by_permalink.sql index 07f359af..05f9d8eb 100644 --- a/api/dbv1/queries/get_track_ids_by_permalink.sql +++ b/api/dbv1/queries/get_track_ids_by_permalink.sql @@ -9,8 +9,6 @@ lower_permalinks AS ( ) SELECT tr.track_id FROM track_routes tr -JOIN tracks t ON t.track_id = tr.track_id AND t.is_current = true - AND t.access_authorities IS NULL JOIN users u ON u.user_id = tr.owner_id JOIN lower_handles lh ON u.handle_lc = lh.handle diff --git a/api/v1_resolve_test.go b/api/v1_resolve_test.go index 4eb6cc20..508cfece 100644 --- a/api/v1_resolve_test.go +++ b/api/v1_resolve_test.go @@ -14,12 +14,13 @@ func TestResolveTrackURL(t *testing.T) { status, _ := testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") assert.Equal(t, 302, status) - // Test that track with access_authorities is not resolved (404) + // Resolve does not filter by access_authorities (no tracks join); filter happens in GetTracks. + // So a gated track still resolves (302), but fetching the track by ID returns empty. require.NotNil(t, app.writePool, "test requires write pool") _, err := app.writePool.Exec(context.Background(), `UPDATE tracks SET access_authorities = ARRAY['0xabc']::text[] WHERE track_id = 500 AND is_current = true`) require.NoError(t, err) status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/TracksByPermalink/track-by-permalink") - assert.Equal(t, 404, status, "resolve must return 404 for track with access_authorities") + assert.Equal(t, 302, status, "resolve still redirects; GetTracks filters gated tracks when fetching by ID") // Test failed track resolution status, _ = testGet(t, app, "/v1/resolve?url=https://audius.co/nonexistent/track") diff --git a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql index 39052ddb..c3ced202 100644 --- a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql +++ b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql @@ -1,7 +1,9 @@ BEGIN; -- Partial index for API-visible tracks (access_authorities not set). --- All track queries filter with access_authorities IS NULL. +-- We index rows WHERE access_authorities IS NULL so our queries (which filter +-- access_authorities IS NULL) can use it. A partial index on IS NOT NULL would +-- store less but would never be used for those queries. CREATE INDEX IF NOT EXISTS idx_tracks_access_authorities_null ON tracks (track_id) WHERE access_authorities IS NULL; From b385ed09f05047ecbce3fe24021d169d1cccc58a Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:43:34 -0800 Subject: [PATCH 5/8] drop the is_current --- api/dbv1/get_track_ids_by_isrc.sql.go | 1 - api/dbv1/queries/get_track_ids_by_isrc.sql | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/api/dbv1/get_track_ids_by_isrc.sql.go b/api/dbv1/get_track_ids_by_isrc.sql.go index af5c757a..116a02e7 100644 --- a/api/dbv1/get_track_ids_by_isrc.sql.go +++ b/api/dbv1/get_track_ids_by_isrc.sql.go @@ -13,7 +13,6 @@ const getTrackIdsByISRC = `-- name: GetTrackIdsByISRC :many SELECT track_id FROM tracks WHERE isrc = ANY($1::text[]) - AND is_current = true AND access_authorities IS NULL ` diff --git a/api/dbv1/queries/get_track_ids_by_isrc.sql b/api/dbv1/queries/get_track_ids_by_isrc.sql index fbab337c..f377e27d 100644 --- a/api/dbv1/queries/get_track_ids_by_isrc.sql +++ b/api/dbv1/queries/get_track_ids_by_isrc.sql @@ -2,5 +2,4 @@ SELECT track_id FROM tracks WHERE isrc = ANY(@isrcs::text[]) - AND is_current = true - AND access_authorities IS NULL; \ No newline at end of file + AND access_authorities IS NULL; From e0188c659d93c4afed9b65c89763a5644cd99fcf Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:48:24 -0800 Subject: [PATCH 6/8] Drop the migration --- .../0191_tracks_access_authorities_partial_index.sql | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 ddl/migrations/0191_tracks_access_authorities_partial_index.sql diff --git a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql b/ddl/migrations/0191_tracks_access_authorities_partial_index.sql deleted file mode 100644 index c3ced202..00000000 --- a/ddl/migrations/0191_tracks_access_authorities_partial_index.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - --- Partial index for API-visible tracks (access_authorities not set). --- We index rows WHERE access_authorities IS NULL so our queries (which filter --- access_authorities IS NULL) can use it. A partial index on IS NOT NULL would --- store less but would never be used for those queries. -CREATE INDEX IF NOT EXISTS idx_tracks_access_authorities_null - ON tracks (track_id) - WHERE access_authorities IS NULL; - -COMMIT; From 7b672af56637eb73b5daf4081397781445e13dcf Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 17:56:51 -0800 Subject: [PATCH 7/8] Add more filters --- api/comms/chat.go | 2 ++ api/comms/validator.go | 2 ++ api/comms_blasts.go | 2 ++ api/v1_playlists_trending.go | 2 ++ api/v1_track_comment_count.go | 2 +- api/v1_track_comments.go | 2 +- api/v1_track_subsequent.go | 6 ++++++ api/v1_tracks_feeling_lucky.go | 1 + api/v1_tracks_recent_premium.go | 2 ++ api/v1_users_feed.go | 1 + api/v1_users_listen_counts_monthly.go | 2 +- api/v1_users_recommended_tracks.go | 2 ++ api/v1_users_related.go | 2 ++ api/v1_users_tags.go | 1 + api/v1_users_tracks.go | 3 ++- api/v1_users_tracks_ai_attributed.go | 1 + api/v1_users_tracks_count.go | 3 ++- ddl/functions/handle_track.sql | 4 +++- sql/01_schema.sql | 5 ++++- 19 files changed, 38 insertions(+), 7 deletions(-) diff --git a/api/comms/chat.go b/api/comms/chat.go index 9888d20d..67202ce7 100644 --- a/api/comms/chat.go +++ b/api/comms/chat.go @@ -364,6 +364,8 @@ func getNewBlasts(tx dbv1.DBTX, ctx context.Context, arg getNewBlastsParams) ([] WHERE blast.audience = 'remixer_audience' AND og.owner_id = blast.from_user_id AND t.owner_id = @user_id + AND t.access_authorities IS NULL + AND og.access_authorities IS NULL AND ( blast.audience_content_id IS NULL OR ( diff --git a/api/comms/validator.go b/api/comms/validator.go index d533f54a..a554142b 100644 --- a/api/comms/validator.go +++ b/api/comms/validator.go @@ -611,6 +611,8 @@ func hasNewBlastFromUser(pool *dbv1.DBPools, ctx context.Context, userID int32, JOIN tracks og ON remixes.parent_track_id = og.track_id WHERE og.owner_id = blast.from_user_id AND t.owner_id = $1 + AND t.access_authorities IS NULL + AND og.access_authorities IS NULL AND ( blast.audience_content_id IS NULL OR ( diff --git a/api/comms_blasts.go b/api/comms_blasts.go index 5d9ef7b0..035b5ea4 100644 --- a/api/comms_blasts.go +++ b/api/comms_blasts.go @@ -64,6 +64,8 @@ func (app *ApiServer) getNewBlasts(c *fiber.Ctx) error { WHERE blast.audience = 'remixer_audience' AND og.owner_id = blast.from_user_id AND t.owner_id = @user_id + AND t.access_authorities IS NULL + AND og.access_authorities IS NULL AND ( blast.audience_content_id IS NULL OR ( diff --git a/api/v1_playlists_trending.go b/api/v1_playlists_trending.go index ee9ebf2a..134b946e 100644 --- a/api/v1_playlists_trending.go +++ b/api/v1_playlists_trending.go @@ -62,6 +62,8 @@ func (app *ApiServer) v1PlaylistsTrending(c *fiber.Ctx) error { pt.is_removed = false AND t.is_delete = false AND t.is_current = true + AND t.stem_of IS NULL + AND t.access_authorities IS NULL ) SELECT playlist_id diff --git a/api/v1_track_comment_count.go b/api/v1_track_comment_count.go index 691e2126..ad1c0d96 100644 --- a/api/v1_track_comment_count.go +++ b/api/v1_track_comment_count.go @@ -16,7 +16,7 @@ func (app *ApiServer) v1TrackCommentCount(c *fiber.Ctx) error { track AS ( SELECT track_id, owner_id FROM tracks - WHERE track_id = @trackId + WHERE track_id = @trackId AND access_authorities IS NULL ), -- Users muted by high-karma users diff --git a/api/v1_track_comments.go b/api/v1_track_comments.go index 839c3bae..1c3e5061 100644 --- a/api/v1_track_comments.go +++ b/api/v1_track_comments.go @@ -14,7 +14,7 @@ func (app *ApiServer) v1TrackComments(c *fiber.Ctx) error { track AS ( SELECT track_id, owner_id FROM tracks - WHERE track_id = @track_id + WHERE track_id = @track_id AND access_authorities IS NULL ), -- Users muted by high-karma users diff --git a/api/v1_track_subsequent.go b/api/v1_track_subsequent.go index 5e83bab3..2180b959 100644 --- a/api/v1_track_subsequent.go +++ b/api/v1_track_subsequent.go @@ -33,6 +33,8 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { FROM tracks WHERE track_id = @trackId AND is_current = true + AND stem_of IS NULL + AND access_authorities IS NULL LIMIT 1 ), same_block_tracks AS ( @@ -40,6 +42,8 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { FROM tracks WHERE is_current = true AND created_at = (SELECT created_at from current_track) AND track_id > @trackId + AND stem_of IS NULL + AND access_authorities IS NULL ORDER BY track_id ASC LIMIT @limit ), @@ -47,6 +51,8 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { SELECT track_id, created_at FROM tracks WHERE is_current = true AND created_at > (SELECT created_at from current_track) + AND stem_of IS NULL + AND access_authorities IS NULL ORDER by created_at ASC, track_ID ASC LIMIT @limit ), diff --git a/api/v1_tracks_feeling_lucky.go b/api/v1_tracks_feeling_lucky.go index a7915fed..895e4d7b 100644 --- a/api/v1_tracks_feeling_lucky.go +++ b/api/v1_tracks_feeling_lucky.go @@ -31,6 +31,7 @@ func (app *ApiServer) v1TracksFeelingLucky(c *fiber.Ctx) error { "is_delete = false", "is_unlisted = false", "stem_of IS NULL", + "access_authorities IS NULL", "aggregate_plays.count >= 250", } if params.MinFollowers != 0 { diff --git a/api/v1_tracks_recent_premium.go b/api/v1_tracks_recent_premium.go index 83d5cf74..a69a3027 100644 --- a/api/v1_tracks_recent_premium.go +++ b/api/v1_tracks_recent_premium.go @@ -28,6 +28,8 @@ func (app *ApiServer) v1TracksRecentPremium(c *fiber.Ctx) error { is_unlisted = false AND is_available = true AND is_delete = false AND + stem_of IS NULL AND + access_authorities IS NULL AND (stream_conditions ? 'usdc_purchase' OR download_conditions ? 'usdc_purchase') AND created_at >= now() - interval '1 month' ORDER BY owner_id, created_at DESC diff --git a/api/v1_users_feed.go b/api/v1_users_feed.go index 12befde6..b67bb5ff 100644 --- a/api/v1_users_feed.go +++ b/api/v1_users_feed.go @@ -92,6 +92,7 @@ func (app *ApiServer) v1UsersFeed(c *fiber.Ctx) error { AND is_unlisted = false AND is_delete = false AND stem_of is null + AND access_authorities IS NULL ) UNION ALL diff --git a/api/v1_users_listen_counts_monthly.go b/api/v1_users_listen_counts_monthly.go index ffe325e2..3e6820f7 100644 --- a/api/v1_users_listen_counts_monthly.go +++ b/api/v1_users_listen_counts_monthly.go @@ -25,7 +25,7 @@ func (app *ApiServer) v1UsersListenCountsMonthly(c *fiber.Ctx) error { SUM(count) AS count FROM aggregate_monthly_plays WHERE play_item_id IN ( - SELECT track_id from tracks where owner_id = @userId + SELECT track_id FROM tracks WHERE owner_id = @userId AND stem_of IS NULL AND access_authorities IS NULL ) AND timestamp >= @startTime AND timestamp < @endTime diff --git a/api/v1_users_recommended_tracks.go b/api/v1_users_recommended_tracks.go index b13556ff..1dd7c855 100644 --- a/api/v1_users_recommended_tracks.go +++ b/api/v1_users_recommended_tracks.go @@ -77,6 +77,8 @@ func (app *ApiServer) v1UsersRecommendedTracks(c *fiber.Ctx) error { t.is_unlisted = false AND t.is_current = true AND t.is_delete = false + AND t.stem_of IS NULL + AND t.access_authorities IS NULL AND u.is_deactivated = false ORDER BY random() LIMIT 10 diff --git a/api/v1_users_related.go b/api/v1_users_related.go index 3a530d43..0d3ac9be 100644 --- a/api/v1_users_related.go +++ b/api/v1_users_related.go @@ -49,6 +49,7 @@ func (app *ApiServer) v1UsersRelated(c *fiber.Ctx) error { AND t.is_unlisted IS false AND t.is_available IS true AND t.stem_of IS NULL + AND t.access_authorities IS NULL AND owner_id = @userId GROUP BY genre ORDER BY count(*) DESC @@ -101,6 +102,7 @@ func (app *ApiServer) v1UsersRelated(c *fiber.Ctx) error { AND is_unlisted = false AND is_available = true AND stem_of IS NULL + AND access_authorities IS NULL AND genre IS NOT NULL GROUP BY genre ORDER BY COUNT(*) DESC diff --git a/api/v1_users_tags.go b/api/v1_users_tags.go index d5041d50..75e3cf80 100644 --- a/api/v1_users_tags.go +++ b/api/v1_users_tags.go @@ -27,6 +27,7 @@ func (app *ApiServer) v1UsersTags(c *fiber.Ctx) error { AND is_unlisted = false AND is_delete = false AND stem_of is null + AND access_authorities IS NULL ) AS split_tags WHERE tag != '' GROUP BY tag diff --git a/api/v1_users_tracks.go b/api/v1_users_tracks.go index 944b9eff..f81e7b02 100644 --- a/api/v1_users_tracks.go +++ b/api/v1_users_tracks.go @@ -75,7 +75,8 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error { AND t.is_delete = false AND t.is_available = true AND ` + trackFilter + ` - AND t.stem_of is null` + gateFilter + ` + AND t.stem_of is null + AND t.access_authorities IS NULL` + gateFilter + ` ORDER BY (CASE WHEN t.track_id = u.artist_pick_track_id THEN 0 ELSE 1 END), ` + orderClause + ` LIMIT @limit OFFSET @offset diff --git a/api/v1_users_tracks_ai_attributed.go b/api/v1_users_tracks_ai_attributed.go index ebfb32a2..18d7c5ed 100644 --- a/api/v1_users_tracks_ai_attributed.go +++ b/api/v1_users_tracks_ai_attributed.go @@ -71,6 +71,7 @@ func (app *ApiServer) v1UserTracksAiAttributed(c *fiber.Ctx) error { AND t.is_available = true AND ` + trackFilter + ` AND t.stem_of is null + AND t.access_authorities IS NULL ORDER BY ` + orderClause + ` LIMIT @limit OFFSET @offset diff --git a/api/v1_users_tracks_count.go b/api/v1_users_tracks_count.go index e11fab6c..4ae31a15 100644 --- a/api/v1_users_tracks_count.go +++ b/api/v1_users_tracks_count.go @@ -42,7 +42,8 @@ func (app *ApiServer) v1UserTracksCount(c *fiber.Ctx) error { AND t.is_delete = false AND t.is_available = true AND ` + trackFilter + ` - AND t.stem_of is null` + gateFilter + AND t.stem_of is null + AND t.access_authorities IS NULL` + gateFilter args := pgx.NamedArgs{ "user_id": userId, diff --git a/ddl/functions/handle_track.sql b/ddl/functions/handle_track.sql index f8e08815..d39da7e3 100644 --- a/ddl/functions/handle_track.sql +++ b/ddl/functions/handle_track.sql @@ -3,7 +3,8 @@ begin return track.is_unlisted = false and track.is_available = true and track.is_delete = false - and track.stem_of is null; + and track.stem_of is null + and track.access_authorities is null; end $$ LANGUAGE plpgsql; @@ -37,6 +38,7 @@ begin and t.is_delete is false and t.is_available is true and t.stem_of is null + and t.access_authorities is null and t.owner_id = new.owner_id ) where user_id = new.owner_id diff --git a/sql/01_schema.sql b/sql/01_schema.sql index 570ce3e0..0fd95f27 100644 --- a/sql/01_schema.sql +++ b/sql/01_schema.sql @@ -3810,6 +3810,7 @@ begin and t.is_delete is false and t.is_available is true and t.stem_of is null + and t.access_authorities is null and t.owner_id = new.owner_id ) where user_id = new.owner_id @@ -5013,6 +5014,7 @@ BEGIN and (t.is_delete is false) and (t.is_unlisted is false) and (t.stem_of is null) + and (t.access_authorities is null) ) with no data; create index trending_params_track_id_idx on public.trending_params using btree (track_id); @@ -5165,7 +5167,8 @@ begin return track.is_unlisted = false and track.is_available = true and track.is_delete = false - and track.stem_of is null; + and track.stem_of is null + and track.access_authorities is null; end $$; From 727b1966b2420fd4c64f8c4472d7295c68b0cdc5 Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Wed, 4 Mar 2026 18:11:42 -0800 Subject: [PATCH 8/8] Revert track_subsequent filters --- api/v1_track_subsequent.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/api/v1_track_subsequent.go b/api/v1_track_subsequent.go index 2180b959..5e83bab3 100644 --- a/api/v1_track_subsequent.go +++ b/api/v1_track_subsequent.go @@ -33,8 +33,6 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { FROM tracks WHERE track_id = @trackId AND is_current = true - AND stem_of IS NULL - AND access_authorities IS NULL LIMIT 1 ), same_block_tracks AS ( @@ -42,8 +40,6 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { FROM tracks WHERE is_current = true AND created_at = (SELECT created_at from current_track) AND track_id > @trackId - AND stem_of IS NULL - AND access_authorities IS NULL ORDER BY track_id ASC LIMIT @limit ), @@ -51,8 +47,6 @@ func (app *ApiServer) v1TrackSubsequent(c *fiber.Ctx) error { SELECT track_id, created_at FROM tracks WHERE is_current = true AND created_at > (SELECT created_at from current_track) - AND stem_of IS NULL - AND access_authorities IS NULL ORDER by created_at ASC, track_ID ASC LIMIT @limit ),