Text to Video with Custom Subtitle Style

This example demonstrates how to create a video with fully customized subtitle styles defined inline. Instead of using saved styles, you can specify all styling properties directly in the API request.

Overview

This example covers:

  • Getting an access token
  • Creating custom subtitle styles with inline configuration
  • Customizing fonts, colors, positions, and animations
  • Applying styles at video level and scene level
  • Understanding all available style properties
  • Monitoring job status and retrieving the final video

Node.js Example

Prerequisites

npm install axios

Complete Code

import axios from "axios";

const API_BASE_URL = "https://api.pictory.ai/pictoryapis";
const CLIENT_ID = "YOUR_CLIENT_ID";
const CLIENT_SECRET = "YOUR_CLIENT_SECRET";

const STORY_TEXT = "AI is poised to significantly impact educators and course creators on social media.";

async function createTextToVideoWithCustomSubtitleStyle() {
  try {
    // Step 1: Get Access Token
    console.log("Step 1: Getting access token...");
    const tokenResponse = await axios.post(
      `${API_BASE_URL}/v1/oauth2/token`,
      {
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
      },
      {
        headers: {
          "Content-Type": "application/json",
        },
      }
    );

    const accessToken = tokenResponse.data.access_token;
    console.log("Access token obtained successfully");
    console.log("Token expires in:", tokenResponse.data.expires_in, "seconds\n");

    // Step 2: Create Video with Custom Subtitle Style
    console.log("Step 2: Creating video with custom subtitle style...");
    const storyboardResponse = await axios.post(
      `${API_BASE_URL}/v2/video/storyboard/render`,
      {
        videoName: "text_to_video_custom_subtitle",
        // Video-level custom subtitle style
        subtitleStyle: {
          fontFamily: "Poppins",
          fontSize: 48,
          color: "rgba(255, 255, 255, 1)", // White text
          backgroundColor: "rgba(0, 0, 0, 0.7)", // Semi-transparent black background
          keywordColor: "rgba(255, 215, 0, 1)", // Gold color for keywords
          shadowColor: "rgba(0, 0, 0, 0.8)",
          shadowWidth: "2%",
          position: "bottom-center",
          alignment: "center",
          decorations: ["bold"],
          case: "capitalize",
          paragraphWidth: "80%",
          showBoxBackground: true,
          animations: [
            {
              name: "fade",
              type: "entry",
              speed: "medium",
            },
            {
              name: "fade",
              type: "exit",
              speed: "fast",
            },
          ],
        },
        scenes: [
          {
            story: STORY_TEXT,
            createSceneOnNewLine: false,
            createSceneOnEndOfSentence: false,
          },
        ],
      },
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: accessToken,
        },
      }
    );

    const renderJobId = storyboardResponse.data.data.jobId;
    console.log("Video with custom subtitle style render job created");
    console.log("Job ID:", renderJobId, "\n");

    // Step 3: Monitor Job Status
    console.log("Step 3: Monitoring job status...");
    let jobCompleted = false;
    let jobResult = null;

    while (!jobCompleted) {
      const jobStatusResponse = await axios.get(`${API_BASE_URL}/v1/jobs/${renderJobId}`, {
        headers: {
          Authorization: accessToken,
        },
      });

      const status = jobStatusResponse.data.data.status;
      console.log("Current status:", status);

      if (status === "completed") {
        jobCompleted = true;
        jobResult = jobStatusResponse.data;
        console.log("\nVideo with custom subtitle style created successfully!");
        console.log("Video URL:", jobResult.data.videoUrl);
      } else if (status === "failed") {
        throw new Error("Job failed: " + JSON.stringify(jobStatusResponse.data));
      } else {
        // Wait 5 seconds before checking again
        await new Promise(resolve => setTimeout(resolve, 5000));
      }
    }

    return jobResult;
  } catch (error) {
    console.error("Error:", error.response?.data || error.message);
    throw error;
  }
}

// Run the function
createTextToVideoWithCustomSubtitleStyle();

Python Example

Prerequisites

pip install requests

Complete Code

import requests
import time
import json

API_BASE_URL = 'https://api.pictory.ai/pictoryapis'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'

STORY_TEXT = "AI is poised to significantly impact educators and course creators on social media."

def create_text_to_video_with_custom_subtitle_style():
    try:
        # Step 1: Get Access Token
        print('Step 1: Getting access token...')
        token_response = requests.post(
            f'{API_BASE_URL}/v1/oauth2/token',
            json={
                'client_id': CLIENT_ID,
                'client_secret': CLIENT_SECRET
            },
            headers={
                'Content-Type': 'application/json'
            }
        )
        token_response.raise_for_status()

        access_token = token_response.json()['access_token']
        print('Access token obtained successfully')
        print(f"Token expires in: {token_response.json()['expires_in']} seconds\n")

        # Step 2: Create Video with Custom Subtitle Style
        print('Step 2: Creating video with custom subtitle style...')
        storyboard_response = requests.post(
            f'{API_BASE_URL}/v2/video/storyboard/render',
            json={
                'videoName': 'text_to_video_custom_subtitle',
                # Video-level custom subtitle style
                'subtitleStyle': {
                    'fontFamily': 'Poppins',
                    'fontSize': 48,
                    'color': 'rgba(255, 255, 255, 1)',          # White text
                    'backgroundColor': 'rgba(0, 0, 0, 0.7)',    # Semi-transparent black background
                    'keywordColor': 'rgba(255, 215, 0, 1)',     # Gold color for keywords
                    'shadowColor': 'rgba(0, 0, 0, 0.8)',
                    'shadowWidth': '2%',
                    'position': 'bottom-center',
                    'alignment': 'center',
                    'decorations': ['bold'],
                    'case': 'capitalize',
                    'paragraphWidth': '80%',
                    'showBoxBackground': True,
                    'animations': [
                        {
                            'name': 'fade',
                            'type': 'entry',
                            'speed': 'medium'
                        },
                        {
                            'name': 'fade',
                            'type': 'exit',
                            'speed': 'fast'
                        }
                    ]
                },
                'scenes': [
                    {
                        'story': STORY_TEXT,
                        'createSceneOnNewLine': False,
                        'createSceneOnEndOfSentence': False
                    }
                ]
            },
            headers={
                'Content-Type': 'application/json',
                'Authorization': access_token
            }
        )
        storyboard_response.raise_for_status()

        render_job_id = storyboard_response.json()['data']['jobId']
        print('Video with custom subtitle style render job created')
        print(f'Job ID: {render_job_id}\n')

        # Step 3: Monitor Job Status
        print('Step 3: Monitoring job status...')
        job_completed = False
        job_result = None

        while not job_completed:
            job_status_response = requests.get(
                f'{API_BASE_URL}/v1/jobs/{render_job_id}',
                headers={
                    'Authorization': access_token
                }
            )
            job_status_response.raise_for_status()

            status = job_status_response.json()['data']['status']
            print(f'Current status: {status}')

            if status == 'completed':
                job_completed = True
                job_result = job_status_response.json()
                print('\nVideo with custom subtitle style created successfully!')
                print(f"Video URL: {job_result['data']['videoUrl']}")
            elif status == 'failed':
                raise Exception(f"Job failed: {json.dumps(job_status_response.json())}")
            else:
                # Wait 5 seconds before checking again
                time.sleep(5)

        return job_result

    except requests.exceptions.RequestException as error:
        print(f'Error: {error}')
        if hasattr(error, 'response') and error.response is not None:
            print(f'Response: {error.response.text}')
        raise

# Run the function
if __name__ == '__main__':
    create_text_to_video_with_custom_subtitle_style()

Available Style Properties

Font Properties

  • fontFamily: Font name (e.g., "Poppins", "Arial", "Roboto")
  • fontUrl (optional): Custom font URL if using a non-standard font
  • fontSize: Font size in pixels (e.g., 48, 64)

Color Properties

All colors must be in RGBA format:

  • color: Text color (e.g., "rgba(255, 255, 255, 1)")
  • backgroundColor: Background color behind text (e.g., "rgba(0, 0, 0, 0.7)")
  • keywordColor: Color for highlighted keywords (e.g., "rgba(255, 215, 0, 1)")
  • shadowColor: Text shadow color (e.g., "rgba(0, 0, 0, 0.8)")

Position and Layout

  • position: Text position on screen
    • Options: "top-left", "top-center", "top-right", "center-left", "center-center", "center-right", "bottom-left", "bottom-center", "bottom-right"
  • alignment: Text alignment
    • Options: "left", "center", "right"
  • paragraphWidth: Width as percentage (e.g., "80%")
  • shadowWidth: Shadow width as percentage (e.g., "2%")

Text Styling

  • decorations: Array of text decorations
    • Options: "bold", "underline", "italics", "linethrough"
  • case: Text casing
    • Options: "uppercase", "lowercase", "capitalize", "smallcapitalize"

Background and Display

  • showBoxBackground: Boolean - Show/hide background box
  • showBullet: Boolean - Show/hide bullet points
  • bulletSize: Bullet size in pixels
  • bulletFillColor: Bullet color in RGBA format

Animations

  • animations: Array of animation objects (max 2: one entry, one exit)
    • name: Animation name
      • Options: "none", "fade", "drift", "wipe", "text reveal", "elastic", "typewriter", "blur", "bulletin"
    • type: "entry" or "exit"
    • speed: "slow", "medium", "fast", or "custom"
    • customSpeedValue: Number (required if speed is "custom", min 0.5)
    • direction: "up", "down", "left", "right" (for applicable animations)
    • writingStyle: "character", "word", "line", "paragraph" (for text reveal animations)

RGBA Color Format

Always use RGBA format for colors:

  • rgba(R, G, B, A) where:
    • R, G, B: 0-255 (red, green, blue)
    • A: 0-1 (alpha/transparency, where 0 is fully transparent, 1 is fully opaque)

Examples:

  • White: rgba(255, 255, 255, 1)
  • Black: rgba(0, 0, 0, 1)
  • Semi-transparent black: rgba(0, 0, 0, 0.7)
  • Gold: rgba(255, 215, 0, 1)
  • Red: rgba(255, 0, 0, 1)

Scene-Level Custom Styles

You can also apply custom styles at the scene level:

scenes: [
  {
    story: "Scene text",
    subtitleStyle: {
      fontFamily: "Arial",
      fontSize: 36,
      color: "rgba(255, 255, 255, 1)",
      position: "bottom-center",
    },
  },
];

Use Cases

  • Create unique, branded subtitle styles without saving them
  • Experiment with different styles quickly
  • Generate videos with dynamic styling based on content
  • A/B test different subtitle appearances
  • Create one-off videos with custom styling

Response

The API returns a job ID for monitoring the video creation progress. Once completed, you'll receive a video URL with your custom subtitle styling applied.

Notes

  • Replace YOUR_CLIENT_ID and YOUR_CLIENT_SECRET with your actual API credentials
  • All color values must be in RGBA format as specified
  • Percentage values must be strings with "%" symbol (e.g., "80%")
  • Maximum 2 animations per subtitle style (one entry, one exit)
  • Scene-level styles override video-level styles