สร้างงานถอดเสียง — POST /new-transcribe

Personal API key ใช้โควตาบัญชีเดียวกับ editor และเข้าถึงเฉพาะงานของเจ้าของ key บัญชีที่ไม่เปิดใช้งานตอบ 403 โควตาไม่พอตอบ 402 ดูวิธีสร้าง key

สร้างงานถอดเสียงใหม่จาก URL ของไฟล์เสียงหรือวิดีโอ ระบบจะตอบกลับ job_id สำหรับติดตามสถานะ แล้วประมวลผลงานในเบื้องหลัง

POST https://api.lomasub.ai/new-transcribe

Headers

Header ค่า จำเป็น
Authorization Bearer <API_KEY> ต้องระบุ
Content-Type application/json ต้องระบุ

Request Body

ฟิลด์ ชนิด จำเป็น คำอธิบาย
source_url string (URL) ต้องระบุ URL แบบ HTTP(S) ของไฟล์เสียง/วิดีโอจาก hostname ที่เซิร์ฟเวอร์อนุญาต (ทุกรูปแบบที่ ffmpeg อ่านได้ worker แปลงเป็น 16 kHz โมโนเอง) ระบบตรวจสอบก่อนรับงานด้วย HEAD และ GET แบบ Range: bytes=0-0 เมื่อจำเป็น สูงสุด 3 ครั้ง
filename string — ชื่อไฟล์สำหรับแสดงผล เก็บไว้กับงานและแสดงเป็น name ใน GET /jobs ระบบตัดเหลือเฉพาะชื่อไฟล์ (ไม่มีโฟลเดอร์) ค่าเริ่มต้นคือชื่อไฟล์ท้าย URL — สตริงว่างหรือยาวเกิน 255 ตัวอักษรตอบ 422
language string "th" คำใบ้ภาษาให้ ASR บันทึกเป็น config.language; ไม่ส่งค่า, null หรือค่าว่างใช้ "th" ระบุ "en" เพื่อเปลี่ยนคำใบ้ได้ ข้อความภาษาอังกฤษและข้อความผสมไทย/อังกฤษจะไม่ถูกลบเพียงเพราะใช้คำใบ้ "th"
config object — ตัวเลือกปรับพฤติกรรมการถอดเสียง ดูตารางด้านล่าง ระบบใช้เฉพาะคีย์ที่ส่งมา ค่าที่ไม่ระบุจะใช้ค่าเริ่มต้น ชื่อฟิลด์จาก API รุ่นเก่าตอบ 422 ส่วนคีย์ที่ระบบไม่เคยรู้จักจะถูกเก็บลง snapshot ตามที่ส่งมา
duration_seconds number ≥ 0 — ความยาวสื่อ (วินาที) เก็บไว้กับงานและแสดงเป็น duration_seconds ใน GET /jobs และเป็นค่าเดียวที่ GET /jobs/match ใช้เทียบ — งานที่สร้างโดยไม่ส่งค่านี้จะไม่ถูกจับคู่เลย แม้ worker จะวัดความยาวได้เท่ากัน ไม่ใช่ job config จึงไม่อยู่ใน snapshot

URL ที่อนุญาต

ค่าเริ่มต้นอนุญาตเฉพาะ lomafastsrt.sgp1.digitaloceanspaces.com และ lomafastsrt.sgp1.cdn.digitaloceanspaces.com ใช้ URL จาก POST /presigned-url หลังอัปโหลดไฟล์ หรือ URL ของไฟล์ที่อยู่บน hostname เหล่านี้

API และ worker ตรวจสอบ hostname แบบตรงตัวและตรวจว่า IP ที่ DNS ตอบกลับทุกตัวเป็นสาธารณะ ไม่รับ URL ที่มี username/password, IP literal หรือพอร์ตที่ไม่ใช่ HTTP 80 / HTTPS 443 รองรับ redirect สูงสุด 5 ครั้ง โดยปลายทางทุกครั้งต้องผ่านเงื่อนไขเดียวกัน URL ที่ไม่ผ่านนโยบายตอบ 400 พร้อม detail: "source_url destination is not allowed" โดยไม่สร้างงาน ข้อมูล URL ที่ผิดรูปแบบยังตอบ 422

ผู้ดูแลเพิ่ม hostname ได้ผ่าน SOURCE_ALLOWED_HOSTS ใน .env ของ API และ worker ทุกเครื่อง ค่านี้เป็น runtime configuration ไม่ใช่ตัวเลือกใน job config แม้ hostname อยู่ในรายการก็ยังเข้าถึง private/loopback/link-local IP ไม่ได้

worker จำกัดขนาด source ที่ดาวน์โหลดจริงไว้ที่ 1 GiB และความยาวเสียงที่ถอดรหัสจริงไว้ที่ 24 ชั่วโมง ค่า runtime SOURCE_MAX_BYTES และ MEDIA_MAX_DURATION_SECONDS ตั้งใน .env ของ worker ไม่ใช่ job config และไม่ใช้ duration_seconds ที่ client แจ้ง เพื่อผ่านข้อจำกัดนี้ งานที่เกินขีดจำกัดจะล้มเหลวใน SPLITTING ก่อนเรียกโมเดล โดยไม่ตัดเสียงแล้วรับงานต่อ timeout ของแต่ละ stage ยังคงมีผลตามเดิม

โควตาและจำนวนงาน

สำหรับ session และ personal API key ค่า duration_seconds เป็นคำใบ้สำหรับจองโควตาตอนสร้างงาน: งานจอง ความยาว × อัตราของโหมด (อย่างน้อยหนึ่งนาที) หลัง SPLITTING worker ใช้ความยาวเสียงที่ถอดรหัสจริง เพื่อจองโควตาเต็มจำนวนก่อนเริ่มถอดเสียง ถ้าโควตาไม่พอ งานจะเป็น FAILED ใน SPLITTING และคืนการจอง โดยยังไม่เรียกโมเดล /finish คิดค่าเสียงจริงเพียงครั้งเดียว ใช้โควตารายเดือนก่อน แล้วจึงใช้เครดิต (เครดิตติดลบได้ถ้างานสุดท้ายใหญ่กว่ายอด)

โหมด (config.promodel) โควตาต่อวินาที ขั้นต่ำต่องาน
ไม่ระบุ (ถอดเสียงปกติ) 0.015 0.9
pro 0.035 2.1
translate-* 0.035 2.1

แต่ละบัญชีมีแพ็กเกจ — บัญชีใหม่เริ่มเป็น free และได้โควตารายเดือนตามแพ็กเกจ รีเซ็ตทุกเดือน เวลา 00:00 ตามวันครบรอบสมัครบัญชีในเวลาไทย โดยไม่สะสมส่วนที่เหลือ (วันที่ 29–31 ใช้วันสุดท้ายของเดือนที่สั้นกว่า)

แพ็กเกจ โควตาต่อเดือน งานที่ยังไม่เสร็จ เสียงต่อหนึ่งงาน ขนาดไฟล์เสียงที่อัปโหลด
free (ฟรี) 54 2 30 นาที 100 MB
creator (ครีเอเตอร์) 324 5 300 นาที (5 ชั่วโมง) 1,000 MB
studio (สตูดิโอ) 1,296 5 300 นาที (5 ชั่วโมง) 1,000 MB

1 MB = 1,000,000 bytes ใช้กับไฟล์เสียงที่อัปโหลดจริง ไม่ใช่วิดีโอต้นฉบับใน browser เกินจำนวนงานตอบ 429 พร้อม Retry-After เสียงเกินเวลาตอบ 422 ไฟล์ใหญ่เกินตอบ 413 โควตาไม่พอตอบ 402 พร้อม detail ภาษาไทยที่บอกยอดคงเหลือทั้งสองส่วนและเวลารีเซ็ตครั้งถัดไป worker ใช้ขีดจำกัดที่ต่ำกว่าระหว่างแพ็กเกจกับ runtime limit และตรวจขนาด/เวลาเสียงจริงก่อนเรียกโมเดล การจองคงอยู่จนงานเป็น READY หรือ FAILED แม้งานจะเกิน 24 ชั่วโมงหรือ lease หมดอายุ อัปเกรดแพ็กเกจแล้วโควตารายเดือนเพิ่มเป็นของแพ็กเกจใหม่ทันที ดาวน์เกรดมีผลกับยอดในรอบรีเซ็ตถัดไป เมื่อจำนวนงานเดิมเกินแพ็กเกจใหม่ จะรับงานใหม่ไม่ได้จนจำนวนลดลง โดยไม่ยกเลิกงานเดิมอัตโนมัติ shared secret ภายในไม่ถูกตรวจโควตาหรือจำกัดจำนวนงานต่อบัญชี แต่ runtime limits ของ worker ยังมีผล

ตัวอย่าง Request

curl -X POST "https://api.lomasub.ai/new-transcribe" \
  -H "Authorization: Bearer $LOMA_API_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source_url": "https://lomafastsrt.sgp1.cdn.digitaloceanspaces.com/cancer.mp3",
    "language": "th",
    "config": {
      "speaker_diarization": true
    }
  }'

ตัวเลือกใน config

ทุกฟิลด์เป็นตัวเลือกเสริม — ระบบจะบันทึกค่าเหล่านี้ไว้กับงาน (config snapshot) ตั้งแต่ตอนสร้าง โดย snapshot คือ ค่าเริ่มต้นของเซิร์ฟเวอร์ → ค่าที่คุณส่ง และ GET /status คืน object นี้กลับมาใน config ให้ตรวจสอบได้ว่า worker ใช้ค่าอะไรจริง ค่าเริ่มต้นในตารางคือค่าที่ติดตั้งมากับซอฟต์แวร์ แต่ละ deployment ปรับได้ — ปัจจุบัน api.lomasub.ai ปรับ 3 ค่า: min_vad_duration_seconds 10.0, min_srt_duration_ms 1200, srt_max_chars 48 (srt_cue_words ใช้ค่าเริ่มต้น 6)

รูปทรงของบรรทัดซับไตเติลกำหนดด้วย 3 คีย์: srt_max_chars คือเพดานตัวอักษร, srt_cue_words คือจำนวนคำที่ตัวตัดบรรทัดพยายามให้ได้, min_srt_duration_ms คือเวลาแสดงผลขั้นต่ำ ชุดค่าที่เข้ากันคือประมาณ จำนวนคำ × 5 ตัวอักษร และ จำนวนคำ × 200 มิลลิวินาที — production ใช้ 6 / 48 / 1200

การสร้างซับไตเติล

ฟิลด์ ชนิด ค่าเริ่มต้น คำอธิบาย
srt_max_chars int 40 จำนวนตัวอักษรสูงสุดต่อบรรทัดซับไตเติล นับเฉพาะข้อความที่พูด (ไม่นับป้ายชื่อผู้พูด) บังคับใช้ทุกโหมดและทุกขั้นตอน รวมถึงตอนรวมบรรทัดสั้น (ยกเว้นกรณีคำเดียวยาวเกิน)
srt_cue_words int 6 จำนวนคำต่อบรรทัดที่ต้องการ (3–15) ตัวตัดบรรทัดจะพยายามให้แต่ละบรรทัดมีคำประมาณนี้ก่อนตัดที่ช่วงหยุดถัดไป — ค่าที่เข้ากันคือ srt_max_chars ≈ 5 ตัวอักษรต่อคำ และ min_srt_duration_ms ≈ 200 มิลลิวินาทีต่อคำ (production ใช้ 6 / 48 / 1200) นอกช่วง 3–15 ตอบ 422 (รูปแบบ list)
min_srt_duration_ms int 2000 ระยะเวลาแสดงผลขั้นต่ำของแต่ละบรรทัด (มิลลิวินาที) บรรทัดที่สั้นกว่านี้จะถูกรวมเข้ากับบรรทัดข้างเคียงที่รอยต่อเงียบน้อยกว่า (ไม่รวมถ้าผู้พูดต่างกัน รอยต่อมีช่วงเงียบ ≥ 1 วินาที หรือข้อความหลังรวมเกิน srt_max_chars) ถ้ารวมไม่ได้จะยืดเวลาสิ้นสุดเข้าไปในช่วงเงียบจนถึงเวลาเริ่มของบรรทัดถัดไป บรรทัดที่ยังสั้นกว่าเกณฑ์อยู่จะคงไว้ตามเดิมและถูกบันทึกใน log ของ worker
promodel string หรือ null null "pro" ตรวจแก้คำก่อน alignment; "translate-th", "translate-en", "translate-ja", "translate-ko", "translate-zh" แปลซับเป็นไทย อังกฤษ ญี่ปุ่น เกาหลี หรือจีนตัวย่อ ตามลำดับ (jp/kr เป็น alias ของ ja/ko); ไม่ส่งหรือ null ใช้ภาษาต้นฉบับ เลือก preset ผ่าน API ไม่ได้
forcealigner_level string "default" ระดับการจัดตำแหน่งเวลาของคำ — "none" ปิดการจัดตำแหน่งละเอียด (ใช้เวลาแบบหยาบ), "default" จัดตำแหน่งด้วย ForceAligner แล้วตัดประโยคด้วยตัวตัดระดับคำแบบกำหนดกฎในเครื่อง (ค่าเริ่มต้น ไม่เรียก LLM ภายนอก), "llmsplitting" ตัดประโยคด้วยโมเดลภาษาภายนอกและจัดตำแหน่งเวลารอบที่สอง โหมด "llmsplitting" เพิ่มขั้นตอน LLM_SPLITTING และ FORCEDALIGN_2ND (ช้ากว่า) ส่วน "none" จะไม่มี result.karaoke.vtt และ result.words.json และ cue ใน result.debug.vtt เป็นข้อความล้วนไม่มี timestamp ระดับคำ ค่าอื่น (รวมถึง "cueseg" เดิม) และฟิลด์ boolean เก่า enable_forcealigner จะถูกปฏิเสธด้วย 422

ซับไตเติลแสดงต่อเนื่องเสมอ: เวลาสิ้นสุดของบรรทัดจะยืดไปจนถึงเวลาเริ่มของบรรทัดถัดไป เว้นแต่มีช่วงเงียบตั้งแต่ 1 วินาทีคั่นอยู่ (เวลาเริ่มของบรรทัดไม่ถูกขยับ) ไม่มีฟิลด์สำหรับตั้งค่าพฤติกรรมนี้

การแบ่งช่วงเสียง (VAD)

ฟิลด์ ชนิด ค่าเริ่มต้น คำอธิบาย
min_vad_duration_seconds float 5.0 ความยาวช่วงเสียงขั้นต่ำ (วินาที) — ช่วงเสียงจะถูกเปิดค้างไว้จนมีเสียงอย่างน้อยเท่านี้ก่อนจึงปิดที่ช่วงเงียบได้ (production ใช้ 10.0)
max_vad_duration_seconds float 25.0 ความยาวช่วงเสียงสูงสุด (วินาที) ที่ส่งให้ Whisper — เสียงพูดต่อเนื่องจะถูกตัดที่ความยาวนี้โดยไม่มีช่องโหว่
vad_threshold float 0.30 ค่าความน่าจะเป็นเสียงพูดของ Silero (ต่อเฟรม 32 ms) ที่ถือว่าเป็นเสียงพูด (0.0–1.0) ค่าต่ำช่วยเก็บบทพูดเสียงเบา ค่าสูงช่วยตัดเพลงและจิงเกิล (ค่าใน dataclass ของโค้ดคือ 0.4 แต่ค่าเริ่มต้นจาก env ตอนบูตคือ 0.30)

การระบุผู้พูด (Speaker Diarization)

ฟิลด์ ชนิด ค่าเริ่มต้น คำอธิบาย
speaker_diarization bool false เปิดการจำแนกและติดป้ายชื่อผู้พูดในซับไตเติล (เพิ่มขั้นตอน DIARIZING) แต่ละบรรทัดจะมีป้าย A:, B: นำหน้าใน SRT และ VTT และไฟล์ VTT จะมี embedding ของแต่ละบรรทัดซ่อนอยู่
speaker_local_distance_threshold float 0.60 ระยะ cosine ที่ต่ำกว่านี้ถือว่าสองบรรทัดในกลุ่มย่อยเดียวกันเป็นผู้พูดคนเดียวกัน (0.0–2.0) ค่าต่ำ = แยกผู้พูดมากขึ้น
speaker_merge_distance_threshold float 0.35 ระยะที่ต่ำกว่านี้จะรวมกลุ่มผู้พูดจากคนละกลุ่มย่อยเป็นผู้พูดคนเดียวกันทั้งงาน (0.0–2.0)
speaker_clustering_drop_dialogue_percentile float 0.02 ผู้พูดที่มีสัดส่วนบรรทัดน้อยกว่าค่านี้ และ จำนวนบรรทัดน้อยกว่า agglomeration_min_appear จะคงข้อความไว้แต่ไม่ได้ป้ายชื่อ (0 ≤ x < 1)
agglomeration_min_appear int 5 กลุ่มผู้พูดขนาดเล็กที่มีบรรทัดตั้งแต่เท่านี้ขึ้นไปจะได้ป้ายชื่อเสมอ
agglomolation_group_size int 300 จำนวนบรรทัดต่อกลุ่มย่อยสำหรับการจัดกลุ่มแบบแบ่งช่วงเวลาในไฟล์ยาว (0 = จัดกลุ่มรอบเดียวทั้งงาน) การสะกดเป็นไปตาม API

Response — 201 Created

{
  "job_id": "0b8a4f3e-6c1d-4a9b-9f2e-7d5c3b1a8e64",
  "status_url": "https://api.lomasub.ai/status?job_id=0b8a4f3e-6c1d-4a9b-9f2e-7d5c3b1a8e64",
  "state": "QUEUED",
  "s1_queued_ts": "2026-07-10T09:15:22.481Z"
}
ฟิลด์ ชนิด คำอธิบาย
job_id string (UUID) รหัสงานสำหรับติดตามสถานะ
status_url string (URL) URL สำเร็จรูปสำหรับเรียกตรวจสอบสถานะ
state string สถานะเริ่มต้นของงาน เป็น QUEUED เสมอ
s1_queued_ts string (timestamp) เวลาที่งานเข้าคิว

ข้อผิดพลาดที่อาจพบ

สถานะ สาเหตุ ตัวอย่างข้อความ
400 Bad Request source_url เข้าถึงไม่ได้ (HEAD และ GET แบบ range ล้มเหลวครบ 3 ครั้ง) "source_url is not reachable"
401 Unauthorized ไม่มีหรือ API key ไม่ถูกต้อง —
422 Unprocessable Entity ชนิดหรือช่วงค่าไม่ถูกต้อง (เช่น srt_cue_words นอกช่วง 3–15, vad_threshold เกิน 1, filename ยาวเกิน 255) ตอบเป็น list ตามรูปแบบ FastAPI / ใช้ชื่อฟิลด์เก่าที่เปลี่ยนชื่อหรือยกเลิกแล้ว ตอบ {"detail": "<ข้อความ>"} ระบุฟิลด์ที่ผิดพร้อมชื่อฟิลด์ที่มาแทน

ขั้นตอนถัดไป: นำ job_id ไปติดตามความคืบหน้าที่ ตรวจสอบสถานะงาน — GET /status

Filler removal

config.remove_filler is a boolean, true by default. Set it to false to keep fillers. Omission or null uses the default; strings and numbers are rejected with 422. The effective value is stored in the job snapshot and returned by /status and /claim-job. Older queued snapshots missing the setting also default to true; completed results are not rewritten.

For Thai and English jobs (including mixed Thai/English speech), the worker removes only separated uh, um, เอ่อ, and อ่า, after ASR cleanup and before optional Pro correction and alignment. Both uh/um and Uh/Um are recognized. All-uppercase abbreviations, quoted terms, URLs, email addresses, compound words, possible numeric um measurements, and attached Thai text (such as อ่าน, เอ่อล้น, or อ่าตำรวจ) are preserved. Other sounds such as อืม, อื้อ, hm, and hmm are never removed by this filter. Other job languages are unchanged.

This is a readability option: removing hesitation can lose conversational nuance, and อ่า sometimes expresses acknowledgement. Only adjacent filler punctuation/whitespace is repaired; surviving words are not rewritten. Clear filler-only chunks may become empty. If no transcript units remain in the entire job, the existing no-content failure applies.

The option applies to all three forcealigner_level values and all generated subtitle formats. Original ASR remains in the worker trace; filler_filter.json records before/after text and removal spans. ASR segment timestamps are retained when cleaned segment text agrees with the cleaned transcript. Otherwise the worker clears stale segments; level none then uses chunk-level coarse timing.

Both CLI clients accept --remove-filler and --no-remove-filler; omission uses the API default:

python3 runjob.py "<audio_url>" --no-remove-filler
PYTHONPATH=api-server api-server/.venv/bin/python api-server/enqueue-job.py "<audio_url>" --no-remove-filler

Pro transcript correction

config.promodel="pro" enables word correction after ASR cleanup and before alignment. The worker sends ordered batches of 50 chunk rows to the fixed worker preset, validates exact replacements, and waits for all batches or the stage deadline. Valid edits are retained even if other edits/batches fail; only unresolved portions keep their original cleaned text. Repeated exact matches within a row are all corrected, with longer phrases taking overlap precedence. No speaker-name inference is performed. Normal and Pro jobs use the same result fields; no separate revised subtitle objects are created.

revise_srt, revise_speakername, and srt_revision_backend are retired and return 422. Worker-only model, concurrency, and timeout settings cannot be selected by a job. ASR omission recovery has been removed for every job.

Translation: one output language per job

Select exactly one mode with config.promodel (CLI: --promodel):

  • translate-th: Thai.
  • translate-en: English.
  • translate-ja: Japanese (translate-jp is accepted and normalized).
  • translate-ko: Korean (translate-kr is accepted and normalized).
  • translate-zh: Simplified Chinese.
{"source_url":"<public audio URL>","language":"th","config":{"promodel":"translate-zh"}}

language is still the source ASR hint. Omission/null keeps original-language output; pro retains its existing correction behavior. Unsupported modes return 422. Translation supports forcealigner_level=default|none; combining it with llmsplitting returns 422 because that adds another LLM processing stage.

Translation uses the same worker preset as Pro with a different prompt, combining conservative recognition correction and translation in one stage. It runs after source cue timing, within BUILDING_SRT, in batches of 100 cues with read-only neighboring context. This batch size is independent of Pro correction's 50 ASR chunk rows. It does not run Pro first. A source/target language match uses conservative same-language correction.

Cue count, timestamps and speaker assignments stay fixed. The source splitter still applies srt_max_chars; target text may exceed it to retain meaning, with length overages logged. Translation never silently returns original-language fallback cues: an incomplete batch after retries fails the job.

The existing srt_url, vtt_url, and compact_url deliver the selected language. debug_vtt_url, when available, contains translated text and source diagnostics. Translation jobs have no karaoke_vtt_url or words_url: source word timings cannot be assigned to translated words. Original text remains in local worker traces only. Quota charging is unchanged.

Job history rows include promodel. GET /jobs/match accepts promodel with the same modes and aliases; absent/empty means normal original-language jobs only. The editor supplies its selected mode so reusing a file cannot load another language.

results matching ""

    No results matching ""