📘

เอกสารเทคนิคฉบับสมบูรณ์

SPVDO AI Video System — สร้างวิดีโอโฆษณาสินค้า Shopee ด้วย Google Veo

โดเมน https://spvdo.sj88ai.com · อัปเดตล่าสุด 8 ก.ย. 2026 · เวอร์ชัน 1.0

1ภาพรวมระบบ

ระบบสร้างวิดีโอโฆษณา AI ติดตั้งแยกบริการ (service) ของตัวเอง ทำงานคู่กับเว็บหลัก spvdo (docker) — ผู้ใช้กดปุ่มบนเว็บหลักแล้วเด้งมาระบบนี้ผ่าน path /v1/

🛒 เว็บหลัก spvdo (docker) ปุ่ม 🎬 ทำ VDO (spvdo-button.js) /v1/freestyle.html หรือ /v1/catalog.html Service :8100 (uvicorn) genaipro.io (Veo) 📥 MP4 4 คลิป + ลิงก์แชร์ + ลิงก์ Shopee
คำถามคำตอบสั้น
ผู้ใช้ต้องใส่คีย์ไหมไม่ต้อง — ใช้คีย์กลางอัตโนมัติ (ใครมีคีย์ตัวเองก็ใช้ของตัวเองได้)
ต้องสมัครไหมไม่ต้อง (Guest จำ 1 ปี) — สมัครอีเมลเพื่อย้ายประวัติข้ามเครื่อง
ได้กี่คลิป4 คลิป/สินค้า/รอบ · Auto Retry สูงสุด 10 · จำกัด 3 งานพร้อมกัน/คน, 2 งานทั้งระบบ
สเปกคลิปMP4 · H264 · 720×1280 (9:16) · 24fps · 8 วินาที (เลือก 16:9 / 1:1 ได้)

2หน้าเว็บทั้งหมด (URL Map)

URLหน้าที่ที่มา
/หน้าหลักเว็บ spvdo + แบนเนอร์ VDO + ปุ่มทำ VDO ทุกการ์ดเว็บหลัก
/v1/freestyle.html?pid=&sku=&source=catalogหน้าสร้างเดี่ยว (replica ต้นฉบับ ttvip V1) — สินค้า+รูป+prompt โหลดอัตโนมัติไฟล์จริงจาก V1
/v1/catalog.htmlแค็ตตาล็อกของเรา — ค้นหาสินค้าจริง 1M+ / เลือกหลายตัว / รันทั้งชุดสร้างเอง
/v1/history.htmlประวัติเต็มรูปแบบ — สถิติ/ฟิลเตอร์/ค้นหา/ดู 4 คลิป/ลบ/ล้าง + ลิงก์แชร์ + ลิงก์ Shopeeสร้างเอง
/v1/login.htmlล็อกอิน/สมัคร VDO (เมลใหม่สมัครให้เอง + ย้ายประวัติ Guest)สร้างเอง
/v1/keys.htmlจัดการคีย์ส่วนตัว (BYOK) — เพิ่ม/สลับ/ลบ เข้ารหัส Fernetสร้างเอง
/v1/manual.htmlคู่มือผู้ใช้ (รูป screenshot จริง + คลิปจริงจาก Veo)สร้างเอง
/v1/docs.htmlเอกสารเทคนิคฉบับนี้สร้างเอง
/v1/share/{token}/{ไฟล์}.mp4ลิงก์วิดีโอสาธารณะ (เปิดได้ทุกที่ ไม่ต้อง login — ปิดได้ทันที)backend

3สถาปัตยกรรม + โครงสร้างไฟล์

เครื่องนี้ (spvdo.sj88ai.com)
├── nginx (host) — /etc/nginx/sites-available/spvdo.sj88ai.com
│   ├── location ^~ /v1/api/auth/       → 127.0.0.1:8100 (rate 10r/s)
│   ├── location ^~ /v1/api/freestyle/submit → :8100 (burst 5)
│   ├── location ^~ /v1/                → 127.0.0.1:8100 (VDO ทั้งหมด)
│   └── location / (เว็บหลัก)            → /opt/spvdo/www-cache (HTML no-cache)
│       └── block *.bak · block wp-admin/.env/.git
│
├── docker (เว็บหลักเดิม — ไม่เกี่ยวกับ VDO โดยตรง)
│   ├── spvdo-api-1  → 127.0.0.1:18765 (/api/research/list, /api/auth/* ของเว็บหลัก)
│   ├── spvdo-ssr-1  → 127.0.0.1:18766
│   └── volume data  → /var/lib/docker/volumes/spvdo_shopee_data/…/shopee/shopee.db (10GB)
│
└── ระบบ VDO (ของเรา)
    ├── systemd: veo-freestyle.service → uvicorn :8100 · Restart=always · enabled
    └── /root/veo-freestyle/
        ├── .env                      ← 🔑 คีย์กลาง + ค่าตั้ง (chmod 600)
        ├── app/
        │   ├── main.py               FastAPI ทุก route + middleware guest + SSRF guard
        │   ├── keys.py               BYOK เข้ารหัส Fernet + resolve user→central
        │   ├── veo.py                ตัวคุยกับ genaipro.io (สร้าง/poll/โหลด)
        │   ├── jobs.py               worker pool (2) + retry state machine
        │   ├── store.py              jobs JSON-per-file + delete/clear + recovery
        │   ├── identity.py           guest cookie 1 ปี + PBKDF2 + session 365 วัน
        │   ├── prompts.py            template V1 (STRICT TIMELINE + negative)
        │   ├── images.py             Pillow composite รวมรูปทุกช่อง
        │   └── config.py             อ่าน .env + hard-fail ถ้าไม่มี SESSION_SECRET
        ├── static/v1/                หน้าเว็บทั้งหมด (freestyle=ไฟล์จริง V1, ที่เหลือสร้างเอง)
        ├── data/                     products.json · users.json · user_keys.json · mock
        └── storage/                  jobs/ · outputs/ · uploads/ · work/ (GC 30 วัน)

4API Reference (mount ทั้ง /api และ /v1/api)

Identity / Auth

Method + Pathรายละเอียด
GET /v1/api/auth/meตัวตนปัจจุบัน — request แรกไม่มี cookie จะได้ guest ref + Set-Cookie HttpOnly/Secure/SameSite=lax อายุ 1 ปี ทันที · มี field videoMode: mock|veo
POST /v1/api/auth/email{email, password≥8, name?} — เมลใหม่=registered / เมลเดิม=logged_in (ผิด=401) · claim งาน Guest เข้าบัญชีเฉพาะตอนยังเป็น guest → คืน claimedJobs
POST /v1/api/auth/logoutล้าง session cookie → กลับ guest

BYOK Keys

GET /v1/api/keys/meรายการคีย์ (prefix 12 ตัวเท่านั้น) + centralAvailable + mode
POST /v1/api/keys/add{name, key} — ต้องขึ้นต้น sk_gap_ · เก็บ เข้ารหัส Fernet (PBKDF2 จาก SESSION_SECRET)
POST /v1/api/keys/{id}/activeสลับคีย์ที่ใช้
DELETE /v1/api/keys/{id}ลบคีย์ (ไม่เหลือ = กลับใช้คีย์กลาง)

Products

GET /v1/api/products/ensure?itemid=&title=&image=&price=&cat=&brand=&score=ลงทะเบียนสินค้าจากหน้าเว็บ — เสริมข้อมูลจาก DB จริงอัตโนมัติ: shopee_link, shop_name, cat 2 ชั้น, sold, rating (route นี้ต้องมาก่อน {pid})
GET /v1/api/products/{pid}ข้อมูลสินค้า (freestyle ใช้โหลด banner)

AI

POST /v1/api/ai/refine{prompt, mode, camera?, lighting?, thai_speech?} → แปลงเป็นบทกำกับ V1 + negative_prompt (แยก 2 โหมด)
POST /v1/api/ai/storyboardบท 3 องก์ (Hook→Demo→Social Proof) — engine: heuristic

Jobs (หัวใจ)

POST /v1/api/freestyle/submitmultipart: file1-3 + file2_url + prompt/mode/aspectRatio/camera/lighting/thaiSpeech/maxAttempts/productId → dedup (prompt+productId เดิมที่ยังรัน = คืน job เดิม) · cap 3 งาน/คน (429) · composite รวมรูปทุกช่อง
POST /v1/api/freestyle/queue-product{product_id, mode, prompt?} — ดึงรูปสินค้าเอง + prompt อัตโนมัติ (drawer ของ V1 ใช้)
POST /v1/api/freestyle/queue-batch{products:[{itemid,title,image,…}], mode} หรือ {product_ids:[…]} — สูงสุด 20/ชุด
GET /v1/api/freestyle/jobs?limit=รายการของตัวเอง (≤100) — แนบ product ล่าสุดเสมอ (มี shopee_link)
GET /v1/api/freestyle/jobs/{id}สถานะ/log/videos/retry — frontend poll ทุก 3.5 วิ
DELETE /v1/api/freestyle/jobs/{id}ลบ + ลบไฟล์ mp4 ด้วย · งานกำลังรัน = 409
POST /v1/api/freestyle/jobs/clear{status:"finished"} ล้างเฉพาะงานจบ + ลบไฟล์ตาม
POST /v1/api/freestyle/jobs/{id}/share{enabled:true/false} → shareToken 32 hex · token คงเดิมตลอด (ปิด→เปิด ลิงก์เดิมกลับมา)
GET /v1/share/{token}/{file}.mp4ลิงก์สาธารณะ ไม่ต้อง login · Cache-Control public 1 ชม. · ปิดแชร์แล้ว 404 ทันที

ตัวอย่างเร็ว

# ล็อกอิน
curl -c c.txt -X POST https://spvdo.sj88ai.com/v1/api/auth/email \
  -H "Content-Type: application/json" \
  -d '{"email":"sj@sj.com","password":"•••"}'

# ส่งงานจากสินค้า
curl -b c.txt -X POST https://spvdo.sj88ai.com/v1/api/freestyle/queue-product \
  -H "Content-Type: application/json" -d '{"product_id":"26311859091"}'

# poll จน completed → โหลดคลิป
curl -b c.txt https://spvdo.sj88ai.com/v1/api/freestyle/jobs/{jobId}
curl -b c.txt -O https://spvdo.sj88ai.com/v1/api/freestyle/output/{jobId}.mp4

5ระบบคีย์ (เหมือน V1)

สร้างงาน มีคีย์ผู้ใช้ (active)?→ ใช่:🔑 คีย์ของผู้ใช้ → ไม่:🏢 คีย์กลาง (.env)

6Provider: genaipro.io

ขั้นเรียก
สร้างงานPOST /v2/veo/ingredients-to-video multipart · 202 = สำเร็จhistories[0].id · timeout 60 วิ (แบบ V1)
pollGET /v2/veo/tasks/{id} ทุก 5 วิ จน completed → file_urls · คุยละะ attempt 600 วิ (10 นาที ตาม V1) · network error/429/5xx ระหว่างรอ = ทน สู้ต่อ ไม่ fail · หมดเวลา → reconcile poll อีกครั้งเดียว แล้วยังไม่จบ = STATE_UNKNOWN หยุด ไม่ยิงซ้ำ
โหลดGET file_url (retry 3 ครั้งต่อไฟล์) → {jobId}.mp4, _v2.._v4
กฎ retry (ตาม V1)retry เฉพาะ: submit 408/425/429/5xx · ConnectTimeout · task failed แบบ transient (เช่น AUDIO_FILTERED) · ห้าม retry: ReadTimeout/หลุดกลางทาง (STATE_UNKNOWN — task อาจถูกเรียกเก็บเงินแล้ว) · 401/403/400 · UNAUTHORIZED/INVALID_API_KEY/INSUFFICIENT_CREDIT/INVALID_ARGUMENT · หน่วงรอบ exponential 5→10→20→40→cap60 วิ
⚠️ บทเรียนสำคัญ provider ตอบ 202 Accepted = งานถูกสร้างแล้ว (ไม่ใช่ error) — ต้องรับ 200/201/202 ทั้งหมด มิฉะนั้นจะ retry ทิ้งซ้ำๆ เปล่า และถ้า timeout กลางทาง = "สถานะไม่ชัด" → หยุด ไม่ยิงซ้ำ (กันเผาเครดิต)

7แหล่งข้อมูล

DB ของ spvdo (อ่านอย่างเดียว — mode=ro)

เส้นทาง: /var/lib/docker/volumes/spvdo_shopee_data/_data/shopee/shopee.db (10GB)

ตารางแถวคอลัมน์สำคัญ
products1,849,493itemid · shopid · shop_name · product_link (ลิงก์ Shopee ตรง) · title · brand · cat1/cat2 · price/sale_price/discount · historical_sold/sold · likes · rating_star · score · is_hot · stock · image_url/image_link · description · seller_name · shop_rating · is_official/is_preferred/is_verified …
price_log26,700,857ประวัติราคารายวัน: snapshot_date · price_change_pct · change_type · stock/sold ย้อนหลัง
ทุกครั้งที่สินค้าเข้าระบบ VDO (ensure/queue) backend จะเปิด DB แบบอ่านอย่างเดียวเสริม: ลิงก์ Shopee · ชื่อร้าน · หมวด 2 ชั้น · ยอดขาย · เรตติ้ง — งานเก่าก็ถูกแนบข้อมูลล่าสุดตอนแสดงผลเสมอ

ไฟล์ JSON ของระบบ

data/products.jsonแคตตาล็อกที่เคยใช้ + ข้อมูล enrich จาก DB
data/users.jsonบัญชีอีเมล — PBKDF2 120k + salt/คน (ไม่มี plaintext)
data/user_keys.jsonคีย์ BYOK เข้ารหัส Fernet ต่อ identity
storage/jobs/*.json1 งาน = 1 ไฟล์ (สถานะ/log/videos/shareToken)
storage/outputs/*.mp4คลิปทั้งหมด — GC เกิน 30 วัน (CLEANUP_DAYS)

8Prompt Engineering

คำสั่งไทยของผู้ใช้ถูกแปลงเป็นบทกำกับมาตรฐานก่อนส่ง Veo เสมอ (นี่คือเหตุผลหลักที่คลิปเนียน):

TIKTOK UGC ECOMMERCE VIDEO (…):
[ATTACHED_FILE_1_CHARACTER] = … / HANDS-ONLY (โหมด Faceless)
SETTING / Thai dialogue: "บทพูดผู้ใช้"
STRICT TIMELINE: 0-2s ชูสินค้าเฟรมแรก / 2-4s สาธิต / 4-6s close-up / 6-8s ปิดการขาย
STRICT RULES: สินค้าตรงรูปต้นฉบับเป๊ะ · ห้ามมือบังโลโก้ · ห้ามหน้าตาการ์ตูน/พลาสติก AI
negative_prompt: (แยกต่างหาก — Faceless เพิ่มชุด "ห้ามเห็นหน้าคน")
ตัดที่ ~1,600 ตัวอักษร

9ความปลอดภัยที่ implement

จุดกลไก
SSRF ผ่าน file2_urlรับเฉพาะ http/https + บล็อก IP private/loopback/link-local + ตรวจ content-type เป็นรูป + cap 10MB
ไฟล์ outputfail-closed (ไม่มี job = 404) · ตอบ 404 เหมือนกันทั้งเจ้าของผิด/ไม่มีไฟล์ (กัน enumeration) · ลบงาน = ลบไฟล์จริง
ลิงก์แชร์token 32 hex เดาไม่ได้ ผูกงานเดียว · ปิด = 404 ทันที · อ่านอย่างเดียว
รหัสผ่านPBKDF2 120k + salt/คน + hmac.compare_digest · เผาเวลาเท่ากันกัน timing oracle
Sessionเซ็น HMAC ฝังวันออก 365 วัน · HttpOnly/Secure/SameSite · ไม่มี SESSION_SECRET จริง = ไม่สตาร์ท
คีย์Fernet at rest · ไม่บันทึกลง job/log
Rate limit (nginx)auth 10r/s · submit burst 5
ต้นทุนcap 3 งาน/คน · worker ทั้งระบบ 2 · dedup งานซ้ำ · "สถานะไม่ชัด = หยุด ไม่ยิงซ้ำ"
อัปโหลด10MB/ไฟล์ + ตรวจนามสกุล + validation ทุก input
การกู้คืนrestart = งานค้างกลายเป็น failed อัตโนมัติ · GC 30 วัน · work dir ลบทุกกรณี
ไฟล์ประจำเครื่อง.env chmod 600 · nginx block *.bak/.env/.git

10Ops Runbook

# สถานะ / log
systemctl status veo-freestyle
journalctl -u veo-freestyle -f

# แก้ค่า (คีย์กลาง/โหมด/จำกัดงาน)
nano /root/veo-freestyle/.env        # GENAIPRO_API_KEY / MOCK_VIDEO / MAX_ACTIVE_JOBS_PER_IDENTITY / CLEANUP_DAYS
systemctl restart veo-freestyle

# สำรอง (แนะนำสัปดาห์ละครั้ง)
tar czf spvdo-vdo-backup-$(date +%F).tar.gz \
  /root/veo-freestyle/storage /root/veo-freestyle/data /root/veo-freestyle/.env

# กู้ nginx config
cp /opt/spvdo/backups/veo-inject/… /etc/nginx/sites-available/spvdo.sj88ai.com && nginx -s reload
⚠️ กฎเหล็ก 2 ข้อ (ระเบิดเวลา)
1) หาก re-run /opt/spvdo/scripts/install_nginx_block.sh — template ต้องมี /v1/ ก่อน (ตอนนี้ sync ไว้แล้วที่ /opt/spvdo/nginx/spvdo.host.conf — อย่าเขียนทับด้วยของเก่า)
2) หาก deploy เว็บหลักทับ www-cache — ต้องแทรก <script src="/v1/spvdo-button.js"> ก่อน </body> ของ index/research/signals/dashboard ใหม่ (สำรองเดิมอยู่ /opt/spvdo/backups/veo-inject/)

11Troubleshooting

อาการสาเหตุ/วิธีแก้
กด "ทำ VDO" เปิด modal เก่าของ spvdoแคชเบราว์เซอร์ → F5 / Ctrl+Shift+R · ถ้าทั้งเว็ย = สคริปต์ถูก deploy ทับ → แทรกใหม่ (ดูกฎเหล็ก 2)
/v1/* ทั้งหมด 404service ตาย (systemctl restart veo-freestyle) หรือ nginx โดนทับ template (กฎเหล็ก 1)
งาน fail "provider ตอบ 401/403"คีย์หมดโควต้า/ไม่ถูก → เช็คคีย์กลางใน .env หรือคีย์ผู้ใช้ที่ /v1/keys.html
งาน fail "หยุดเพื่อป้องกันงานซ้ำ"เน็ตหลุดกลางทาง (งานอาจสำเร็จที่ provider) — กดสร้างใหม่ได้ ระบบไม่ยิงซ้ำเองเพื่อกันเครดิตซ้อน
คลิปเพี้ยน/ไม่ตรงสินค้าธรรมชาติ Veo — เลือกดีที่สุดจาก 4 เวอร์ชัน หรือเจนใหม่ · ใส่รูปสินค้าชัด/มุมเสริมช่วยได้มาก
429 งานเกินcap 3 งาน/คน — รองานเสร็จ (ปรับได้ที่ MAX_ACTIVE_JOBS_PER_IDENTITY)
ลิงก์แชร์เปิดไม่ได้ถูกปิดแชร์หรืองานถูกลบ → กดเปิดแชร์ใหม่ที่หน้าประวัติ (ลิงก์เดิมกลับมา)
ล็อกอินไม่ได้หลังแตะ .envเปลี่ยน SESSION_SECRET = session เดิมตายหมด + คีย์ BYOK ถอดไม่ออก — อย่าเปลี่ยนถ้าไม่จำเป็น

12Changelog

วันที่งาน
7 ก.ย. 66ศึกษา ttvip V1 (read-only) → พอร์ตระบบ: prompt template V1 / guest 1 ปี / claim / retry / 4 คลิป · ติดตั้ง service+nginx+ปุ่มบนเว็บหลัก · E2E ผ่าน
7 ก.ย. 66คู่มือผู้ใช้ + screenshot จริง + วิดีโอตัวอย่าง
7 ก.ย. 66Audit 3 ซับเอเจนต์ (โค้ด/การเชื่อม/production 34 เช็ค) → แก้ 12 จุด: SSRF, fail-open, ระเบิดเวลา nginx template, popup blocker, startup recovery, rate limit, ฯลฯ
7 ก.ย. 66เพิ่ม queue-product/queue-batch + หน้า Catalog ของเรา + cap งาน + composite รวมรูปนายแบบ + GC
7-8 ก.ย. 66ต่อ provider genaipro.io จริง (คีย์กลางใน .env + BYOK Fernet + แก้ bug 202=สำเร็จ) → เจนจริงผ่าน: Diller Thermos 4 คลิป H264 9:16 8 วิ · ซับเอเจนต์ QA เฟรม 7.5/10 · คู่มือเปลี่ยนเป็นคลิปจริง
8 ก.ย. 66ปรับ UX ให้เหมือน V1 (Score log-scale, หมวดไม่ซ้ำ) · หน้าประวัติเต็มรูปแบบ · ลิงก์แชร์วิดีโอ + คัดลอก (token คงเดิมตลอด) · ลิงก์ Shopee + คัดลอก (enrich จาก DB จริง) · หน้า login · แบนเนอร์หน้าหลัก · ทดสอบบัญชี sj@sj.com ผูก/ข้ามเครื่อง ผ่าน
SPVDO AI Video System · เอกสารเทคนิค v1.0 · 8 กันยายน 2026 · ดูคู่มือผู้ใช้ที่ /v1/manual.html