Skip to content

Commit a7a9e17

Browse files
rickyromboCopilot
andauthored
Fix playlist writes in client (#13811)
- `publishTracks` returns string track IDs, which were being incorrectly parsed as though they were numbers that needed converting. This was changed behavior from recent SDK changes made to match the POST endpoints as this was working previously - `createPlaylist` wasn't equipped to handle using a preset `playlistId` like our client expects, rejecting calls that had `playlistId` already set in the metadata (which would happen on our creation of playlists from scratch). - `createPlaylistInternal` was being passed parsed parameters in the `createPlaylist` case, and unparsed in the `uploadPlaylist` case, and used types that made it hard to squeeze both callsites in. This was resulting in incorrectly setting some IDs to hash IDs (eg in `playlistContents`) and was uncovered when fixing the playlistId bug above - `updatePlaylist` had incorrect schema still referencing `coverArtCid` instead of `playlistImageSizesMultihash`, blocking any playlist updates that included an image update --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent f91de06 commit a7a9e17

4 files changed

Lines changed: 57 additions & 56 deletions

File tree

.changeset/cool-onions-argue.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@audius/sdk': patch
3+
---
4+
5+
Fix create/upload/update playlist in legacy path
6+
7+
- `publishTracks` returns string track IDs, which were being incorrectly parsed as though they were numbers that needed converting. This was changed behavior from recent SDK changes made to match the POST endpoints as this was working previously
8+
- `createPlaylist` wasn't equipped to handle using a preset `playlistId` like our client expects, rejecting calls that had `playlistId` already set in the metadata (which would happen on our creation of playlists from scratch).
9+
- `createPlaylistInternal` was being passed parsed parameters in the `createPlaylist` case, and unparsed in the `uploadPlaylist` case, and used types that made it hard to squeeze both callsites in. This was resulting in incorrectly setting some IDs to hash IDs (eg in `playlistContents`) and was uncovered when fixing the playlistId bug above
10+
- `updatePlaylist` had incorrect schema still referencing `coverArtCid` instead of `playlistImageSizesMultihash`, blocking any playlist updates that included an image update

packages/common/src/api/tan-query/upload/usePublishCollection.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,13 @@ const getPublishCollectionOptions = (context: PublishCollectionContext) =>
103103
const metadata = albumMetadataForCreateWithSDK(
104104
params.collectionMetadata
105105
)
106-
metadata.playlistContents = publishedTracks.map((t) => ({
107-
timestamp: Math.round(Date.now() / 1000),
108-
trackId: Id.parse(t.trackId),
109-
metadataTimestamp: Math.round(Date.now() / 1000)
110-
}))
106+
metadata.playlistContents = publishedTracks
107+
.filter((t) => !!t.trackId)
108+
.map((t) => ({
109+
timestamp: Math.round(Date.now() / 1000),
110+
trackId: t.trackId!,
111+
metadataTimestamp: Math.round(Date.now() / 1000)
112+
}))
111113
return await sdk.albums.createAlbum({
112114
userId: Id.parse(userId),
113115
imageFile: coverArtFile,
@@ -117,11 +119,13 @@ const getPublishCollectionOptions = (context: PublishCollectionContext) =>
117119
const metadata = playlistMetadataForCreateWithSDK(
118120
params.collectionMetadata
119121
)
120-
metadata.playlistContents = publishedTracks.map((t) => ({
121-
timestamp: Math.round(Date.now() / 1000),
122-
trackId: Id.parse(t.trackId),
123-
metadataTimestamp: Math.round(Date.now() / 1000)
124-
}))
122+
metadata.playlistContents = publishedTracks
123+
.filter((t) => !!t.trackId)
124+
.map((t) => ({
125+
timestamp: Math.round(Date.now() / 1000),
126+
trackId: t.trackId!,
127+
metadataTimestamp: Math.round(Date.now() / 1000)
128+
}))
125129
return await sdk.playlists.createPlaylist({
126130
userId: Id.parse(userId),
127131
imageFile: coverArtFile,

packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -90,26 +90,15 @@ export class PlaylistsApi extends GeneratedPlaylistsApi {
9090
params: EntityManagerCreatePlaylistRequest,
9191
advancedOptions?: AdvancedOptions
9292
) {
93-
// Parse inputs
94-
const parsedParameters = await parseParams(
95-
'createPlaylist',
96-
CreatePlaylistSchema
97-
)(params)
98-
99-
// Call createPlaylistInternal with parsed inputs
100-
return await this.createPlaylistInternal(parsedParameters, advancedOptions)
93+
return await this.createPlaylistInternal(params, advancedOptions)
10194
}
10295

10396
override async createPlaylist(
10497
params: CreatePlaylistRequestWithFiles,
10598
requestInit?: RequestInit
10699
) {
107100
if (this.entityManager) {
108-
const { metadata } = params
109-
return await this.createPlaylistWithEntityManager({
110-
userId: params.userId,
111-
metadata
112-
})
101+
return await this.createPlaylistWithEntityManager(params)
113102
}
114103
return super.createPlaylist(params, requestInit)
115104
}
@@ -175,9 +164,9 @@ export class PlaylistsApi extends GeneratedPlaylistsApi {
175164
// Transform track metadata (cast: SDK upload schema and API body types align at runtime)
176165
const trackMetadata = this.combineMetadata(
177166
this.trackUploadHelper.transformTrackUploadMetadataV2(
178-
t as unknown as CreateTrackRequestBody,
167+
t as CreateTrackRequestBody,
179168
userId
180-
) as CreateTrackRequestBody,
169+
),
181170
playlistMetadata
182171
)
183172

@@ -729,43 +718,40 @@ export class PlaylistsApi extends GeneratedPlaylistsApi {
729718
}
730719

731720
/** @internal
732-
* Method to create a playlist with already parsed inputs
721+
* Method to create a playlist from raw inputs, parsing them with CreatePlaylistSchema
733722
* This is used for both playlists and albums
734723
*/
735-
public async createPlaylistInternal<Metadata extends PlaylistMetadata>(
736-
{
737-
userId,
738-
imageFile,
739-
metadata,
740-
onProgress,
741-
trackIds,
742-
playlistId: providedPlaylistId
743-
}: z.infer<typeof CreatePlaylistSchema> & { metadata: Metadata },
724+
public async createPlaylistInternal(
725+
params: z.input<typeof CreatePlaylistSchema>,
744726
advancedOptions?: AdvancedOptions
745727
) {
728+
const { userId, imageFile, metadata, onProgress } = await parseParams(
729+
'createPlaylistInternal',
730+
CreatePlaylistSchema
731+
)(params)
732+
746733
// Upload cover art to storage node
747734
const coverArtResponse =
748735
imageFile &&
749736
(await this.storage
750737
.uploadFile({
751738
file: imageFile,
752-
onProgress,
739+
onProgress: (event) =>
740+
onProgress?.(event.loaded / event.total, {
741+
...event,
742+
key: 'image'
743+
}),
753744
metadata: {
754745
template: 'img_square'
755746
}
756747
})
757748
.start())
758749

750+
const providedPlaylistId = metadata.playlistId
759751
const playlistId = providedPlaylistId || (await this.generatePlaylistId())
760-
const timestamp = getCurrentTimestamp()
761752

762-
// Update metadata to include track ids
763753
const updatedMetadata = {
764754
...metadata,
765-
playlistContents: (trackIds ?? []).map((trackId) => ({
766-
trackId,
767-
timestamp
768-
})),
769755
playlistImageSizesMultihash: coverArtResponse?.orig_file_cid
770756
}
771757

packages/sdk/src/sdk/api/playlists/types.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export type PlaylistsApiServicesConfig = {
2020

2121
const CreatePlaylistMetadataSchema = z
2222
.object({
23+
playlistId: z.optional(HashId),
2324
description: z.optional(z.string().max(1000)),
2425
playlistName: z.string(),
2526
isPrivate: z.optional(z.boolean()),
@@ -53,12 +54,24 @@ export type CreatePlaylistMetadata = z.input<
5354
typeof CreatePlaylistMetadataSchema
5455
>
5556

57+
export const UploadPlaylistProgressEventSchema = ProgressEventSchema.extend({
58+
/**
59+
* Index of the track being uploaded in the playlist tracks array, or 'image' if for the image
60+
*/
61+
key: z.number().or(z.literal('image'))
62+
})
63+
64+
export const UploadPlaylistProgressHandlerSchema = z
65+
.function()
66+
.args(z.number(), UploadPlaylistProgressEventSchema)
67+
.returns(z.void())
68+
5669
export const CreatePlaylistSchema = z
5770
.object({
5871
playlistId: z.optional(HashId),
5972
imageFile: z.optional(ImageFile),
6073
metadata: CreatePlaylistMetadataSchema,
61-
onProgress: z.optional(z.function()),
74+
onProgress: UploadPlaylistProgressHandlerSchema.optional(),
6275
trackIds: z.optional(z.array(HashId)),
6376
userId: HashId
6477
})
@@ -101,25 +114,13 @@ const PlaylistTrackMetadataSchema = UploadTrackMetadataSchema.partial({
101114
*/
102115
export type PlaylistTrackMetadata = z.infer<typeof PlaylistTrackMetadataSchema>
103116

104-
export const UploadPlaylistProgressEventSchema = ProgressEventSchema.extend({
105-
/**
106-
* Index of the track being uploaded in the playlist tracks array, or 'image' if for the image
107-
*/
108-
key: z.number().or(z.literal('image'))
109-
})
110-
111117
/**
112118
* The progress event for updating a playlist
113119
*/
114120
type UploadPlaylistProgressEvent = z.input<
115121
typeof UploadPlaylistProgressEventSchema
116122
>
117123

118-
export const UploadPlaylistProgressHandlerSchema = z
119-
.function()
120-
.args(z.number(), UploadPlaylistProgressEventSchema)
121-
.returns(z.void())
122-
123124
export type UploadPlaylistProgressHandler = (
124125
/**
125126
* Overall progress percentage (0-1)
@@ -166,7 +167,7 @@ export const UpdatePlaylistMetadataSchema =
166167
})
167168
)
168169
),
169-
coverArtCid: z.optional(z.string())
170+
playlistImageSizesMultihash: z.optional(z.string())
170171
})
171172
)
172173
.strict()

0 commit comments

Comments
 (0)