OmniSaleGmbH / lalalai

127 stars 10 forks source link

Lacking TypeScript Documentation with Detailed Code Example #5

Open kevinrss01 opened 8 months ago

kevinrss01 commented 8 months ago

I have noticed that the current TypeScript documentation in this repository, specifically for the lalal.ai API, could benefit from additional details and examples. The lalal.ai API provides powerful capabilities in splitting voices and music within an audio file. A more robust TypeScript guide can enhance developers' understanding and effectiveness when integrating this API into their applications.

To address this, I propose the addition of a detailed TypeScript code example that I have developed. This example encompasses various use-cases and illustrates how to properly implement and use the lalal.ai API features using TypeScript.

kevinrss01 commented 8 months ago
import axios from "axios"
import qs from "qs"
import fs from "fs"
import FormData from "form-data"

interface LalalAPIResponse {
  status: "success" | "error"
  result: Result
}

interface Result {
  [key: string]: SplitDetail
  archive: SplitDetail
  batch: SplitDetail
}

interface SplitDetail {
  status: "success" | "error"
  name?: string
  size?: number
  duration?: number
  stem?: string
  splitter?: "orion" | "phoenix"
  preview?: Preview | null
  split?: any
  player?: Player | null
  task?: TaskDetail
  error?: string
}

interface ArchiveBatchResult {
  url: string
  size: number
}

interface Preview {
  duration: number
  stem_track: string
  stem_track_size: number
  back_track: string
  back_track_size: number
}

interface Player {
  stem_track: string
  stem_track_size: number
  back_track: string
  back_track_size: number
}

interface TaskDetail {
  id: string[]
  state: "success" | "error" | "progress" | "cancelled"
  progress?: number
  split_id?: string
  error?: string
}

interface ApiUploadResponse {
  status: "success" | "error"
  id?: string
  size?: number
  duration?: number
  expires?: number
  error?: string
}

const licenseKey = "YOUR_LALAL_LICENSE_KEY"
const apiUrlBase = "https://www.lalal.ai/api"
let fileId: string = ""

const checkStatus = async (fileId: string) => {
  let isCompleted = false
  let statusData: null | LalalAPIResponse = null

  while (!isCompleted) {
    try {
      const data = qs.stringify({ id: fileId })
      const response = await axios.post(`${apiUrlBase}/check/`, data, {
        headers: {
          Authorization: `license ${licenseKey}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
      })

      if (response.data.status === "success") {
        const taskState = response.data.result[fileId]?.task?.state

        if (taskState === "success") {
          isCompleted = true
          if (response.data.result.archive) {
            statusData = response.data
          } else {
            console.log(
              "Split operation details:",
              response.data.result[fileId].split
            )
          }
        } else {
          console.log("Task in progress, checking again in 1 second...")
          await new Promise((resolve) => setTimeout(resolve, 1000))
        }
      } else {
        console.error("Error checking status:", response.data.error)
        throw new Error(response.data.error || "Status check failed")
      }
    } catch (error) {
      console.error("An error occurred while checking status:", error)
      throw error // Rethrow the error to exit the while loop and handle it outside
    }
  }

  if (!statusData) throw new Error("No status data found")

  return statusData
}

const processAudio = async (filePath: string): Promise<LalalAPIResponse> => {
  try {
    const form = new FormData()
    form.append("file", fs.createReadStream(filePath), {
      filename: filePath.split("/").pop(),
    })

    const uploadResponse = await axios.post<ApiUploadResponse>(
      `${apiUrlBase}/upload/`,
      form,
      {
        headers: {
          ...form.getHeaders(),
          "Content-Disposition": `attachment; filename=${filePath
            .split("/")
            .pop()}`,
          Authorization: `license ${licenseKey}`,
        },
      }
    )

    if (uploadResponse.data.status !== "success") {
      console.error("Upload failed:", uploadResponse.data.error)
      throw new Error("Upload failed")
    }

    if (!uploadResponse.data.id) throw new Error("No file ID")
    fileId = uploadResponse.data.id
    if (!fileId) throw new Error("No file ID")
    console.log("File uploaded successfully, ID:", fileId)

    interface SplitParams {
      id: string
      stem:
        | "vocals"
        | "drum"
        | "bass"
        | "piano"
        | "electric_guitar"
        | "acoustic_guitar"
        | "synthesizer"
        | "voice"
        | "strings"
        | "wind"
      splitter: "orion" | "phoenix"
      filter: 0 | 1 | 2
    }

    //Neural network selection option. Currently, the "Orion" neural network only supports the stems "vocal" and "voice"
    // Split audio
    const params: SplitParams[] = [
      {
        id: fileId,
        stem: "voice",
        splitter: "orion",
        filter: 1,
      },
    ]

    const splitResponse = await axios.post(
      `${apiUrlBase}/split/`,
      qs.stringify({ params: JSON.stringify(params) }),
      {
        headers: {
          ...form.getHeaders(),
          Authorization: `license ${licenseKey}`,
          "Content-Type": "application/x-www-form-urlencoded",
        },
      }
    )

    if (splitResponse.data.status !== "success") {
      console.error("Split operation failed:", splitResponse.data.error)
      throw new Error("Split operation failed.")
    }

    console.log("Split operation initiated successfully")
    return await checkStatus(fileId)
  } catch (error) {
    console.error("Process failed:", error)
    throw new Error("Error while processing audio")
  }
}

//Example of use

const filePath = "YOUR_FILE"

const getLalalResponse = async () => {
  const lalalResponse = await processAudio(filePath)
  const vocals = lalalResponse.result[fileId].split.stem_track
  const accompaniment = lalalResponse.result[fileId].split.back_track

  console.log("Vocals:", vocals)
  console.log("Accompaniment:", accompaniment)
}

getLalalResponse()