โมเดลราคาองค์กร
500+ AI Model API ทั้งหมดในหนึ่ง API เพียงแค่ใน CometAPI
API โมเดล
นักพัฒนา
เริ่มต้นอย่างรวดเร็วเอกสารประกอบแดชบอร์ด API
บริษัท
เกี่ยวกับเราองค์กร
ทรัพยากร
โมเดล AIบล็อกบันทึกการเปลี่ยนแปลงสนับสนุน
ข้อกำหนดการให้บริการนโยบายความเป็นส่วนตัว
© 2026 CometAPI · All rights reserved
Home/Models/OpenAI/Sora 2 Pro
O

Sora 2 Pro

ต่อวินาที:$0.24
Sora 2 Pro เป็นโมเดลสร้างสื่อที่ล้ำหน้าและทรงพลังที่สุดของเรา ที่สามารถสร้างวิดีโอพร้อมเสียงที่ซิงโครไนซ์ได้ สามารถสร้างคลิปวิดีโอที่ละเอียดและไดนามิกจากภาษาธรรมชาติหรือภาพได้
ใหม่
ใช้งานเชิงพาณิชย์
Playground
ภาพรวม
คุณสมบัติ
ราคา
API

คุณสมบัติหลัก

  • การสร้างแบบหลายโมดัล (วิดีโอ + เสียง) — Sora-2-Pro สร้างเฟรมวิดีโอพร้อมเสียงที่ซิงก์กัน (บทสนทนา เสียงบรรยากาศ เสียงเอฟเฟกต์) แทนการสร้างวิดีโอและเสียงแยกกัน
  • ความคมชัดสูงขึ้น / ระดับ “Pro” — ปรับแต่งมาเพื่อ คุณภาพภาพที่สูงขึ้น, ช็อตที่ยากขึ้น (การเคลื่อนไหวซับซ้อน การบังวัตถุ และปฏิสัมพันธ์ทางกายภาพ) และความสม่ำเสมอต่อฉากที่ยาวนานกว่า Sora-2 (รุ่น non-Pro) โดยอาจใช้เวลาเรนเดอร์นานกว่ารุ่นมาตรฐาน Sora-2
  • ความยืดหยุ่นของอินพุต — รองรับพรอมต์ข้อความล้วน และสามารถรับเฟรมภาพอินพุตหรือภาพอ้างอิงเพื่อช่วยกำหนดองค์ประกอบภาพได้ (เวิร์กโฟลว์ input_reference)
  • Cameos / การใส่ภาพลักษณ์บุคคล — สามารถแทรกภาพลักษณ์ที่บันทึกไว้ของผู้ใช้ลงในฉากที่สร้างขึ้นได้ โดยผ่านเวิร์กโฟลว์การให้ความยินยอมภายในแอป
  • ความสมจริงทางกายภาพ: ปรับปรุงความคงอยู่ของวัตถุและความเที่ยงตรงของการเคลื่อนไหว (เช่น โมเมนตัม แรงลอยตัว) ลดอาร์ติแฟกต์การ “เทเลพอร์ต” ที่ไม่สมจริงซึ่งพบได้บ่อยในระบบรุ่นก่อน
  • ความสามารถในการควบคุม: รองรับพรอมต์แบบมีโครงสร้างและคำสั่งระดับช็อต เพื่อให้ครีเอเตอร์สามารถกำหนดกล้อง แสง และลำดับหลายช็อตได้

รายละเอียดทางเทคนิคและพื้นผิวการเชื่อมต่อ

ตระกูลโมเดล: Sora 2 (พื้นฐาน) และ Sora 2 Pro (รุ่นคุณภาพสูง)
รูปแบบอินพุต: พรอมต์ข้อความ, ภาพอ้างอิง, และวิดีโอ/เสียง cameo ที่บันทึกสั้น ๆ สำหรับภาพลักษณ์บุคคล
รูปแบบเอาต์พุต: วิดีโอที่เข้ารหัสแล้ว (พร้อมเสียง) — พารามิเตอร์ถูกเปิดให้ใช้งานผ่านเอ็นด์พอยต์ /v1/videos (เลือกโมเดลผ่าน model: "sora-2-pro" ) พื้นผิว API เป็นไปตามตระกูลเอ็นด์พอยต์วิดีโอของ OpenAI สำหรับการสร้าง/เรียกดู/แสดงรายการ/ลบ

การฝึกและสถาปัตยกรรม (สรุปสาธารณะ): OpenAI อธิบายว่า Sora 2 ได้รับการฝึกบนข้อมูลวิดีโอขนาดใหญ่ พร้อม post-training เพื่อปรับปรุงการจำลองโลก รายละเอียดเฉพาะ (ขนาดโมเดล ชุดข้อมูลที่แน่นอน และการทำ tokenization) ยังไม่ได้เปิดเผยต่อสาธารณะอย่างละเอียดเป็นบรรทัดต่อบรรทัด คาดว่าจะใช้การประมวลผลหนัก, video tokenizer/สถาปัตยกรรมเฉพาะทาง และองค์ประกอบการจัดแนวหลายโมดัล


เอ็นด์พอยต์ API และเวิร์กโฟลว์: แสดงเวิร์กโฟลว์แบบอิงงาน: ส่งคำขอสร้างแบบ POST (model="sora-2-pro"), รับ job id หรือตำแหน่ง จากนั้นโพลหรือรอให้เสร็จสิ้นและดาวน์โหลดไฟล์ผลลัพธ์ โดยพารามิเตอร์ที่พบบ่อยในตัวอย่างที่เผยแพร่ ได้แก่ prompt, seconds/duration, size/resolution และ input_reference สำหรับการเริ่มต้นแบบมีภาพนำทาง

พารามิเตอร์ทั่วไป :

  • model: "sora-2-pro"
  • prompt: คำอธิบายฉากด้วยภาษาธรรมชาติ โดยอาจมีคิวบทสนทนาด้วย
  • seconds / duration: ความยาวคลิปเป้าหมาย (Pro รองรับคุณภาพสูงสุดในช่วงความยาวที่มีให้ใช้งาน)
  • size / resolution: รายงานจากชุมชนระบุว่า Pro รองรับได้สูงสุดถึง 1080p ในหลายกรณีการใช้งาน

อินพุตคอนเทนต์: สามารถส่งไฟล์ภาพ (JPEG/PNG/WEBP) เป็นเฟรมหรือภาพอ้างอิงได้ เมื่อใช้งาน ภาพควรตรงกับความละเอียดเป้าหมายและทำหน้าที่เป็นจุดยึดองค์ประกอบภาพ

พฤติกรรมการเรนเดอร์: Pro ถูกปรับแต่งให้ให้ความสำคัญกับความต่อเนื่องระหว่างเฟรมและฟิสิกส์ที่สมจริง ซึ่งโดยทั่วไปหมายถึงเวลาในการประมวลผลที่นานขึ้นและต้นทุนต่อคลิปที่สูงกว่ารุ่น non-Pro

ประสิทธิภาพตามการเปรียบเทียบ

จุดแข็งเชิงคุณภาพ: OpenAI ได้ปรับปรุงความสมจริง ความสอดคล้องของฟิสิกส์ และเสียงที่ซิงก์กัน** เมื่อเทียบกับโมเดลวิดีโอก่อนหน้า ขณะที่ผล VBench อื่น ๆ ระบุว่า Sora-2 และรุ่นต่อยอดอยู่ในระดับสูงสุดหรือใกล้เคียงสูงสุดในบรรดาระบบปิดร่วมสมัย และด้านความต่อเนื่องเชิงเวลา

เวลา/อัตราการประมวลผลจากการทดสอบอิสระ (ตัวอย่างการเปรียบเทียบ): Sora-2-Pro ใช้เวลาเฉลี่ย ~2.1 นาที สำหรับคลิป 1080p ความยาว 20 วินาทีในการเปรียบเทียบหนึ่งครั้ง ขณะที่คู่แข่ง (Runway Gen-3 Alpha Turbo) เร็วกว่า (~1.7 นาที) ในงานเดียวกัน — เป็นการแลกเปลี่ยนระหว่างคุณภาพกับความหน่วงในการเรนเดอร์และการปรับแต่งแพลตฟอร์ม

ข้อจำกัด (เชิงปฏิบัติและความปลอดภัย)

  • ฟิสิกส์/ความสอดคล้องยังไม่สมบูรณ์ — ดีขึ้นแต่ยังไม่ไร้ที่ติ; ยังอาจเกิดอาร์ติแฟกต์ การเคลื่อนไหวที่ไม่เป็นธรรมชาติ หรือข้อผิดพลาดในการซิงก์เสียงได้
  • ข้อจำกัดด้านระยะเวลาและการประมวลผล — คลิปยาวใช้ทรัพยากรสูง; เวิร์กโฟลว์เชิงปฏิบัติหลายแบบจึงจำกัดคลิปให้สั้น (เช่น ระดับเลขหลักเดียวถึงหลักสิบต้น ๆ ของวินาทีสำหรับเอาต์พุตคุณภาพสูง)
  • ความเสี่ยงด้านความเป็นส่วนตัว / การยินยอม — การใส่ภาพลักษณ์บุคคล (“cameos”) ก่อให้เกิดความเสี่ยงด้านการยินยอมและการให้ข้อมูลผิด/การบิดเบือน; OpenAI มีการควบคุมความปลอดภัยและกลไกการเพิกถอนอย่างชัดเจนภายในแอป แต่การผสานรวมอย่างรับผิดชอบยังคงจำเป็น
  • ต้นทุนและความหน่วง — การเรนเดอร์คุณภาพระดับ Pro อาจมีราคาแพงและช้ากว่าโมเดลขนาดเบาหรือคู่แข่ง; ควรคำนึงถึงการคิดค่าบริการต่อวินาที/ต่อการเรนเดอร์และการเข้าคิว
  • การกรองเนื้อหาเพื่อความปลอดภัย — การสร้างเนื้อหาที่เป็นอันตรายหรือละเมิดลิขสิทธิ์ถูกจำกัด; โมเดลและแพลตฟอร์มมีชั้นความปลอดภัยและการกลั่นกรองรวมอยู่แล้ว

กรณีใช้งานทั่วไปและที่แนะนำ

กรณีใช้งาน:

  • ต้นแบบการตลาดและโฆษณา — สร้าง proof of concept แบบภาพยนตร์ได้อย่างรวดเร็ว
  • การทำพรีวิชวลไลเซชัน — สตอรี่บอร์ด การบล็อกกล้อง การแสดงภาพช็อต
  • คอนเทนต์โซเชียลแบบสั้น — คลิปสไตล์เฉพาะพร้อมบทสนทนาและ SFX ที่ซิงก์กัน
  • วิธีเข้าถึง Sora 2 Pro API

ขั้นตอนที่ 1: สมัครเพื่อรับ API Key

เข้าสู่ระบบที่ cometapi.com หากคุณยังไม่ใช่ผู้ใช้ของเรา โปรดลงทะเบียนก่อน ลงชื่อเข้าใช้ CometAPI console ของคุณ รับข้อมูลรับรองการเข้าถึง API key ของอินเทอร์เฟซ คลิก “Add Token” ที่ส่วน API token ในศูนย์ส่วนบุคคล รับ token key: sk-xxxxx แล้วส่งคำขอ

cometapi-key

ขั้นตอนที่ 2: ส่งคำขอไปยัง Sora 2 Pro API

เลือกเอ็นด์พอยต์ “sora-2-pro” เพื่อส่งคำขอ API และตั้งค่า request body วิธีการร้องขอและ request body สามารถดูได้จากเอกสาร API บนเว็บไซต์ของเรา เว็บไซต์ของเรายังมีการทดสอบผ่าน Apifox เพื่อความสะดวกของคุณ แทนที่ <YOUR_API_KEY> ด้วย CometAPI key จริงจากบัญชีของคุณ base url คือ official Create video

ใส่คำถามหรือคำขอของคุณลงในฟิลด์ content — นี่คือสิ่งที่โมเดลจะตอบกลับ ประมวลผลการตอบกลับของ API เพื่อรับคำตอบที่สร้างขึ้น

ขั้นตอนที่ 3: ดึงข้อมูลและตรวจสอบผลลัพธ์

ประมวลผลการตอบกลับของ API เพื่อรับคำตอบที่สร้างขึ้น หลังจากประมวลผลแล้ว API จะตอบกลับสถานะงานและข้อมูลเอาต์พุต

  • การฝึกภายใน / การจำลอง — สร้างภาพสถานการณ์สำหรับงานวิจัย RL หรือหุ่นยนต์ (ด้วยความระมัดระวัง)
  • งานสร้างสรรค์เชิงการผลิต — เมื่อใช้ร่วมกับการตัดต่อโดยมนุษย์ (ต่อคลิปสั้น ปรับเกรดสี แทนที่เสียง)

คำถามที่พบบ่อย

Does Sora 2 Pro generate synchronized audio with video?

ใช่ Sora 2 Pro สร้างเฟรมวิดีโอพร้อมเสียงที่ซิงค์กัน รวมถึงบทสนทนา เสียงบรรยากาศ และเอฟเฟกต์เสียง โดยไม่ได้สร้างแยกกัน แต่เป็นเอาต์พุตแบบรวมเป็นหนึ่งเดียว

What resolution and duration does Sora 2 Pro support?

Sora 2 Pro รองรับความละเอียดสูงสุดถึง 1080p โดยได้รับการปรับแต่งมาสำหรับคลิปสั้นคุณภาพสูง โดยทั่วไปอยู่ในช่วงไม่กี่วินาทีจนถึงหลักสิบวินาทีต้น ๆ เพื่อให้ได้ความเที่ยงตรงสูงสุด

How does Sora 2 Pro differ from standard Sora 2?

Sora 2 Pro ได้รับการปรับแต่งเพื่อคุณภาพภาพที่สูงกว่า รองรับช็อตที่ยากกว่าได้ดีขึ้น (การเคลื่อนไหวซับซ้อน การบังวัตถุ ปฏิสัมพันธ์ทางกายภาพ) และคงความสม่ำเสมอต่อฉากได้ยาวนานกว่า โดยแลกมากับเวลาเรนเดอร์ที่นานขึ้น

Can Sora 2 Pro use reference images to guide video generation?

ได้ Sora 2 Pro รองรับเวิร์กโฟลว์ input_reference ซึ่งใช้ภาพ JPEG/PNG/WEBP เป็นจุดยึดขององค์ประกอบเพื่อชี้นำเฟรมเริ่มต้นหรือสไตล์ของวิดีโอที่สร้างขึ้น

Does Sora 2 Pro support likeness injection (cameos)?

ใช่ Sora 2 Pro สามารถแทรกภาพลักษณ์ของผู้ใช้ที่บันทึกไว้ลงในฉากที่สร้างขึ้นได้ OpenAI มีเวิร์กโฟลว์การให้ความยินยอมและกลไกการเพิกถอนในตัวเพื่อจัดการความเสี่ยงด้านความเป็นส่วนตัวและการนำไปใช้ในทางที่ผิด

How long does Sora 2 Pro take to render a video?

การทดสอบเปรียบเทียบแสดงให้เห็นว่า Sora 2 Pro ใช้เวลาเฉลี่ยประมาณ 2.1 นาทีสำหรับคลิป 1080p ความยาว 20 วินาที Pro ให้ความสำคัญกับคุณภาพมากกว่าความเร็ว ดังนั้นคาดว่าจะใช้เวลาเรนเดอร์นานกว่า Sora 2 มาตรฐาน

What physics improvements does Sora 2 Pro offer?

Sora 2 Pro ปรับปรุงความคงอยู่ของวัตถุและความเที่ยงตรงของการเคลื่อนไหว โดยโมเมนตัม การลอยตัว และปฏิสัมพันธ์ทางกายภาพดูสมจริงมากขึ้น พร้อมอาร์ติแฟกต์แบบ 'เทเลพอร์ต' ที่พบน้อยลงซึ่งมักเกิดในโมเดลวิดีโอยุคก่อนหน้า

When should I choose Sora 2 Pro over Google Veo 3?

เลือก Sora 2 Pro หากคุณต้องการการผสานรวมกับระบบนิเวศของ OpenAI การแทรกภาพลักษณ์บุคคล และการสร้างฉากทางกายภาพที่ซับซ้อน Veo 3 อาจสร้างได้เร็วกว่าและมีรูปแบบราคาที่แตกต่างกัน ควรประเมินตามความต้องการด้านความหน่วงและงบประมาณของคุณ

ราคาสำหรับ Sora 2 Pro

สำรวจราคาที่แข่งขันได้สำหรับ Sora 2 Pro ที่ออกแบบมาให้เหมาะสมกับงบประมาณและความต้องการการใช้งานที่หลากหลาย แผนการบริการที่ยืดหยุ่นของเรารับประกันว่าคุณจะจ่ายเฉพาะสิ่งที่คุณใช้เท่านั้น ทำให้สามารถขยายขนาดได้ง่ายเมื่อความต้องการของคุณเพิ่มขึ้น ค้นพบว่า Sora 2 Pro สามารถยกระดับโปรเจกต์ของคุณได้อย่างไรในขณะที่ควบคุมต้นทุนให้อยู่ในระดับที่จัดการได้
Model NameTagsOrientationResolutionPrice
sora-2-provideosPortrait720x1280$0.24 / sec
sora-2-provideosLandscape1280x720$0.24 / sec
sora-2-provideosPortrait (High Res)1024x1792$0.40 / sec
sora-2-provideosLandscape (High Res)1792x1024$0.40 / sec
sora-2-pro-all-Universal / All-$0.80000

โค้ดตัวอย่างและ API สำหรับ Sora 2 Pro

Sora-2-pro คือโมเดลสร้างวิดีโอ+เสียงเรือธงของ OpenAI ที่ออกแบบมาเพื่อสร้างคลิปวิดีโอสั้นที่สมจริงอย่างมาก พร้อมบทสนทนาที่ซิงโครไนซ์ เอฟเฟกต์เสียง และการจำลองฟิสิกส์/โลกที่ดีกว่าโมเดลวิดีโอรุ่นก่อนหน้า โดยวางตำแหน่งให้เป็นรุ่น “Pro” คุณภาพสูงกว่าที่พร้อมให้บริการแก่ผู้ใช้แบบชำระเงินและผ่าน API สำหรับการสร้างแบบโปรแกรมได้ โมเดลนี้เน้นความสามารถในการควบคุม ความต่อเนื่องตามเวลา และการซิงโครไนซ์เสียง สำหรับกรณีการใช้งานด้านภาพยนตร์และโซเชียล
POST
/v1/videos
Curl
Python
JavaScript
# Create a video with sora-2-pro
# Step 1: Submit the video generation request
echo "Submitting video generation request..."
response=$(curl -s https://api.cometapi.com/v1/videos \
  -H "Authorization: Bearer $COMETAPI_KEY" \
  -F "model=sora-2-pro" \
  -F "prompt=A calico cat playing a piano on stage")

echo "Response: $response"

# Extract video_id from response (handle JSON with spaces like "id": "xxx")
video_id=$(echo "$response" | tr -d '
' | sed 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
echo "Video ID: $video_id"

# Step 2: Poll for progress until 100%
echo ""
echo "Checking video generation progress..."
while true; do
  status_response=$(curl -s "https://api.cometapi.com/v1/videos/$video_id" \
    -H "Authorization: Bearer $COMETAPI_KEY")

  # Parse progress from "progress": "0%" format
  progress=$(echo "$status_response" | grep -o '"progress":"[^"]*"' | head -1 | sed 's/"progress":"//;s/"$//')
  # Parse status from the outer level
  status=$(echo "$status_response" | grep -o '"status":"[^"]*"' | head -1 | sed 's/"status":"//;s/"$//')

  echo "Progress: $progress, Status: $status"

  if [ "$progress" = "100%" ]; then
    echo "Video generation completed!"
    break
  fi

  if [ "$status" = "FAILURE" ] || [ "$status" = "failed" ]; then
    echo "Video generation failed!"
    echo "$status_response"
    exit 1
  fi

  sleep 10
done

# Step 3: Download the video to output directory
echo ""
echo "Downloading video to ./output/$video_id.mp4..."
mkdir -p ./output
curl -s "https://api.cometapi.com/v1/videos/$video_id/content" \
  -H "Authorization: Bearer $COMETAPI_KEY" \
  -o "./output/$video_id.mp4"

if [ -f "./output/$video_id.mp4" ]; then
  echo "Video saved to ./output/$video_id.mp4"
  ls -la "./output/$video_id.mp4"
else
  echo "Failed to download video"
  exit 1
fi

Curl Code Example

# Create a video with sora-2-pro
# Step 1: Submit the video generation request
echo "Submitting video generation request..."
response=$(curl -s https://api.cometapi.com/v1/videos \
  -H "Authorization: Bearer $COMETAPI_KEY" \
  -F "model=sora-2-pro" \
  -F "prompt=A calico cat playing a piano on stage")

echo "Response: $response"

# Extract video_id from response (handle JSON with spaces like "id": "xxx")
video_id=$(echo "$response" | tr -d '\n' | sed 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')
echo "Video ID: $video_id"

# Step 2: Poll for progress until 100%
echo ""
echo "Checking video generation progress..."
while true; do
  status_response=$(curl -s "https://api.cometapi.com/v1/videos/$video_id" \
    -H "Authorization: Bearer $COMETAPI_KEY")
  
  # Parse progress from "progress": "0%" format
  progress=$(echo "$status_response" | grep -o '"progress":"[^"]*"' | head -1 | sed 's/"progress":"//;s/"$//')
  # Parse status from the outer level
  status=$(echo "$status_response" | grep -o '"status":"[^"]*"' | head -1 | sed 's/"status":"//;s/"$//')
  
  echo "Progress: $progress, Status: $status"
  
  if [ "$progress" = "100%" ]; then
    echo "Video generation completed!"
    break
  fi
  
  if [ "$status" = "FAILURE" ] || [ "$status" = "failed" ]; then
    echo "Video generation failed!"
    echo "$status_response"
    exit 1
  fi
  
  sleep 10
done

# Step 3: Download the video to output directory
echo ""
echo "Downloading video to ./output/$video_id.mp4..."
mkdir -p ./output
curl -s "https://api.cometapi.com/v1/videos/$video_id/content" \
  -H "Authorization: Bearer $COMETAPI_KEY" \
  -o "./output/$video_id.mp4"

if [ -f "./output/$video_id.mp4" ]; then
  echo "Video saved to ./output/$video_id.mp4"
  ls -la "./output/$video_id.mp4"
else
  echo "Failed to download video"
  exit 1
fi

Python Code Example

# Create a video with sora-2-pro using raw HTTP requests
import os
import time
import requests

api_key = os.environ.get("COMETAPI_KEY")
base_url = "https://api.cometapi.com/v1"

headers = {"Authorization": f"Bearer {api_key}"}

# Step 1: Submit the video generation request
print("Submitting video generation request...")
response = requests.post(
    f"{base_url}/videos",
    headers=headers,
    files={
        "model": (None, "sora-2-pro"),
        "prompt": (None, "A calico cat playing a piano on stage"),
    },
)

result = response.json()
print(f"Response: {result}")

video_id = result.get("id")
print(f"Video ID: {video_id}")

# Step 2: Poll for progress until 100%
print("\nChecking video generation progress...")
while True:
    try:
        status_response = requests.get(f"{base_url}/videos/{video_id}", headers=headers)
        status_result = status_response.json()

        # Parse progress and status from response
        data = status_result.get("data", {})
        if data is None:
            data = {}
        progress = data.get("progress", "0%")
        status = data.get("status", "unknown")

        print(f"Progress: {progress}, Status: {status}")

        if status in ["FAILURE", "failed"]:
            print("Video generation failed!")
            print(status_result)
            exit(1)

        if progress == "100%":
            print("Video generation completed!")
            break
    except Exception as e:
        print(f"Temporary error: {e}, retrying...")

    time.sleep(10)

# Step 3: Download the video to output directory
print(f"\nDownloading video to ./output/{video_id}.mp4...")
os.makedirs("./output", exist_ok=True)

video_response = requests.get(f"{base_url}/videos/{video_id}/content", headers=headers)

output_path = f"./output/{video_id}.mp4"
with open(output_path, "wb") as f:
    f.write(video_response.content)

if os.path.exists(output_path):
    file_size = os.path.getsize(output_path)
    print(f"Video saved to {output_path}")
    print(f"File size: {file_size} bytes")
else:
    print("Failed to download video")
    exit(1)

JavaScript Code Example

// Create a video with sora-2-pro using raw HTTP requests
import fs from "fs";
import path from "path";

const apiKey = process.env.COMETAPI_KEY;
const baseUrl = "https://api.cometapi.com/v1";

async function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function main() {
  // Step 1: Submit the video generation request
  console.log("Submitting video generation request...");

  const formData = new FormData();
  formData.append("model", "sora-2-pro");
  formData.append("prompt", "A calico cat playing a piano on stage");

  const submitResponse = await fetch(`${baseUrl}/videos`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
    body: formData,
  });

  const result = await submitResponse.json();
  console.log("Response:", JSON.stringify(result, null, 2));

  const videoId = result.id;
  console.log("Video ID:", videoId);

  // Step 2: Poll for progress until 100%
  console.log("\nChecking video generation progress...");

  while (true) {
    try {
      const statusResponse = await fetch(`${baseUrl}/videos/${videoId}`, {
        headers: {
          Authorization: `Bearer ${apiKey}`,
        },
      });

      const text = await statusResponse.text();
      if (text.startsWith("<")) {
        console.log("Temporary server error, retrying...");
        await sleep(10000);
        continue;
      }

      const statusResult = JSON.parse(text);

      // Parse progress and status from response
      const data = statusResult.data || {};
      const progress = data.progress || "0%";
      const status = data.status || "unknown";

      console.log(`Progress: ${progress}, Status: ${status}`);

      if (status === "FAILURE" || status === "failed") {
        console.log("Video generation failed!");
        console.log(JSON.stringify(statusResult, null, 2));
        process.exit(1);
      }

      if (progress === "100%") {
        console.log("Video generation completed!");
        break;
      }
    } catch (e) {
      console.log(`Temporary error: ${e.message}, retrying...`);
    }

    await sleep(10000);
  }

  // Step 3: Download the video to output directory
  console.log(`\nDownloading video to ./output/${videoId}.mp4...`);

  const outputDir = "./output";
  if (!fs.existsSync(outputDir)) {
    fs.mkdirSync(outputDir, { recursive: true });
  }

  const videoResponse = await fetch(`${baseUrl}/videos/${videoId}/content`, {
    headers: {
      Authorization: `Bearer ${apiKey}`,
    },
  });

  const outputPath = path.join(outputDir, `${videoId}.mp4`);
  const videoBuffer = Buffer.from(await videoResponse.arrayBuffer());
  fs.writeFileSync(outputPath, videoBuffer);

  if (fs.existsSync(outputPath)) {
    const stats = fs.statSync(outputPath);
    console.log(`Video saved to ${outputPath}`);
    console.log(`File size: ${stats.size} bytes`);
  } else {
    console.log("Failed to download video");
    process.exit(1);
  }
}

main().catch(console.error);