Aller au contenu principal
LocoPal AiLocoPal Ai
Bibliothèque de ressources

Guide

Processus d'intégration des ressources de portrait et des vidéos de référence

Processus complet et exécutable, de la création du groupe et de l'import des images à la soumission multi-images avec vidéo de référence

Voir le Markdown

Processus d'intégration des ressources de portrait et des vidéos de référence

Cette page présente une séquence d'appels côté serveur directement applicable. Les champs, exemples et codes d'erreur de chaque étape se trouvent dans les pages d'interface ; ici, seuls l'ordre des opérations et le code complet sont présentés :

https://locopalai.com
Authorization: Bearer <API_KEY>
Content-Type: application/json

Ordre des appels

Ressources virtuelles

POST /api/v1/materials/groups
  → POST /api/v1/materials/assets
  → GET  /api/v1/materials/assets/{asset_id}   轮询到 status 为 Active
  → POST /v1/video/generations
  → GET  /v1/video/generations/{task_id}

Ressources de portrait

POST /api/v1/materials/groups {"type":"real"}
  → POST /api/v1/materials/portrait/sessions   拿到二维码链接
  → 真人本人扫码并完成手机采集
  → POST /api/v1/materials/portrait/resolve
  → POST /api/v1/materials/sync
  → GET  /api/v1/materials/assets?group_id=...
  → POST /v1/video/generations

Créer un groupe real ne signifie pas que la plateforme effectue l'autorisation à la place de la personne concernée ; lorsqu'une personne réelle est concernée, c'est toujours elle qui doit effectuer la collecte et l'autorisation.

1. Import d'un portrait virtuel

  1. Créer un groupe : POST /api/v1/materials/groups, passez virtual à type, et enregistrez le data.group.id de la réponse. Les champs et exemples sont décrits dans Groupes de ressources.
  2. Soumettre l'image : POST /api/v1/materials/assets, avec group_id + image_url (ou data_url), et enregistrez data.asset_id. title peut rester vide ; la plateforme le nommera automatiquement d'après le nom de fichier.
  3. Interroger le statut : GET /api/v1/materials/assets/{asset_id}, de préférence toutes les 5 à 10 secondes, en interrogeant uniquement le même ID de ressource.
data.asset.statusTraitement
ProcessingInterrogez plus tard le même asset_id
ActiveVous pouvez utiliser asset://asset-id dans la requête vidéo
FailedLisez error_message, corrigez l'image puis créez une nouvelle ressource

2. Autorisation de ressources de portrait par scan de code QR

  1. POST /api/v1/materials/portrait/sessions pour obtenir authorization_url et session_token, puis convertissez l'adresse en code QR et remettez-le à la personne concernée pour qu'elle le scanne.
  2. Une fois la collecte effectuée par la personne elle-même, appelez POST /api/v1/materials/portrait/resolve (avec session_token) pour obtenir le group.id du groupe de portrait.
  3. POST /api/v1/materials/sync pour synchroniser une fois, puis interrogez la liste des ressources pour lire l'asset_id et le status de la ressource de portrait.

Les paramètres, réponses et codes d'erreur sont décrits dans Autorisation de ressources de portrait.

3. Soumission multi-images + vidéo de référence

Le format de « multi-images + vidéo de profondeur » dans l'interface publique est le suivant : chaque image occupe un élément content, la vidéo de référence utilise video_url et role: "reference_video", et il n'existe pas de champ deep_video distinct.

export LOCOPAL_API_KEY="sk-你的APIKey"
export LOCOPAL_VIDEO_MODEL_ID="从 /v1/models 返回的视频模型 ID"
export LOCOPAL_PERSON_IMAGE_URL="https://你的对象存储域名/person.png"
export LOCOPAL_PRODUCT_IMAGE_URL="https://你的对象存储域名/product.png"
export LOCOPAL_REFERENCE_VIDEO_URL="https://你的对象存储域名/motion.mp4"
const origin = 'https://locopalai.com'
const apiKey = process.env.LOCOPAL_API_KEY!
const model = process.env.LOCOPAL_VIDEO_MODEL_ID!
const personImage = process.env.LOCOPAL_PERSON_IMAGE_URL!
const productImage = process.env.LOCOPAL_PRODUCT_IMAGE_URL!
const referenceVideo = process.env.LOCOPAL_REFERENCE_VIDEO_URL!

const response = await fetch(`${origin}/v1/video/generations`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model,
    content: [
      { type: 'text', text: '以图片1的人物为主体,参考图片2的产品外观,按照参考视频的动作节奏和运镜生成视频。' },
      { type: 'image_url', role: 'reference_image', image_url: { url: personImage } },
      { type: 'image_url', role: 'reference_image', image_url: { url: productImage } },
      { type: 'video_url', role: 'reference_video', video_url: { url: referenceVideo } },
    ],
    resolution: '720p',
    ratio: '16:9',
    duration: 5,
    generate_audio: true,
    watermark: false,
  }),
})

const created = (await response.json()) as { id?: string; error?: { message?: string } }
if (!response.ok || !created.id) throw new Error(created.error?.message ?? `创建视频失败:HTTP ${response.status}`)

// 保存 created.id。网络中断后只查询这个 ID,不要重复创建。
const query = await fetch(`${origin}/v1/video/generations/${encodeURIComponent(created.id)}`, {
  headers: { Authorization: `Bearer ${apiKey}` },
})
if (!query.ok) throw new Error(await query.text())
console.log(await query.json())

Si les images ont déjà été validées par la bibliothèque de ressources, remplacez l'URL de l'image par asset://<asset_id> ; chaque image doit toujours occuper un élément content distinct. Le nombre d'images de référence, la capacité de vidéo de référence, la résolution, la durée et les capacités audio dépendent des capacités du modèle renvoyées par /v1/models et des erreurs réelles.

4. Exemple Node.js complet

Le code ci-dessous enchaîne « créer un groupe → soumettre l'image → interroger le statut → soumettre la vidéo » ; la clé API est lue depuis les variables d'environnement.

const origin = process.env.LOCOPAL_API_ORIGIN ?? 'https://locopalai.com'
const apiKey = process.env.LOCOPAL_API_KEY
const imageUrl = process.env.LOCOPAL_IMAGE_URL
const videoModelId = process.env.LOCOPAL_VIDEO_MODEL_ID

if (!apiKey || !imageUrl || !videoModelId) {
  throw new Error('请设置 LOCOPAL_API_KEY、LOCOPAL_IMAGE_URL、LOCOPAL_VIDEO_MODEL_ID')
}

async function locopal<T>(path: string, init: RequestInit = {}) {
  const response = await fetch(`${origin}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
      ...init.headers,
    },
  })
  const payload = (await response.json()) as { code?: number; data?: T; msg?: string }
  if (!response.ok || payload.code !== 0) throw new Error(payload.msg ?? `请求失败:HTTP ${response.status}`)
  return payload.data as T
}

const group = await locopal<{ group: { id: string } }>('/api/v1/materials/groups', {
  method: 'POST',
  body: JSON.stringify({ name: 'API 人像参考素材', description: '虚拟素材分组', type: 'virtual' }),
})

const asset = await locopal<{ asset_id: string }>('/api/v1/materials/assets', {
  method: 'POST',
  body: JSON.stringify({ group_id: group.group.id, title: '人物参考图', image_url: imageUrl }),
})

const state = await locopal<{ asset: { status: string; error_message?: string | null } }>(
  `/api/v1/materials/assets/${encodeURIComponent(asset.asset_id)}`,
)
if (state.asset.status !== 'Active') {
  throw new Error(state.asset.error_message ?? '素材尚未可用')
}

const video = await fetch(`${origin}/v1/video/generations`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    model: videoModelId,
    content: [
      { type: 'text', text: '让人物自然转身,保持人物外观一致。' },
      { type: 'image_url', role: 'reference_image', image_url: { url: `asset://${asset.asset_id}` } },
    ],
    resolution: '720p',
    ratio: '16:9',
    duration: 5,
  }),
})
if (!video.ok) throw new Error(await video.text())
console.log(await video.json())

5. Exemple complet d'autorisation de portrait en Node.js

const origin = 'https://locopalai.com'
const apiKey = process.env.LOCOPAL_API_KEY!

async function api<T>(path: string, init: RequestInit = {}) {
  const response = await fetch(`${origin}${path}`, {
    ...init,
    headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', ...init.headers },
  })
  const payload = (await response.json()) as { code?: number; data?: T; msg?: string }
  if (!response.ok || payload.code !== 0) throw new Error(payload.msg ?? `请求失败:HTTP ${response.status}`)
  return payload.data as T
}

const created = await api<{ session: { authorization_url: string; session_token: string } }>(
  '/api/v1/materials/portrait/sessions',
  { method: 'POST', body: JSON.stringify({ continuous_create: false }) },
)

// 把 authorization_url 转成二维码展示给真人本人。
// 等用户在手机端完成采集后,再执行下面的解析请求。
const resolved = await api<{ group: { id: string; type: string } }>('/api/v1/materials/portrait/resolve', {
  method: 'POST',
  body: JSON.stringify({ session_token: created.session.session_token }),
})

await api('/api/v1/materials/sync', { method: 'POST', body: '{}' })
const assets = await api<{ assets: Array<{ id: string; status: string }> }>(
  `/api/v1/materials/assets?group_id=${encodeURIComponent(resolved.group.id)}&page=1&page_size=20`,
)
console.log({ group: resolved.group, assets })

6. Changement de modèle et gestion des erreurs

Les ressources virtuelles continuent d'utiliser le même asset://<asset_id> ; lors d'un changement de modèle, seul le champ model change. Les ressources de portrait ne peuvent être soumises qu'à des modèles prenant en charge la bibliothèque de ressources de portrait ; si le modèle actuel ne le prend pas en charge, l'interface renvoie une erreur lisible, et il n'est pas possible de convertir la ressource de portrait en URL d'image ordinaire pour contourner la limitation.

  • 401 : vérifiez si la clé API est absente, erronée ou expirée ;
  • 400 : vérifiez l'intégrité des champs du corps de requête et du jeton de session ;
  • 404 : vérifiez si l'ID de ressource et l'ID de groupe appartiennent au compte actuel ;
  • 503 : le service de ressources ou d'autorisation de portrait est temporairement indisponible ; conservez les informations de requête et contactez le support de la plateforme ;
  • Processing : continuez d'interroger le même ID de ressource, ne réimportez pas ;
  • Failed : lisez error_message, corrigez le fichier source puis créez une nouvelle ressource.
Cette page vous a-t-elle aidé ?

Sur cette page