From cc17ccf34e23452bebb984a7e65ab816e9e7a743 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:17:29 -0800 Subject: [PATCH 1/5] Add UploadApi --- packages/sdk/src/sdk/api/upload/UploadApi.ts | 91 +++++++++++++++++++ packages/sdk/src/sdk/api/upload/types.ts | 5 + .../sdk/src/sdk/createSdkWithoutServices.ts | 14 ++- 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 packages/sdk/src/sdk/api/upload/UploadApi.ts create mode 100644 packages/sdk/src/sdk/api/upload/types.ts diff --git a/packages/sdk/src/sdk/api/upload/UploadApi.ts b/packages/sdk/src/sdk/api/upload/UploadApi.ts new file mode 100644 index 00000000000..751e53a5163 --- /dev/null +++ b/packages/sdk/src/sdk/api/upload/UploadApi.ts @@ -0,0 +1,91 @@ +import { type ProgressHandler } from '../../services' +import type { CrossPlatformFile } from '../../types/File' + +import type { UploadApiServicesConfig } from './types' + +export default class UploadApi { + private readonly storage + + constructor(services: UploadApiServicesConfig) { + this.storage = services.storageService + } + + /** + * Uploads an audio file to a validator and returns the resulting CIDs and + * metadata. + * + * Optionally accepts a callback for tracking upload progress, a list of + * placement hosts to prefer for storage, and a start time in seconds + * for generating a preview clip. + */ + public async uploadAudio({ + file, + onProgress, + previewStartSeconds, + placementHosts + }: { + file: CrossPlatformFile + onProgress?: ProgressHandler + placementHosts?: string[] + previewStartSeconds?: number + }) { + const res = await this.storage + .uploadFile({ + file, + onProgress, + metadata: { + template: 'audio', + filename: file.name ?? undefined, + filetype: file.type ?? undefined, + previewStartSeconds, + placementHosts: placementHosts?.join(',') + } + }) + .start() + return { + trackCid: res.results['320'], + previewCid: + previewStartSeconds !== undefined && previewStartSeconds !== null + ? res.results[`320_preview|${previewStartSeconds}`] + : undefined, + origFileCid: res.orig_file_cid, + origFilename: res.orig_filename, + duration: parseInt(res?.probe?.format?.duration ?? '0', 10), + bpm: res.audio_analysis_results?.bpm, + musicalKey: res.audio_analysis_results?.key + } + } + + /** + * Uploads an image file to a validator and returns the resulting CID. + * + * Optionally accepts a callback for tracking upload progress, a list of + * placement hosts to prefer for storage, and a boolean indicating whether + * the image is a backdrop (as opposed to square, e.g. for profile banners). + */ + public async uploadImage({ + file, + onProgress, + isBackdrop = false, + placementHosts + }: { + file: CrossPlatformFile + onProgress?: ProgressHandler + isBackdrop?: boolean + placementHosts?: string[] + }) { + const res = await this.storage + .uploadFile({ + file, + onProgress, + metadata: { + template: isBackdrop ? 'img_backdrop' : 'img_square', + filename: file.name ?? undefined, + filetype: file.type ?? undefined, + placementHosts: placementHosts?.join(',') + } + }) + .start() + return res.orig_file_cid + } +} diff --git a/packages/sdk/src/sdk/api/upload/types.ts b/packages/sdk/src/sdk/api/upload/types.ts new file mode 100644 index 00000000000..d65290a1c17 --- /dev/null +++ b/packages/sdk/src/sdk/api/upload/types.ts @@ -0,0 +1,5 @@ +import type { StorageService } from '../../services' + +export type UploadApiServicesConfig = { + storageService: StorageService +} diff --git a/packages/sdk/src/sdk/createSdkWithoutServices.ts b/packages/sdk/src/sdk/createSdkWithoutServices.ts index a4ef92a7063..ca5bc7476cc 100644 --- a/packages/sdk/src/sdk/createSdkWithoutServices.ts +++ b/packages/sdk/src/sdk/createSdkWithoutServices.ts @@ -21,6 +21,7 @@ import { WalletApi, type Middleware } from './api/generated/default' +import UploadApi from './api/upload/UploadApi' import { developmentConfig } from './config/development' import { productionConfig } from './config/production' import { @@ -29,7 +30,7 @@ import { } from './middleware' import { addBearerTokenMiddleware } from './middleware/addBearerTokenMiddleware' import { OAuth } from './oauth' -import { Logger } from './services' +import { Logger, Storage, StorageNodeSelector } from './services' import { type SdkConfig } from './types' export const createSdkWithoutServices = (config: SdkConfig) => { @@ -122,6 +123,15 @@ export const createSdkWithoutServices = (config: SdkConfig) => { coins: new CoinsApi(apiConfig), wallets: new WalletApi(apiConfig), challenges: new ChallengesApi(apiConfig), - prizes: new PrizesApi(apiConfig) + prizes: new PrizesApi(apiConfig), + upload: new UploadApi({ + storageService: + services?.storage ?? + new Storage({ + storageNodeSelector: + services?.storageNodeSelector ?? new StorageNodeSelector({}), + logger + }) + }) } } From c482aacd61e2a59661e186060c91effa70fcc719 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:24:49 -0800 Subject: [PATCH 2/5] use handles --- packages/sdk/src/sdk/api/upload/UploadApi.ts | 85 ++++++++++---------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/packages/sdk/src/sdk/api/upload/UploadApi.ts b/packages/sdk/src/sdk/api/upload/UploadApi.ts index 751e53a5163..452ffefdfe7 100644 --- a/packages/sdk/src/sdk/api/upload/UploadApi.ts +++ b/packages/sdk/src/sdk/api/upload/UploadApi.ts @@ -11,14 +11,14 @@ export default class UploadApi { } /** - * Uploads an audio file to a validator and returns the resulting CIDs and - * metadata. + * Creates an audio file upload task that uploads to a validator and returns + * the resulting CIDs and analysis metadata. * * Optionally accepts a callback for tracking upload progress, a list of * placement hosts to prefer for storage, and a start time in seconds * for generating a preview clip. */ - public async uploadAudio({ + public createAudioUpload({ file, onProgress, previewStartSeconds, @@ -29,41 +29,43 @@ export default class UploadApi { placementHosts?: string[] previewStartSeconds?: number }) { - const res = await this.storage - .uploadFile({ - file, - onProgress, - metadata: { - template: 'audio', - filename: file.name ?? undefined, - filetype: file.type ?? undefined, - previewStartSeconds, - placementHosts: placementHosts?.join(',') - } - }) - .start() + const upload = this.storage.uploadFile({ + file, + onProgress, + metadata: { + template: 'audio', + filename: file.name ?? undefined, + filetype: file.type ?? undefined, + previewStartSeconds, + placementHosts: placementHosts?.join(',') + } + }) return { - trackCid: res.results['320'], - previewCid: - previewStartSeconds !== undefined && previewStartSeconds !== null - ? res.results[`320_preview|${previewStartSeconds}`] - : undefined, - origFileCid: res.orig_file_cid, - origFilename: res.orig_filename, - duration: parseInt(res?.probe?.format?.duration ?? '0', 10), - bpm: res.audio_analysis_results?.bpm, - musicalKey: res.audio_analysis_results?.key + abort: upload.abort, + start: upload.start().then((res) => ({ + trackCid: res.results['320'], + previewCid: + previewStartSeconds !== undefined && previewStartSeconds !== null + ? res.results[`320_preview|${previewStartSeconds}`] + : undefined, + origFileCid: res.orig_file_cid, + origFilename: res.orig_filename, + duration: parseInt(res?.probe?.format?.duration ?? '0', 10), + bpm: res.audio_analysis_results?.bpm, + musicalKey: res.audio_analysis_results?.key + })) } } /** - * Uploads an image file to a validator and returns the resulting CID. + * Creates an image upload task that uploads to a validator and returns the + * resulting CID. * * Optionally accepts a callback for tracking upload progress, a list of * placement hosts to prefer for storage, and a boolean indicating whether * the image is a backdrop (as opposed to square, e.g. for profile banners). */ - public async uploadImage({ + public createImageUpload({ file, onProgress, isBackdrop = false, @@ -74,18 +76,19 @@ export default class UploadApi { isBackdrop?: boolean placementHosts?: string[] }) { - const res = await this.storage - .uploadFile({ - file, - onProgress, - metadata: { - template: isBackdrop ? 'img_backdrop' : 'img_square', - filename: file.name ?? undefined, - filetype: file.type ?? undefined, - placementHosts: placementHosts?.join(',') - } - }) - .start() - return res.orig_file_cid + const upload = this.storage.uploadFile({ + file, + onProgress, + metadata: { + template: isBackdrop ? 'img_backdrop' : 'img_square', + filename: file.name ?? undefined, + filetype: file.type ?? undefined, + placementHosts: placementHosts?.join(',') + } + }) + return { + abort: upload.abort, + start: upload.start().then((res) => res.orig_file_cid) + } } } From 9bad50405f119b3c79af38eefa728be051409989 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:41:24 -0800 Subject: [PATCH 3/5] plural --- .../sdk/api/{upload/UploadApi.ts => uploads/UploadsApi.ts} | 6 +++--- packages/sdk/src/sdk/api/{upload => uploads}/types.ts | 2 +- packages/sdk/src/sdk/createSdkWithoutServices.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename packages/sdk/src/sdk/api/{upload/UploadApi.ts => uploads/UploadsApi.ts} (94%) rename packages/sdk/src/sdk/api/{upload => uploads}/types.ts (68%) diff --git a/packages/sdk/src/sdk/api/upload/UploadApi.ts b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts similarity index 94% rename from packages/sdk/src/sdk/api/upload/UploadApi.ts rename to packages/sdk/src/sdk/api/uploads/UploadsApi.ts index 452ffefdfe7..3960b4cf5c1 100644 --- a/packages/sdk/src/sdk/api/upload/UploadApi.ts +++ b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts @@ -1,12 +1,12 @@ import { type ProgressHandler } from '../../services' import type { CrossPlatformFile } from '../../types/File' -import type { UploadApiServicesConfig } from './types' +import type { UploadsApiServicesConfig } from './types' -export default class UploadApi { +export class UploadsApi { private readonly storage - constructor(services: UploadApiServicesConfig) { + constructor(services: UploadsApiServicesConfig) { this.storage = services.storageService } diff --git a/packages/sdk/src/sdk/api/upload/types.ts b/packages/sdk/src/sdk/api/uploads/types.ts similarity index 68% rename from packages/sdk/src/sdk/api/upload/types.ts rename to packages/sdk/src/sdk/api/uploads/types.ts index d65290a1c17..74ce0a4f3f2 100644 --- a/packages/sdk/src/sdk/api/upload/types.ts +++ b/packages/sdk/src/sdk/api/uploads/types.ts @@ -1,5 +1,5 @@ import type { StorageService } from '../../services' -export type UploadApiServicesConfig = { +export type UploadsApiServicesConfig = { storageService: StorageService } diff --git a/packages/sdk/src/sdk/createSdkWithoutServices.ts b/packages/sdk/src/sdk/createSdkWithoutServices.ts index ca5bc7476cc..11de0334138 100644 --- a/packages/sdk/src/sdk/createSdkWithoutServices.ts +++ b/packages/sdk/src/sdk/createSdkWithoutServices.ts @@ -21,7 +21,7 @@ import { WalletApi, type Middleware } from './api/generated/default' -import UploadApi from './api/upload/UploadApi' +import { UploadsApi } from './api/uploads/UploadsApi' import { developmentConfig } from './config/development' import { productionConfig } from './config/production' import { @@ -124,7 +124,7 @@ export const createSdkWithoutServices = (config: SdkConfig) => { wallets: new WalletApi(apiConfig), challenges: new ChallengesApi(apiConfig), prizes: new PrizesApi(apiConfig), - upload: new UploadApi({ + uploads: new UploadsApi({ storageService: services?.storage ?? new Storage({ From 971a60c430f37ec70706949b896e23adc674be2d Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:46:07 -0800 Subject: [PATCH 4/5] changeset --- .changeset/metal-stingrays-marry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/metal-stingrays-marry.md diff --git a/.changeset/metal-stingrays-marry.md b/.changeset/metal-stingrays-marry.md new file mode 100644 index 00000000000..1c4e083fe5e --- /dev/null +++ b/.changeset/metal-stingrays-marry.md @@ -0,0 +1,5 @@ +--- +"@audius/sdk": minor +--- + +Added UploadsApi From 241cd7de7a70d1e04703b50e73689631af39e5f0 Mon Sep 17 00:00:00 2001 From: Marcus Pasell <3690498+rickyrombo@users.noreply.github.com> Date: Tue, 24 Feb 2026 16:49:53 -0800 Subject: [PATCH 5/5] better init --- packages/sdk/src/sdk/createSdkWithoutServices.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/sdk/createSdkWithoutServices.ts b/packages/sdk/src/sdk/createSdkWithoutServices.ts index 11de0334138..ef5884fd6cb 100644 --- a/packages/sdk/src/sdk/createSdkWithoutServices.ts +++ b/packages/sdk/src/sdk/createSdkWithoutServices.ts @@ -129,7 +129,11 @@ export const createSdkWithoutServices = (config: SdkConfig) => { services?.storage ?? new Storage({ storageNodeSelector: - services?.storageNodeSelector ?? new StorageNodeSelector({}), + services?.storageNodeSelector ?? + new StorageNodeSelector({ + endpoint: basePath, + logger + }), logger }) })