ก่อนอื่น ต้องบอกหากหายจากการเขียน blog ไปเป็นปี แล้วโผล่มาก็เขียนอะไรก็ไม่รู้ แต่ทนๆ อ่านหน่อยนะ 🤣
จุดเริ่มต้น: ความวุ่นวายของคนตามไอดอล
ผมตามไอดลอสายจิกะ (坂道) มาสักพักใหญ่ ๆ ซึ่งคนที่ตามวงเหล่านี้รู้ดีว่า ตารางงานมันเยอะมากและกระจัดกระจาย มีหลายวง หลายช่วงเวลา เราในฐานะผู้ติดตามก็แค่อยากรู้ว่า "วงที่ชอบขึ้นเวทีกี่โมง? และซ้อนทับกับใครบ้าง หากมีหลายเวที" และงานที่ทำให้ผมประสบปัญหามากที่สุดคืองาน Japan Expo ที่ CTW เมื่อเดือนกุมภาพันธ์ที่ผ่านมา โดยงานก็ไม่ได้ให้ข้อมูลที่ดูได้ง่ายนัก ผมจึงทำระบบนี้ขึ้นมาใช้งานเอง ซึ่งก็ได้รับความสนใจจากเพื่อนๆ แฟนคลับอยู่พอสมควร
และพอจบงานดังกล่าว ก็ทำให้รองรับงานที่มีทุกอาทิตย์แทน และปรับปรุงเรื่อยมาโดยให้ตอบโจทย์ความต้องการส่วนตัวเป็นหลัก (คนทำใช้เอง รู้จุดว่าต้องการอะไร)

Idol Stage Timetable คืออะไร
stage-idol-calendar คือระบบปฏิทินอีเวนต์แบบ Open Source ที่ผมออกแบบมาเพื่อจัดการตารางงานแสดงไอดอลโดยเฉพาะ ตอนนี้ใช้งานจริงอยู่ที่ Idol Track — Live Idol TH และยังเอาไปประยุกต์ใช้งานได้หลากหลายอีกด้วย
สิ่งที่มันทำได้ มีอะไรบ้าง:
ตารางงาน
- 3 โหมดการดู — List View (ตารางปกติ), Gantt Chart (Timeline เห็นว่าเวทีไหนซ้อนกัน), และ Calendar View (ปฏิทินรายเดือน สำหรับกิจกรรมสตรีมออนไลน์)
- Multi-Event รองรับหลายงาน — เปลี่ยนงานผ่าน Event Picker Modal แบบ Card Grid ค้นหาได้ กรองตามสถานะ (กำลังจัด / กำลังจะมา / จบแล้ว)
- Homepage Calendar — ปฏิทินบนหน้าแรก กดวันไหนก็เห็นว่ามีงานอะไรบ้าง พร้อมลิงก์ไปดูตาราง
กรอง & ค้นหา
- Advanced Filtering — กรองตามศิลปิน, เวที, ประเภทโปรแกรม (Stage/Booth/Meet & Greet ฯลฯ) หรือพิมพ์ค้นหาคำ
- Quick Filter Badges — เห็นชื่อศิลปินหรือประเภทงานในตาราง กดที่ Badge ได้เลย มันจะเพิ่ม Filter ให้ทันที ไม่ต้องไปเปิด Dropdown
- Program Type System — ระบบประเภทโปรแกรม (free-text + autocomplete) แสดง Badge สีฟ้าบนชื่อรายการ
ศิลปิน & ติดตาม
- Artist Profile — หน้าโปรไฟล์ศิลปินแต่ละคน รวมทุกงานที่เคยขึ้น หากเป็นวงก็แสดงสมาชิกวง และชื่อ Variant/Alias
- Also appears in — ส่วนท้ายหน้าบอกว่าศิลปินจากงานนี้จะไปออกงานอื่นอีกไหม พร้อมลิงก์ข้ามไป
- Anonymous Favorites — Follow ศิลปินโดยไม่ต้อง Login ไม่ต้องสมัคร Account ระบบสร้าง URL ส่วนตัวให้ Bookmark
- My Upcoming Programs — หน้าส่วนตัว รวมโปรแกรมที่กำลังจะมาจากศิลปินที่ Follow ทั้งหมด พร้อม Mini Calendar กดดูรายวันได้
- My Favorites — หน้ารวมศิลปินที่ Follow ไว้ แยกส่วน เดี่ยวหรือกลุ่ม เรียง A→Z หรือ Z→A ได้ กด Unfollow ได้ในคลิกเดียว
- Artist Reuse System — ศิลปินคนเดียว Record เดียว ใช้ซ้ำได้ทุกงาน รองรับชื่อ Variant/Alias
Export & Subscribe
- Export .ics — ดาวน์โหลดตารางที่กรองแล้วเป็นไฟล์ .ics (iCalendar) เอาไปใส่ Google Calendar, Apple Calendar ได้ทันที มี Alarm แจ้งเตือน 15 นาทีก่อนเริ่ม
- Live Subscription Feed — Subscribe ผ่าน
webcal://แล้วปฏิทินจะ Auto-Sync เมื่อตารางเปลี่ยน ไม่ต้อง Export ใหม่ รองรับทั้ง Apple Calendar, Google Calendar, Outlook, Thunderbird - Artist Feed — Subscribe ติดตามรายศิลปิน ปฏิทินจะรวมทุกงานของศิลปินคนนั้นให้ สมาชิกวงก็มี Feed แยกสำหรับงานในนามของวง
- Personal Feed — Subscribe ติดตามเฉพาะศิลปินที่ Follow ไว้ รวมทุกอีเวนต์ในลิงก์เดียว
แชร์ & แสดงผล
- Save as Image — กดปุ่มเดียว ได้ภาพ PNG เก็บบนมือถือ (Render ฝั่ง Server ด้วย PHP GD ไม่พึ่ง JS) ไปแชร์ใน Twitter/Line ได้เลย รองรับ 3 ฟอนต์ (Thai/Latin, CJK, Symbol) และเปลี่ยนสีตามธีม
- Live Stream Links — โปรแกรมที่มี Stream URL จะแสดงไอคอนแพลตฟอร์ม (📷 IG / 𝕏 / ▶️ YouTube / 🔴) พร้อมปุ่ม "เข้าร่วม"
- Request Changes — ผู้ชมส่ง Request เพิ่ม/แก้ไขโปรแกรมได้ มี Rate Limit 10 ครั้ง/ชม.
ภาษา & ธีม
- รองรับ 3 ภาษา — ไทย, อังกฤษ, ญี่ปุ่น (日本語) เปลี่ยนแล้วทุกส่วนอัปเดตทันที รวมถึงปฏิทิน วันเดือน และ Modal ต่าง ๆ
- 7 ธีมสี — Sakura 🌸 (ค่าเริ่มต้น), Ocean, Forest, Midnight, Sunset, Dark, Gray — ตั้งได้ทั้งระดับ Global และ Per-Event
- Site Title & Disclaimer — เปลี่ยนชื่อเว็บ และข้อความ Disclaimer ได้จากหน้า Admin ไม่ต้องแก้โค้ด
สำหรับผู้จัดงาน (Admin)
- Full CRUD — จัดการโปรแกรม, อีเวนต์, ศิลปิน, Credits, Contact Channels ครบจบในหน้า Admin
- Bulk Operations — เลือกหลายรายการแล้ว Edit/Delete ได้ทีเดียว สูงสุด 100 รายการ
- Bulk Import Artists — Import รายชื่อศิลปินทีละ 500 คน พร้อมกำหนดกลุ่มได้
- ICS Upload & Import — อัปโหลดไฟล์ .ics แล้ว Preview ก่อน Import ได้ รองรับ field พิเศษอย่าง
X-PROGRAM-TYPEแล้วรองรับ Variant/Alias ของ Artists เวลา import จะ auto assign Artists ได้เลย - Backup/Restore — Backup ฐานข้อมูล มี Auto-backup ก่อน Restore ทุกครั้ง
- Setup Wizard — ติดตั้งผ่าน Wizard 6 ขั้นตอน รองรับ 2 ภาษา (TH/EN) ไม่ต้องแตะ CLI ก็ได้
เบื้องหลังฟีเจอร์ที่ผมภูมิใจ
Anonymous Favorites — เพราะแค่อยากรู้ว่าวงตัวเองขึ้นกี่โมง
อันนี้เป็นฟีเจอร์ที่ผมอยากได้มาตลอด ในฐานะคนดูที่แค่อยากรู้ว่าวงที่ตามขึ้นเวทีเมื่อไหร่ แต่ปัญหาคือ เว็บแบบนี้มันไม่ควรบังคับให้คนสมัคร Account เพื่อกดติดตามศิลปิน ผู้ชมงานแค่อยากรู้ตารางเวลา ไม่ได้อยากมาจำรหัสผ่านอีกที่ อีกอย่าง จะได้ไม่ต้องวุ่นวายเรื่องการบริหารข้อมูลส่วนบุคคล
หลักคิด: ไม่มี Account แต่ต้อง Secure
ผมออกแบบให้ระบบ Favorites ทำงานโดยไม่มี Database สำหรับ User เลย ไม่มีตาราง users ไม่มี Login ทั้งหมดทำงานผ่าน Token-Based System บน File Storage
กระบวนการทำงานคือ:
สร้าง Token — เมื่อผู้ใช้กด Follow ศิลปินคนแรก ระบบจะสร้าง UUID v7 (time-ordered UUID ที่ sort ตาม timestamp ได้) แล้วนำไป Sign ด้วย HMAC-SHA256 โดยใช้ Secret Key ที่เก็บไว้ฝั่ง Server
Slug = UUID + HMAC ตัดสั้น — ถ้าใครแก้ UUID แล้ว HMAC ไม่ตรง ระบบจะ Reject ทันที ป้องกันการ Enumerate URL ไปดู Favorites ของคนอื่น
เก็บเป็นไฟล์ JSON แบบ Sharded — ข้อมูล Favorites ของแต่ละคนเก็บเป็นไฟล์
.jsonบนโครงสร้าง Sharded Directory เพื่อกระจายไฟล์ไม่ให้โฟลเดอร์เดียวมีไฟล์เป็นหมื่น โดยข้อมูลในไฟล์ก็แค่ Array ของ Artist ID ที่ Follow ไว้localStorage เป็นแค่ Shortcut — Slug จะถูก Save ลง
localStorageของ Browser ด้วย Key หนึ่ง เพื่อให้ครั้งหน้าเปิดเว็บมา ระบบรู้ว่าเป็นคนเดิม แล้วแสดงเมนูระบบ Favorites ที่มุมบนซ้ายของทุกหน้า — โดยที่localStorageไม่ใช่ Source of Truth จริง ๆ มันเป็นแค่ Cache ฝั่ง Client ข้อมูลจริงอยู่ที่ไฟล์ JSON บน ServerTTL 365 วัน + Auto-Touch — ไฟล์
.jsonมีอายุ 1 ปี แต่ทุกครั้งที่เข้ามาดูหน้า My Favorites หรือ My Upcoming Programs ระบบจะ Touch ไฟล์ (อัปเดตmtime) ให้นับ 365 วันใหม่ ตราบใดที่ยังเข้ามาใช้อยู่ ข้อมูลจะไม่หายGarbage Collection แบบ Probabilistic — ฟังก์ชัน cleanup จะสุ่มรันทุกครั้งที่มี Request เข้ามา (ด้วยความน่าจะเป็นต่ำ) เพื่อลบไฟล์ที่หมดอายุออก ไม่ต้องตั้ง Cron Job แยก
Recovery UX — ไม่ต้องเปิด DevTools
ปัญหาที่เจอคือ ถ้า Token ใน localStorage หมดอายุ หรือ User เคลียร์ Browser Data หน้า My Favorites จะเปิดไม่ได้ ตอนแรกต้องเปิด DevTools ลบ key ใน localStorage เอง ซึ่งไม่ User-Friendly เลย ผมเลยเพิ่ม Recovery UX — เมื่อ Token ไม่ Valid ช่วยให้คนใช้งานแก้ไขได้ด้วยตัวเอง นอกจากนี้ยังมี Silent Self-Healing — ทุกครั้งที่แสดงเมนู Favorites มันจะไปเช็ค Slug กับ Server เบื้องหลัง ถ้าได้ 400/404 กลับมา (Token หมดอายุหรือไม่มีอยู่) ก็จะลบข้อมูลใน key จาก localStorage และเอาปุ่มออกเงียบ ๆ ไม่ต้องรอให้ User เจอหน้า Error
ทำไมไม่ใช้ Database? กับระบบ Favorites
เพราะหลักการของโปรเจกต์นี้คือ "ไม่มี Dependencies ที่ไม่จำเป็น" ระบบทั้งหมดรันบน SQLite ตัวเดียว ถ้าเพิ่มตาราง Users + Favorites ลง SQLite มันก็ทำได้ แต่จะเกิดปัญหาอื่น — ต้องมี Session Management สำหรับ Anonymous User, ต้อง GC Session ที่หมดอายุ, ต้อง Handle Race Condition ตอน Write พร้อมกัน (SQLite Lock) สำหรับข้อมูลที่ไม่ Critical อย่าง Favorites List การใช้ File-Based Storage + HMAC Validation มันง่ายกว่า เร็วกว่า และ Scale ได้ดีพอสำหรับ Use Case นี้ (เผื่อรองรับการใช้ Object storage ในอนาคตด้วย จะได้สเกลได้)
สรุปแล้ว Favorites ของแต่ละคนเป็นแค่ไฟล์ .json ขนาดไม่กี่ร้อย Bytes ที่อยู่ในโฟลเดอร์ที่ Protected ด้วย (Deny from all) อ่านไม่ได้จาก Web โดยตรง ต้องผ่าน API เท่านั้น ซึ่ง API ก็ Validate HMAC ทุก Request
สิ่งที่ต่อยอดจาก Favorites
พอมี Favorites แล้ว ผมก็ต่อยอดฟีเจอร์ได้อีกหลายตัว:
- My Upcoming Programs — รวมโปรแกรมจากศิลปินที่ Follow ทุกอีเวนต์ เรียงตามวันเวลา มี Mini Calendar กดดูรายวันได้
- My Favorites Page — แยกส่วน Solo / Group Artists เรียง A→Z / Z→A ได้ กด Unfollow ได้ทันที
- Personal ICS Feed — Subscribe ผ่าน webcal แล้วปฏิทินมือถือจะรวมทุกงานของศิลปินที่ Follow ไว้ให้อัตโนมัติ Admin เพิ่มโปรแกรมใหม่ ปฏิทินก็อัปเดตเอง
- Group Programs Auto-Include — ถ้า Follow ศิลปินที่เป็นสมาชิกกลุ่ม ระบบจะรวมโปรแกรมของกลุ่มเข้ามาใน Feed และ My Upcoming ให้ด้วย ไม่ต้อง Follow กลุ่มแยก
- Persistent Nav Shortcuts — ทุกหน้าของเว็บจะแสดงเมนู Favorites ที่มุมบนซ้าย (ถ้ามี key ใน
localStorage) กดข้ามไปหน้า Favorites หรือ Upcoming ได้ทันที
ทั้งหมดนี้ทำงานได้โดยไม่มี User Account ไม่มี Login ไม่มี Password — แค่ URL เดียวที่ Bookmark ไว้

Server-Side Image Export — แชร์ตารางสวย ๆ โดยไม่พึ่ง JavaScript
ตอนแรกผมใช้ html2canvas แต่มันมีปัญหาเรื่อง layout บนมือถือกับบนคอมพิวเตอร์มันไม่เหมือนกันเลยเปลี่ยนมา Render ฝั่ง Server ด้วย PHP GD ทั้งหมด สร้าง Three-Font Architecture ที่แยก Thai/Latin, CJK (ญี่ปุ่น/จีน), และ Symbol ออกจากกัน ระบบจะวิเคราะห์แต่ละตัวอักษรแล้วเลือกฟอนต์ที่ถูกต้อง ภาพที่ได้จะเปลี่ยนสีตามธีมของแต่ละงานด้วย มี Cache 1 ชม. เพื่อไม่ให้ Server หนัก
Live Stream Links — เพราะยุคนี้งานไอดอลไม่ได้มีแค่ On-site
หลายค่าย หลายวงมีตารางงานให้เมมเบอร์มี Live ไม่ว่าจะ IG Live, X Spaces หรือ YouTube Live ผมเลยเพิ่มระบบ Stream URL เข้าไป
ICS Feed ที่ต่อสู้กับ Outlook
เรื่องนี้สนุก ตอน Implement Subscription Feed ผมเจอบั๊กที่ทำให้ Outlook ลบอีเวนต์ทั้งหมดออกจากปฏิทินทุกรอบที่ Sync เพราะ ORGANIZER;CN="..." มีการ Escape เครื่องหมาย Comma ผิดวิธี (RFC 5545 QUOTED-STRING ไม่ใช้ Backslash Escaping) Outlook Parser เจอ Backslash แปลก ๆ ก็ Reject ทั้งไฟล์ แล้วก็เคลียร์ข้อมูลเก่าออก สุดท้ายแก้โดยเอา ORGANIZER ออกจาก VEVENT ไปเลย เพราะมันเป็น Optional นอกจากนี้ยังต้องจัดการ Line Folding ให้ถูกตาม RFC 5545 (ทุกบรรทัดเกิน 75 bytes ต้องตัด โดยไม่ตัดกลาง Multi-byte character) และแก้ CATEGORIES Delimiter ที่ Escape Comma ผิดทำให้ Outlook รวมศิลปินหลายคนเป็นชื่อเดียว ทุกอย่างมี Test ครอบคลุมหมด
ทำไมถึงเลือก PHP + SQLite + Vanilla JS (ไม่มี Framework)
หลายคนอาจสงสัย ทำไมไม่ใช้ React, Next.js หรือ Framework อะไรสักตัว
คำตอบง่าย ๆ คือ: ผมอยากให้มันเบา, Deploy ง่าย, ใครจะเอาไปใช้ก็แค่มี PHP กับ SQLite ไม่ต้อง Build, ไม่ต้องหา Database Server แยก จะรันบน Shared Hosting ราคาถูก ๆ ก็ได้ หรือจะรันผ่าน Docker ก็มี docker-compose ให้พร้อม
ตัว SQLite เหมาะกับระบบแบบนี้มาก เพราะข้อมูลไม่ได้เยอะขนาดต้องไปใช้ PostgreSQL/MySQL ที่มันยิ่งใหญ่เกินไป แต่เร็วกว่าอ่าน .ics ไฟล์ตรง ๆ ประมาณ 10-20 เท่า
สู่ Open Source
โปรเจกต์นี้เกิดจากความต้องการส่วนตัว 100% ผมอยากมีที่เดียวที่ดูตารางงานไอดอลได้ง่าย ๆ แต่พอทำเสร็จ ผมคิดว่ามันน่าจะมีประโยชน์กับคนอื่นด้วย ไม่ว่าจะเป็นผู้จัดงานที่อยากมี Timetable ให้ผู้ชม หรือแฟนคลับที่อยากจัดการข้อมูลงานของตัวเอง เลยเปิดเป็น Open Source ภายใต้ MIT License ใครจะเอาไปใช้ ดัดแปลง หรือแม้แต่ใช้เชิงพาณิชย์ก็ได้เลย
สิ่งที่อยากทำต่อไป
- หน้ารายการศิลปินและวง
- รองรับ Multi-timezone ให้ดีขึ้น สำหรับคนที่ตามงานจากญี่ปุ่นมาไทย
- ระบบ Notification แจ้งเตือนก่อนงานเริ่ม
- เชื่อมข้อมูลจากแหล่งต่าง ๆ แบบ Semi-auto เช่น ดึงจากเว็บทางการ แต่ลำบากสุดเพราะตอนนี้ทำด้วย OCR และ prompt ที่ทำไว้เป็น template เพื่อให้ได้ .ics โยน import ได้ทันที
- รองรับ PWA เพื่อใช้งานแบบ Offline บนมือถือได้
ลองใช้กันดู
- GitHub: github.com/fordantitrust/stage-idol-calendar
- Live Site: fordantitrust.com/idoltrack/
- Twitter: @FordAntiTrust
ลอง Star ⭐ ให้กำลังใจกันได้ หรือจะ Fork ไปใช้กับงานอีเวนต์ของตัวเองก็ยินดีมาก ๆ ครับ
🌸 Made with ❤️ for event organizers and idol fans everywhere