Files
kr-phim/API_ophim.md
T

23 KiB
Raw Blame History

OPhim API — tài liệu chi tiết cho AI Agent / Antigravity / Codex

Nguồn tổng hợp từ trang API public của OPhim và các ví dụ endpoint public đang được cộng đồng sử dụng. Tài liệu này viết theo hướng để AI coding agent có thể đọc, hiểu nhanh và tự sinh code tích hợp.

1. Tổng quan

OPhim cung cấp API REST để lấy dữ liệu phim, danh sách phim, tìm kiếm, lọc theo thể loại/quốc gia/năm và lấy chi tiết phim.

Thông tin chung

Thuộc tính Giá trị
Base URL chính https://ophim1.com
Kiểu dữ liệu JSON
Encoding UTF-8
HTTP method GET
Auth Không thấy yêu cầu token ở docs public
Image CDN thường dùng https://img.ophim.live/uploads/movies/

Quy ước quan trọng

  • API trả về dữ liệu phim dạng JSON.
  • Dữ liệu danh sách thường có items hoặc data.items, tùy endpoint cũ/mới.
  • Ảnh poster/thumb thường chỉ trả về filename, ví dụ one-piece-thumb.jpg. Muốn hiển thị ảnh cần nối với pathImage hoặc CDN:
const imageUrl = `${pathImage}${movie.thumb_url}`;
// hoặc fallback
const imageUrl = `https://img.ophim.live/uploads/movies/${movie.thumb_url}`;
  • Danh sách phim chỉ nên dùng để lấy metadata cơ bản và slug.
  • Muốn lấy link xem phim / tập phim thì gọi chi tiết phim theo slug.
  • Không nên crawl toàn bộ detail của hàng chục nghìn phim cùng lúc. Nên phân trang, cache, debounce search, và chỉ gọi chi tiết khi user mở trang phim.

2. Hai nhóm endpoint cần biết

Hiện có 2 kiểu endpoint thường gặp:

2.1. Endpoint legacy

Các endpoint dạng ngắn:

GET https://ophim1.com/danh-sach/phim-moi-cap-nhat?page=1
GET https://ophim1.com/phim/{slug}

Endpoint legacy thường trả response dạng:

interface LegacyListResponse {
  status: boolean;
  items: MovieListItem[];
  pathImage: string;
  pagination: Pagination;
}

2.2. Endpoint v1

Các endpoint dạng /v1/api/...:

GET https://ophim1.com/v1/api/home
GET https://ophim1.com/v1/api/danh-sach/{type}?page=1
GET https://ophim1.com/v1/api/phim/{slug}

Endpoint v1 thường được cộng đồng dùng theo dạng:

interface V1Response<T> {
  status: boolean;
  msg?: string;
  data: T;
}

Trong code nên viết adapter để hỗ trợ cả 2 format nếu cần.


3. Kiểu dữ liệu TypeScript đề xuất

export interface Pagination {
  totalItems?: number;
  totalItemsPerPage?: number;
  currentPage?: number | null;
  totalPages?: number;
}

export interface TmdbInfo {
  type?: 'movie' | 'tv' | string;
  id?: string | number;
  season?: number | null;
  vote_average?: number;
  vote_count?: number;
}

export interface ImdbInfo {
  id?: string;
  vote_average?: number;
  vote_count?: number;
}

export interface MovieListItem {
  _id?: string;
  name: string;
  slug: string;
  origin_name?: string;
  thumb_url?: string;
  poster_url?: string;
  year?: number;
  modified?: {
    time?: string;
  };
  tmdb?: TmdbInfo;
  imdb?: ImdbInfo;
}

export interface CategoryItem {
  id?: string;
  name: string;
  slug: string;
}

export interface CountryItem {
  id?: string;
  name: string;
  slug: string;
}

export interface EpisodeItem {
  name: string;
  slug?: string;
  filename?: string;
  link_embed?: string;
  link_m3u8?: string;
}

export interface EpisodeServer {
  server_name: string;
  server_data: EpisodeItem[];
}

export interface MovieDetail {
  _id?: string;
  name: string;
  slug: string;
  origin_name?: string;
  content?: string;
  type?: string;
  status?: string;
  thumb_url?: string;
  poster_url?: string;
  trailer_url?: string;
  time?: string;
  episode_current?: string;
  episode_total?: string;
  quality?: string;
  lang?: string;
  year?: number;
  actor?: string[];
  director?: string[];
  category?: CategoryItem[];
  country?: CountryItem[];
}

export interface MovieDetailResponse {
  movie?: MovieDetail;
  item?: MovieDetail;
  episodes?: EpisodeServer[];
}

4. Endpoint chi tiết

4.1. Lấy phim mới cập nhật — legacy

Mục đích

Dùng để lấy danh sách phim mới cập nhật theo trang.

Request

GET /danh-sach/phim-moi-cap-nhat?page={page}

URL đầy đủ

https://ophim1.com/danh-sach/phim-moi-cap-nhat?page=1

Params

Param Kiểu Bắt buộc Mô tả
page number Không Trang cần lấy. Mặc định thường là 1.

Response shape thực tế

{
  "status": true,
  "items": [
    {
      "tmdb": {
        "type": "tv",
        "id": "37854",
        "season": 1,
        "vote_average": 8.7,
        "vote_count": 5216
      },
      "imdb": {
        "id": "tt0388629",
        "vote_average": 9,
        "vote_count": 341535
      },
      "modified": {
        "time": "2026-04-27T11:45:40.000Z"
      },
      "_id": "6220e66e8481266c5b7f154f",
      "name": "Đảo Hải Tặc",
      "origin_name": "One Piece (Luffy)",
      "thumb_url": "one-piece-thumb.jpg",
      "slug": "one-piece",
      "year": 1999,
      "poster_url": "one-piece-poster.jpg"
    }
  ],
  "pathImage": "https://img.ophim.live/uploads/movies/",
  "pagination": {
    "totalItems": 35382,
    "totalItemsPerPage": 24,
    "currentPage": 1,
    "totalPages": 1475
  }
}

Cách dùng

async function getLatestMovies(page = 1) {
  const res = await fetch(`https://ophim1.com/danh-sach/phim-moi-cap-nhat?page=${page}`);
  if (!res.ok) throw new Error(`OPhim error: ${res.status}`);
  return res.json() as Promise<LegacyListResponse>;
}

Ghi chú cho AI agent

  • Dùng endpoint này để render trang “Mới cập nhật”.
  • Mỗi item có slug, dùng slug để gọi chi tiết phim.
  • Ảnh lấy bằng pathImage + thumb_url hoặc pathImage + poster_url.

4.2. Lấy danh sách phim — v1

Mục đích

Dùng để lấy phim theo loại danh sách: phim mới cập nhật, phim lẻ, phim bộ, phim đang chiếu, phim hoàn thành, phim vietsub, thuyết minh, lồng tiếng, sắp chiếu...

Request

GET /v1/api/danh-sach/{type}?page={page}

URL đầy đủ

https://ophim1.com/v1/api/danh-sach/phim-moi-cap-nhat?page=1

Path params

Param Kiểu Bắt buộc Ví dụ Mô tả
type string phim-moi-cap-nhat Loại danh sách phim cần lấy.

Một số type thường dùng

phim-moi-cap-nhat
phim-le
phim-bo
phim-bo-dang-chieu
phim-bo-hoan-thanh
phim-sap-chieu
phim-vietsub
phim-thuyet-minh
phim-long-tieng
hoat-hinh
tv-shows
subteam

Lưu ý: một số type có thể thay đổi theo server. Nếu 404 hoặc empty data thì cần test lại slug danh sách từ website/menu.

Query params thường dùng

Param Kiểu Bắt buộc Ví dụ Mô tả
page number Không 1 Phân trang.
sort_field string Không _id, modified.time, year Field sort. Tùy server hỗ trợ.
sort_type string Không desc, asc Kiểu sắp xếp.
sort_lang string Không vietsub, thuyet-minh, long-tieng Lọc theo ngôn ngữ/phụ đề nếu server hỗ trợ.
category string Không hanh-dong Lọc theo thể loại.
genre string Không hanh-dong Một số ví dụ cộng đồng dùng genre; nếu category không chạy có thể thử genre.
country string Không han-quoc Lọc theo quốc gia.
year number/string Không 2024 Lọc theo năm.
limit number Không 24 Số item/trang, nếu server hỗ trợ.

Response shape dự kiến

interface V1MovieListData {
  seoOnPage?: unknown;
  breadCrumb?: unknown[];
  titlePage?: string;
  items: MovieListItem[];
  params?: unknown;
  type_list?: string;
  APP_DOMAIN_FRONTEND?: string;
  APP_DOMAIN_CDN_IMAGE?: string;
  pagination: Pagination;
}

interface V1MovieListResponse {
  status: boolean;
  msg?: string;
  data: V1MovieListData;
}

Cách dùng

async function getMoviesByList(type: string, page = 1) {
  const url = new URL(`https://ophim1.com/v1/api/danh-sach/${type}`);
  url.searchParams.set('page', String(page));

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error(`OPhim error: ${res.status}`);

  const json = await res.json();
  return json.data?.items ?? [];
}

Ví dụ có filter

async function getFilteredMovies() {
  const url = new URL('https://ophim1.com/v1/api/danh-sach/phim-moi-cap-nhat');
  url.searchParams.set('page', '1');
  url.searchParams.set('category', 'hanh-dong');
  url.searchParams.set('country', 'han-quoc');
  url.searchParams.set('year', '2024');

  const res = await fetch(url.toString());
  return res.json();
}

4.3. Tìm kiếm phim

Mục đích

Tìm phim theo từ khóa.

Request

GET /v1/api/tim-kiem?keyword={keyword}&page={page}

URL đầy đủ

https://ophim1.com/v1/api/tim-kiem?keyword=one%20piece&page=1

Params

Param Kiểu Bắt buộc Ví dụ Mô tả
keyword string one piece Từ khóa tìm kiếm. Cần encode URL.
page number Không 1 Trang kết quả.

Cách dùng

async function searchMovies(keyword: string, page = 1) {
  const url = new URL('https://ophim1.com/v1/api/tim-kiem');
  url.searchParams.set('keyword', keyword);
  url.searchParams.set('page', String(page));

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error(`OPhim search error: ${res.status}`);

  const json = await res.json();
  return json.data?.items ?? [];
}

Ghi chú cho AI agent

  • Luôn dùng URLSearchParams, không tự nối chuỗi keyword trực tiếp.
  • Nên debounce search ở frontend khoảng 300500ms.
  • Không gọi search mỗi lần user gõ một ký tự nếu chưa debounce.

4.4. Lấy danh sách thể loại

Mục đích

Lấy toàn bộ thể loại để render dropdown/menu filter.

Request

GET /v1/api/the-loai

URL đầy đủ

https://ophim1.com/v1/api/the-loai

Response shape dự kiến

interface CategoryListResponse {
  status: boolean;
  data: {
    items: CategoryItem[];
  };
}

Item dự kiến

{
  "name": "Hành Động",
  "slug": "hanh-dong"
}

Cách dùng

async function getCategories() {
  const res = await fetch('https://ophim1.com/v1/api/the-loai');
  if (!res.ok) throw new Error(`OPhim category error: ${res.status}`);

  const json = await res.json();
  return Array.isArray(json.data?.items) ? json.data.items : [];
}

4.5. Lấy phim theo thể loại

Mục đích

Lấy danh sách phim thuộc một thể loại cụ thể.

Request

GET /v1/api/the-loai/{slug}?page={page}

URL đầy đủ

https://ophim1.com/v1/api/the-loai/hanh-dong?page=1

Path params

Param Kiểu Bắt buộc Ví dụ Mô tả
slug string hanh-dong Slug thể loại lấy từ endpoint /v1/api/the-loai.

Query params

Param Kiểu Bắt buộc Mô tả
page number Không Phân trang.

Cách dùng

async function getMoviesByCategory(categorySlug: string, page = 1) {
  const url = new URL(`https://ophim1.com/v1/api/the-loai/${categorySlug}`);
  url.searchParams.set('page', String(page));

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error(`OPhim category movies error: ${res.status}`);

  const json = await res.json();
  return json.data?.items ?? [];
}

4.6. Lấy danh sách quốc gia

Mục đích

Lấy toàn bộ quốc gia để render dropdown/menu filter.

Request

GET /v1/api/quoc-gia

URL đầy đủ

https://ophim1.com/v1/api/quoc-gia

Response shape dự kiến

interface CountryListResponse {
  status: boolean;
  data: {
    items: CountryItem[];
  };
}

Item dự kiến

{
  "name": "Hàn Quốc",
  "slug": "han-quoc"
}

Cách dùng

async function getCountries() {
  const res = await fetch('https://ophim1.com/v1/api/quoc-gia');
  if (!res.ok) throw new Error(`OPhim country error: ${res.status}`);

  const json = await res.json();
  return Array.isArray(json.data?.items) ? json.data.items : [];
}

4.7. Lấy phim theo quốc gia

Mục đích

Lấy danh sách phim thuộc một quốc gia cụ thể.

Request

GET /v1/api/quoc-gia/{slug}?page={page}

URL đầy đủ

https://ophim1.com/v1/api/quoc-gia/han-quoc?page=1

Path params

Param Kiểu Bắt buộc Ví dụ Mô tả
slug string han-quoc Slug quốc gia lấy từ endpoint /v1/api/quoc-gia.

Cách dùng

async function getMoviesByCountry(countrySlug: string, page = 1) {
  const url = new URL(`https://ophim1.com/v1/api/quoc-gia/${countrySlug}`);
  url.searchParams.set('page', String(page));

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error(`OPhim country movies error: ${res.status}`);

  const json = await res.json();
  return json.data?.items ?? [];
}

4.8. Lấy phim theo năm phát hành

Mục đích

Lấy danh sách phim theo năm phát hành.

Request

GET /v1/api/nam/{year}?page={page}

URL đầy đủ

https://ophim1.com/v1/api/nam/2024?page=1

Path params

Param Kiểu Bắt buộc Ví dụ Mô tả
year number/string 2024 Năm phát hành.

Query params

Param Kiểu Bắt buộc Mô tả
page number Không Phân trang.

Cách dùng

async function getMoviesByYear(year: number, page = 1) {
  const url = new URL(`https://ophim1.com/v1/api/nam/${year}`);
  url.searchParams.set('page', String(page));

  const res = await fetch(url.toString());
  if (!res.ok) throw new Error(`OPhim year movies error: ${res.status}`);

  const json = await res.json();
  return json.data?.items ?? [];
}

Nếu endpoint /v1/api/nam/{year} không chạy, thử kiểm tra lại docs/API Postman hiện tại vì trang docs gọi nhóm này là “Năm Phát Hành”, nhưng HTML public không expose path chi tiết.


4.9. Lấy chi tiết phim — legacy

Mục đích

Lấy thông tin đầy đủ của một phim, bao gồm mô tả, diễn viên, đạo diễn, thể loại, quốc gia và danh sách tập/link xem.

Request

GET /phim/{slug}

URL đầy đủ

https://ophim1.com/phim/one-piece

Path params

Param Kiểu Bắt buộc Ví dụ Mô tả
slug string one-piece Slug lấy từ item danh sách hoặc kết quả search.

Response shape thường gặp

interface LegacyMovieDetailResponse {
  status: boolean;
  movie: MovieDetail;
  episodes: EpisodeServer[];
}

Cách dùng

async function getMovieDetailLegacy(slug: string) {
  const res = await fetch(`https://ophim1.com/phim/${slug}`);
  if (!res.ok) throw new Error(`OPhim movie detail error: ${res.status}`);

  const json = await res.json();

  return {
    movie: json.movie,
    episodes: json.episodes ?? [],
  };
}
const detail = await getMovieDetailLegacy('one-piece');

const firstServer = detail.episodes[0];
const firstEpisode = firstServer?.server_data?.[0];

console.log(firstEpisode?.link_embed);
console.log(firstEpisode?.link_m3u8);

Ghi chú cho AI agent

  • episodes là mảng server.
  • Mỗi server có server_nameserver_data.
  • server_data là danh sách tập.
  • Mỗi tập có thể có link_embed và/hoặc link_m3u8.
  • Khi render player, ưu tiên link_embed nếu dùng iframe, hoặc link_m3u8 nếu dùng HLS player.

4.10. Lấy chi tiết phim — v1

Mục đích

Tương tự legacy detail nhưng theo format v1.

Request

GET /v1/api/phim/{slug}

URL đầy đủ

https://ophim1.com/v1/api/phim/one-piece

Response shape dự kiến

interface V1MovieDetailData {
  seoOnPage?: unknown;
  breadCrumb?: unknown[];
  params?: unknown;
  item: MovieDetail;
  episodes: EpisodeServer[];
  APP_DOMAIN_CDN_IMAGE?: string;
}

interface V1MovieDetailResponse {
  status: boolean;
  msg?: string;
  data: V1MovieDetailData;
}

Cách dùng

async function getMovieDetailV1(slug: string) {
  const res = await fetch(`https://ophim1.com/v1/api/phim/${slug}`);
  if (!res.ok) throw new Error(`OPhim v1 movie detail error: ${res.status}`);

  const json = await res.json();

  return {
    movie: json.data?.item,
    episodes: json.data?.episodes ?? [],
    imageDomain: json.data?.APP_DOMAIN_CDN_IMAGE,
  };
}

5. Adapter chuẩn hóa response

Nên viết adapter để code không phụ thuộc quá cứng vào legacy hoặc v1.

export function normalizeMovieListResponse(json: any) {
  const items = json?.items ?? json?.data?.items ?? [];
  const pagination = json?.pagination ?? json?.data?.pagination ?? null;
  const pathImage =
    json?.pathImage ??
    json?.data?.APP_DOMAIN_CDN_IMAGE ??
    'https://img.ophim.live/uploads/movies/';

  return {
    status: Boolean(json?.status),
    items,
    pagination,
    pathImage,
  };
}

export function normalizeMovieDetailResponse(json: any) {
  const movie = json?.movie ?? json?.data?.item ?? json?.item ?? null;
  const episodes = json?.episodes ?? json?.data?.episodes ?? [];
  const pathImage =
    json?.pathImage ??
    json?.data?.APP_DOMAIN_CDN_IMAGE ??
    'https://img.ophim.live/uploads/movies/';

  return {
    status: Boolean(json?.status),
    movie,
    episodes,
    pathImage,
  };
}

6. Service TypeScript hoàn chỉnh

const OPHIM_BASE_URL = 'https://ophim1.com';
const OPHIM_IMAGE_FALLBACK = 'https://img.ophim.live/uploads/movies/';

async function requestJson<T>(url: string): Promise<T> {
  const res = await fetch(url, {
    method: 'GET',
    headers: {
      Accept: 'application/json',
    },
  });

  if (!res.ok) {
    throw new Error(`OPhim API error ${res.status}: ${res.statusText}`);
  }

  return res.json() as Promise<T>;
}

export function getMovieImageUrl(pathImage: string | undefined, fileName?: string) {
  if (!fileName) return '';
  return `${pathImage || OPHIM_IMAGE_FALLBACK}${fileName}`;
}

export async function getLatestMovies(page = 1) {
  const url = `${OPHIM_BASE_URL}/danh-sach/phim-moi-cap-nhat?page=${page}`;
  const json = await requestJson<any>(url);
  return normalizeMovieListResponse(json);
}

export async function getMovieDetail(slug: string) {
  const url = `${OPHIM_BASE_URL}/phim/${encodeURIComponent(slug)}`;
  const json = await requestJson<any>(url);
  return normalizeMovieDetailResponse(json);
}

export async function searchMovies(keyword: string, page = 1) {
  const url = new URL(`${OPHIM_BASE_URL}/v1/api/tim-kiem`);
  url.searchParams.set('keyword', keyword);
  url.searchParams.set('page', String(page));

  const json = await requestJson<any>(url.toString());
  return normalizeMovieListResponse(json);
}

export async function getCategories() {
  const json = await requestJson<any>(`${OPHIM_BASE_URL}/v1/api/the-loai`);
  return json?.data?.items ?? [];
}

export async function getCountries() {
  const json = await requestJson<any>(`${OPHIM_BASE_URL}/v1/api/quoc-gia`);
  return json?.data?.items ?? [];
}

7. Luồng tích hợp frontend đề xuất

Trang danh sách phim mới

  1. Gọi getLatestMovies(page).
  2. Render items.
  3. Ảnh: pathImage + item.thumb_url.
  4. Khi user click phim, navigate tới /movie/{slug}.

Trang chi tiết phim

  1. Lấy slug từ route.
  2. Gọi getMovieDetail(slug).
  3. Render movie.name, movie.content, movie.category, movie.country, movie.actor, movie.director.
  4. Render danh sách episode từ episodes.
  5. Khi user chọn tập:
    • Nếu dùng iframe: set src = episode.link_embed.
    • Nếu dùng HLS: set player source = episode.link_m3u8.

Trang tìm kiếm

  1. User nhập keyword.
  2. Debounce 300500ms.
  3. Gọi searchMovies(keyword, page).
  4. Render items giống trang list.

8. Prompt ngắn cho Antigravity / Codex

Có thể đưa đoạn này cho AI coding agent:

Build a movie browsing integration using OPhim API.

Base URL: https://ophim1.com
Data format: JSON, UTF-8, method GET.

Use these endpoints:
- Latest movies: GET /danh-sach/phim-moi-cap-nhat?page={page}
- Movie detail: GET /phim/{slug}
- Search: GET /v1/api/tim-kiem?keyword={keyword}&page={page}
- Categories: GET /v1/api/the-loai
- Countries: GET /v1/api/quoc-gia
- List by type: GET /v1/api/danh-sach/{type}?page={page}
- Movies by category: GET /v1/api/the-loai/{slug}?page={page}
- Movies by country: GET /v1/api/quoc-gia/{slug}?page={page}
- Movies by year: GET /v1/api/nam/{year}?page={page}

Important response rules:
- Legacy list response has: status, items, pathImage, pagination.
- v1 response usually has: status, msg, data.
- v1 list items are usually in data.items.
- Movie detail usually has movie/item and episodes.
- Use movie.slug from list/search to call detail.
- To render images, concatenate pathImage or CDN image domain with thumb_url/poster_url.
- Image fallback CDN: https://img.ophim.live/uploads/movies/
- Episode links are usually in episodes[].server_data[].link_embed or link_m3u8.

Implementation requirements:
- Create an API service module.
- Add safe fetch wrapper with error handling.
- Normalize legacy and v1 responses.
- Do not crawl all detail pages at once.
- Add pagination support.
- Add debounce for search.

9. Lưu ý production

  • API public có thể thay đổi format/path, nên code cần defensive parsing.
  • Cần xử lý lỗi mạng, response rỗng, item thiếu ảnh, item thiếu slug.
  • Nên cache danh sách thể loại/quốc gia vì ít thay đổi.
  • Nên cache detail phim sau khi user mở phim.
  • Với website thật, không nên phụ thuộc 100% vào API public. Có thể lưu dữ liệu về database riêng để giảm rủi ro API đổi/chậm/lỗi.
  • Nếu dùng link_m3u8, cần kiểm tra CORS/player support.
  • Nếu dùng iframe link_embed, cần kiểm tra domain có cho embed không.

10. Checklist cho AI agent khi code

  • Có file ophim.service.ts hoặc tương đương.
  • requestJson() xử lý lỗi HTTP.
  • normalizeMovieListResponse().
  • normalizeMovieDetailResponse().
  • Có hàm getLatestMovies(page).
  • Có hàm getMovieDetail(slug).
  • Có hàm searchMovies(keyword, page).
  • Có hàm getCategories().
  • Có hàm getCountries().
  • UI danh sách dùng movie.slug để route detail.
  • UI ảnh dùng pathImage + thumb_url hoặc fallback CDN.
  • UI episode đọc từ episodes[].server_data[].
  • Không fetch detail hàng loạt cho toàn bộ list.