Podcast — Post site phụ (secondary / virtual)#

Worker site phụ import RSS feed từ site chính lên các platform podcast khác (anchor.fm, spotify.com, pocketcasts.com, …). Site phụ KHÔNG có row trong podcast_post_link lúc pull — chỉ là virtual task synthesize từ podcast_account.

Base URL: https://manager.likepion.com/api/v1/podcast

Filter param: tất cả GET /podcast/links ở mục này dùng ?type=secondary để chỉ trả site phụ.


1. Lấy danh sách task site phụ #

curl -X GET 'https://manager.likepion.com/api/v1/podcast/links?type=secondary&limit=5' \
  -H 'X-API-Key: <api_key>'
ParamMặc địnhMô tả
type(không)secondary để chỉ trả task site phụ. Bỏ trống → trả cả 2
limit5Số task (max 1000)

Claim guard 15 phút: ngay khi virtual task được trả, BE auto set podcast_account.posting_started_at = NOW(). Worker khác pull tiếp KHÔNG nhận lại account đó trong 15p. Worker xong gọi /insert → BE auto clear. Worker crash → 15p sau tự eligible lại.

Response#

Site phụ = virtual task không có row trong DB. id = account_id. request_id = post_id của post chính đang chạy.

{
  "success": true,
  "data": [
    {
      "id": "uuid-account",
      "request_id": "uuid-post",
      "type_request": "post",
      "status": "queued",
      "account_id": "uuid-account",
      "group_id": "uuid",
      "target": 100,
      "accounts_remaining": 8,
      "domain": "anchor.fm",
      "website": "https://likepion.com",
      "email": "pod.real@anchor.fm",
      "username": "realpoduser",
      "password": "realPwd!",
      "pass_mail": "xxxx xxxx xxxx xxxx",
      "twofa": "JBSWY3DPEHPK3PXP",
      "app_password": "xxxx xxxx xxxx xxxx",
      "link_rss": "https://anchor.fm/s/xxxxx/podcast/rss",
      "cookie": "session=abc123"
    }
  ]
}

Fields#

FieldMô tả
idVirtual task ID = account_id
request_idUUID podcast_post.id đang chạy cùng group
type_request"post"
statusStatus virtual task: luôn "queued" (chưa có row, đợi worker import RSS)
account_idUUID account site phụ (= id)
group_idUUID group
targetSố tập (= podcast_post.target)
accounts_remainingSố account site phụ trong group chưa có task post
link_rssURL RSS feed của site chính → worker import vào site phụ. BẮT BUỘC site chính phải xong trước, gọi /account set link_rss
email/username/password/…Credentials account site phụ
cookieSession cookie (cuối)

2. Claim explicit account — /podcast/accounts/claim #

Worker chủ động claim 1 hoặc nhiều account để block worker khác trong 15p. BE set posting_started_at = NOW().

Khi nào dùng?

  • Sau pull virtual task → re-affirm claim (BE đã auto set, gọi này extend TTL).
  • Worker xử lý dài > 15p → định kỳ gọi để refresh (tránh stale).
  • Claim batch trước khi xử lý.

Idempotent — gọi nhiều lần với cùng account chỉ refresh timestamp.

# Single
curl -X POST 'https://manager.likepion.com/api/v1/podcast/accounts/claim' \
  -H 'X-API-Key: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"account_id": "uuid-account"}'

# Bulk (max 200)
curl -X POST 'https://manager.likepion.com/api/v1/podcast/accounts/claim' \
  -H 'X-API-Key: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{"account_ids": ["uuid1","uuid2","uuid3"]}'
FieldBắt buộcMô tả
account_id✔*UUID 1 account
account_ids✔*Mảng UUID account (max 200)

* phải có ít nhất 1 trong 2.

Response:

{
  "success": true,
  "claimed": ["uuid1", "uuid2"],
  "count": 2,
  "ttl_min": 15,
  "not_found": ["uuid3"]
}

Worker import RSS xong → gọi endpoint này tạo record podcast_post_link với link_post sẵn. BE auto clear posting_started_at (nhả claim).

Format: 1 input item = 1 site (domain) × N URL (link_post chuỗi phân tách bởi ,) → BE expand thành N record.

Hỗ trợ:

  • Single object: 1 site, N link_post → N record.
  • Bulk array [{...},{...}] hoặc {"items": [...]}: nhiều site, max 200 record sau expand.

Resolve account 2 cách:

  1. Theo domain (khuyến nghị): BE lookup account trong group có site_name = domain.
  2. Theo account_id: explicit UUID.

KHÔNG dedupe: 1 account có thể có N row trong cùng post (vd N episode trên cùng platform). Mỗi URL trong link_post = 1 row mới, kể cả cùng (post_id, account_id). Worker tự chịu trách nhiệm không gửi cùng URL 2 lần.

Format theo domain#

curl -X POST 'https://manager.likepion.com/api/v1/podcast/links/insert' \
  -H 'X-API-Key: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '{
    "request_id": "uuid-post-request",
    "domain": "anchor.fm",
    "status": "completed",
    "link_post": "https://anchor.fm/ep-1,https://anchor.fm/ep-2,https://anchor.fm/ep-3",
    "cookie": "session=abc123"
  }'

→ Resolve account = account đầu tiên trong group có site_name = "anchor.fm". Insert 3 record.

Bulk#

curl -X POST 'https://manager.likepion.com/api/v1/podcast/links/insert' \
  -H 'X-API-Key: <api_key>' \
  -H 'Content-Type: application/json' \
  -d '[
    { "request_id": "uuid-post", "domain": "anchor.fm",   "status": "completed", "link_post": "https://anchor.fm/ep-1,https://anchor.fm/ep-2" },
    { "request_id": "uuid-post", "domain": "spotify.com", "status": "completed", "link_post": "https://open.spotify.com/episode/abc" }
  ]'

Body fields#

FieldBắt buộcMô tả
request_id (alias post_id)UUID podcast_post.id
domain✔*Site name. BE lookup account trong group
account_id✔*UUID account explicit (skip domain lookup)
statustuỳ chọn"completed" (default) / "failed" / "cancel"
link_post1 URL hoặc nhiều URL phân tách bởi ,. Mỗi URL = 1 record
cookie/email/password/…tuỳ chọnCredentials → propagate sang podcast_account (1 lần per account)
notetuỳ chọnGhi chú

* phải có 1 trong domain hoặc account_id.

Response#

{
  "success": true,
  "input": 2,
  "count": 3,
  "inserted": 3,
  "failed": 0,
  "results": [
    { "index": 0, "link_index": 0, "post_id": "uuid", "account_id": "uuid-anchor", "domain": "anchor.fm", "link_post": "https://anchor.fm/ep-1", "success": true },
    { "index": 0, "link_index": 1, "post_id": "uuid", "account_id": "uuid-anchor", "domain": "anchor.fm", "link_post": "https://anchor.fm/ep-2", "success": true },
    { "index": 1, "link_index": 0, "post_id": "uuid", "account_id": "uuid-spotify", "domain": "spotify.com", "link_post": "https://open.spotify.com/episode/abc", "success": true }
  ]
}

4. Lấy chi tiết account — GET /podcast/accounts/:id #

Trả 1 account dưới shape giống virtual secondary task (mục 1). Worker pull task xong giữ account_id, sau này cần refetch (token expired, retry import RSS, sync state) gọi endpoint này → nhận lại JSON cùng cấu trúc, plug-and-play.

Khác GET /podcast/links?type=secondary:

  • Không áp claim guard 15p — luôn trả dù worker khác vừa pull.
  • Không filter status (active/completed) — trả cả account đang failed/banned
  • Không set posting_started_at.

Các field request_id / target / accounts_remaining / link_rss tham chiếu podcast_post mới nhất cùng group (status pending/running). Nếu group không có post active → request_id="", target=0, accounts_remaining=0, link_rss="".

curl -X GET 'https://manager.likepion.com/api/v1/podcast/accounts/<account_id>' \
  -H 'X-API-Key: <api_key>'

Response#

{
  "success": true,
  "data": {
    "id": "uuid-account",
    "request_id": "uuid-post",
    "type_request": "post",
    "status": "queued",
    "account_id": "uuid-account",
    "group_id": "uuid-group",
    "target": 20,
    "accounts_remaining": 10,
    "domain": "anchor.fm",
    "website": "https://likepion.com",
    "email": "pod.real@anchor.fm",
    "username": "realpoduser",
    "password": "realPwd!",
    "pass_mail": "xxxx xxxx xxxx xxxx",
    "twofa": "JBSWY3DPEHPK3PXP",
    "app_password": "xxxx xxxx xxxx xxxx",
    "link_rss": "https://anchor.fm/s/xxxxx/podcast/rss",
    "cookie": "session=abc123; csrf=xyz"
  }
}

Fields#

Tham chiếu mục 1 — schema y hệt secondary task.