Video và việc sinh nội dung

Dựng video từ kịch bản có sẵn, hoặc nhờ AI viết kịch bản trước rồi mới đem đi dựng.

Vòng đời một video

POST /videos nhận việc và trả về ngay — video chưa dựng xong ở thời điểm đó. Theo dõi bằng status khi gọi lại chi tiết:

statusNghĩa là
DRAFTĐã lưu nhưng chưa có bản dựng
PENDINGĐã nhận, chờ tới lượt vào hàng đợi
PENDING_DISPATCHChờ chỗ trống trong hàng đợi dựng của tài khoản
RENDERINGĐang dựng
GENERATINGĐang sinh video bằng AI, chưa can thiệp được
READYDựng xong, đang chờ bạn gọi đăng
QUEUEDĐã xếp hàng đăng hoặc đã hẹn giờ
POSTEDĐã đăng — xem kết quả từng kênh trong mảng targets
FAILEDHỏng; đọc errorCode và errorDetail

Giá trị viết hoa và so sánh đúng từng ký tự. Mảng targets có danh mục riêng ở mức từng kênh: QUEUED, PROCESSING, POSTED, PUBLICLY_AVAILABLE, UNAVAILABLE, FAILED, DRAFT.

Hỏi lại mỗi 10–15 giây là đủ. Bắn dồn không làm khâu dựng nhanh hơn và dễ chạm trần tốc độ.

Tạo video

POST /videos

Bắt buộc gửi header Idempotency-Key — xem Quy ước chung.

{
  "title": "Bản tin sáng 22/09",
  "script": "Nội dung cho giọng đọc…",
  "caption": "Tin nóng sáng nay",
  "hashtags": ["tintuc", "sangnay"],
  "connectionIds": ["4e1c…"],
  "voiceId": "ngochuyennew",
  "mediaUrls": ["https://…/anh-1.jpg"],
  "keepAsReady": true
}
TrườngÝ nghĩa
connectionIdsKênh nhận bài. Lấy id từ GET /channels.
voiceIdGiọng đọc. Danh sách giọng xem trên giao diện web.
mediaUrlsẢnh dùng để dựng hình. Tải lên qua /media rồi lấy url trả về.
keepAsReadytrue = chỉ dựng rồi dừng, chờ bạn gọi đăng. false = dựng xong đăng luôn.
scheduleAtHẹn giờ đăng theo ISO 8601. Bỏ trống là đăng ngay sau khi dựng xong.
templateIdMẫu thương hiệu. Lấy từ GET /templates; bỏ trống là dùng mẫu mặc định.
bgmSource, bgmKey, bgmVolumeNhạc nền và mức âm lượng của nó.
youtubeConfig, facebookConfigTuỳ chọn riêng của hai nền tảng đó, ví dụ chế độ hiển thị.

Phản hồi 201 trả về video vừa tạo, kèm mảng warnings. Cảnh báo không chặn việc tạo — hiện chỉ có một loại: kênh đã vượt mốc khuyến nghị số bài trong 24 giờ.

Lượt gọi này trừ credit

Credit bị giữ ngay khi việc vào hàng đợi và quyết toán khi dựng xong. Hỏi GET /credits trước nếu bạn muốn chắc chắn số dư đủ. Xem Cách tính credit.

Đọc và theo dõi

GET /videos?page=1&pageSize=25&status=rendering
GET /videos/{id}

Mảng targets nói kết quả trên từng kênh: channelId, platform, status, postUrl khi đã lên, và errorCode khi hỏng. Một video đăng nhiều kênh có thể thành công ở kênh này và hỏng ở kênh kia.

videoUrl là link tải bản dựng, chỉ có giá trị khi dựng xong. Link này có hạn theo chính sách lưu trữ của gói — tải về trước nếu bạn cần giữ lâu.

Đăng và huỷ

Dựng

POST /videos/{id}/render → 202

Chỉ áp dụng cho video chưa có bản dựng. Trừ credit như một lượt dựng mới, nên cần header chống trùng.

Đăng

POST /videos/{id}/publish → 202

Dành cho video tạo với keepAsReady: true. Thân yêu cầu có thể đổi connectionIds, caption, hashtags, scheduleAt và comment (bình luận đầu tiên tự đăng kèm). Bỏ trống connectionIds thì giữ nguyên các kênh đã gắn lúc tạo; bỏ trống scheduleAt là đăng ngay.

Đường này đòi quyền posts:write chứ không phải videos:write: đây là bước đưa nội dung ra kênh thật, nên tách riêng để bạn cấp được khoá chỉ dựng mà không đăng.

Huỷ đăng

DELETE /videos/{id}

Gỡ các lượt đăng đang chờ và đưa video về trạng thái sẵn sàng — bản dựng được giữ lại, nên bạn gọi đăng lại được sau đó. Kênh nào đang đăng dở sẽ chặn thao tác này, và bài đã lên kênh thì không gỡ xuống được bằng đường này.

Không cần header chống trùng: huỷ hai lần cho kết quả giống hệt huỷ một lần.

Sửa tiêu đề, mô tả, hashtag

PATCH /videos/{id}

{ "caption": "Mô tả mới", "hashtags": ["meo"] }

Chỉ nhận title, caption, hashtags; trường bỏ trống giữ nguyên. Không dựng lại, không đổi trạng thái hay lịch đăng. Kênh chưa đăng sẽ dùng nội dung mới; bài đã lên kênh thì không đổi. Quyền videos:write, không cần header chống trùng.

Đổi giờ hẹn

Không có đường sửa lịch riêng: gọi DELETE /videos/{id} để gỡ lượt đăng đang chờ (bản dựng được giữ), rồi POST /videos/{id}/publish với scheduleAt mới. Bỏ trống connectionIds là giữ nguyên các kênh cũ.

Nhờ AI viết nội dung

Khi bạn chưa có sẵn kịch bản, /jobs là bước trước đó: xếp một việc cho mô hình, lấy kết quả, rồi mới gọi POST /videos.

POST /jobs
Idempotency-Key: …

{
  "kind": "video-talk-script",
  "params": { … }
}
kindSinh ra
video-talk-scriptKịch bản cho video lồng tiếng
storyboardKịch bản phân cảnh cho video kiến thức
storyboard-fit-extrasBước 2 của luồng trên: khớp phần thừa vào các cảnh
comic-from-storyPhân cảnh truyện tranh từ một câu chuyện
campaign-topicsGợi ý chủ đề cho chiến dịch từ mô tả kênh (briefChannel, count 1–200, language)

Chỉ nhận các loại liệt kê trong trang này; kind khác bị từ chối với mã 422.

params của mỗi loại được kiểm theo đúng bộ ràng buộc mà giao diện web dùng, nên tên trường gõ sai bị chặn ngay ở lượt gọi chứ không hỏng lặng lẽ ở khâu chạy nền.

GET /jobs/{jobId}
  • state: waiting, active, completed, failed, delayed hoặc unknown.
  • result chỉ có khi state = completed.
  • errorCode chỉ có khi state = failed. Đây là mã để so sánh, không phải câu để hiển thị.

Lượt gọi trả về 202, không phải 200

Phản hồi chỉ mang jobId và kind. Cố ý không kèm trạng thái: ngay lúc trả lời, việc có thể đã chạy, đã xong, hoặc còn nằm chờ — GET /jobs/{id} mới là chỗ nói được sự thật.

Video kiến thức

Gửi nội dung gốc và một mẫu video kiến thức; máy chủ tự viết lại thành từng cảnh theo mẫu, chọn bố cục, tìm ảnh rồi dựng. Mẫu tạo trên giao diện web, lấy id ở GET /video-templates (chỉ mẫu có kind = storyboard).

POST /knowledge/render → 201
Idempotency-Key: …

{
  "templateId": "…",
  "script": "Nội dung gốc, 100–30000 ký tự…",
  "title": "Vì sao biển mặn",
  "voiceId": "ngochuyennew",
  "language": "vi"
}
  • Bắt buộc: templateId và script. Tuỳ chọn: title, caption, hashtags, language, voiceId, voiceVolume, bgmSource/bgmKey/bgmVolume, advanced, watermark.
  • Mẫu không phải kiến thức hoặc chưa có kịch bản cảnh trả 422 knowledge_template_required; mẫu không thuộc bạn trả 404 — cả hai đều bị chặn trước khi giữ credit.
  • Đường này chỉ dựng. Theo dõi bằng GET /videos/{id} (GENERATING → RENDERING → READY), rồi đăng bằng POST /videos/{id}/publish. Khi tài khoản đang có nhiều video kiến thức dựng cùng lúc, video mới nằm ở PENDING_DISPATCH — đang chờ tới lượt, không phải lỗi — và tự chuyển sang GENERATING.

Khác với job storyboard

Job storyboard ở trên chỉ để xem trước kịch bản phân cảnh; kết quả của nó không đưa thẳng vào đây được. Đường này tự viết lại nội dung theo đúng kịch bản cảnh của mẫu.

Người dẫn AI và clone video

Hai thể loại này dựng theo cảnh, mỗi cảnh cố định 10 giây, giá tính theo giây. Bước viết nội dung bằng AI mất cả phút nên cả việc dựng cũng đi qua /jobs thay vì POST /videos. Khi việc dựng xong, result.postIds[0] là video trong kho — theo dõi tiếp bằng GET /videos/{id}. Khoá ảnh lấy từ POST /media/upload-ticket.

kindLàm gìTrừ credit
ai-presenter-scriptViết lời thoại từng cảnh theo chủ đề, trả dialoguesKhông
ai-presenter-renderDựng video người dẫn từ ảnh chân dung; cảnh bỏ trống lời thoại thì AI tự viếtCó
video-clone-analyzeĐọc 2–5 ảnh chụp video mẫu, trả analysisId (giữ 24 giờ) và bản tóm tắtKhông
video-clone-renderDựng video mới theo bản phân tích, từ 1–5 ảnh đầu vào của bạnCó

Người dẫn AI

POST /jobs
Idempotency-Key: …

{
  "kind": "ai-presenter-render",
  "params": {
    "presenterImageKey": "uploads/…/presenter.jpg",
    "setting": "production_studio",
    "hologram": true,
    "tone": "blue",
    "cameraMove": "dolly_in",
    "aspectRatio": "16:9",
    "transition": "auto",
    "style": "explainer",
    "language": "vi",
    "topic": "3 xu hướng AI năm nay",
    "scenes": [
      { "shot": "medium" },
      { "shot": "close_up", "dialogue": "Cảm ơn bạn đã theo dõi." }
    ],
    "connectionIds": [],
    "allowComment": true,
    "allowContentReuse": true,
    "keepAsReady": true
  }
}

Clone video

Gọi video-clone-analyze trước với { "frameKeys": [...], "summaryLanguage": "vi" }, đọc bản tóm tắt, rồi mới gọi bước dựng:

{
  "kind": "video-clone-render",
  "params": {
    "analysisId": "…",
    "inputImageKeys": ["uploads/…/product.jpg"],
    "sceneCount": 3,
    "aspect": "source",
    "language": "vi",
    "changeNote": "Đổi sang sản phẩm son môi",
    "connectionIds": [],
    "allowComment": true,
    "allowContentReuse": true,
    "keepAsReady": true
  }
}

Ảnh đầu vào khác ảnh chụp video mẫu

frameKeys là ảnh chụp màn hình video mẫu, chỉ dùng để phân tích phong cách. inputImageKeys là ảnh của bạn (sản phẩm, nhân vật…) để dựng video mới. Bản phân tích hết hạn sau 24 giờ; quá hạn thì phân tích lại.