Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Valid values are: `none`, `error`, `warn`, `info`, `debug` (case insensitive). I

### [v1.audioToVideo](src/resources/v1/audio-to-video/README.md)

- [generate](src/resources/v1/audio-to-video/README.md#generate) - Audio To Video Generate Workflow
- [create](src/resources/v1/audio-to-video/README.md#create) - Audio-to-Video

### [v1.autoSubtitleGenerator](src/resources/v1/auto-subtitle-generator/README.md)
Expand Down
49 changes: 49 additions & 0 deletions src/resources/v1/audio-to-video/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,55 @@

## Module Functions

<!-- CUSTOM DOCS START -->

### Audio To Video Generate Workflow <a name="generate"></a>

The workflow performs the following action

1. upload local assets to Magic Hour storage. So you can pass in a local path instead of having to upload files yourself
2. trigger a generation
3. poll for a completion status. This is configurable
4. if success, download the output to local directory

> [!TIP]
> This is the recommended way to use the SDK unless you have specific needs where it is necessary to split up the actions.

#### Parameters

In addition to the parameters listed in the `create` section below, `generate` introduces 3 new parameters:

- `waitForCompletion` (boolean, default true): Whether to wait for the project to complete.
- `downloadOutputs` (boolean, default true): Whether to download the generated files
- `downloadDirectory` (string, optional): Directory to save downloaded files (defaults to current directory)

#### Example Snippet

```typescript
import { Client } from "magic-hour";

const client = new Client({ token: process.env["API_TOKEN"]!! });
const res = await client.v1.audioToVideo.generate(
{
assets: {
audioFilePath: "/path/to/1234.mp3",
imageFilePath: "/path/to/1234.png",
},
endSeconds: 15.0,
name: "Audio To Video video",
resolution: "720p",
startSeconds: 0.0,
},
{
waitForCompletion: true,
downloadOutputs: true,
downloadDirectory: ".",
},
);
```

<!-- CUSTOM DOCS END -->

### Audio-to-Video <a name="create"></a>

**What this API does**
Expand Down
135 changes: 134 additions & 1 deletion src/resources/v1/audio-to-video/resource-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,149 @@ import {
ResourceClientOptions,
} from "make-api-request-js";

import { types } from "magic-hour";
import {
GenerateOptions,
GenerateRequestType,
} from "magic-hour/helpers/generate-type";
import { getLogger } from "magic-hour/logger";
import { FilesClient } from "magic-hour/resources/v1/files";
import * as requests from "magic-hour/resources/v1/audio-to-video/request-types";
import * as types from "magic-hour/types";
import { VideoProjectsClient } from "magic-hour/resources/v1/video-projects";
import { Schemas$V1AudioToVideoCreateBody } from "magic-hour/types/v1-audio-to-video-create-body";
import { Schemas$V1AudioToVideoCreateResponse } from "magic-hour/types/v1-audio-to-video-create-response";

type GenerateRequest = GenerateRequestType<
requests.CreateRequest,
{
/**
* The path of the audio file. This value is either
* - a direct URL to the audio file
* - a path to a local file
*
* Note: if the path begins with `api-assets`, it will be assumed to already be uploaded to Magic Hour's storage, and will not be uploaded again.
*/
audioFilePath: string;
/**
* Reference image for the initial frame of the video. This value is either
* - a direct URL to the image file
* - a path to a local file
*
* Note: if the path begins with `api-assets`, it will be assumed to already be uploaded to Magic Hour's storage, and will not be uploaded again.
*/
imageFilePath?: string | undefined;
}
>;

export class AudioToVideoClient extends CoreResourceClient {
constructor(coreClient: CoreClient, opts: ResourceClientOptions) {
super(coreClient, opts);
}

/**
* Audio-to-Video
*
* Create a Audio To Video video
*
* This method provides a convenient way to create a request and automatically wait for completion and download outputs.
*
* @example
* ```typescript
* import { Client } from "magic-hour";
*
* const client = new Client({ token: process.env["API_TOKEN"]!! });
* const res = await client.v1.audioToVideo.generate(
* {
* assets: {
* audioFilePath: "/path/to/1234.mp3",
* imageFilePath: "/path/to/1234.png",
* },
* endSeconds: 15.0,
* name: "Audio To Video video",
* resolution: "720p",
* startSeconds: 0.0,
* },
* {
* waitForCompletion: true,
* downloadOutputs: true,
* downloadDirectory: ".",
* },
* );
* ```
*/
async generate(request: GenerateRequest, opts: GenerateOptions = {}) {
const {
waitForCompletion = true,
downloadOutputs = true,
downloadDirectory = undefined,
...createOpts
} = opts;

const fileClient = new FilesClient(this._client, this._opts);
const { audioFilePath, imageFilePath, ...restAssets } = request.assets;

getLogger().debug(
`Uploading file ${audioFilePath} to Magic Hour's storage`,
);
if (imageFilePath) {
getLogger().debug(
`Uploading file ${imageFilePath} to Magic Hour's storage`,
);
}

const [uploadedAudioFilePath, uploadedImageFilePath] = await Promise.all([
fileClient.uploadFile(audioFilePath),
imageFilePath
? fileClient.uploadFile(imageFilePath)
: Promise.resolve(imageFilePath),
]);

getLogger().info(
`Uploaded file ${audioFilePath} to Magic Hour's storage as ${uploadedAudioFilePath}`,
);
if (imageFilePath) {
getLogger().info(
`Uploaded file ${imageFilePath} to Magic Hour's storage as ${uploadedImageFilePath}`,
);
}

const createResponse = await this.create(
{
...request,
assets: {
...restAssets,
audioFilePath: uploadedAudioFilePath,
imageFilePath: imageFilePath
? uploadedImageFilePath
: imageFilePath,
},
},
createOpts,
);

getLogger().info(
`Created AudioToVideoClient project ${createResponse.id}`,
);

const projectsClient = new VideoProjectsClient(this._client, this._opts);

getLogger().debug(
`Checking result for AudioToVideoClient project ${createResponse.id}`,
);

const result = await projectsClient.checkResult(
{ id: createResponse.id },
{
waitForCompletion,
downloadOutputs,
downloadDirectory,
...createOpts,
},
);

return result;
}

/**
* Audio-to-Video
*
Expand Down
Loading