راهنمای فنی برای توسعه دهندگان- سرویس API پلاتونیا (گیت‌وی)

برای استفاده از مدل‌های تولید ویدیو مانند sora-2 و sora-2-pro

خلاصه
– به‌جای کلید OpenAI، از «کلید اختصاصی Platonia (با پیشوند `pltn_`)» استفاده می‌کنید.
– درخواست‌ها را به «Endpoint واحد گیت‌وی» می‌فرستید و فقط با پارامتر `openai_path`، مسیر واقعی OpenAI را مشخص می‌کنید.
– ساخت ویدیو «آسنکرون» است:
  – `POST /videos` ⬅️ شروع Job و دریافت `video_id` + `status`
  – `GET /videos/{video_id}` ⬅️ وضعیت و پیشرفت
  – `GET /videos/{video_id}/content` ⬅️ دانلود MP4 / thumbnail / spritesheet
  – `POST /videos/{video_id}/remix` ⬅️ ریمیکس
  – `GET /videos` ⬅️ لیست ویدیوهایی که با همین کلید از طریق گیت‌وی ساخته‌اید
  – `DELETE /videos/{video_id}` ⬅️ حذف ویدیو
گیت‌وی پلاتونیا برای مدل‌های Sora؛
– هزینه را بر اساس ثانیه و مدل (`sora-2` یا `sora-2-pro`) محاسبه می‌کند.
– برای ساخت ویدئو (`POST /videos`) قبل از تماس با OpenAI، اعتبار شما را پیش‌چک می‌کند؛ اگر کافی نباشد، خطا دریافت می‌کنید و هیچ هزینه‌ای از شما کم نمی‌شود.
– ویدیوها را در سطح کلید ایزوله می‌کند: فقط همان کلید (کاربر) که ویدیو را ساخته، می‌تواند آن را ببینید/دانلود/ریمیکس یا حذف کند.

۱) مفاهیم کلیدی سرویس و Endpointها

– Endpoint ثابت گیت‌وی (سرویس اصلی پلاتونیا):

 

“`text
https://platonia.co/wp-json/platonia-dev/v1/proxy
“`

 

> همیشه با متد `POST` به همین URL درخواست می‌فرستید؛ نوع عملیات واقعی را با پارامتر `method` در بدنه مشخص می‌کنید.

 

– احراز هویت:

 

“`http
Authorization: Bearer USER_API_KEY
“`

 

– `USER_API_KEY` همان «کلید اختصاصی پلاتونیا» است که با `pltn_` شروع می‌شود.

 

– پارامترهای کلیدی در بدنهٔ JSON:

 

  – `openai_path`  
    مسیر اصلی OpenAI بدون `/v1/`:
  •  `videos` ⬅️ ساخت ویدئو (`POST`) یا لیست (`GET`)
  •  `videos/{video_id}` ⬅️ وضعیت (`GET`) / حذف (`DELETE`)
  •  `videos/{video_id}/content` ⬅️ دانلود باینری (`GET`)
  • `videos/{video_id}/remix`⬅️ ریمیکس (`POST`)

 

 – `method`  

  •     یکی از متدهای `POST`، `GET` یا `DELETE` (نوع درخواست واقعی سمت OpenAI).
 
  – `model`  
    یکی از مدل‌های ویدیویی مجاز شما، معمولاً:
  • `sora-2`
  • `sora-2-pro`
 
  – سایر پارامترها:
  • `prompt`، `size` (مثل `1280×720`)، `seconds` (مثلاً `”8″`)، `variant` برای دانلود (video / thumbnail / spritesheet)، و در صورت نیاز `input_reference_*` برای تصویر مرجع.
 
– هدرهای مفید در پاسخ:

 

  – `X-Gateway`: همیشه `platonia-dev`
  – `X-Tokens-Used`: واحد خام مصرف‌شده (برای sora معادل ثانیه × ضریب داخلی)
  – `X-Cost-Credits`: هزینه اعشاری (قبل از گرد کردن)
  – `X-Deducted-Units`: واحد نهایی کسرشده از اعتبار
  – `X-Key-Cost-Used`: هزینه تجمیعی این کلید تا الان
  – `X-Key-Cost-Cap`: سقف هزینه این کلید (اگر تعریف شده باشد)
  – `X-Elapsed-ms`: زمان اجرای درخواست سمت گیت‌وی (برحسب میلی‌ثانیه)

 

 
– خطاهای پرتکرار:

 

  – `401 invalid_api_key` → کلید اشتباه یا Revoke شده
  – `403 domain_forbidden` → دامنهٔ شما در لیست مجاز کلید ثبت نشده
  – `400 model_not_allowed` → مدل ویدیویی در allowlist اکانت شما فعال نشده
  – `402 no_credit` / `429 quota_exceeded` / `quota_would_be_exceeded` → اعتبار شما کافی نیست
  – `429 key_cost_exceeded` → سقف هزینهٔ این کلید پر شده
  – `500 server_misconfigured` → کلید OpenAI سمت سرور تنظیم نشده
  – `502 upstream_error` → خطای OpenAI یا شبکه

 

 
۲) پیش‌نیازهای استفاده
در پلاتونیا ثبت‌نام کنید و وارد حساب کاربری خود شوید.
در پنل ««سرویس API»»:
  • یک کلید API بسازید (شکل: `pltn_…`).
  • دامنه‌های مجاز را برای همان کلید ثبت کنید (مثلاً `example.com` یا `*.example.com`).

 اعتبار خود را به‌صورت ریالی شارژ کنید. (در بدو عضویت، مقداری اعتبار رایگان به عنوان هدیه به شما تعلق می‌گیرد)

 مطمئن شوید مدل‌های `sora-2` و/یا `sora-2-pro` در لیست مدل‌های مجاز شما فعال شده‌اند (این موارد توسط ادمین پلاتونیا فعال شده‌اند).
 اگر از مرورگر مستقیماً به گیت‌وی وصل می‌شوید:
   – دامنهٔ Frontend شما باید حتماً جزو دامنه‌های مجاز آن کلید باشد، وگرنه `403 domain_forbidden` می‌گیرید.

۳) نمونه درخواست‌های ساخت ویدیو (Create) متن به ویدیو (چند زبان)

۱-۳) ساخت ساده (بدون تصویر مرجع)

JavaScript (Browser / Frontend)
				
					const ENDPOINT = 'https://platonia.co/wp-json/platonia-dev/v1/proxy';
const USER_KEY = 'pltn_xxx...'; // کلید پلاتونیا

async function createVideo(prompt) {
  const payload = {
    openai_path: 'videos',   // معادل POST /v1/videos
    method: 'POST',
    model: 'sora-2',         // یا 'sora-2-pro'
    prompt,
    size: '1280x720',        // رزولوشن خروجی
    seconds: '8'             // طول ویدیو به ثانیه
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'Request failed');
  }
  // data شکل شیٔ ویدیوی OpenAI است
  // { id, status, model, progress, seconds, size, ... }
  return data;
}

// نمونه استفاده
createVideo('Wide shot of a teal coupe driving through a desert highway, cinematic, golden hour.')
  .then(v => console.log('Video job started:', v.id, v.status))
  .catch(console.error);
				
			
Python (requests)
				
					import requests

ENDPOINT = 'https://platonia.co/wp-json/platonia-dev/v1/proxy'
USER_KEY = 'pltn_xxx...'

payload = {
    'openai_path': 'videos',
    'method': 'POST',
    'model': 'sora-2-pro',
    'prompt': 'A cinematic shot of ocean waves at sunset, slow camera dolly.',
    'size': '1280x720',
    'seconds': '8'
}

r = requests.post(ENDPOINT, json=payload, headers={
    'Authorization': f'Bearer {USER_KEY}'
})
data = r.json()
if not r.ok:
    raise SystemExit(data.get('message') or (data.get('error') or {}).get('message') or 'Request failed')

print('Job started:', data['id'], data['status'])
				
			
cURL (CLI)
				
					curl -X POST https://platonia.co/wp-json/platonia-dev/v1/proxy \
  -H "Authorization: Bearer pltn_xxx..." \
  -H "Content-Type: application/json" \
  -d '{
    "openai_path": "videos",
    "method": "POST",
    "model": "sora-2",
    "prompt": "A video of the words \"Thank you\" in sparkling letters",
    "size": "1280x720",
    "seconds": "8"
  }'
```

پاسخ، آبجکت ویدیو با `id` و `status` (مثلاً `queued` یا `in_progress`) است.

---
				
			

۴) ساخت ویدیو با تصویر مرجع (Input Reference)

برای اینکه  فریم اول ویدیو دقیقاً مطابق یک تصویر مشخص باشد (لوگو، کاراکتر، محیط خاص)، می‌توانید یک تصویر مرجع بفرستید.
– فرمت مجاز: `image/jpeg`, `image/png`, `image/webp`
–  الزام OpenAI: ابعاد تصویر مرجع باید با `size` ویدیو کاملاً یکسان باشد (مثلاً برای `1280×720` باید `1280×720` پیکسل باشد).
در گیت‌وی پلاتونیا، ساده‌ترین راه ارسال تصویر به‌صورت Base64 در JSON است؛ گیت‌وی خودش آن را به `multipart/form-data` تبدیل و به OpenAI ارسال می‌کند.
JavaScript (Browser / Frontend)
				
					async function createVideoWithImage(prompt, file) {
  // file: از input[type=file]
  const arrayBuffer = await file.arrayBuffer();
  const b64 = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));

  const payload = {
    openai_path: 'videos',
    method: 'POST',
    model: 'sora-2-pro',
    prompt,
    size: '1280x720',     // باید با رزولوشن تصویر هم‌خوانی داشته باشد
    seconds: '8',
    input_reference_b64: b64,
    input_reference_name: file.name,
    input_reference_mime: file.type || 'image/jpeg'
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'Request failed');
  }
  return data;
}
```


				
			
نکته: اگر ابعاد تصویر مرجع با `size` انتخابی هم‌خوانی نداشته باشد، OpenAI درخواست را رد می‌کند. در سمت کلاینت/سرور می‌توانید قبل از ارسال، با کتابخانه‌های تصویر ابعاد را چک کنید.
مهم: OpenAI قوانین سخت‌گیرانه‌ای برای تولید ویدیو از تصویر مرجع دارد. در عمده‌ی موارد، تصاویر افراد واقعی، و یا تصاویر دارای حق مالکیت، از طرف OpenAI ریجکت می‌شوند.

۵) Polling وضعیت و پیشرفت (`GET /videos/{id}`)

ساخت ویدیو ممکن است  چند ده ثانیه تا چند دقیقه طول بکشد. برای بروزرسانی UI، می‌توانید وضعیت را دوره‌ای (مثلاً هر ۱۰–۲۰ ثانیه) Poll کنید.
JavaScript – Polling ساده با Backoff
				
					async function getVideoStatus(videoId) {
  const payload = {
    openai_path: `videos/${videoId}`,
    method: 'GET'
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'Request failed');
  }
  // data: { id, status, progress, model, seconds, size, ... }
  return data;
}

async function pollUntilDone(videoId) {
  let delay = 10_000;       // 10 ثانیه
  const maxDelay = 25_000;  // حداکثر ~25 ثانیه

  while (true) {
    const v = await getVideoStatus(videoId);
    console.log('Status:', v.status, 'Progress:', v.progress ?? '-');

    if (v.status === 'completed') return v;
    if (v.status === 'failed') throw new Error('Video failed: ' + (v.error?.message || ''));

    await new Promise(r => setTimeout(r, delay));
    delay = Math.min(maxDelay, Math.round(delay * 1.5));
  }
}
				
			
وضعیت‌های معمول:
  • `queued`
  • `in_progress`
  • `completed`
  • `failed`

۶) دانلود ویدیو و Assetها (`/videos/{id}/content`)

وقتی ویدیو `completed` شد:

 

– برای MP4 اصلی:
  – `GET /videos/{video_id}/content` (بدون `variant` یا با `variant=video`)
 
– برای Thumbnail:
  – `variant=thumbnail`
 
– برای Spritesheet:
  – `variant=spritesheet`

 

 
در گیت‌وی:

 

– همچنان با POST به Endpoint گیت‌وی می‌روید، اما در بدنه:
  – `openai_path: “videos/{id}/content”`
  – `method: “GET”`
  – و در صورت نیاز `variant`
JavaScript – دریافت به‌صورت Blob در مرورگر
				
					async function downloadVideoBlob(videoId, variant = 'video') {
  const payload = {
    openai_path: `videos/${videoId}/content`,
    method: 'GET'
  };
  if (variant && variant !== 'video') payload.variant = variant;

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  if (!res.ok) {
    let err;
    try { err = await res.json(); } catch (e) {}
    throw new Error(err?.message || (err?.error && err.error.message) || 'Download failed');
  }
  const blob = await res.blob();
  return blob;
}

// مثال: ذخیرهٔ ویدیو در مرورگر
downloadVideoBlob('video_abc123').then(blob => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'video.mp4';
  a.click();
  URL.revokeObjectURL(url);
}).catch(console.error);
				
			
Python – ذخیره روی دیسک
				
					import requests

ENDPOINT = 'https://platonia.co/wp-json/platonia-dev/v1/proxy'
USER_KEY = 'pltn_xxx...'
VIDEO_ID = 'video_abc123'

payload = {
    'openai_path': f'videos/{VIDEO_ID}/content',
    'method': 'GET',
    'variant': 'video'  # یا 'thumbnail' / 'spritesheet'
}

r = requests.post(ENDPOINT, json=payload, headers={
    'Authorization': f'Bearer {USER_KEY}'
}, stream=True)

if not r.ok:
    try:
        data = r.json()
        raise SystemExit(data.get('message') or (data.get('error') or {}).get('message') or 'Download failed')
    except ValueError:
        raise SystemExit('Download failed')

with open('video.mp4', 'wb') as f:
    for chunk in r.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

print('Saved video.mp4')
				
			
نکته: در سمت گیت‌وی، برای عبور صحیح باینری، گزینهٔ Allow Binary Download باید فعال باشد (در سرویس رسمی پلاتونیا فعال است).

۷) ریمیکس ویدیو (`POST /videos/{id}/remix`)

ریمیکس به شما اجازه می‌دهد روی یک ویدیوی کامل‌شده، تغییرات «هدفمند» و «کوچک» اعمال کنید (مثلاً فقط تغییر رنگ یا نور)، بدون تولید دوبارهٔ کل صحنه.
 
– ورودی:
  • `video_id` ویدیوی مبدا
  • `prompt` جدید که تغییر را توصیف می‌کند
– مثال تغییر پالت رنگ، اضافه‌کردن یک عنصر کوچک، تغییر سرعت، و…
ریمیکس با JavaScript
				
					async function remixVideo(sourceId, prompt) {
  const payload = {
    openai_path: `videos/${sourceId}/remix`,
    method: 'POST',
    prompt
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'Remix failed');
  }
  // data.id ویدیوی جدید است
  return data;
}

remixVideo('video_abc123', 'Shift the color palette to teal, sand, and rust, with a warm backlight.')
  .then(v => console.log('Remix started:', v.id, v.status))
  .catch(console.error);
				
			
توجه: اگر با کلید API مشخصی، یک ویدیو ساخته باشید، فقط با همان کلید مجاز به ریمیکس ویدیو خواهید بود. در صورت تغییر دادن کلید API خطای `403 video_forbidden` دریافت خواهید کرد.

۸) لیست و حذف ویدیوها

۱-۸) لیست (`GET /videos` – محلی روی گیت‌وی)

گیت‌وی برای هر کلید، یک جدول محلی از ویدیوهایی که با همان کلید ساخته شده‌اند نگه می‌دارد. 
				
					json
{
  "openai_path": "videos",
  "method": "GET",
  "limit": 20,
  "order": "desc"
}
				
			

می‌توانید «لیست محلی» ویدیوهای آن کلید را دریافت کنید (بدون تماس با OpenAI):

JavaScript

				
					async function listMyVideos(pageSize = 20) {
  const payload = {
    openai_path: 'videos',
    method: 'GET',
    limit: pageSize,
    order: 'desc'
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'List failed');
  }
  // data.data → آرایه‌ای از { id, object:'video', created_at, status:'unknown' }
  return data.data;
}
				
			
توجه: این لیست فقط شامل ویدیوهایی است که از طریق همین گیت‌وی و با همین کلید ساخته شده‌اند؛ همگام‌سازی کامل با تاریخچهٔ OpenAI انجام نمی‌شود.

۲-۸) حذف ویدیو (`DELETE /videos/{id}`)

JavaScript

				
					async function deleteVideo(videoId) {
  const payload = {
    openai_path: `videos/${videoId}`,
    method: 'DELETE'
  };

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + USER_KEY
    },
    body: JSON.stringify(payload)
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.message || (data.error && data.error.message) || 'Delete failed');
  }
  return true;
}
				
			

توجه: فقط صاحب همان کلید می‌تواند ویدیو را حذف کند؛ در غیر این صورت `403 video_forbidden` دریافت می‌شود.

۹) مدیریت خطا، کوئوتا و پایداری

– پیام خطا را همیشه از `data.message` یا `data.error.message` بخوانید تا از نمایش `[object Object]` جلوگیری شود.
– خطاهای مهم:
  •  `401 invalid_api_key` ⬅️ کلید را بررسی کنید؛ باید با `pltn_` شروع شود.
  •  `403 domain_forbidden` ⬅️ دامنهٔ Origin/Referer را به لیست دامنه‌های مجاز در پنل سرویس API اضافه کنید.
  •  `400 model_not_allowed` ⬅️ مدل `sora-2` / `sora-2-pro` برای شما فعال نشده؛ با پشتیبانی یا ادمین هماهنگ کنید.
  •  `402 no_credit` / `429 quota_exceeded` ⬅️ اعتبارتان تمام شده؛ باید شارژ کنید.
  •  `429 quota_would_be_exceeded` ⬅️ این درخواست، اعتبار باقیمانده را رد می‌کند (مثلاً `seconds` زیاد).
  •  `502 upstream_error` ⬅️ اختلال OpenAI یا شبکه؛ کمی صبر و **Backoff نمایی** کمک می‌کند.
Backoff نمایی ساده (JS)
				
					async function withBackoff(fn, max = 5) {
  let delay = 1000;
  for (let i = 0; i < max; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === max - 1) throw e;
      await new Promise(r => setTimeout(r, delay * (1 + Math.random())));
      delay *= 2;
    }
  }
}
				
			

۱۰) امنیت، CORS و دامنه‌های مجاز

– اگر از مرورگر مستقیماً به گیت‌وی وصل می‌شوید:
  •  هدرهای `Origin` یا `Referer` شما توسط گیت‌وی خوانده می‌شود.
  • این دامنه باید در لیست دامنه‌های مجاز آن کلید (در پنل پلاتونیا) ثبت شده باشد، در غیر این صورت خطای `403 domain_forbidden` دریافت می‌کنید.
  • الگوی `*.example.com` برای wildcard پشتیبانی می‌شود.

 

– برای  محیط Production توصیه می‌شود:

  •  درخواست‌ها را از  بک‌اند خودتان  (Node, Python, PHP, …) به گیت‌وی بفرستید.
  • کلید `pltn_…` را در متغیر محیطی یا Secret Manager نگه دارید، نه در Frontend.

 

– گیت‌وی:

  • کلید OpenAI را فقط روی سرور نگه می‌دارد و هرگز آن را به کلاینت برنمی‌گرداند.
  • ویدیوها را per‑key ایزوله می‌کند: اگر با کلید B به ویدیوی ساخته‌شده باشد و سعی کنید با کلید A دسترسی بگیرید، `403 video_forbidden` دریافت می‌کنید.

۱۱) نکات مهم برای کار با Sora از طریق گیت‌وی

انتخاب مدل:
  • `sora-2` ⬅️ ارزان‌تر و سریع‌تر برای ایده‌پردازی، سوشال و Preview
  • `sora-2-pro` ⬅️ خروجی باکیفیت‌تر برای کارهای نهایی، تبلیغات و تدوین جدی‌تر
 
Prompt خوب بنویسید:
  • نوع شات (wide, close-up)، سوژه، حرکت دوربین، نورپردازی، زمان روز و حس صحنه را توصیف کنید.
  • مثال:  «Wide shot of a child flying a red kite in a grassy park, golden hour, slow pan upward.»
  • پرامپت فارسی هم مجاز است.
 
طول ویدیو (`seconds`) را منطقی تنظیم کنید:
  • هر ثانیهٔ بیشتر روی هزینه و زمان رندر اثر مستقیم دارد.
  • ابتدا با ویدیوهای کوتاه (۴ ثانیه) تست کنید، سپس به طول‌های بیشتر بروید.
 
رزولوشن (`size`):
  • سایزهای رایج: `1280×720`, `720×1280`, `1920×1080`.
  • اگر از تصویر مرجع استفاده می‌کنید، حتماً رزولوشن فایل با `size` یکسان باشد.
 
نگهداری ویدیوها:
  • لینک دانلودی OpenAI معمولاً **تا ~۱ ساعت معتبر** است؛ با توجه به این، فایل را به سسیتم ذخیره‌سازی خود (S3، فضای ابری، سرور فایل) منتقل کنید.
 
مصرف اعتبار:
  • برای Sora، مصرف بر اساس `seconds × ضریب مدل` محاسبه می‌شود؛ خطاهای ۴۰۲/۴۲۹ را جدی بگیرید.
  • هدرهای `X-Tokens-Used`, `X-Cost-Credits`, `X-Deducted-Units` را برای مانیتورینگ مصرف می‌توانید لاگ کنید.
 
Streaming (SSE):
  • API ویدیوی Sora ذاتاً آسنکرون است (Job + Polling)، و در نسخه فعلی گیت‌وی پاسخ به‌صورت JSON عادی برمی‌گردد؛ SSE برای ویدیو اینجا موضوعیت ندارد (دانلود نهایی MP4 به‌صورت باینری انجام می‌شود).

۱۲) افزونه وردپرسی PlatoniaVid

اگر ادمین یا طراح سایت‌های وردپرسی هستید، می‌توانید به‌جای پیاده‌سازی UI از صفر، از افزونهٔ PlatoniaVid – Sora Video Builder استفاده کنید که همین گیت‌وی را در پشت صحنه صدا می‌زند و فرم ساخت، لیست و ریمیکس ویدیو را آماده در اختیار شما می‌گذارد؛ اما برای استفادهٔ مستقیم از API، الگوهای بالا تمام چیزی است که نیاز دارید.