Developer Documentation

Tài liệu Tích hợp API v1 - HoaThinh3D Cloud

Hướng dẫn gọi API tìm kiếm phim, lấy Master HLS, mã nhúng Iframe và cấu hình Native App (ExoPlayer, AVPlayer, Flutter).

🚀
Phần 01

Bắt đầu nhanh

POST/api/v1/videos/search

Tạo API key ở tab Quản lý API Key, lưu key vào biến môi trường phía máy chủ rồi gửi yêu cầu đầu tiên:

bash
curl -X POST "https://app.hoathinh3d.cloud/api/v1/videos/search" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"searchText": "Tập 152","pageSize": 10,"pageIndex": 1,"categoryId": 24}'
🔑
Phần 02

Xác thực API Key

Mọi yêu cầu phải gửi API key qua HTTP header. Key chỉ hiển thị một lần khi tạo và có thể thu hồi bất cứ lúc nào trong trang quản trị.

Authorization: Bearer YOUR_API_KEY
⚠️ Bảo mật: Không nhúng API key vào JavaScript, HTML hoặc ứng dụng client. Hãy gọi API từ PHP/Node.js backend của website và lưu key bằng biến môi trường như RENDER_HLS_API_KEY.
📦
Phần 04

Dữ liệu trả về (Response)

API chỉ trả về video thuộc tài khoản hiện tại và đã sẵn sàng phát. Trường m3u8 là Master HLS dùng để tích hợp trình phát, trường subtitles chứa danh sách file phụ đề đa ngôn ngữ.

json
{
  "data": [
    {
      "id": 152,
      "title": "Trọng Lực Vương Giả - Tập 12",
      "description": "Video đã render hoàn tất",
      "status": "ready",
      "duration": 1420,
      "posterUrl": "https://cdn.example.com/videos/152/poster.jpg",
      "m3u8": "https://cdn.example.com/videos/152/master.m3u8",
      "embedUrl": "https://app.hoathinh3d.cloud/embed/152",
      "subtitles": [
        {
          "language": "vi",
          "label": "Tiếng Việt",
          "format": "vtt",
          "url": "https://cdn.example.com/videos/152/subtitles/vi.vtt",
          "isDefault": true
        }
      ],
      "qualities": ["720p", "1080p"]
    }
  ],
  "meta": {
    "pageIndex": 1,
    "pageSize": 10,
    "total": 1
  }
}
🔒
Phần 05

Phiên phát có Token bảo mật (Tùy chọn VIP)

POSThttps://app.hoathinh3d.cloud/api/v1/playback/sessions

Lưu ý: Để phát video nhanh và tối ưu bộ nhớ đệm CDN nhất, bạn chỉ cần lấy link m3u8 trực tiếp từ API Tìm kiếm phía trên. Endpoint này chỉ sử dụng khi bạn cần tính năng kiểm soát truy cập nâng cao (Signed Token ngắn hạn cho gói VIP, chống chia sẻ tài khoản xem chung, hoặc giới hạn số thiết bị phát đồng thời):

Tham sốKiểuMặc địnhMô tả
videoIdnumberBắt buộcID của video cần phát (phải thuộc tài khoản của bạn).
ttlSecondsnumber300Thời gian sống của từng token tính bằng giây (tối thiểu 60s, tối đa 86400s).
maxSessionTtlSecondsnumber14400 (4h)Thời lượng tối đa của cả phiên xem phim. Quá thời gian này sẽ cần tạo phiên mới.
accessstring"free"Phân quyền gói nội dung: "free" (Miễn phí) hoặc "vip" (Tài khoản VIP).
deliverystring"hls"Hình thức phát: "hls" (Trình phát trực tiếp) hoặc "embed" (Nhúng Iframe).
originstring-Domain yêu cầu phát (phải nằm trong danh sách domain đã khai báo trong cài đặt).
subjectstring-Định danh người xem (User ID / Device ID) để ghi vết phiên phát và kiểm soát VIP.

Ví dụ tích hợp

Chọn ngôn ngữ để xem mã nguồn tương ứng.

JavaScript / Node.js
const response = await fetch("https://app.hoathinh3d.cloud/api/v1/playback/sessions", {
  method: "POST",
  headers: {
    Authorization: "Bearer YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    videoId: 797,
    ttlSeconds: 300,
    maxSessionTtlSeconds: 14400,
    access: "vip",
    delivery: "hls",
    subject: "user_vip_999",
  }),
});

const result = await response.json();
console.log("Master HLS URL:", result.url);

Dữ liệu trả về (kèm Signed URLs):

json
{
  "sessionId": "ps_b7f29a0c1e84",
  "videoId": 797,
  "url": "https://cdn.hoathinh3d.cloud/videos/797/.../master.m3u8?pt=eyJhbGciOi...",
  "embedUrl": "https://app.hoathinh3d.cloud/embed/797?pt=eyJhbGciOi...",
  "expiresAt": "2026-08-26T04:30:00.000Z",
  "subtitles": [
    {
      "language": "vi",
      "label": "Thuyết Minh",
      "format": "vtt",
      "url": "https://cdn.hoathinh3d.cloud/videos/797/.../vi.vtt?pt=eyJhbGciOi...",
      "isDefault": true
    }
  ]
}
💬
Phần 06

Quản lý & Tích hợp Phụ đề (Subtitles)

GEThttps://app.hoathinh3d.cloud/api/v1/videos/{id}/subtitles

Phụ đề được tự động đính kèm trong mảng subtitles khi tìm kiếm video, hoặc bạn có thể gọi trực tiếp endpoint GET phía trên với ID video để lấy danh sách phụ đề (.vtt, .srt, .ass) đã lưu trữ trên CDN:

Thuộc tínhKiểuVí dụMô tả
languagestring"vi", "zh"Mã chuẩn ngôn ngữ (ISO 639-1) của phụ đề.
labelstring"Tiếng Việt"Tên hiển thị thân thiện trên menu chọn phụ đề của Player.
format"vtt" | "srt" | "ass""vtt"Định dạng file phụ đề. Khuyến nghị chuẩn WebVTT (.vtt) cho web player.
urlstring (URL)https://cdn.example.com/.../vi.vttĐường dẫn CDN tải trực tiếp file phụ đề (hỗ trợ CORS đầy đủ).
isDefaultbooleantrueĐánh dấu phụ đề mặc định tự động kích hoạt khi video bắt đầu phát.

Ví dụ tích hợp dữ liệu phim & Master HLS vào Custom Web Player (HLS.js / ArtPlayer):

javascript
// 1. Lấy dữ liệu phim từ API tìm kiếm (/api/v1/videos/search)
const res = await fetch('https://app.hoathinh3d.cloud/api/v1/videos/search', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer ' + process.env.RENDER_HLS_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ searchText: 'Trụ Tiên Tập 81', pageSize: 1 })
});
const { data } = await res.json();
const video = data[0];

// 2. Lấy cấu hình vị trí phụ đề Realtime từ Admin (tự động đồng bộ theo Admin):
const subConfigRes = await fetch('https://app.hoathinh3d.cloud/api/v1/playback/subtitles-config');
const { subtitlePositions } = await subConfigRes.json();

// 3. Khởi tạo ArtPlayer phát trực tiếp link m3u8
const art = new Artplayer({
  container: '#player',
  url: video.m3u8,
  subtitle: {
    url: video.subtitles?.[0]?.url || '',
    type: 'vtt',
    style: {
      color: '#FFFFFF',
      fontSize: 'clamp(18px, 3.2cqi, 40px)',
      textShadow: '0 2px 4px rgba(0,0,0,0.9)',
      bottom: '10%', // Sát đáy mặc định
    },
  },
  customType: {
    m3u8: function(videoElement, url, artInstance) {
      if (Hls.isSupported()) {
        const hls = new Hls({ maxBufferLength: 30, maxMaxBufferLength: 60 });
        hls.loadSource(url);
        hls.attachMedia(videoElement);
        artInstance.hls = hls;
      } else if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
        videoElement.src = url;
      }
    }
  }
});

// 4. Tự động đồng bộ vị trí phụ đề Realtime theo Admin và tỷ lệ khung hình video:
art.on('video:loadedmetadata', () => {
  const v = art.video;
  let targetBottom = subtitlePositions?.pos16x9 || 10;
  if (v && v.videoWidth && v.videoHeight) {
    const ratio = v.videoWidth / v.videoHeight;
    if (ratio >= 2.05) targetBottom = subtitlePositions?.pos21x9 || 22; // 21:9 Cinema
    else if (ratio <= 0.8) targetBottom = subtitlePositions?.pos9x16 || 28; // 9:16 Shorts
  }
  art.subtitle.style('bottom', targetBottom + '%');
});
🖼️
Phần 07

Nhúng Trình phát (Iframe Embed) & Tùy biến Sub

Bạn có thể nhúng trực tiếp Player chất lượng cao vào web phim bằng thẻ <iframe>. Trình phát hỗ trợ truyền các tham số URL để thiết lập dải âm thanh, vị trí, cỡ chữ, font chữ và màu sắc phụ đề mặc định:

html
<iframe
  src="https://app.hoathinh3d.cloud/embed/248?audio=vietsub&sub_pos=22&sub_size=xl&sub_font=be-vietnam-pro&sub_bg=transparent&sub_color=ffffff&autoplay=1"
  width="100%"
  height="100%"
  frameborder="0"
  allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
  allowfullscreen>
</iframe>
Tham số URLKiểu / Giá trịMặc địnhMô tả
audiovietsub | thuyet-minhvietsubLựa chọn dải âm thanh mặc định khi mở video: vietsub (Tiếng Trung gốc + Phụ đề), thuyet-minh (Thuyết Minh Tiếng Việt).
sub_posnumber (1 - 50)22Vị trí nâng phụ đề từ đáy video theo tỷ lệ % (tránh đè sub tiếng Trung gốc). Ví dụ: sub_pos=22 hoặc sub_pos=16.
sub_sizesm | md | lg | xlxlCỡ chữ phụ đề tự động co giãn theo khung hình (sm: Nhỏ gọn, md: Tiêu chuẩn, lg: Lớn, xl: Rất lớn rõ nét).
sub_fontstringbe-vietnam-proFont chữ: be-vietnam-pro (Tối ưu Tiếng Việt), montserrat, roboto, oswald, nunito.
sub_bgstringtransparentNền che phụ đề gốc: transparent (Không nền), semi (Mờ 50%), dark (Đậm 90%), black (Đen đục).
sub_colorstring (HEX)ffffffMã màu chữ phụ đề (ví dụ: ffffff cho Trắng, ffeb3b cho Vàng tươi).
autoplay1 | 01Tự động phát video khi tải xong trình phát.

🔌API Điều khiển Player qua PostMessage (Dành cho App Desktop Electron & Web)

Khi nhúng Player trong App Desktop (Electron) hoặc trang web cha, bạn có thể điều khiển trình phát (Play, Pause, Tua, Đổi âm thanh, Đổi độ phân giải) và nhận sự kiện thời gian thực (Tiến độ phát, Kết thúc video để tự chuyển tập) thông qua window.postMessage:

1. Danh sách lệnh điều khiển từ App ➔ Iframe Embed (Commands):

ActionTham số dữ liệu (data)Mô tả tác vụ
play{}Bắt đầu / tiếp tục phát video
pause{}Tạm dừng phát video
togglePlay{}Chuyển đổi trạng thái Play / Pause
seek{ time: 120.5 }Tua đến giây thứ time
setVolume{ volume: 0.8 }Chỉnh âm lượng từ 0.0 đến 1.0
mute / unmute{}Tắt / Bật tiếng
setPlaybackRate{ rate: 1.5 }Đổi tốc độ phát (0.5, 1.0, 1.25, 1.5, 2.0...)
setQuality{ height: 1080 } | { quality: "auto" }Đổi chất lượng (720, 1080, 1440, 2160, hoặc "auto")
setAudio{ audio: "thuyetminh" } | "vietsub"Đổi dải âm thanh Vietsub hoặc Thuyết Minh
setSubtitle{ subtitle: "off" } | url vttBật / Tắt phụ đề
toggleFullscreen{}Bật / Tắt chế độ toàn màn hình
requestState{}Yêu cầu Player gửi lại toàn bộ snapshot trạng thái hiện tại

2. Danh sách sự kiện Player trả về App ➔ Desktop (Events):

Event NamePayload trả vềỨng dụng thực tế
ready{ videoId, duration, qualities, audioTracks, subtitles }Khởi tạo menu điều khiển & hiển thị danh sách tập
timeupdate{ currentTime, duration, progress }Bắn 4 lần/giây để App lưu tiến độ xem tiếp vào Database/Local
play / playing{ currentTime, duration }Cập nhật nút Play trên giao diện App
pause{ currentTime, duration }Cập nhật nút Pause trên giao diện App
buffering{ isBuffering: true, currentTime }Hiển thị loading indicator trên App
ended{ ended: true, currentTime, duration }Tự động chuyển tập tiếp theo khi xem hết phim
quality_change{ quality, height }Cập nhật nhãn chất lượng đang phát (VD: 1080p, 1440p)
audio_change{ audioTrack, name, isDub }Cập nhật trạng thái Vietsub / Thuyết Minh

Code mẫu JavaScript / TypeScript tích hợp PostMessage trên App Desktop (Electron):

javascript
// 1. Nhúng Iframe trong App Electron / Web
<iframe
  id="videoPlayerFrame"
  src="https://app.hoathinh3d.cloud/embed/1095?autoplay=1"
  allow="autoplay; fullscreen; picture-in-picture; encrypted-media"
  allowfullscreen
></iframe>

// 2. Gửi lệnh điều khiển từ App Desktop tới Player:
function sendPlayerCommand(action, data = {}) {
  const iframe = document.getElementById("videoPlayerFrame");
  if (iframe && iframe.contentWindow) {
    iframe.contentWindow.postMessage({
      source: "hoathinh3d-app",
      action: action,
      data: data
    }, "*");
  }
}

// Các lệnh điều khiển thông dụng:
sendPlayerCommand("play");
sendPlayerCommand("pause");
sendPlayerCommand("seek", { time: 120.5 }); // Tua đến 2 phút
sendPlayerCommand("setQuality", { height: 1080 }); // 720, 1080, 1440, 2160, "auto"
sendPlayerCommand("setAudio", { audio: "thuyetminh" }); // "vietsub" hoặc "thuyetminh"
sendPlayerCommand("setVolume", { volume: 0.8 }); // 0.0 đến 1.0

// 3. Lắng nghe Event từ Player gửi về App (Lưu tiến độ & Tự chuyển tập):
window.addEventListener("message", (event) => {
  if (event.origin !== "https://app.hoathinh3d.cloud") return;
  if (event.data?.source !== "hoathinh3d-embed") return;

  const { event: ev, videoId, currentTime, duration, progress, quality, name } = event.data;

  switch (ev) {
    case "ready":
      console.log("Player sẵn sàng:", event.data);
      break;
    case "timeupdate":
      // Lưu tiến độ xem tiếp (4 lần/giây, đã throttle 250ms)
      saveProgress(videoId, currentTime, duration);
      break;
    case "ended":
      console.log("Tập phim đã kết thúc -> Tự động chuyển tập tiếp theo!");
      playNextEpisode();
      break;
    case "quality_change":
      console.log("Đã đổi chất lượng:", quality);
      break;
    case "audio_change":
      console.log("Đã đổi âm thanh:", name);
      break;
  }
});
📱
Phần 08

Tích hợp Video trên App Mobile & Desktop (Flutter, React Native, Android, iOS, Desktop)

📱Cơ chế phát luồng HLS mã hóa AES-128 trên App Native

Các video xuất ra từ hệ thống sử dụng chuẩn mã hóa HLS AES-128 chuẩn quốc tế (RFC 8216). Các thư viện phát video native trên Android (ExoPlayer), iOS (AVPlayer), Flutter, React Native, và Desktop (LibVLC) đều hỗ trợ tự động giải mã 100%.

⚡ Yêu cầu cấu hình Header trên App: Khi nạp URL .m3u8 vào Video Player của App, hãy gắn kèm HTTP Header Origin:

Origin: https://native.hoathinh3d.cloud

(https://native.hoathinh3d.cloud là mã định danh bảo mật chuẩn mặc định của toàn hệ thống HoaThinh3D Cloud. Bạn có thể sử dụng trực tiếp mã này trên mọi nền tảng App Desktop & Mobile, hoặc thay bằng tên miền riêng đã đăng ký trong mục Cài đặt → Domain phát video (Playback Origins)).

🎧Quản lý Đa âm thanh (Vietsub & Thuyết Minh) trên App

File master.m3u8 chứa 2 luồng âm thanh độc lập với thẻ #EXT-X-MEDIA:TYPE=AUDIO:

Track 1: Vietsub (Âm thanh gốc)

Ngôn ngữ: und hoặc zh / Tên hiển thị: Vietsub

Track 2: Thuyết Minh (Tiếng Việt)

Ngôn ngữ: vi hoặc vie / Tên hiển thị: Thuyết Minh

Ví dụ tích hợp

Chọn ngôn ngữ để xem mã nguồn tương ứng.

Flutter (Dart / better_player)
// 1. Cài đặt package: better_player hoặc video_player
// 2. Khởi tạo nguồn phát HLS có bảo mật AES-128:
BetterPlayerDataSource dataSource = BetterPlayerDataSource(
  BetterPlayerDataSourceType.network,
  videoM3u8Url, // Link m3u8 lấy từ API
  headers: {
    // Mã định danh chuẩn mặc định của hệ thống
    "Origin": "https://native.hoathinh3d.cloud",
    "User-Agent": "YourApp/1.0",
  },
  videoFormat: BetterPlayerVideoFormat.hls,
  // Bật cache để xem mượt mà không giật lag
  cacheConfiguration: BetterPlayerCacheConfiguration(
    useCache: true,
    maxCacheSize: 100 * 1024 * 1024, // 100MB RAM/Disk
  ),
);

_betterPlayerController.setupDataSource(dataSource);

// 3. Đổi track âm thanh (Vietsub <-> Thuyết Minh):
// Track 0: Vietsub (Gốc) | Track 1: Thuyết Minh (Tiếng Việt)
_betterPlayerController.setAudioTrack(audioTrackIndex);
⚠️
Phần 09

Mã lỗi & Cách xử lý

HTTPCodeCách xử lý
400invalid_requestKiểm tra kiểu dữ liệu và giới hạn phân trang.
400invalid_jsonGửi body JSON hợp lệ và Content-Type đúng.
400video_not_readyVideo chưa render xong hoặc thiếu file Master HLS.
401missing_api_keyBổ sung Authorization header.
401invalid_api_keyKey sai, hết hiệu lực hoặc đã bị thu hồi.
401missing_playback_tokenThiếu token pt trên link phát khi CDN ở chế độ enforced.
401expired_playback_tokenToken đã hết hạn phát, backend cần gọi API tạo phiên mới.
403invalid_playback_tokenToken không đúng định dạng hoặc chữ ký HMAC-SHA256 không hợp lệ.
403video_scope_mismatchToken được cấp cho video khác, không thể dùng chéo video.
403origin_not_allowedThêm origin vào cài đặt hoặc chuyển request về backend.
404video_not_foundVideo không tồn tại hoặc không thuộc quyền sở hữu của API Key.
429rate_limit_exceededGiảm tần suất và thử lại ở phút kế tiếp.
error response
{"error":{"code":"invalid_api_key","message":"API key không hợp lệ."}}
🛡️
Phần 10

CORS & Vận hành an toàn

  • • Mỗi API key chỉ truy cập dữ liệu của đúng tài khoản sở hữu key.
  • • Có thể giới hạn origin và số request mỗi phút khi tạo key.
  • • Backend PHP/Node.js không cần gửi header Origin; trình duyệt thì phải khớp danh sách cho phép.
  • • Khi nghi ngờ key bị lộ, thu hồi key cũ và tạo key mới ngay trong tab Quản lý API Key.