Tài liệu API XuniBOX

API chỉ đọc để lấy danh sách nhạc, nghệ sĩ và thể loại từ kho XuniBOX. Mỗi chủ đề dưới đây là một trang riêng, có đường dẫn riêng để bạn chia sẻ cho đội kỹ thuật.

Metadata âm thanh & sóng âm

Mỗi bài nhạc được phân tích một lần lúc admin đăng lên, kết quả lưu sẵn trong cơ sở dữ liệu. Nhờ vậy bạn dựng được sóng âm và ghép nhạc theo nhịp mà không phải tải file nhạc về để tự phân tích — tiết kiệm băng thông và CPU của cả hai bên.

GET/tracks/:id/waveform

Mảng peaks sóng âm ở số cột tuỳ chọn, kèm khối analysis.

pointsintegerSố cột sóng âm cần lấy, từ 16 đến 512, mặc định 120. Server tự gộp trung bình để đúng số cột bạn yêu cầu.

Ví dụ gọi

Request
curl --request GET \
  --url 'https://xunibox.vn/api/public/tracks/550e8400-e29b-41d4-a716-446655440000/waveform?points=64' \
  --header 'X-API-Key: xnb_live_your_key'
Response
HTTP/1.1 200 OK
Cache-Control: public, max-age=300, s-maxage=3600, stale-while-revalidate=86400
ETag: "9c1f0a72"

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "duration": "3:24",
  "points": 64,
  "scale": "0-100",
  "peaks": [4, 11, 28, 46, 63, 71, 68, 55, 41, 33, 29, 38, 57, 74, 88, 92, 85, 70, 52, 40, "…"],
  "analysis": {
    "bpm": 128,
    "musical_key": "A minor",
    "energy": 0.74,
    "loudness_db": -8.3
  }
}
Sóng âm gần như không đổi sau khi bài được đăng, nên endpoint này cache rất lâu và hỗ trợ ETag. Gửi kèm If-None-Match để nhận 304 và không tốn băng thông.

Các trường phân tích

TrườngKiểu / khoảngÝ nghĩa & cách dùng
peaksnumber[] thang 0–100Biên độ trung bình từng đoạn. Chia cho 100 rồi nhân chiều cao khung để vẽ cột.
pointsintegerSố phần tử thực tế trong peaks. Bằng 0 nếu bài chưa được phân tích sóng âm.
scalestringLuôn là "0-100" ở phiên bản v1. Đọc trường này thay vì hardcode để an toàn với v2.
durationstringThời lượng bản đầy đủ dạng m:ss. Lưu ý đoạn preview ngắn hơn giá trị này.
analysis.bpmnumber | nullSố nhịp mỗi phút. Dùng để khớp nhịp cắt cảnh video.
analysis.musical_keystring | nullTông nhạc, ví dụ "A minor". Hữu ích khi nối nhiều bài trong một video.
analysis.energynumber 0–1 | nullMức sôi động. Dưới 0.35 là chill, trên 0.7 là mạnh.
analysis.loudness_dbnumber | nullĐộ to trung bình tính bằng dB. Dùng để chuẩn hoá âm lượng khi trộn với tiếng gốc.
Mọi trường trong analysis đều có thể là null với bài cũ đăng trước khi hệ thống phân tích được bật. Luôn kiểm tra null trước khi tính toán, đừng giả định lúc nào cũng có BPM.

Vẽ sóng âm bằng canvas

JavaScript — canvas 2D
// Vẽ sóng âm bằng canvas từ /tracks/:id/waveform — không cần tải file nhạc.
async function drawWaveform(canvas, trackId, points = 120) {
  const { peaks, analysis, duration } = await fetch(
    `/api/xunibox/tracks/${trackId}/waveform?points=${points}`,
  ).then((r) => r.json());

  const ctx = canvas.getContext("2d");
  const width = canvas.width;
  const height = canvas.height;
  const barWidth = width / peaks.length;

  ctx.clearRect(0, 0, width, height);
  peaks.forEach((peak, index) => {           // peak nằm trong thang 0-100
    const barHeight = Math.max(2, (peak / 100) * height);
    const gradient = ctx.createLinearGradient(0, 0, 0, height);
    gradient.addColorStop(0, "#ff4d2d");
    gradient.addColorStop(1, "#ff9425");
    ctx.fillStyle = gradient;
    ctx.fillRect(index * barWidth, (height - barHeight) / 2, barWidth * 0.7, barHeight);
  });

  console.log(duration, analysis.bpm, analysis.musical_key); // "3:24", 128, "A minor"
}

Ghép nhạc theo nhịp và cảm xúc

Đây là phần tạo khác biệt cho một bộ chọn nhạc: thay vì bắt người dùng nghe thử hàng chục bài, hãy gợi ý sẵn theo độ dài video và không khí họ muốn.

JavaScript — pickByTempo
// Ghép nhạc theo nhịp: chọn bài có BPM khớp độ dài cảnh quay.
// Ví dụ video 15 giây, muốn cắt cảnh 4 lần đều nhau => 1 cảnh / 3.75s
// => BPM lý tưởng = 60 / 3.75 * 4 ≈ 64, hoặc bội số 128.

function pickByTempo(tracks, targetBpm, tolerance = 6) {
  return tracks
    .filter((track) => track.bpm != null)
    .map((track) => {
      // Chấp nhận cả bội số / ước số: 64 và 128 cảm giác cùng nhịp.
      const candidates = [track.bpm, track.bpm / 2, track.bpm * 2];
      const distance = Math.min(...candidates.map((bpm) => Math.abs(bpm - targetBpm)));
      return { track, distance };
    })
    .filter((item) => item.distance <= tolerance)
    .sort((a, b) => a.distance - b.distance)
    .map((item) => item.track);
}

// Ghép theo cảm xúc: energy 0-1, càng cao càng sôi động.
const chill  = tracks.filter((t) => (t.energy ?? 0) < 0.35);
const hype   = tracks.filter((t) => (t.energy ?? 0) > 0.7);

// Chuẩn hoá âm lượng khi trộn nhạc nền với tiếng gốc của video:
// gain_dB = mục_tiêu - loudness_db, ví dụ mục tiêu -14 LUFS.
const gainDb = -14 - (track.loudness_db ?? -14);
Mục tiêu của người dùngBộ lọc gợi ýNhãn hiển thị
Vlog đời thườngenergy 0.25–0.55, bpm 80–110Nhẹ nhàng
Chuyển cảnh nhanh, nhảyenergy > 0.7, bpm 120–140Sôi động
Nội dung cảm xúc, kể chuyệnenergy < 0.35, musical_key chứa 'minor'Sâu lắng
Quảng cáo sản phẩmenergy 0.5–0.75, commercial_use_allowed = trueThương hiệu

Sẵn sàng tích hợp?

Gửi thông tin sản phẩm để admin XuniBOX xét duyệt và cấp API key.

Xin quyền tích hợp