Open API, เว็บฮุกและ Embed player
สำหรับนักพัฒนาที่ต้องการนำข้อมูลสถานีไปแสดงในเว็บไซต์ แอป หรือระบบอื่น
ภาพรวม
- เอกสารแบบเครื่องอ่านได้: /api/v1/openapi.json (OpenAPI 3.1) ไม่ต้องใช้คีย์
- ข้อมูลสาธารณะไม่ต้องใช้คีย์ ข้อมูลส่วนตัวและการควบคุมต้องใช้คีย์ API ของสถานี
- เวลาเป็น ISO 8601 แบบ UTC
/api/v1/ตอบทุกโดเมน (เปิด CORS) และรับGET,POSTและOPTIONS- การจำกัดจำนวนคำขอนับตามที่อยู่ IP ถ้าเกิน ระบบตอบ
429 {slug}คือชื่อย่อสถานี แบบเดียวกับใน/s/{slug}สถานีที่ไม่มีหรือถูกระงับตอบ404พร้อม{"error":"not_found"}
ข้อมูลสาธารณะ (ไม่ต้องใช้คีย์)
| คำขอ | ได้อะไร | จำกัด |
|---|---|---|
GET /api/v1/stations |
สารบบสถานี: items แต่ละรายการมีข้อมูลสถานี on_air และ now_playing |
300 ครั้งต่อนาที |
GET /api/v1/stations/{slug} |
ข้อมูลสถานีและลิงก์สตรีม | 300 ครั้งต่อนาที |
GET /api/v1/stations/{slug}/nowplaying |
สิ่งที่กำลังออกอากาศ | 300 ครั้งต่อนาที |
GET /api/v1/stations/{slug}/schedule |
ผังรายการล่วงหน้า | 300 ครั้งต่อนาที |
GET /api/v1/stations/{slug}/history |
เพลงที่เพิ่งเล่น | 300 ครั้งต่อนาที |
GET /api/v1/stations/{slug}/episodes |
ตอนฟังย้อนหลังที่เผยแพร่แล้ว | 300 ครั้งต่อนาที |
รายละเอียด
- stations: กรองด้วย
q,genre,province,language(thหรือen) การค้นหาเข้าใจภาษาไทยที่ไม่ใส่วรรณยุกต์ ข้อความที่พิมพ์ผิดแป้นภาษา และภาษาไทยที่เขียนเป็นอักษรโรมัน แสดงเฉพาะสถานีที่อยู่ในสารบบ - ข้อมูลสถานี:
slug,name,tagline,language,timezone,genre,province,url,logo(url,alt) หรือ null,embed_urlและstreamsสตรีม: มีmp3เสมอ มีaac48 kbps เมื่อสถานีเปิดสตรีมประหยัดเน็ต มีhlsเมื่อเปิด HLS - nowplaying:
on_air,source(live,autodj,scheduled,fallbackหรือoffline),now_playing(title,artist,source,started_at,show),show(name,host,starts_at,ends_at) หรือ null,listeners,capacity(null = ไม่จำกัด) และalerts(ประกาศฉุกเฉินที่ใช้งานอยู่:id,severity,title,message,instruction,language,expires_at) - schedule:
daysตั้งแต่ 1 ถึง 31 ค่าเริ่มต้น 7 แต่ละรายการมีshow,host,kind(live,prerecorded,playlist),starts_at,ends_at - history:
limitตั้งแต่ 1 ถึง 50 ค่าเริ่มต้น 10 แต่ละรายการมีtitle,artist,source,started_at - episodes: 50 ตอนล่าสุดที่เผยแพร่แล้ว:
id,title,show,published_at,duration_s,url,audio_url,transcript_urlโดยtranscript_urlเป็นไฟล์ WebVTT หรือเป็น null จนกว่าทีมงานสถานีจะอนุมัติบทถอดเสียง
ข้อมูลส่วนตัวและการควบคุม (ต้องใช้คีย์)
ผู้ดูแลสถานีสร้างคีย์ได้ที่ จัดการสถานี → เชื่อมต่อบริการอื่น → คีย์ Open API เลือกสิทธิ์ read (เลือกไว้ให้แล้ว) และ control คีย์ขึ้นต้นด้วย rp_ และแสดงครั้งเดียว ส่งคีย์แบบนี้
Authorization: Bearer rp_...
| คำขอ | สิทธิ์ | จำกัด | ได้อะไร |
|---|---|---|---|
GET /api/v1/stations/{slug}/stats?from=YYYY-MM-DD&to=YYYY-MM-DD |
read |
300 ครั้งต่อนาที | ตัวเลขเดียวกับหน้าสถิติ: summary, byDay, byHour, byStream, byClient, byCountry, byLength, topSongs, topShows |
GET /api/v1/stations/{slug}/listeners |
read |
300 ครั้งต่อนาที | total และ by_stream (ผู้ฟังแยกตามสตรีม) |
POST /api/v1/stations/{slug}/skip |
control |
20 ครั้งต่อนาที | {"ok": true} เมื่อข้ามเพลงของเล่นเพลงอัตโนมัติแล้ว |
- stats: ถ้าไม่ระบุวันที่ ได้ 7 วันล่าสุดถึงวันนี้ตามเขตเวลาของสถานี ถ้า
fromอยู่หลังtoหรือช่วงยาวเกิน 370 วัน ระบบตอบ400พร้อม{"error":"bad_range"} - skip: ตอบ
409ขณะดีเจออกอากาศสด (ให้ข้ามเพลงจากห้องจัดรายการแทน) ตอบ502เมื่อระบบออกอากาศไม่ตอบ - ไม่มีคีย์ คีย์ผิด หรือคีย์ไม่มีสิทธิ์ที่ต้องใช้ ระบบตอบ
401พร้อมส่วนหัวWWW-Authenticateที่บอกชื่อสิทธิ์
สถานะ
| คำขอ | ใช้ทำอะไร |
|---|---|
GET /api/public/stations/{slug}/status |
สถานะเครื่องเล่นของสถานีหนึ่ง ไม่ต้องใช้คีย์ ไม่จำกัดจำนวนคำขอ เก็บแคช 2 วินาที |
GET /status.json |
สถานะแพลตฟอร์มสำหรับระบบเฝ้าระวัง ไม่ต้องใช้คีย์ เปิด CORS |
สถานะสถานี ส่ง station (slug, name), online, source, now_playing, show (name, ends_at), next_show (name, starts_at) ภายใน 7 วัน, listeners, capacity และ full ถ้าสถานีจำกัดการฟังตามประเทศ จะส่ง region_blocked สำหรับผู้เรียกด้วย
/status.json ส่ง time, components (website, database, streaming: up หรือ down), target_pct และข้อมูลของแต่ละสถานีที่เปิดอยู่: slug, name, engine, source, off_air, availability_30d_pct, measured_minutes_30d พร้อม incidents ใน 14 วันที่ผ่านมา (station, start, end, minutes, cause, ongoing) ถ้าผู้ดูแลระบบซ่อนความพร้อมใช้งานในหน้าสถานะ ตัวเลขความพร้อมใช้งานเป็น null และ incidents ว่าง
เหตุการณ์สด (Server-Sent Events)
GET /api/public/stations/{slug}/events เปิดการเชื่อมต่อ text/event-stream ค้างไว้ ไม่ต้องใช้คีย์ เมื่อเชื่อมต่อ จะได้เพลงปัจจุบันและประกาศที่ใช้งานอยู่ทันที ระบบส่งบรรทัดหมายเหตุ : ping ทุก 25 วินาที
| เหตุการณ์ | ข้อมูล |
|---|---|
nowplaying |
เพลงใหม่: title, artist, source, started_at และ show, media_id ถ้ามี |
chat |
แชตของสถานี: type เป็น message (id, display_name, body, is_staff, created_at) หรือ delete (id) |
alert |
{ "type": "alert", "alert": { id, severity, title, message, instruction, language, expires_at } } |
alert_end |
{ "type": "alert_end", "id": ... } เมื่อประกาศสิ้นสุด |
const es = new EventSource('https://cfbtradio.com/api/public/stations/saint-fm/events');
es.addEventListener('nowplaying', (e) => console.log(JSON.parse(e.data)));
es.addEventListener('alert', (e) => console.log(JSON.parse(e.data).alert));
เว็บฮุก
ผู้ดูแลสถานีเพิ่มเว็บฮุกได้ที่ จัดการสถานี → เชื่อมต่อบริการอื่น → เว็บฮุก ใส่ลิงก์ปลายทาง (HTTPS) และเลือกเหตุการณ์ Secret สำหรับตรวจลายมือชื่อขึ้นต้นด้วย whsec_ และแสดงครั้งเดียว ปุ่ม "ส่งทดสอบ" ส่งเหตุการณ์ test
| เหตุการณ์ | เมื่อไร |
|---|---|
track.started |
เริ่มเพลงหรือรายการใหม่ |
live.started |
ดีเจเริ่มออกอากาศสด |
live.ended |
ดีเจจบการออกอากาศสด |
request.received |
มีคำขอเพลงหรือข้อความใหม่ |
episode.published |
เผยแพร่ตอนฟังย้อนหลัง |
listeners.peak |
ผู้ฟังพร้อมกันสูงสุดใหม่ของวัน |
station.silence |
สัญญาณเงียบหรือกลับมามีเสียง |
การส่งแต่ละครั้งเป็น POST แบบ JSON เนื้อหาคือ { id, event, station_id, created_at, data } พร้อมส่วนหัวต่อไปนี้
| ส่วนหัว | ค่า |
|---|---|
X-Radio-Event |
ชื่อเหตุการณ์ |
X-Radio-Delivery |
หมายเลขการส่ง |
X-Radio-Timestamp |
เวลา Unix เป็นวินาที |
X-Radio-Signature |
sha256= ตามด้วย HMAC-SHA256 แบบเลขฐานสิบหกของ timestamp + "." + body โดยใช้ Secret เป็นกุญแจ |
วิธีตรวจ: คำนวณลายมือชื่อจากเนื้อหาดิบ เทียบแบบใช้เวลาคงที่ ปฏิเสธเวลาที่เก่าเกินไป (เช่น เกิน 5 นาที) และใช้ id กันการรับซ้ำ
const expected = 'sha256=' + crypto.createHmac('sha256', SECRET).update(`${req.headers['x-radio-timestamp']}.${rawBody}`).digest('hex');
const given = Buffer.from(req.headers['x-radio-signature'] || '');
if (given.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(expected), given)) return res.status(401).end();
กติกาการส่ง
- ตอบด้วยรหัส 2xx ใดก็ได้ภายใน 10 วินาที ระบบไม่ตามการเปลี่ยนเส้นทาง (redirect)
- ถ้าส่งไม่สำเร็จ ระบบลองใหม่ 5 ครั้ง หลัง 30 วินาที 1, 2, 4 และ 8 นาที
- ระบบไม่ส่งไปยังที่อยู่ในเครือข่ายภายในของแพลตฟอร์ม เว้นแต่เซิร์ฟเวอร์อนุญาต
- หน้าเชื่อมต่อบริการอื่นแสดงการส่ง 5 ครั้งล่าสุดของแต่ละเว็บฮุกบันทึกการส่งเก็บไว้ 30 วัน
Embed player
คัดลอกโค้ดจาก จัดการสถานี → เชื่อมต่อบริการอื่น → เครื่องเล่นสำหรับฝังในเว็บอื่น (Embed)
<iframe src="https://cfbtradio.com/s/<station>/embed" title="เครื่องเล่นวิทยุ: ชื่อสถานี" width="100%" height="190" style="border:0;max-width:34rem" allow="autoplay"></iframe>
| ตัวเลือก | ค่า | ค่าเริ่มต้น |
|---|---|---|
theme |
light, dark |
ตามการตั้งค่าของอุปกรณ์ |
lang |
th, en |
ภาษาหลักของสถานี |
ตัวอย่าง: https://cfbtradio.com/s/<station>/embed?theme=dark&lang=en
เครื่องเล่นนี้
- ไม่เล่นอัตโนมัติ
- มีปุ่มเล่น/หยุดที่บอกสถานะ แถบปรับระดับเสียงที่มีชื่อกำกับ (เริ่มที่ 80) และลิงก์ไปเว็บสถานี
- มีช่อง "อ่านชื่อเพลงเมื่อเปลี่ยน" และช่อง "ประหยัดเน็ต" เมื่อสถานีมีสตรีมประหยัดเน็ต
- บอกเมื่อสถานีเต็ม ออฟไลน์ หรือไม่เปิดให้ฟังในประเทศของผู้ฟัง และเชื่อมต่อใหม่เมื่อสัญญาณหลุด
- แสดงประกาศฉุกเฉินด้านบนและอ่านออกเสียง
ต้องคงแอตทริบิวต์ title ไว้เสมอ เพื่อให้โปรแกรมอ่านหน้าจอบอกได้ว่ากรอบนี้คืออะไร เครื่องเล่นของสถานีที่ถูกระงับจะแสดงว่าไม่พบ