useCreateAsset is a React Hook for creating a new asset. It returns a mutate function that can be called to create an asset. Multiple assets can be created at once by passing in an array of sources.

Usage

import { useCreateAsset } from '@livepeer/react';

The asset ID should only be passed to users who are allowed to modify the asset. Ensure viewers are only provided with the playbackId, which is a limited identifier that provides only the ability to stream media using the Player.

The following example shows how an asset can be created with the mutate function. The variables are passed into the useCreateAsset hook, and createAsset return type is defined when the variables are truthy.

function SomeComponent() {
  const [video, setVideo] = useState<File | undefined>(undefined);
  const {
    mutate: createAsset,
    data: assets,
    status,
    progress,
    error,
  } = useCreateAsset(
    // we use a `const` assertion here to provide better Typescript types
    // for the returned data
    video
      ? {
          sources: [
            {
              name: video.name,
              file: video,
              storage: {
                ipfs: true,
                metadata: {
                  name: "interesting video",
                  description: "a great description of the video",
                },
              },
            },
          ] as const,
        }
      : null,
  );

  return (
    <div>
      <input
        type="file"
        multiple={false}
        accept="video/*"
        onChange={(e) => {
          if (e.target.files) {
            setVideo(e.target.files[0]);
          }
        }}
      />
      <button
        disabled={status === "loading" || !createAsset}
        onClick={() => {
          createAsset?.();
        }}
      >
        Create Asset
      </button>
      {assets?.map((asset) => (
        <div key={asset.id}>
          <div>
            <div>Asset Name: {asset?.name}</div>
            <div>Playback URL: {asset?.playbackUrl}</div>
            <div>IPFS CID: {asset?.storage?.ipfs?.cid ?? "None"}</div>
          </div>
        </div>
      ))}

      {error && <div>{error.message}</div>}
    </div>
  );
}

Mutation

Return Value

The return value is partially based on Tanstack Query, with some return types aggregated for simplicity.

The value progress is an array of progress values which contains the upload progress from tus, as well as the processing progress of the asset from the provider. These values are polled consistently until the asset succeeds or fails to be processed by the provider.

{
  data?: Asset[],
  progress?: CreateAssetProgress,
  error?: Error,
  isError: boolean,
  isIdle: boolean,
  isLoading: boolean,
  isSuccess: boolean,
  status: 'idle' | 'loading' | 'success' | 'error',
  mutate: () => void,
  mutateAsync: () => Promise<Asset[]>,
  variables?: CreateAssetArgs
}

Configuration

sources

The sources used for the asset creation can be an array of either a URL or a local File/ReadStream/object with a uri string. It is highly recommended to use const assertions to infer types properly from the sources parameter - please see below for an example.

"
import { useCreateAsset } from "@livepeer/react";

function SomeComponent() {
  const { mutate: createAsset, status } = useCreateAsset({
    sources: [
      {
        name: video.name,
        file: video,
        creatorId: "0xabcd000000000000000000000000000000001234",
        storage: {
          ipfs: true,
          metadata: {
            name: "interesting video",
            description: "a great description of the video",
          },
        },
      },
    ] as const,
  });
}

The useCreateAsset hook uses tus for resumable uploads and checks for existing uploads by default, before starting a new upload. It also automatically polls the livepeer provider’s API to update status until the import job is complete.

The sources type can be either a URL or a File/ReadStream:

type CreateAssetSourceUrl = {
  /** Name for the new asset */
  name: string;
  /** External URL to be imported */
  url: string;
  /** The storage configs for the new asset (e.g. IPFS) */
  storage?: Storage;
  /**
   * Sets the playback policy for the asset created.
   */
  playbackPolicy?: PlaybackPolicy;
  /**
   * Sets the creator ID for the asset that is created.
   * This should be an wallet's public address.
   */
  creatorId?: CreateAssetCreatorId;
};

/** React Native file type */
type NativeFile = File & {
  uri: string;
};

type CreateAssetSourceFile = {
  /** Name for the new asset */
  name: string;
  /** Content to be uploaded or streamed */
  file: File | ReadStream | NativeFile;
  /** The storage configs for the new asset (e.g. IPFS) */
  storage?: Storage;
  /**
   * Sets the playback policy for the asset created.
   */
  playbackPolicy?: PlaybackPolicy;
  /**
   * Sets the creator ID for the asset that is created.
   * This should be an wallet's public address.
   */
  creatorId?: CreateAssetCreatorId;
};

/** Source(s) to upload - can be a file or URL source */
type Sources = CreateAssetSourceFile[] | CreateAssetSourceUrl[];

We are planning on adding further features to the creator ID such as provable claims. Reach out to us on Discord if you’re interested!

If CreateAssetSourceFile is passed in the array, the useCreateAsset hook uses tus for resumable uploads and checks for existing uploads by default, before starting a new upload.

If CreateAssetSourceUrl is passed in the array, the hook will make a request to the background to start an import. This can also be an IPFS URL (ipfs://<CID>) or Arweave URL (ar://<HASH>). Either a URL or file must be provided.

storage

The storage configs can be applied on asset creation. See useUpdateAsset for more details on these configs. If the storage parameter is omitted, the asset will not be stored in decentralized storage.

If you would like to use Arweave as storage, we recommend you upload to Arweave first and then import the video.

type Storage = {
  /**
   * If the asset should be stored on IPFS.
   */
  ipfs?: boolean;
  /**
   * Metadata exported to the storage provider. This will be deep merged with the default
   * metadata from the livepeer provider. This should ideally be EIP-721/EIP-1155 compatible.
   *
   * @see {@link https://eips.ethereum.org/EIPS/eip-721}
   */
  metadata?: Partial<Metadata> & {
    [k: string]: unknown;
  };
  /**
   * The metadata template to use. `player` will embed the Livepeer Player's IPFS CID while `file`
   * will reference only the immutable media files.
   */
  metadataTemplate?: "player" | "file";
};

The metadata can be overridden when the Asset and its metadata are exported to IPFS - we provide some helper types for metadata best practices based on ERC-721 and ERC-1155:

type Metadata = {
  /** Name of the Asset */
  name?: string;
  /** Description of the Asset */
  description?: string;
  /** Image URL for the Asset */
  image?: string;
  /** Properties of the Asset */
  properties?: {
    [k: string]: unknown;
  };
  /**
   * Background color for the Asset (OpenSea Standard)
   *
   * @see {@link https://docs.opensea.io/docs/metadata-standards}
   */
  background_color?: string;
  /**
   * Attributes for the Asset (OpenSea Standard)
   *
   * @see {@link https://docs.opensea.io/docs/metadata-standards}
   */
  attributes?: {
    [k: string]: unknown;
  };
};

noWait

The noWait config option skips the polling mechanism which waits for the Asset upload to succeed or fail. It will immediately return after the file upload is complete, or in the case of a URL upload, it will return after the URL is POSTed to the backend.

import { useCreateAsset } from "@livepeer/react";

function SomeComponent() {
  const { mutate: createAsset } = useCreateAsset({
    sources: [{ name: video.name, file: video }] as const,
    noWait,
  });
}

mutationConfig

The mutationConfig parameter allows for any Tanstack Query useMutation options, such as cacheTime or retry. These override any configs passed by default by the internal hook.

import { useCreateAsset } from "@livepeer/react";

function SomeComponent() {
  const { mutate: createAsset } = useCreateAsset({
    sources: [{ name: video.name, file: video }] as const,
    mutationConfig: { retry: 5 },
  });
}