diff --git a/packages/sdk/src/sdk/api/albums/AlbumsApi.ts b/packages/sdk/src/sdk/api/albums/AlbumsApi.ts index bde13441a96..bd0ea6d697c 100644 --- a/packages/sdk/src/sdk/api/albums/AlbumsApi.ts +++ b/packages/sdk/src/sdk/api/albums/AlbumsApi.ts @@ -7,10 +7,7 @@ import type { SolanaRelayService, StorageService } from '../../services' -import type { - EntityManagerService, - AdvancedOptions -} from '../../services/EntityManager/types' +import type { EntityManagerService } from '../../services/EntityManager/types' import type { LoggerService } from '../../services/Logger' import type { SolanaClient } from '../../services/Solana/programs/SolanaClient' import { parseParams } from '../../utils/parseParams' @@ -112,10 +109,7 @@ export class AlbumsApi { * Upload an album * Uploads the specified tracks and combines them into an album */ - async uploadAlbum( - params: UploadAlbumRequest, - advancedOptions?: AdvancedOptions - ) { + async uploadAlbum(params: UploadAlbumRequest) { await parseParams('uploadAlbum', UploadAlbumSchema)(params) const { albumName, ...playlistMetadata } = params.metadata @@ -129,10 +123,7 @@ export class AlbumsApi { } } - return await this.playlistsApi.uploadPlaylist( - playlistParams, - advancedOptions - ) + return await this.playlistsApi.uploadPlaylist(playlistParams) } /** @hidden diff --git a/packages/sdk/src/sdk/api/generated/default/.openapi-generator/FILES b/packages/sdk/src/sdk/api/generated/default/.openapi-generator/FILES index 41a9ffa2a57..134ac6d39eb 100644 --- a/packages/sdk/src/sdk/api/generated/default/.openapi-generator/FILES +++ b/packages/sdk/src/sdk/api/generated/default/.openapi-generator/FILES @@ -1,3 +1,4 @@ +.openapi-generator-ignore apis/ChallengesApi.ts apis/CoinsApi.ts apis/CommentsApi.ts @@ -73,6 +74,8 @@ models/CreateDeveloperAppRequestBody.ts models/CreateDeveloperAppResponse.ts models/CreateGrantRequestBody.ts models/CreatePlaylistRequestBody.ts +models/CreatePlaylistRequestBodyCopyrightLine.ts +models/CreatePlaylistRequestBodyProducerCopyrightLine.ts models/CreatePlaylistResponse.ts models/CreateRewardCodeRequestBody.ts models/CreateRewardCodeResponse.ts @@ -85,6 +88,8 @@ models/CreateUserRequestBodyEvents.ts models/CreateUserResponse.ts models/DashboardWalletUser.ts models/DashboardWalletUsersResponse.ts +models/DdexCopyright.ts +models/DdexResourceContributor.ts models/DeactivateAccessKeyRequestBody.ts models/DeactivateAccessKeyResponse.ts models/DecodedUserToken.ts diff --git a/packages/sdk/src/sdk/api/generated/default/apis/TracksApi.ts b/packages/sdk/src/sdk/api/generated/default/apis/TracksApi.ts index 4525fd626c5..d7f6d92dfd8 100644 --- a/packages/sdk/src/sdk/api/generated/default/apis/TracksApi.ts +++ b/packages/sdk/src/sdk/api/generated/default/apis/TracksApi.ts @@ -270,6 +270,16 @@ export interface GetTrendingUSDCPurchaseTracksWithVersionRequest { time?: GetTrendingUSDCPurchaseTracksWithVersionTimeEnum; } +export interface GetTrendingUndergroundWinnersRequest { + week?: Date; + userId?: string; +} + +export interface GetTrendingWinnersRequest { + week?: Date; + userId?: string; +} + export interface GetUndergroundTrendingTracksRequest { offset?: number; limit?: number; @@ -1524,6 +1534,76 @@ export class TracksApi extends runtime.BaseAPI { return await response.value(); } + /** + * @hidden + * Gets weekly trending underground winners from the trending_results table. Returns full track objects for the specified week. Defaults to the most recent week with data when no week is provided. + */ + async getTrendingUndergroundWinnersRaw(params: GetTrendingUndergroundWinnersRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + if (params.week !== undefined) { + queryParameters['week'] = (params.week as any).toISOString().substr(0,10); + } + + if (params.userId !== undefined) { + queryParameters['user_id'] = params.userId; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/tracks/trending/underground/winners`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => TracksResponseFromJSON(jsonValue)); + } + + /** + * Gets weekly trending underground winners from the trending_results table. Returns full track objects for the specified week. Defaults to the most recent week with data when no week is provided. + */ + async getTrendingUndergroundWinners(params: GetTrendingUndergroundWinnersRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getTrendingUndergroundWinnersRaw(params, initOverrides); + return await response.value(); + } + + /** + * @hidden + * Gets weekly trending winners from the trending_results table. Returns full track objects for the specified week. Defaults to the most recent week with data when no week is provided. + */ + async getTrendingWinnersRaw(params: GetTrendingWinnersRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { + const queryParameters: any = {}; + + if (params.week !== undefined) { + queryParameters['week'] = (params.week as any).toISOString().substr(0,10); + } + + if (params.userId !== undefined) { + queryParameters['user_id'] = params.userId; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + const response = await this.request({ + path: `/tracks/trending/winners`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, initOverrides); + + return new runtime.JSONApiResponse(response, (jsonValue) => TracksResponseFromJSON(jsonValue)); + } + + /** + * Gets weekly trending winners from the trending_results table. Returns full track objects for the specified week. Defaults to the most recent week with data when no week is provided. + */ + async getTrendingWinners(params: GetTrendingWinnersRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + const response = await this.getTrendingWinnersRaw(params, initOverrides); + return await response.value(); + } + /** * @hidden * Gets the top 100 trending underground tracks on Audius diff --git a/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBody.ts b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBody.ts index 3e0ff804c66..c613727e1a1 100644 --- a/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBody.ts +++ b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBody.ts @@ -20,6 +20,24 @@ import { AccessGateFromJSONTyped, AccessGateToJSON, } from './AccessGate'; +import type { CreatePlaylistRequestBodyCopyrightLine } from './CreatePlaylistRequestBodyCopyrightLine'; +import { + CreatePlaylistRequestBodyCopyrightLineFromJSON, + CreatePlaylistRequestBodyCopyrightLineFromJSONTyped, + CreatePlaylistRequestBodyCopyrightLineToJSON, +} from './CreatePlaylistRequestBodyCopyrightLine'; +import type { CreatePlaylistRequestBodyProducerCopyrightLine } from './CreatePlaylistRequestBodyProducerCopyrightLine'; +import { + CreatePlaylistRequestBodyProducerCopyrightLineFromJSON, + CreatePlaylistRequestBodyProducerCopyrightLineFromJSONTyped, + CreatePlaylistRequestBodyProducerCopyrightLineToJSON, +} from './CreatePlaylistRequestBodyProducerCopyrightLine'; +import type { DdexResourceContributor } from './DdexResourceContributor'; +import { + DdexResourceContributorFromJSON, + DdexResourceContributorFromJSONTyped, + DdexResourceContributorToJSON, +} from './DdexResourceContributor'; import type { Genre } from './Genre'; import { GenreFromJSON, @@ -50,7 +68,7 @@ export interface CreatePlaylistRequestBody { * @type {string} * @memberof CreatePlaylistRequestBody */ - playlistId?: string | null; + playlistId?: string; /** * Playlist or album name * @type {string} @@ -62,67 +80,67 @@ export interface CreatePlaylistRequestBody { * @type {string} * @memberof CreatePlaylistRequestBody */ - description?: string | null; + description?: string; /** * Whether the playlist is private * @type {boolean} * @memberof CreatePlaylistRequestBody */ - isPrivate?: boolean | null; + isPrivate?: boolean; /** * Whether this is an album * @type {boolean} * @memberof CreatePlaylistRequestBody */ - isAlbum?: boolean | null; + isAlbum?: boolean; /** * * @type {Genre} * @memberof CreatePlaylistRequestBody */ - genre?: Genre | null; + genre?: Genre; /** * * @type {Mood} * @memberof CreatePlaylistRequestBody */ - mood?: Mood | null; + mood?: Mood; /** * Comma-separated tags * @type {string} * @memberof CreatePlaylistRequestBody */ - tags?: string | null; + tags?: string; /** * License type * @type {string} * @memberof CreatePlaylistRequestBody */ - license?: string | null; + license?: string; /** * Universal Product Code (for albums) * @type {string} * @memberof CreatePlaylistRequestBody */ - upc?: string | null; + upc?: string; /** * Release date * @type {Date} * @memberof CreatePlaylistRequestBody */ - releaseDate?: Date | null; + releaseDate?: Date; /** * IPFS CID for cover art * @type {string} * @memberof CreatePlaylistRequestBody */ - coverArtCid?: string | null; + coverArtCid?: string; /** * Array of tracks in the playlist * @type {Array} * @memberof CreatePlaylistRequestBody */ - playlistContents?: Array | null; + playlistContents?: Array; /** * Whether streaming is restricted behind an access gate * @type {boolean} @@ -146,31 +164,31 @@ export interface CreatePlaylistRequestBody { * @type {string} * @memberof CreatePlaylistRequestBody */ - ddexApp?: string | null; + ddexApp?: string; /** * DDEX release identifiers - * @type {object} + * @type {{ [key: string]: string; }} * @memberof CreatePlaylistRequestBody */ - ddexReleaseIds?: object | null; + ddexReleaseIds?: { [key: string]: string; } | null; /** * DDEX resource contributors / artists - * @type {Array} + * @type {Array} * @memberof CreatePlaylistRequestBody */ - artists?: Array | null; + artists?: Array | null; /** - * DDEX copyright line - * @type {object} + * + * @type {CreatePlaylistRequestBodyCopyrightLine} * @memberof CreatePlaylistRequestBody */ - copyrightLine?: object | null; + copyrightLine?: CreatePlaylistRequestBodyCopyrightLine | null; /** - * DDEX producer copyright line - * @type {object} + * + * @type {CreatePlaylistRequestBodyProducerCopyrightLine} * @memberof CreatePlaylistRequestBody */ - producerCopyrightLine?: object | null; + producerCopyrightLine?: CreatePlaylistRequestBodyProducerCopyrightLine | null; /** * Parental warning type * @type {string} @@ -215,17 +233,17 @@ export function CreatePlaylistRequestBodyFromJSONTyped(json: any, ignoreDiscrimi 'tags': !exists(json, 'tags') ? undefined : json['tags'], 'license': !exists(json, 'license') ? undefined : json['license'], 'upc': !exists(json, 'upc') ? undefined : json['upc'], - 'releaseDate': !exists(json, 'release_date') ? undefined : (json['release_date'] === null ? null : new Date(json['release_date'])), + 'releaseDate': !exists(json, 'release_date') ? undefined : (new Date(json['release_date'])), 'coverArtCid': !exists(json, 'cover_art_cid') ? undefined : json['cover_art_cid'], - 'playlistContents': !exists(json, 'playlist_contents') ? undefined : (json['playlist_contents'] === null ? null : (json['playlist_contents'] as Array).map(PlaylistAddedTimestampFromJSON)), + 'playlistContents': !exists(json, 'playlist_contents') ? undefined : ((json['playlist_contents'] as Array).map(PlaylistAddedTimestampFromJSON)), 'isStreamGated': !exists(json, 'is_stream_gated') ? undefined : json['is_stream_gated'], 'isScheduledRelease': !exists(json, 'is_scheduled_release') ? undefined : json['is_scheduled_release'], 'streamConditions': !exists(json, 'stream_conditions') ? undefined : AccessGateFromJSON(json['stream_conditions']), 'ddexApp': !exists(json, 'ddex_app') ? undefined : json['ddex_app'], 'ddexReleaseIds': !exists(json, 'ddex_release_ids') ? undefined : json['ddex_release_ids'], - 'artists': !exists(json, 'artists') ? undefined : json['artists'], - 'copyrightLine': !exists(json, 'copyright_line') ? undefined : json['copyright_line'], - 'producerCopyrightLine': !exists(json, 'producer_copyright_line') ? undefined : json['producer_copyright_line'], + 'artists': !exists(json, 'artists') ? undefined : (json['artists'] === null ? null : (json['artists'] as Array).map(DdexResourceContributorFromJSON)), + 'copyrightLine': !exists(json, 'copyright_line') ? undefined : CreatePlaylistRequestBodyCopyrightLineFromJSON(json['copyright_line']), + 'producerCopyrightLine': !exists(json, 'producer_copyright_line') ? undefined : CreatePlaylistRequestBodyProducerCopyrightLineFromJSON(json['producer_copyright_line']), 'parentalWarningType': !exists(json, 'parental_warning_type') ? undefined : json['parental_warning_type'], 'isImageAutogenerated': !exists(json, 'is_image_autogenerated') ? undefined : json['is_image_autogenerated'], }; @@ -250,17 +268,17 @@ export function CreatePlaylistRequestBodyToJSON(value?: CreatePlaylistRequestBod 'tags': value.tags, 'license': value.license, 'upc': value.upc, - 'release_date': value.releaseDate === undefined ? undefined : (value.releaseDate === null ? null : value.releaseDate.toISOString().substr(0,10)), + 'release_date': value.releaseDate === undefined ? undefined : (value.releaseDate.toISOString().substr(0,10)), 'cover_art_cid': value.coverArtCid, - 'playlist_contents': value.playlistContents === undefined ? undefined : (value.playlistContents === null ? null : (value.playlistContents as Array).map(PlaylistAddedTimestampToJSON)), + 'playlist_contents': value.playlistContents === undefined ? undefined : ((value.playlistContents as Array).map(PlaylistAddedTimestampToJSON)), 'is_stream_gated': value.isStreamGated, 'is_scheduled_release': value.isScheduledRelease, 'stream_conditions': AccessGateToJSON(value.streamConditions), 'ddex_app': value.ddexApp, 'ddex_release_ids': value.ddexReleaseIds, - 'artists': value.artists, - 'copyright_line': value.copyrightLine, - 'producer_copyright_line': value.producerCopyrightLine, + 'artists': value.artists === undefined ? undefined : (value.artists === null ? null : (value.artists as Array).map(DdexResourceContributorToJSON)), + 'copyright_line': CreatePlaylistRequestBodyCopyrightLineToJSON(value.copyrightLine), + 'producer_copyright_line': CreatePlaylistRequestBodyProducerCopyrightLineToJSON(value.producerCopyrightLine), 'parental_warning_type': value.parentalWarningType, 'is_image_autogenerated': value.isImageAutogenerated, }; diff --git a/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyCopyrightLine.ts b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyCopyrightLine.ts new file mode 100644 index 00000000000..0b12a95deeb --- /dev/null +++ b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyCopyrightLine.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +/** + * API + * Audius V1 API + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * DDEX copyright line + * @export + * @interface CreatePlaylistRequestBodyCopyrightLine + */ +export interface CreatePlaylistRequestBodyCopyrightLine { + /** + * Copyright year (4 characters) + * @type {string} + * @memberof CreatePlaylistRequestBodyCopyrightLine + */ + year: string; + /** + * Copyright text + * @type {string} + * @memberof CreatePlaylistRequestBodyCopyrightLine + */ + text: string; +} + +/** + * Check if a given object implements the CreatePlaylistRequestBodyCopyrightLine interface. + */ +export function instanceOfCreatePlaylistRequestBodyCopyrightLine(value: object): value is CreatePlaylistRequestBodyCopyrightLine { + let isInstance = true; + isInstance = isInstance && "year" in value && value["year"] !== undefined; + isInstance = isInstance && "text" in value && value["text"] !== undefined; + + return isInstance; +} + +export function CreatePlaylistRequestBodyCopyrightLineFromJSON(json: any): CreatePlaylistRequestBodyCopyrightLine { + return CreatePlaylistRequestBodyCopyrightLineFromJSONTyped(json, false); +} + +export function CreatePlaylistRequestBodyCopyrightLineFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreatePlaylistRequestBodyCopyrightLine { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'year': json['year'], + 'text': json['text'], + }; +} + +export function CreatePlaylistRequestBodyCopyrightLineToJSON(value?: CreatePlaylistRequestBodyCopyrightLine | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'year': value.year, + 'text': value.text, + }; +} + diff --git a/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyProducerCopyrightLine.ts b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyProducerCopyrightLine.ts new file mode 100644 index 00000000000..9cdc90e6517 --- /dev/null +++ b/packages/sdk/src/sdk/api/generated/default/models/CreatePlaylistRequestBodyProducerCopyrightLine.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +/** + * API + * Audius V1 API + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * DDEX producer copyright line + * @export + * @interface CreatePlaylistRequestBodyProducerCopyrightLine + */ +export interface CreatePlaylistRequestBodyProducerCopyrightLine { + /** + * Copyright year (4 characters) + * @type {string} + * @memberof CreatePlaylistRequestBodyProducerCopyrightLine + */ + year: string; + /** + * Copyright text + * @type {string} + * @memberof CreatePlaylistRequestBodyProducerCopyrightLine + */ + text: string; +} + +/** + * Check if a given object implements the CreatePlaylistRequestBodyProducerCopyrightLine interface. + */ +export function instanceOfCreatePlaylistRequestBodyProducerCopyrightLine(value: object): value is CreatePlaylistRequestBodyProducerCopyrightLine { + let isInstance = true; + isInstance = isInstance && "year" in value && value["year"] !== undefined; + isInstance = isInstance && "text" in value && value["text"] !== undefined; + + return isInstance; +} + +export function CreatePlaylistRequestBodyProducerCopyrightLineFromJSON(json: any): CreatePlaylistRequestBodyProducerCopyrightLine { + return CreatePlaylistRequestBodyProducerCopyrightLineFromJSONTyped(json, false); +} + +export function CreatePlaylistRequestBodyProducerCopyrightLineFromJSONTyped(json: any, ignoreDiscriminator: boolean): CreatePlaylistRequestBodyProducerCopyrightLine { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'year': json['year'], + 'text': json['text'], + }; +} + +export function CreatePlaylistRequestBodyProducerCopyrightLineToJSON(value?: CreatePlaylistRequestBodyProducerCopyrightLine | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'year': value.year, + 'text': value.text, + }; +} + diff --git a/packages/sdk/src/sdk/api/generated/default/models/DdexCopyright.ts b/packages/sdk/src/sdk/api/generated/default/models/DdexCopyright.ts new file mode 100644 index 00000000000..2cb755e92de --- /dev/null +++ b/packages/sdk/src/sdk/api/generated/default/models/DdexCopyright.ts @@ -0,0 +1,76 @@ +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +/** + * API + * Audius V1 API + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface DdexCopyright + */ +export interface DdexCopyright { + /** + * Copyright year (4 characters) + * @type {string} + * @memberof DdexCopyright + */ + year: string; + /** + * Copyright text + * @type {string} + * @memberof DdexCopyright + */ + text: string; +} + +/** + * Check if a given object implements the DdexCopyright interface. + */ +export function instanceOfDdexCopyright(value: object): value is DdexCopyright { + let isInstance = true; + isInstance = isInstance && "year" in value && value["year"] !== undefined; + isInstance = isInstance && "text" in value && value["text"] !== undefined; + + return isInstance; +} + +export function DdexCopyrightFromJSON(json: any): DdexCopyright { + return DdexCopyrightFromJSONTyped(json, false); +} + +export function DdexCopyrightFromJSONTyped(json: any, ignoreDiscriminator: boolean): DdexCopyright { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'year': json['year'], + 'text': json['text'], + }; +} + +export function DdexCopyrightToJSON(value?: DdexCopyright | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'year': value.year, + 'text': value.text, + }; +} + diff --git a/packages/sdk/src/sdk/api/generated/default/models/DdexResourceContributor.ts b/packages/sdk/src/sdk/api/generated/default/models/DdexResourceContributor.ts new file mode 100644 index 00000000000..fa056f790c9 --- /dev/null +++ b/packages/sdk/src/sdk/api/generated/default/models/DdexResourceContributor.ts @@ -0,0 +1,84 @@ +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck +/** + * API + * Audius V1 API + * + * The version of the OpenAPI document: 1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface DdexResourceContributor + */ +export interface DdexResourceContributor { + /** + * Contributor name + * @type {string} + * @memberof DdexResourceContributor + */ + name: string; + /** + * Contributor roles + * @type {Array} + * @memberof DdexResourceContributor + */ + roles: Array; + /** + * Sequence number for ordering + * @type {number} + * @memberof DdexResourceContributor + */ + sequenceNumber?: number; +} + +/** + * Check if a given object implements the DdexResourceContributor interface. + */ +export function instanceOfDdexResourceContributor(value: object): value is DdexResourceContributor { + let isInstance = true; + isInstance = isInstance && "name" in value && value["name"] !== undefined; + isInstance = isInstance && "roles" in value && value["roles"] !== undefined; + + return isInstance; +} + +export function DdexResourceContributorFromJSON(json: any): DdexResourceContributor { + return DdexResourceContributorFromJSONTyped(json, false); +} + +export function DdexResourceContributorFromJSONTyped(json: any, ignoreDiscriminator: boolean): DdexResourceContributor { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'name': json['name'], + 'roles': json['roles'], + 'sequenceNumber': !exists(json, 'sequence_number') ? undefined : json['sequence_number'], + }; +} + +export function DdexResourceContributorToJSON(value?: DdexResourceContributor | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + 'name': value.name, + 'roles': value.roles, + 'sequence_number': value.sequenceNumber, + }; +} + diff --git a/packages/sdk/src/sdk/api/generated/default/models/UpdatePlaylistRequestBody.ts b/packages/sdk/src/sdk/api/generated/default/models/UpdatePlaylistRequestBody.ts index 3bb06656671..b365194f1b0 100644 --- a/packages/sdk/src/sdk/api/generated/default/models/UpdatePlaylistRequestBody.ts +++ b/packages/sdk/src/sdk/api/generated/default/models/UpdatePlaylistRequestBody.ts @@ -14,6 +14,30 @@ */ import { exists, mapValues } from '../runtime'; +import type { AccessGate } from './AccessGate'; +import { + AccessGateFromJSON, + AccessGateFromJSONTyped, + AccessGateToJSON, +} from './AccessGate'; +import type { CreatePlaylistRequestBodyCopyrightLine } from './CreatePlaylistRequestBodyCopyrightLine'; +import { + CreatePlaylistRequestBodyCopyrightLineFromJSON, + CreatePlaylistRequestBodyCopyrightLineFromJSONTyped, + CreatePlaylistRequestBodyCopyrightLineToJSON, +} from './CreatePlaylistRequestBodyCopyrightLine'; +import type { CreatePlaylistRequestBodyProducerCopyrightLine } from './CreatePlaylistRequestBodyProducerCopyrightLine'; +import { + CreatePlaylistRequestBodyProducerCopyrightLineFromJSON, + CreatePlaylistRequestBodyProducerCopyrightLineFromJSONTyped, + CreatePlaylistRequestBodyProducerCopyrightLineToJSON, +} from './CreatePlaylistRequestBodyProducerCopyrightLine'; +import type { DdexResourceContributor } from './DdexResourceContributor'; +import { + DdexResourceContributorFromJSON, + DdexResourceContributorFromJSONTyped, + DdexResourceContributorToJSON, +} from './DdexResourceContributor'; import type { Genre } from './Genre'; import { GenreFromJSON, @@ -44,55 +68,73 @@ export interface UpdatePlaylistRequestBody { * @type {string} * @memberof UpdatePlaylistRequestBody */ - playlistName?: string | null; + playlistName?: string; /** * Playlist description * @type {string} * @memberof UpdatePlaylistRequestBody */ - description?: string | null; + description?: string; /** * Whether the playlist is private * @type {boolean} * @memberof UpdatePlaylistRequestBody */ - isPrivate?: boolean | null; + isPrivate?: boolean; + /** + * Whether this is an album + * @type {boolean} + * @memberof UpdatePlaylistRequestBody + */ + isAlbum?: boolean; /** * * @type {Genre} * @memberof UpdatePlaylistRequestBody */ - genre?: Genre | null; + genre?: Genre; /** * * @type {Mood} * @memberof UpdatePlaylistRequestBody */ - mood?: Mood | null; + mood?: Mood; /** * Comma-separated tags * @type {string} * @memberof UpdatePlaylistRequestBody */ - tags?: string | null; + tags?: string; /** * License type * @type {string} * @memberof UpdatePlaylistRequestBody */ - license?: string | null; + license?: string; + /** + * Universal Product Code (for albums) + * @type {string} + * @memberof UpdatePlaylistRequestBody + */ + upc?: string; /** * Release date * @type {Date} * @memberof UpdatePlaylistRequestBody */ - releaseDate?: Date | null; + releaseDate?: Date; + /** + * IPFS CID for cover art + * @type {string} + * @memberof UpdatePlaylistRequestBody + */ + coverArtCid?: string; /** * Array of track IDs to include in the playlist * @type {Array} * @memberof UpdatePlaylistRequestBody */ - playlistContents?: Array | null; + playlistContents?: Array; /** * Whether streaming is restricted behind an access gate * @type {boolean} @@ -100,23 +142,59 @@ export interface UpdatePlaylistRequestBody { */ isStreamGated?: boolean | null; /** - * IPFS CID for cover art - * @type {string} + * Whether the playlist/album is a scheduled release + * @type {boolean} * @memberof UpdatePlaylistRequestBody */ - coverArtCid?: string | null; + isScheduledRelease?: boolean | null; /** - * Universal Product Code (for albums) + * + * @type {AccessGate} + * @memberof UpdatePlaylistRequestBody + */ + streamConditions?: AccessGate | null; + /** + * DDEX application identifier * @type {string} * @memberof UpdatePlaylistRequestBody */ - upc?: string | null; + ddexApp?: string; + /** + * DDEX release identifiers + * @type {{ [key: string]: string; }} + * @memberof UpdatePlaylistRequestBody + */ + ddexReleaseIds?: { [key: string]: string; } | null; + /** + * DDEX resource contributors / artists + * @type {Array} + * @memberof UpdatePlaylistRequestBody + */ + artists?: Array | null; + /** + * + * @type {CreatePlaylistRequestBodyCopyrightLine} + * @memberof UpdatePlaylistRequestBody + */ + copyrightLine?: CreatePlaylistRequestBodyCopyrightLine | null; + /** + * + * @type {CreatePlaylistRequestBodyProducerCopyrightLine} + * @memberof UpdatePlaylistRequestBody + */ + producerCopyrightLine?: CreatePlaylistRequestBodyProducerCopyrightLine | null; /** * Parental warning type * @type {string} * @memberof UpdatePlaylistRequestBody */ parentalWarningType?: string | null; + /** + * Whether the image is autogenerated + * @type {boolean} + * @memberof UpdatePlaylistRequestBody + */ + isImageAutogenerated?: boolean | null; } /** @@ -141,16 +219,25 @@ export function UpdatePlaylistRequestBodyFromJSONTyped(json: any, ignoreDiscrimi 'playlistName': !exists(json, 'playlist_name') ? undefined : json['playlist_name'], 'description': !exists(json, 'description') ? undefined : json['description'], 'isPrivate': !exists(json, 'is_private') ? undefined : json['is_private'], + 'isAlbum': !exists(json, 'is_album') ? undefined : json['is_album'], 'genre': !exists(json, 'genre') ? undefined : GenreFromJSON(json['genre']), 'mood': !exists(json, 'mood') ? undefined : MoodFromJSON(json['mood']), 'tags': !exists(json, 'tags') ? undefined : json['tags'], 'license': !exists(json, 'license') ? undefined : json['license'], - 'releaseDate': !exists(json, 'release_date') ? undefined : (json['release_date'] === null ? null : new Date(json['release_date'])), - 'playlistContents': !exists(json, 'playlist_contents') ? undefined : (json['playlist_contents'] === null ? null : (json['playlist_contents'] as Array).map(PlaylistAddedTimestampFromJSON)), - 'isStreamGated': !exists(json, 'is_stream_gated') ? undefined : json['is_stream_gated'], - 'coverArtCid': !exists(json, 'cover_art_cid') ? undefined : json['cover_art_cid'], 'upc': !exists(json, 'upc') ? undefined : json['upc'], + 'releaseDate': !exists(json, 'release_date') ? undefined : (new Date(json['release_date'])), + 'coverArtCid': !exists(json, 'cover_art_cid') ? undefined : json['cover_art_cid'], + 'playlistContents': !exists(json, 'playlist_contents') ? undefined : ((json['playlist_contents'] as Array).map(PlaylistAddedTimestampFromJSON)), + 'isStreamGated': !exists(json, 'is_stream_gated') ? undefined : json['is_stream_gated'], + 'isScheduledRelease': !exists(json, 'is_scheduled_release') ? undefined : json['is_scheduled_release'], + 'streamConditions': !exists(json, 'stream_conditions') ? undefined : AccessGateFromJSON(json['stream_conditions']), + 'ddexApp': !exists(json, 'ddex_app') ? undefined : json['ddex_app'], + 'ddexReleaseIds': !exists(json, 'ddex_release_ids') ? undefined : json['ddex_release_ids'], + 'artists': !exists(json, 'artists') ? undefined : (json['artists'] === null ? null : (json['artists'] as Array).map(DdexResourceContributorFromJSON)), + 'copyrightLine': !exists(json, 'copyright_line') ? undefined : CreatePlaylistRequestBodyCopyrightLineFromJSON(json['copyright_line']), + 'producerCopyrightLine': !exists(json, 'producer_copyright_line') ? undefined : CreatePlaylistRequestBodyProducerCopyrightLineFromJSON(json['producer_copyright_line']), 'parentalWarningType': !exists(json, 'parental_warning_type') ? undefined : json['parental_warning_type'], + 'isImageAutogenerated': !exists(json, 'is_image_autogenerated') ? undefined : json['is_image_autogenerated'], }; } @@ -166,16 +253,25 @@ export function UpdatePlaylistRequestBodyToJSON(value?: UpdatePlaylistRequestBod 'playlist_name': value.playlistName, 'description': value.description, 'is_private': value.isPrivate, + 'is_album': value.isAlbum, 'genre': GenreToJSON(value.genre), 'mood': MoodToJSON(value.mood), 'tags': value.tags, 'license': value.license, - 'release_date': value.releaseDate === undefined ? undefined : (value.releaseDate === null ? null : value.releaseDate.toISOString().substr(0,10)), - 'playlist_contents': value.playlistContents === undefined ? undefined : (value.playlistContents === null ? null : (value.playlistContents as Array).map(PlaylistAddedTimestampToJSON)), - 'is_stream_gated': value.isStreamGated, - 'cover_art_cid': value.coverArtCid, 'upc': value.upc, + 'release_date': value.releaseDate === undefined ? undefined : (value.releaseDate.toISOString().substr(0,10)), + 'cover_art_cid': value.coverArtCid, + 'playlist_contents': value.playlistContents === undefined ? undefined : ((value.playlistContents as Array).map(PlaylistAddedTimestampToJSON)), + 'is_stream_gated': value.isStreamGated, + 'is_scheduled_release': value.isScheduledRelease, + 'stream_conditions': AccessGateToJSON(value.streamConditions), + 'ddex_app': value.ddexApp, + 'ddex_release_ids': value.ddexReleaseIds, + 'artists': value.artists === undefined ? undefined : (value.artists === null ? null : (value.artists as Array).map(DdexResourceContributorToJSON)), + 'copyright_line': CreatePlaylistRequestBodyCopyrightLineToJSON(value.copyrightLine), + 'producer_copyright_line': CreatePlaylistRequestBodyProducerCopyrightLineToJSON(value.producerCopyrightLine), 'parental_warning_type': value.parentalWarningType, + 'is_image_autogenerated': value.isImageAutogenerated, }; } diff --git a/packages/sdk/src/sdk/api/generated/default/models/UpdateTrackRequestBody.ts b/packages/sdk/src/sdk/api/generated/default/models/UpdateTrackRequestBody.ts index 26a68e88db7..b205d019d82 100644 --- a/packages/sdk/src/sdk/api/generated/default/models/UpdateTrackRequestBody.ts +++ b/packages/sdk/src/sdk/api/generated/default/models/UpdateTrackRequestBody.ts @@ -188,7 +188,7 @@ export interface UpdateTrackRequestBody { * @type {boolean} * @memberof UpdateTrackRequestBody */ - isStreamGated?: boolean | null; + isStreamGated?: boolean; /** * * @type {AccessGate} diff --git a/packages/sdk/src/sdk/api/generated/default/models/index.ts b/packages/sdk/src/sdk/api/generated/default/models/index.ts index 4339d2f0ee2..9f15f71149e 100644 --- a/packages/sdk/src/sdk/api/generated/default/models/index.ts +++ b/packages/sdk/src/sdk/api/generated/default/models/index.ts @@ -58,6 +58,8 @@ export * from './CreateDeveloperAppRequestBody'; export * from './CreateDeveloperAppResponse'; export * from './CreateGrantRequestBody'; export * from './CreatePlaylistRequestBody'; +export * from './CreatePlaylistRequestBodyCopyrightLine'; +export * from './CreatePlaylistRequestBodyProducerCopyrightLine'; export * from './CreatePlaylistResponse'; export * from './CreateRewardCodeRequestBody'; export * from './CreateRewardCodeResponse'; @@ -70,6 +72,8 @@ export * from './CreateUserRequestBodyEvents'; export * from './CreateUserResponse'; export * from './DashboardWalletUser'; export * from './DashboardWalletUsersResponse'; +export * from './DdexCopyright'; +export * from './DdexResourceContributor'; export * from './DeactivateAccessKeyRequestBody'; export * from './DeactivateAccessKeyResponse'; export * from './DecodedUserToken'; diff --git a/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts b/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts index ad1ff516ce9..6f8e04fc68f 100644 --- a/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts +++ b/packages/sdk/src/sdk/api/playlists/PlaylistsApi.ts @@ -10,19 +10,21 @@ import { AdvancedOptions } from '../../services/EntityManager/types' import type { LoggerService } from '../../services/Logger' -import { encodeHashId } from '../../utils/hashId' +import { decodeHashId, encodeHashId } from '../../utils/hashId' import { parseParams } from '../../utils/parseParams' import { retry3 } from '../../utils/retry' import { Configuration, PlaylistsApi as GeneratedPlaylistsApi, + TracksApi, type DeletePlaylistRequest, type RepostPlaylistRequest, type UnrepostPlaylistRequest, type FavoritePlaylistRequest, type UnfavoritePlaylistRequest, type SharePlaylistRequest, - type UpdateTrackRequestBody + type UpdateTrackRequestBody, + type CreateTrackRequestBody } from '../generated/default' import { TrackUploadHelper } from '../tracks/TrackUploadHelper' @@ -45,7 +47,6 @@ import { FavoritePlaylistSchema, EntityManagerUnfavoritePlaylistRequest, UnfavoritePlaylistSchema, - UploadPlaylistRequest, UploadPlaylistSchema, UpdatePlaylistSchema, UpdatePlaylistMetadataSchema, @@ -54,7 +55,8 @@ import { EntityManagerCreatePlaylistRequest, EntityManagerUpdatePlaylistRequest, type UpdatePlaylistRequestWithImage, - type CreatePlaylistRequestWithFiles + type CreatePlaylistRequestWithFiles, + type UploadPlaylistRequest } from './types' // Returns current timestamp in seconds, which is the expected @@ -66,6 +68,7 @@ const getCurrentTimestamp = () => { export class PlaylistsApi extends GeneratedPlaylistsApi { private readonly trackUploadHelper: TrackUploadHelper + private readonly tracksApi: TracksApi constructor( configuration: Configuration, private readonly storage: StorageService, @@ -73,6 +76,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { private readonly logger: LoggerService ) { super(configuration) + this.tracksApi = new TracksApi(configuration) this.trackUploadHelper = new TrackUploadHelper(configuration) this.logger = logger.createPrefixedLogger('[playlists-api]') } @@ -100,14 +104,10 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { ) { if (this.entityManager) { const { metadata } = params - const res = await this.createPlaylistWithEntityManager({ + return await this.createPlaylistWithEntityManager({ userId: params.userId, metadata }) - return { - success: true, - transactionHash: res.transactionHash - } } return super.createPlaylist(params, requestInit) } @@ -118,16 +118,152 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { */ async uploadPlaylist( params: UploadPlaylistRequest, - advancedOptions?: AdvancedOptions + requestInit?: RequestInit ) { - // Parse inputs - const parsedParameters = await parseParams( + const { metadata: playlistMetadata, trackMetadatas } = params + const { userId, imageFile, audioFiles, onProgress } = await parseParams( 'uploadPlaylist', UploadPlaylistSchema )(params) - // Call uploadPlaylistInternal with parsed inputs - return await this.uploadPlaylistInternal(parsedParameters, advancedOptions) + const progresses = audioFiles.map(() => 0) + const [imageUploadResponse, ...audioUploadResponses] = await Promise.all([ + params.imageFile && + this.storage + .uploadFile({ + file: imageFile, + onProgress: (event) => + onProgress?.(event.loaded / event.total, { + ...event, + key: 'image' + }), + metadata: { + template: 'img_square' + } + }) + .start(), + ...audioFiles.map((trackFile, idx) => + this.storage + .uploadFile({ + file: trackFile, + onProgress: (progress) => { + progresses[idx] = + (progress.loaded / progress.total) * 0.5 + + progress.transcode * 0.5 + const overallProgress = + progresses.reduce((a, b) => a + b, 0) / audioFiles.length + onProgress?.(overallProgress, { + ...progress, + key: idx + }) + }, + metadata: { + template: 'audio', + placementHosts: trackMetadatas[idx]?.placementHosts, + previewStartSeconds: trackMetadatas[idx]?.previewStartSeconds + } + }) + .start() + ) + ]) + + // Write tracks to chain + const trackIds = await Promise.all( + trackMetadatas.map(async (t, i) => { + // Transform track metadata + const trackMetadata = this.combineMetadata( + this.trackUploadHelper.transformTrackUploadMetadataV2(t, userId), + playlistMetadata + ) + + const audioResponse = audioUploadResponses[i] + + if (!audioResponse) { + throw new Error(`Failed to upload track: ${t.title}`) + } + + // Update metadata to include uploaded CIDs + const updatedMetadata = + this.trackUploadHelper.populateTrackMetadataWithUploadResponseV2( + trackMetadata, + audioResponse, + imageUploadResponse + ) as CreateTrackRequestBody + + if (this.entityManager) { + const trackId = await this.trackUploadHelper.generateId('track') + await this.entityManager.manageEntity({ + userId, + entityType: EntityType.TRACK, + entityId: trackId, + action: Action.CREATE, + metadata: JSON.stringify({ + cid: '', + data: snakecaseKeys(updatedMetadata) + }) + }) + + return trackId + } + + const res = await this.tracksApi.createTrack( + { + userId: encodeHashId(userId)!, + metadata: updatedMetadata + }, + requestInit + ) + return decodeHashId(res.trackId!)! + }) + ) + + const timestamp = getCurrentTimestamp() + + if (this.entityManager) { + // Update metadata to include track ids + const updatedMetadata = { + ...params.metadata, + playlistContents: (trackIds ?? []).map((trackId) => ({ + trackId, + timestamp + })), + playlistImageSizesMultihash: imageUploadResponse?.orig_file_cid + } + const playlistId = await this.generatePlaylistId() + // Write playlist metadata to chain + const response = await this.entityManager.manageEntity({ + userId, + entityType: EntityType.PLAYLIST, + entityId: playlistId, + action: Action.CREATE, + metadata: JSON.stringify({ + cid: '', + data: snakecaseKeys(updatedMetadata) + }) + }) + + return { + ...response, + playlistId: encodeHashId(playlistId) + } + } + + // Update metadata to include track ids + const updatedMetadata = { + ...params.metadata, + playlistContents: (trackIds ?? []).map((trackId) => ({ + trackId: encodeHashId(trackId)!, + timestamp + })), + playlistImageSizesMultihash: imageUploadResponse?.orig_file_cid + } + return super.createPlaylist( + { + userId: encodeHashId(userId)!, + metadata: updatedMetadata + }, + requestInit + ) } /** @hidden @@ -270,15 +406,11 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { } if (this.entityManager) { - const res = await this.updatePlaylistWithEntityManager({ + return await this.updatePlaylistWithEntityManager({ userId: params.userId, playlistId: params.playlistId, metadata }) - return { - success: true, - transactionHash: res.transactionHash - } } return super.updatePlaylist(params, requestInit) } @@ -310,11 +442,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { requestInit?: RequestInit ) { if (this.entityManager) { - const res = await this.deletePlaylistWithEntityManager(params) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.deletePlaylistWithEntityManager(params) } return super.deletePlaylist(params, requestInit) } @@ -347,11 +475,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { requestInit?: RequestInit ) { if (this.entityManager) { - const res = await this.favoritePlaylistWithEntityManager(params) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.favoritePlaylistWithEntityManager(params) } return super.favoritePlaylist(params, requestInit) } @@ -383,11 +507,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { requestInit?: RequestInit ) { if (this.entityManager) { - const res = await this.unfavoritePlaylistWithEntityManager(params) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.unfavoritePlaylistWithEntityManager(params) } return super.unfavoritePlaylist(params, requestInit) } @@ -401,7 +521,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { ) { // Parse inputs const { userId, playlistId, metadata } = await parseParams( - 'respostPlaylist', + 'repostPlaylist', RepostPlaylistSchema )(params) @@ -426,11 +546,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { userId: params.userId, metadata: params.repostRequestBody } - const res = await this.repostPlaylistWithEntityManager(entityManagerParams) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.repostPlaylistWithEntityManager(entityManagerParams) } return super.repostPlaylist(params, requestInit) } @@ -462,11 +578,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { requestInit?: RequestInit ) { if (this.entityManager) { - const res = await this.unrepostPlaylistWithEntityManager(params) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.unrepostPlaylistWithEntityManager(params) } return super.unrepostPlaylist(params, requestInit) } @@ -498,11 +610,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { requestInit?: RequestInit ) { if (this.entityManager) { - const res = await this.sharePlaylistWithEntityManager(params) - return { - success: true, - transactionHash: res.transactionHash - } + return await this.sharePlaylistWithEntityManager(params) } return super.sharePlaylist(params, requestInit) } @@ -511,10 +619,9 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { * Combines the metadata for a track and a collection (playlist or album), * taking the metadata from the playlist when the track is missing it. */ - private combineMetadata( - trackMetadata: UpdateTrackRequestBody, - playlistMetadata: PlaylistMetadata - ) { + private combineMetadata< + T extends CreateTrackRequestBody | UpdateTrackRequestBody + >(trackMetadata: T, playlistMetadata: PlaylistMetadata) { const metadata = trackMetadata if (!metadata.mood) metadata.mood = playlistMetadata.mood @@ -594,153 +701,6 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { ) } - /** @internal - * Method to upload a playlist with already parsed inputs - * This is used for both playlists and albums - */ - public async uploadPlaylistInternal( - { - userId, - imageFile, - audioFiles, - onProgress, - metadata, - trackMetadatas - }: z.infer & { - metadata: Metadata - }, - advancedOptions?: AdvancedOptions - ) { - const progresses = audioFiles.map(() => 0) - // Upload track audio and cover art to storage node - const [coverArtResponse, ...audioResponses] = await Promise.all([ - retry3( - async () => - await this.storage - .uploadFile({ - file: imageFile, - onProgress: (progress) => - onProgress?.( - progresses.reduce((a, b) => a + b, 0) / audioFiles.length, - { ...progress, key: 'image' } - ), - metadata: { - template: 'img_square' - } - }) - .start(), - (e) => { - this.logger.info('Retrying uploadPlaylistCoverArt', e) - } - ), - ...audioFiles.map( - async (trackFile, idx) => - await retry3( - async () => - await this.storage - .uploadFile({ - file: trackFile, - onProgress: (progress) => { - progresses[idx] = - (progress.loaded / progress.total) * 0.5 + - progress.transcode * 0.5 - const overallProgress = - progresses.reduce((a, b) => a + b, 0) / audioFiles.length - onProgress?.(overallProgress, { - ...progress, - key: idx - }) - }, - metadata: { - template: 'audio', - ...this.trackUploadHelper.extractMediorumUploadOptions( - trackMetadatas[idx]! - ) - } - }) - .start(), - (e) => { - this.logger.info('Retrying uploadTrackAudio', e) - } - ) - ) - ]) - - // Write tracks to chain - const trackIds = await Promise.all( - trackMetadatas.map(async (parsedTrackMetadata, i) => { - // Transform track metadata - const trackMetadata = this.combineMetadata( - this.trackUploadHelper.transformTrackUploadMetadata( - parsedTrackMetadata, - userId - ), - metadata - ) - - const audioResponse = audioResponses[i] - - if (!audioResponse) { - throw new Error(`Failed to upload track: ${trackMetadata.title}`) - } - - // Update metadata to include uploaded CIDs - const updatedMetadata = - this.trackUploadHelper.populateTrackMetadataWithUploadResponse( - trackMetadata, - audioResponse, - coverArtResponse - ) - - const trackId = await this.trackUploadHelper.generateId('track') - await this.entityManager.manageEntity({ - userId, - entityType: EntityType.TRACK, - entityId: trackId, - action: Action.CREATE, - metadata: JSON.stringify({ - cid: '', - data: snakecaseKeys(updatedMetadata) - }), - ...advancedOptions - }) - - return trackId - }) - ) - - const playlistId = await this.trackUploadHelper.generateId('playlist') - const timestamp = getCurrentTimestamp() - - // Update metadata to include track ids and cover art cid - const updatedMetadata = { - ...metadata, - isPrivate: false, - playlistContents: trackIds.map((trackId) => ({ - trackId, - timestamp - })), - playlistImageSizesMultihash: coverArtResponse?.orig_file_cid - } - - // Write playlist metadata to chain - const response = await this.entityManager.manageEntity({ - userId, - entityType: EntityType.PLAYLIST, - entityId: playlistId, - action: Action.CREATE, - metadata: JSON.stringify({ - cid: '', - data: snakecaseKeys(updatedMetadata) - }), - ...advancedOptions - }) - return { - ...response, - playlistId: encodeHashId(playlistId) - } - } - /** @internal * Method to create a playlist with already parsed inputs * This is used for both playlists and albums @@ -803,7 +763,7 @@ export class PlaylistsApi extends GeneratedPlaylistsApi { return { ...response, - playlistId: encodeHashId(playlistId) + playlistId: encodeHashId(playlistId) ?? undefined } } diff --git a/packages/sdk/src/sdk/api/playlists/types.ts b/packages/sdk/src/sdk/api/playlists/types.ts index 51ff35b41b6..18e549b79c7 100644 --- a/packages/sdk/src/sdk/api/playlists/types.ts +++ b/packages/sdk/src/sdk/api/playlists/types.ts @@ -162,12 +162,6 @@ export type PlaylistImageParameters = { onProgress?: UploadPlaylistProgressHandler } -export type PlaylistAudioParameters = { - audioFiles?: z.input[] - trackMetadatas?: z.input[] - onProgress?: UploadPlaylistProgressHandler -} - export type CreatePlaylistRequestWithFiles = CreatePlaylistRequest & PlaylistImageParameters diff --git a/packages/sdk/src/sdk/api/tracks/TrackUploadHelper.ts b/packages/sdk/src/sdk/api/tracks/TrackUploadHelper.ts index 28077dcc98f..3fd5f6b3285 100644 --- a/packages/sdk/src/sdk/api/tracks/TrackUploadHelper.ts +++ b/packages/sdk/src/sdk/api/tracks/TrackUploadHelper.ts @@ -63,7 +63,7 @@ export class TrackUploadHelper extends BaseAPI { public transformTrackUploadMetadataV2< T extends CreateTrackRequestBody | UpdateTrackRequestBody - >(inputMetadata: T, userId: number) { + >(inputMetadata: T, userId: number): T { const metadata: T = { ...inputMetadata, ownerId: userId @@ -99,7 +99,7 @@ export class TrackUploadHelper extends BaseAPI { trackMetadata: T, audioResponse?: UploadResponse, coverArtResponse?: UploadResponse - ) { + ): T { let updated = { ...trackMetadata } @@ -113,7 +113,7 @@ export class TrackUploadHelper extends BaseAPI { ? audioResponse.results[ `320_preview|${trackMetadata.previewStartSeconds}` ] - : trackMetadata.previewCid!, + : trackMetadata.previewCid, origFileCid: audioResponse.orig_file_cid, origFilename: audioResponse.orig_filename || trackMetadata.origFilename, duration: parseInt(audioResponse?.probe?.format?.duration ?? '0', 10), diff --git a/packages/sdk/src/sdk/api/tracks/TracksApi.ts b/packages/sdk/src/sdk/api/tracks/TracksApi.ts index b5ad44e89f1..a26b7f0d6fd 100644 --- a/packages/sdk/src/sdk/api/tracks/TracksApi.ts +++ b/packages/sdk/src/sdk/api/tracks/TracksApi.ts @@ -369,7 +369,7 @@ export class TracksApi extends GeneratedTracksApi { onProgress: params.onProgress }).start() - metadata = this.trackUploadHelper.transformTrackUploadMetadata( + metadata = this.trackUploadHelper.transformTrackUploadMetadataV2( metadata, decodeHashId(params.userId)! ) diff --git a/packages/sdk/src/sdk/api/tracks/types.ts b/packages/sdk/src/sdk/api/tracks/types.ts index 967be835c8f..d47aa42d47b 100644 --- a/packages/sdk/src/sdk/api/tracks/types.ts +++ b/packages/sdk/src/sdk/api/tracks/types.ts @@ -95,9 +95,9 @@ export const USDCPurchaseConditions = z .strict() export const UploadStemMetadataSchema = z.object({ - category: z - .enum(Object.values(StemCategory) as [StemCategory, ...StemCategory[]]) - .default(StemCategory.Other), + category: z.enum( + Object.values(StemCategory) as [StemCategory, ...StemCategory[]] + ), parentTrackId: HashId }) diff --git a/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts b/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts index 44ca8969bb7..71976c8067d 100644 --- a/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts +++ b/packages/web/src/common/store/cache/collections/createPlaylistSaga.ts @@ -191,11 +191,20 @@ function* createAndConfirmPlaylist( throw new Error('No userId set, cannot repost collection') } + const metadata = playlistMetadataForCreateWithSDK(formFields) + metadata.playlistId = Id.parse(playlistId) + metadata.playlistContents = initTrack + ? [ + { + timestamp: Math.round(Date.now() / 1000), // must use seconds + trackId: Id.parse(initTrack.track_id) + } + ] + : [] + yield* call([sdk.playlists, sdk.playlists.createPlaylist], { userId: Id.parse(userId), - playlistId: Id.parse(playlistId), - trackIds: initTrack ? [Id.parse(initTrack.track_id)] : undefined, - metadata: playlistMetadataForCreateWithSDK(formFields) + metadata }) // Merge the confirmed playlist with the optimistic playlist, preferring