Docs do Nodaro
DocumentaçãoReferência de nósModelosAgentes de IA (MCP)DesenvolvedoresSelf-hostingPesquisa
SDK para TypeScript

Recast

Cote, compre e acompanhe execuções do Recast em TypeScript, responda aos pontos de decisão, importe um roteiro próprio e mude a mixagem de um recast pronto.

Disponível em Nodaro Cloud

client.recast executa o Recast: ele gera de novo um vídeo de origem já analisado com o seu próprio elenco e também pode renderizar um roteiro que você escreveu, um “movie as JSON”. Você faz a cotação de uma execução, compra, acompanha e responde aos pontos de decisão dela de elenco, folhas, quadros-âncora e música. Os métodos chamam a API REST do Recast. Veja Recast pelo MCP para o guia de criação.

O Recast é executado no Nodaro Cloud. Em uma instalação self-hosted, estes métodos lançam NotFoundError.

Métodos

MétodoO que faz
authoringSkill()Lê o guia de criação de roteiros
validateScript(script)Verifica um roteiro próprio
importScript(script, opts)Armazena um roteiro próprio como uma análise
estimate(input)Faz a cotação de uma execução, de graça
create(input)Compra o plano e cria a execução
get(recastId)Lê o status da execução e o ponto de decisão pendente
start(recastId, opts?)Começa a renderizar uma execução planejada
resolveGate(recastId, input)Responde a um ponto de decisão pendente
estimateRescore(recastId, input)Faz a cotação de uma nova mixagem ou de uma nova faixa de música
rescore(recastId, input)Aplica a alteração de áudio cotada

Roteiros próprios

authoringSkill()

Retorna o guia de criação, em Markdown (GET /v1/video-analysis/authoring-skill). Ele cobre o documento do roteiro, os vocabulários e os limites dele, as regras de áudio e um exemplo verificado. É gratuito.

authoringSkill(): Promise<string>
const guide = await client.recast.authoringSkill()

Entregue o guia ao modelo de linguagem que escreve o seu roteiro.

validateScript(script)

Verifica um roteiro próprio e retorna { valid, errors, warnings } (POST /v1/video-analysis/import/validate). É gratuito e não armazena nada.

validateScript(script: Record<string, unknown>): Promise<{
  valid: boolean
  errors: Array<{ path: string; message: string; hint?: string }>
  warnings: string[]
}>

Prop

Type

let check = await client.recast.validateScript(script)
while (!check.valid) {
  script = await fixWithModel(script, check.errors) // each error has a path, a message and usually a hint
  check = await client.recast.validateScript(script)
}

Cada erro indica o path que está errado e normalmente traz uma hint escrita para um modelo de linguagem. Corrija e valide de novo até valid ser true.

importScript(script, opts)

Armazena um roteiro válido como um job de análise concluído (POST /v1/video-analysis/import). Use o jobId dele como o analysisJobId de um recast. É gratuito.

importScript(script: Record<string, unknown>, opts: { rightsAttested: true }): Promise<{
  jobId: string
  created: boolean
  warnings: string[]
  json: Record<string, unknown>
}>

Prop

Type

const { jobId: analysisJobId, json } = await client.recast.importScript(script, { rightsAttested: true })

Um recast de roteiro próprio é renderizado exatamente como foi escrito, então importe só obras que são suas. json é o seu documento com os campos que o servidor adiciona; prefira-o à sua entrada. Importar o mesmo roteiro de novo retorna created: false.

A execução

estimate(input)

Faz a cotação de uma execução em créditos (POST /v1/recast/estimate). É gratuito. O corpo é o mesmo de create(), sem workflowId, rightsAttested e clientCapabilities.

estimate(input: EstimateRecastInput): Promise<{ totalCredits?: number; breakdown?: Record<string, number> }>

Prop

Type

const quote = await client.recast.estimate({ analysisJobId, fidelity: "faithful" })
console.log(quote.totalCredits, quote.breakdown)

O segmentSec no SDK 2.17.0. As rotas do Recast recebem o nome de um pacote em segmentSec: "scenes-max", "scenes" ou "max". O SDK 2.17.0 tipa o campo como número, então passe o nome com um cast, como segmentSec: "scenes" as unknown as number, ou deixe o campo de fora para usar o padrão do servidor. Um número como 10 é recusado com um 400. O mesmo vale para create() e start().

create(input)

Cria a execução e compra o plano (POST /v1/recast). Faça a cotação com estimate() antes.

create(input: CreateRecastInput): Promise<{ recastId: string }>

Prop

Type

const { recastId } = await client.recast.create({
  workflowId,
  analysisJobId,
  fidelity: "faithful",
  rightsAttested: true,
  interactive: true,
  clientCapabilities: ["sheet-gate"],
})

Sem workflowId, a chamada falha com 400 workflow_id_required; um workflow desconhecido ou de outra pessoa falha com um 404.

get(recastId)

Lê a execução (GET /v1/recast/:id). Consulte-a periodicamente para acompanhar o progresso. Em uma execução interativa, interactive.next indica o passo ou o ponto de decisão pendente.

get(recastId: string): Promise<RecastRunSnapshot>

Prop

Type

const run = await client.recast.get(recastId)
console.log(run.status, run.interactive)

O snapshot tem status, interactive, capabilities e, em uma execução concluída com camadas de áudio separadas, audio. Veja Alterar a mixagem da música.

start(recastId, opts?)

Começa a renderizar uma execução no estado planned (POST /v1/recast/:id/start). A cotação do plano já cobre essa renderização, e uma chamada repetida não muda nada.

start(recastId: string, opts?: { segmentSec?: number; provider?: string }): Promise<{ gvpJobId?: string }>

Prop

Type

const { gvpJobId } = await client.recast.start(recastId)

resolveGate(recastId, input)

Responde a um ponto de decisão pendente em uma execução interativa (POST /v1/recast/:id/select). A plataforma avança todos os outros passos sozinha, então um cliente só consulta get() periodicamente e responde aos pontos de decisão. A escolha em si é gratuita.

resolveGate(recastId: string, input: ResolveRecastGateInput): Promise<Record<string, unknown>>

Prop

Type

await client.recast.resolveGate(recastId, { gate: "music", musicPick: 1 })
await client.recast.resolveGate(recastId, { finishAuto: true })

Um ponto de decisão só é aberto para os tipos que o seu create() declarou em clientCapabilities. A plataforma decide os demais automaticamente.

Alterar a mixagem da música

Um recast concluído pode manter a música e o som do próprio vídeo como camadas separadas. Quando get() retorna capabilities.audioLayers definido como 1, o manifesto audio descreve essas camadas:

  • revision identifica o áudio atual. Toda alteração precisa dele.
  • present lista as camadas que o recast tem, music e video.
  • layers guarda arquivos de prévia das camadas que existem.
  • bakedEffectiveGain descreve os níveis do download atual.
  • pendingRescore descreve uma alteração ainda em andamento. Ele continua lá depois que a página é recarregada.

Nunca invente uma revisão e nunca deduza a mixagem a partir de uma prévia ausente.

estimateRescore(recastId, input)

Faz a cotação de uma alteração de áudio: uma nova mixagem, uma nova faixa de música ou as duas (POST /v1/recast/:id/estimate-rescore). É gratuito e retorna { credits, audioRevision, noOp }.

estimateRescore(recastId: string, input: EstimateRecastRescoreInput): Promise<{ credits: number; audioRevision: string; noOp: boolean }>

Prop

Type

rescore(recastId, input)

Aplica a alteração de áudio (POST /v1/recast/:id/rescore). Envie a mesma operação que você cotou, mais um novo requestId, um UUID. Reutilize esse requestId só para repetir a mesma requisição depois de uma falha de rede.

rescore(recastId: string, input: EstimateRecastRescoreInput & { requestId: string }): Promise<
  | { recastId: string; jobId: string }
  | { recastId: string; noOp: true; audioRevision: string }
>

Prop

Type

const { total: available } = await client.credits.balance()
const run = await client.recast.get(recastId)

if (run.capabilities?.audioLayers === 1 && run.audio) {
  const operation = {
    expectedAudioRevision: run.audio.revision,
    mix: {
      music: { gain: 55, muted: false },
      video: { gain: 100, muted: false },
    },
  }
  const quote = await client.recast.estimateRescore(recastId, operation)
  if (!quote.noOp && available >= quote.credits) {
    await client.recast.rescore(recastId, { ...operation, requestId: crypto.randomUUID() })
  }
}

A alteração é executada como um job. Consulte get(recastId) periodicamente até audio.pendingRescore desaparecer e audio.revision mudar. Uma alteração que não faria nada retorna noOp: true, sem job.

  • Uma operação contém uma mixagem, uma substituição de música (audioUrl ou sections), ou uma substituição com a mixagem dela.
  • Com uma substituição, envie a mixagem completa que você quer. Deixá-la de fora só funciona para os níveis padrão antigos exatos e, nos outros casos, falha com 409 legacy_mix_mismatch.
  • Um expectedAudioRevision desatualizado falha com 409 stale_audio_revision, e uma segunda alteração enquanto outra está em execução falha com 409 rescore_in_progress. Leia o recast de novo e faça a cotação de novo.
  • Uma operação inválida falha com um 400, como validation_error, unknown_section, duplicate_section ou all_audio_silent.
  • Uma cotação nunca reserva créditos e retorna o preço mesmo quando o seu saldo é insuficiente.

Perguntas frequentes

Última atualização

Nesta página