Skip to content

Commit

Permalink
[C-3475] Allow editing album track order (#6876)
Browse files Browse the repository at this point in the history
  • Loading branch information
amendelsohn authored Dec 14, 2023
1 parent 5b9d32c commit ac8aa98
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 14 deletions.
6 changes: 4 additions & 2 deletions packages/common/src/services/remote-config/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ export enum FeatureFlags {
BUY_USDC_VIA_SOL = 'buy_usdc_via_sol',
IOS_USDC_PURCHASE_ENABLED = 'ios_usdc_purchase_enabled',
SCHEDULED_RELEASES = 'scheduled_releases',
BUY_WITH_COINFLOW = 'buy_with_coinflow'
BUY_WITH_COINFLOW = 'buy_with_coinflow',
EDIT_ALBUMS = 'edit_albums'
}

type FlagDefaults = Record<FeatureFlags, boolean>
Expand Down Expand Up @@ -126,5 +127,6 @@ export const flagDefaults: FlagDefaults = {
[FeatureFlags.BUY_USDC_VIA_SOL]: false,
[FeatureFlags.IOS_USDC_PURCHASE_ENABLED]: true,
[FeatureFlags.SCHEDULED_RELEASES]: false,
[FeatureFlags.BUY_WITH_COINFLOW]: false
[FeatureFlags.BUY_WITH_COINFLOW]: false,
[FeatureFlags.EDIT_ALBUMS]: false
}
Original file line number Diff line number Diff line change
Expand Up @@ -1034,3 +1034,144 @@ def get_events_side_effect(_, tx_receipt):
)

assert total_changes == 0


def test_index_add_tracks_to_collections(app, mocker):
"Tests adding tracks to albums and playlists. Tracks not owned by the album's owner should be ignored. Playlists allow all."

# setup db and mocked txs
with app.app_context():
db = get_db()
web3 = Web3()
challenge_event_bus: ChallengeEventBus = setup_challenge_bus()
update_task = UpdateTask(web3, challenge_event_bus)

test_metadata = {
"AlbumTracklistUpdate": {
"playlist_contents": {
"track_ids": [
{"time": 1660927554, "track": 1},
{"time": 1660927554, "track": 2},
]
}
},
"PlaylistTracklistUpdate": {
"playlist_contents": {
"track_ids": [
{"time": 1660927554, "track": 1},
{"time": 1660927554, "track": 2},
]
}
},
}

album_tracklist_update_json = json.dumps(test_metadata["AlbumTracklistUpdate"])
playlist_tracklist_update_json = json.dumps(
test_metadata["PlaylistTracklistUpdate"]
)

tx_receipts = {
"UpdateAlbumTracklistUpdate": [
{
"args": AttributeDict(
{
"_entityId": PLAYLIST_ID_OFFSET,
"_entityType": "Playlist",
"_userId": 1,
"_action": "Update",
"_metadata": f'{{"cid": "AlbumTracklistUpdate", "data": {album_tracklist_update_json}}}',
"_signer": "user1wallet",
}
)
}
],
"UpdatePlaylistTracklistUpdate": [
{
"args": AttributeDict(
{
"_entityId": PLAYLIST_ID_OFFSET + 1,
"_entityType": "Playlist",
"_userId": 1,
"_action": "Update",
"_metadata": f'{{"cid": "PlaylistTracklistUpdate", "data": {playlist_tracklist_update_json}}}',
"_signer": "user1wallet",
}
)
}
],
}

entity_manager_txs = [
AttributeDict({"transactionHash": update_task.web3.to_bytes(text=tx_receipt)})
for tx_receipt in tx_receipts
]

def get_events_side_effect(_, tx_receipt):
return tx_receipts[tx_receipt["transactionHash"].decode("utf-8")]

mocker.patch(
"src.tasks.entity_manager.entity_manager.get_entity_manager_events_tx",
side_effect=get_events_side_effect,
autospec=True,
)

entities = {
"users": [
{"user_id": 1, "handle": "user-1", "wallet": "user1wallet"},
],
"tracks": [
{"track_id": 1, "owner_id": 1},
{"track_id": 2, "owner_id": 2},
],
"playlists": [
{
"playlist_id": PLAYLIST_ID_OFFSET,
"playlist_owner_id": 1,
"is_album": True,
},
{
"playlist_id": PLAYLIST_ID_OFFSET + 1,
"playlist_owner_id": 1,
"is_album": False,
},
],
}
populate_mock_db(db, entities)
with db.scoped_session() as session:
# index transactions
entity_manager_update(
update_task,
session,
entity_manager_txs,
block_number=0,
block_timestamp=1585336422,
block_hash=hex(0),
)

# Validate album got only the single owned track
all_playlists: List[Playlist] = session.query(Playlist).all()
assert len(all_playlists) == 2

album: Playlist = (
session.query(Playlist)
.filter(Playlist.playlist_id == PLAYLIST_ID_OFFSET)
.first()
)
assert album.is_album == True
assert len(album.playlist_contents["track_ids"]) == 1
assert album.playlist_contents["track_ids"] <= [
{"time": 1585336422, "track": 1, "metadata_time": 1660927554}
]

# Validate playlist got both tracks
playlist: Playlist = (
session.query(Playlist)
.filter(Playlist.playlist_id == PLAYLIST_ID_OFFSET + 1)
.first()
)
assert playlist.is_album == False
assert len(playlist.playlist_contents["track_ids"]) == 2
assert playlist.playlist_contents["track_ids"] <= [
{"metadata_time": 1660927554, "time": 1585336422, "track": 1},
{"metadata_time": 1660927554, "time": 1585336422, "track": 2},
]
Original file line number Diff line number Diff line change
Expand Up @@ -304,13 +304,22 @@ def delete_playlist(params: ManageEntityParameters):
params.add_record(params.entity_id, deleted_playlist)


def process_playlist_contents(playlist_record, playlist_metadata, block_integer_time):
def process_playlist_contents(
playlist_record, playlist_metadata, block_integer_time, existing_track_records
):
updated_tracks = []
for track in playlist_metadata["playlist_contents"]["track_ids"]:
track_id = track["track"]
metadata_time = track["time"]
index_time = block_integer_time # default to current block for new tracks

track_metadata = existing_track_records.get(track_id)
if playlist_record.is_album and (
not track_metadata
or (track_metadata.owner_id != playlist_record.playlist_owner_id)
):
continue

previous_playlist_tracks = playlist_record.playlist_contents["track_ids"]
for previous_track in previous_playlist_tracks:
previous_track_id = previous_track["track"]
Expand Down Expand Up @@ -346,10 +355,13 @@ def process_playlist_data_event(
# Update the playlist_record when the corresponding field exists
# in playlist_metadata
if key == "playlist_contents":
if not playlist_metadata.get(key) or playlist_record.is_album:
if not playlist_metadata.get(key):
continue
playlist_record.playlist_contents = process_playlist_contents(
playlist_record, playlist_metadata, block_integer_time
playlist_record,
playlist_metadata,
block_integer_time,
params.existing_records["Track"],
)
elif key in playlist_metadata:
if key in immutable_playlist_fields and params.action == Action.UPDATE:
Expand Down
11 changes: 4 additions & 7 deletions packages/discovery-provider/src/tasks/index_payment_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@
from decimal import Decimal
from typing import List, Optional, Tuple, TypedDict, Union, cast

from src.models.users.payment_router import PaymentRouterTx
from redis import Redis
from solders.instruction import CompiledInstruction
from solders.message import Message
from solders.pubkey import Pubkey
from solders.token.associated import get_associated_token_address
from solders.rpc.responses import GetTransactionResp
from solders.token.associated import get_associated_token_address
from solders.transaction_status import UiTransactionStatusMeta

from sqlalchemy import and_, desc
from sqlalchemy.orm.session import Session

from src.challenges.challenge_event import ChallengeEvent
from src.challenges.challenge_event_bus import ChallengeEventBus
from src.exceptions import SolanaTransactionFetchError
from src.models.tracks.track_price_history import TrackPriceHistory
from src.models.tracks.track import Track
from src.models.tracks.track_price_history import TrackPriceHistory
from src.models.users.payment_router import PaymentRouterTx
from src.models.users.usdc_purchase import PurchaseType, USDCPurchase
from src.models.users.usdc_transactions_history import (
USDCTransactionMethod,
Expand All @@ -36,9 +35,7 @@
USDC_DECIMALS,
)
from src.solana.solana_client_manager import SolanaClientManager
from src.solana.solana_helpers import (
get_base_address,
)
from src.solana.solana_helpers import get_base_address
from src.tasks.celery_app import celery
from src.utils.cache_solana_program import (
cache_latest_sol_db_tx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
CollectionTrack,
CollectionPageTrackRecord,
CollectionsPageType,
DogEarType
DogEarType,
FeatureFlags
} from '@audius/common'

import {
Expand All @@ -23,6 +24,7 @@ import Page from 'components/page/Page'
import { SuggestedTracks } from 'components/suggested-tracks'
import { Tile } from 'components/tile'
import { TracksTable, TracksTableColumn } from 'components/tracks-table'
import { useFlag } from 'hooks/useRemoteConfig'
import { computeCollectionMetadataProps } from 'pages/collection-page/store/utils'

import styles from './CollectionPage.module.css'
Expand Down Expand Up @@ -118,6 +120,8 @@ const CollectionPage = ({
onClickReposts,
onClickFavorites
}: CollectionPageProps) => {
const { isEnabled: isEditAlbumsEnabled } = useFlag(FeatureFlags.EDIT_ALBUMS)

// TODO: Consider dynamic lineups, esp. for caching improvement.
const [dataSource, playingIndex] =
tracks.status === Status.SUCCESS
Expand Down Expand Up @@ -264,7 +268,7 @@ const CollectionPage = ({
userId !== null &&
userId === playlistOwnerId &&
allowReordering &&
!isAlbum
(!isAlbum || isEditAlbumsEnabled)
}
removeText={`${messages.remove} ${
isAlbum ? messages.type.album : messages.type.playlist
Expand Down

0 comments on commit ac8aa98

Please sign in to comment.