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 diff --git a/packages/sdk/src/sdk/api/uploads/UploadsApi.ts b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts new file mode 100644 index 00000000000..3960b4cf5c1 --- /dev/null +++ b/packages/sdk/src/sdk/api/uploads/UploadsApi.ts @@ -0,0 +1,94 @@ +import { type ProgressHandler } from '../../services' +import type { CrossPlatformFile } from '../../types/File' + +import type { UploadsApiServicesConfig } from './types' + +export class UploadsApi { + private readonly storage + + constructor(services: UploadsApiServicesConfig) { + this.storage = services.storageService + } + + /** + * 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 createAudioUpload({ + file, + onProgress, + previewStartSeconds, + placementHosts + }: { + file: CrossPlatformFile + onProgress?: ProgressHandler + placementHosts?: string[] + previewStartSeconds?: number + }) { + const upload = this.storage.uploadFile({ + file, + onProgress, + metadata: { + template: 'audio', + filename: file.name ?? undefined, + filetype: file.type ?? undefined, + previewStartSeconds, + placementHosts: placementHosts?.join(',') + } + }) + return { + 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 + })) + } + } + + /** + * 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 createImageUpload({ + file, + onProgress, + isBackdrop = false, + placementHosts + }: { + file: CrossPlatformFile + onProgress?: ProgressHandler + isBackdrop?: boolean + placementHosts?: string[] + }) { + 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) + } + } +} diff --git a/packages/sdk/src/sdk/api/uploads/types.ts b/packages/sdk/src/sdk/api/uploads/types.ts new file mode 100644 index 00000000000..74ce0a4f3f2 --- /dev/null +++ b/packages/sdk/src/sdk/api/uploads/types.ts @@ -0,0 +1,5 @@ +import type { StorageService } from '../../services' + +export type UploadsApiServicesConfig = { + storageService: StorageService +} diff --git a/packages/sdk/src/sdk/createSdkWithoutServices.ts b/packages/sdk/src/sdk/createSdkWithoutServices.ts index a4ef92a7063..ef5884fd6cb 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 { UploadsApi } from './api/uploads/UploadsApi' 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,19 @@ 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), + uploads: new UploadsApi({ + storageService: + services?.storage ?? + new Storage({ + storageNodeSelector: + services?.storageNodeSelector ?? + new StorageNodeSelector({ + endpoint: basePath, + logger + }), + logger + }) + }) } }