อัปเดต Stage Idol Calendar v16.5.1: จากปฏิทินดูตารางงานส่วนตัว สู่แพลตฟอร์ม

ต่อเนื่องจากบล็อกที่แล้ว ที่ผมเคยเล่าถึงจุดเริ่มต้นของโปรเจกต์ stage idol calendar ที่ทำขึ้นมาเพื่อแก้ปัญหาความวุ่นวายในการตามตารางงานไอดอล ตอนแรกก็กะว่าทำใช้เองขำๆ ตอบโจทย์ความต้องการส่วนตัวเป็นหลัก แต่ไปๆ มาๆ เมื่อนำไปใช้งานจริงอย่างต่อเนื่อง ตัวระบบมันก็โตขึ้นเรื่อยๆ ตาม Requirement ที่เพิ่มเข้ามา จนตอนนี้ลากยาวมาถึงเวอร์ชัน v16.5.1 เรียบร้อยแล้วครับ

จากระบบ Event-timetable ธรรมดาๆ ตอนนี้มันกลายร่างเป็น “แพลตฟอร์ม” ไปแล้ว วันนี้เลยอยากมาอัปเดตฟีเจอร์ใหม่ๆ และเล่าเบื้องหลังแนวคิดการพัฒนาที่ผมสนุกไปกับมันในช่วงที่ผ่านมาให้ฟังกันครับ

  1. ยกระดับหน้าตาบ้านใหม่ และแถบ Live Now
    – หน้าแรกของระบบถูกรื้อทำใหม่ให้ดูโปรขึ้นในสไตล์ Ticket-Marketplace มี Hero Carousel พร้อมปฏิทินขนาดย่อม และที่ผมชอบเป็นพิเศษคือการทำแถบ Live Now ที่จะโชว์ว่าตอนนี้มีเวทีไหนกำลังแสดงอยู่ หรือกำลังจะเริ่มในอีก 60 นาที โดยตัวระบบจะดึงเวลาจากเครื่องของผู้ชมมาคำนวณและรีเฟรชตัวเองทุกๆ 30 วินาที
  2. ปรับการค้นหาใหม่ทั้งหมด
    – ระบบค้นหา Full-text Search: ใช้พลังของ SQLite FTS5 ทำให้สามารถค้นหา โปรแกรม, อีเวนต์ และศิลปิน ได้อย่างรวดเร็วและแม่นยำมากยิ่งขึ้น
  3. ระบบ Organizer
    – ปลดล็อกให้ผู้จัดงานดูแลตารางตัวเองได้ ซึ่งเมื่อก่อนคนอัปเดตตารางมีแค่ Admin หลัก แต่พอสเกลงานมันใหญ่ขึ้น ผมเลยพัฒนาระบบสิทธิ์ผู้ใช้งานและเพิ่ม Role “Organizer” เข้ามา ตอนนี้ผู้จัดงานสามารถล็อกอินเข้ามาสร้างเวที จัด Line-up และอัปเดตข้อมูลศิลปินในงานของตัวเองได้อย่างอิสระ โดยระบบจะล็อกการมองเห็นและการแก้ไขไว้เฉพาะอีเวนต์ที่ตัวเองรับผิดชอบเท่านั้น
  4. โอน Favorites ข้ามเครื่อง และ PWA Offline Cache
    – จำระบบ Anonymous Favorites แบบไม่พึ่ง Database จากบล็อกที่แล้วได้ไหมครับ? ปัญหาที่เจอต่อมาคือ พอคนเอาเว็บไป Add to Home Screen เป็น PWA บน iOS ตัว Safari มันดันแยกล็อก localStorage ออกจากเบราว์เซอร์หลัก ทำให้วงที่กด Follow ไว้หายหมด!
    – แทนที่จะยอมแพ้แล้วบังคับคนล็อกอิน ผมเลือกแก้ปัญหานี้ด้วย Cross-device Favorites transfer โดยการสร้าง QR Code ขึ้นมา ให้ผู้ใช้เอามือถืออีกเครื่อง (หรือแอปกล้อง) สแกนเพื่อส่งผ่าน Token Slug ไปตั้งค่าใน Environment ใหม่ได้เลย ง่ายและยังคงคอนเซปต์ Privacy ไว้ได้เหมือนเดิม
    – นอกจากนี้ผมยังทำ PWA Offline Cache แบบจริงจัง เพราะเข้าใจฟีลเวลาไปงานใหญ่ๆ ที่คนเยอะๆ แล้วเน็ตมือถือชอบพัง ตัว Service Worker จะช่วยคอยดึงข้อมูลจาก Cache มาแสดง ทำให้เรายังเปิดดูตารางวงที่ชอบต่อได้แม้เน็ตจะดับไปชั่วขณะก็ตาม
  5. การจัดการสถานที่ (Venues) และศิลปิน (Artists) ที่ดียิ่งขึ้น
    – ระบบ Venue Dedup: จัดการสถานที่จัดงานให้เป็นระเบียบขึ้น มีหน้า Profile ของสถานที่นั้นๆ พร้อมระบุพิกัดแผนที่ และสามารถแยกประเภท “แพลตฟอร์มออนไลน์” เพื่อซ่อนจากหน้าค้นหาสถานที่ได้
    – ข้อมูลศิลปินแน่นขึ้น: เพิ่มช่องทาง Social Media ของศิลปิน มีเครื่องมือ CLI สำหรับ Import ข้อมูลแบบ Bulk ได้, เพิ่มปุ่มกดซื้อตั๋วในแต่ละอีเวนต์ และระบบสามารถแสดงผลกลุ่มวงดนตรี (Parent group) ของศิลปินเดี่ยวได้
  6. Timezone ขั้นสุด และ Web Push Notification ฉบับทำเอง
    – เรื่องสุดท้ายที่ท้าทายมากคือการแจ้งเตือน (Notifications) ในเวอร์ชันนี้ผมได้เพิ่ม Web Push Notifications เข้ามาเสริมทัพจากเดิมที่มีแค่ Telegram ความตึงคือผมเลือกที่จะเขียนมันขึ้นมาด้วย pure PHP ล้วนๆ ไม่พึ่งพา Composer หรือ Library ภายนอกเลย (ยังคงคอนเซปต์ “ไม่มี Dependencies ที่ไม่จำเป็น” จากตอนที่แล้ว) โดยเขียนให้รองรับ VAPID และ RFC 8291 เต็มรูปแบบ
    – พร้อมกันนี้ยังได้แก้ปัญหา Timezone แบบ End-to-End ไม่ว่าผู้ใช้งานจะอยู่ที่ไหน ระบบจะปรับเวลาให้ตรงกับ Local Time เสมอ คราวนี้ไม่ว่าจะคอยตามตารางเวที หรือจะตั้งแจ้งเตือนรอตารางมีคิวจะเปิดไลฟ์สด ระบบก็จะเด้งเตือนล่วงหน้า ไม่มีพลาดแน่นอน

ส่วนอื่นๆ ที่ไม่ใช่ความสามารถของคนทั่วไป แต่เสริมให้ระบบแข็งแรงขึ้น อย่างหลังบ้านมี Audit Log บันทึกประวัติ และรองรับ 2FA (TOTP) ถือว่ามาไกลกว่าจุดเริ่มต้นที่แค่อยากทำปฏิทินดูตารางงานเองไปมากเลยครับ

สำหรับนักพัฒนาที่ติดตามอยู่
– เนื่องจากการเดินทางจากเวอร์ชัน 7 มาถึง 16 มีการเปลี่ยนแปลงโครงสร้างฐานข้อมูลไปหลายส่วน (เช่น การเพิ่มฟิลด์รูปภาพ, เพิ่มระบบสิทธิ์ผู้ใช้งาน, ระบบ FTS5 ฯลฯ)
– ตรวจสอบรายการ Files changed ได้จาก Release note ของ v16.5.1
– ในฝั่ง database แนะนำให้ backup database ของเก่าไว้ก่อน แล้ว เข้าไปที่ไฟล์ setup.php
– กดปุ่ม “Run All Migrations” ตัวสคริปต์จะจัดการปรับโครงสร้างฐานข้อมูลทุกอย่างให้โดยอัตโนมัติ
– รันคำสั่ง php sync-sw-version.php เพื่ออัปเดต Service-worker PWA cache ให้ตรงกับเวอร์ชันปัจจุบันของแอปพลิเคชัน

ใครที่สนใจเอาไปใช้งาน จัดงานอีเวนต์ งานคอนเสิร์ตที่มีหลายเวที หรืออยากเข้าไปดูซอร์สโค้ด ก็ยังสามารถเข้าไปดาวน์โหลดและ Contribute กันได้ที่ GitHub – stage-idol-calendar (v16.5.1) เหมือนเดิมครับ!

ใครอยากลองว่ามันทำอะไรได้บ้างก็ที่นี่ครับ https://idoltrack.thaicyberpoint.com/

 

สร้างปฏิทินงานไอดอลใช้เอง

ก่อนอื่น ต้องบอกหากหายจากการเขียน 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

กระบวนการทำงานคือ:

  1. สร้าง Token — เมื่อผู้ใช้กด Follow ศิลปินคนแรก ระบบจะสร้าง UUID v7 (time-ordered UUID ที่ sort ตาม timestamp ได้) แล้วนำไป Sign ด้วย HMAC-SHA256 โดยใช้ Secret Key ที่เก็บไว้ฝั่ง Server

  2. Slug = UUID + HMAC ตัดสั้น — ถ้าใครแก้ UUID แล้ว HMAC ไม่ตรง ระบบจะ Reject ทันที ป้องกันการ Enumerate URL ไปดู Favorites ของคนอื่น

  3. เก็บเป็นไฟล์ JSON แบบ Sharded — ข้อมูล Favorites ของแต่ละคนเก็บเป็นไฟล์ .json บนโครงสร้าง Sharded Directory เพื่อกระจายไฟล์ไม่ให้โฟลเดอร์เดียวมีไฟล์เป็นหมื่น โดยข้อมูลในไฟล์ก็แค่ Array ของ Artist ID ที่ Follow ไว้

  4. localStorage เป็นแค่ Shortcut — Slug จะถูก Save ลง localStorage ของ Browser ด้วย Key หนึ่ง เพื่อให้ครั้งหน้าเปิดเว็บมา ระบบรู้ว่าเป็นคนเดิม แล้วแสดงเมนูระบบ Favorites ที่มุมบนซ้ายของทุกหน้า — โดยที่ localStorage ไม่ใช่ Source of Truth จริง ๆ มันเป็นแค่ Cache ฝั่ง Client ข้อมูลจริงอยู่ที่ไฟล์ JSON บน Server

  5. TTL 365 วัน + Auto-Touch — ไฟล์ .json มีอายุ 1 ปี แต่ทุกครั้งที่เข้ามาดูหน้า My Favorites หรือ My Upcoming Programs ระบบจะ Touch ไฟล์ (อัปเดต mtime) ให้นับ 365 วันใหม่ ตราบใดที่ยังเข้ามาใช้อยู่ ข้อมูลจะไม่หาย

  6. 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 บนมือถือได้

ลองใช้กันดู

ลอง Star ⭐ ให้กำลังใจกันได้ หรือจะ Fork ไปใช้กับงานอีเวนต์ของตัวเองก็ยินดีมาก ๆ ครับ

🌸 Made with ❤️ for event organizers and idol fans everywhere

 

ลิขสิทธิ์ในผลงานรับจ้างฟรีแลนซ์

ปรกติรับจ้างพัฒนาซอฟต์แวร์ในรูปแบบ “ฟรีแลนซ์” ตัวผลงานที่ออกมา จะอ้างอิงจากตัวลิขสิทธิ์ของการ “รับจ้างทำของ” ซึ่งทำให้ตัวผลงานนั้นเป็นของผู้ว่าจ้าง ไม่ใช่ของผู้สร้าง แต่เราก็สามารถทำเอกสารสัญญาเพิ่มเติมได้

ฉะนั้น เวลามีคนมาปรึกษาเรื่องรับจ้างพัฒนาซอฟต์แวร์ ผมจะแนะนำให้ทำเอกสารสัญญาพ่วงท้ายตกลงความเป็นเจ้าของให้ชัดเจนไปเลย ลงรายละเอียดในระดับ library, framework และ UI ด้วยเพื่อป้องกันความฟ้องร้อง หรืออ้างสิทธิ์ที่ไม่ชัดเจนภายหลัง

ตัวอย่าง

  1. ตัว UI โดยเฉพาะ web template มักจะมีประเด็นบ่อย เพราะบางครั้งเราซื้อสำเร็จรูปมา ก็ต้องลงไว้ว่าสำเร็จรูปมาจากที่นี่ ไม่งั้น เวลาคนว่าจ้างไปเห็นเหมือนกับตัวเอง เค้าจะมาฟ้องเราได้ ทั้ง ๆ ที่เราซื้อลิขสิทธิ์เป็นรายครั้งจากเว็บขายต่าง ๆ แยกลูกค้าแต่ละรายต่างหาก และกันความเข้าใจผิดว่าเราเอางานเค้าไปขายต่อ ทั้ง ๆ ที่ work flow ต่าง ๆ ในตัวโปรแกรมคนละอย่างเลย เป็นเรื่องละเอียดอ่อนที่ต้องทำไว้กันโดนฟ้อง
  2. library ที่สร้างใช้งานเอง หรือ plugin เฉพาะที่เราสร้างขึ้นมา อันนี้ก็ต้องลงไว้ ว่าเราให้สิทธิ์ใช้งานในโครงการนี้ และมิใช่การขายสิทธิ์ให้ผู้จ้าง รวมไปถึงการกระทำใด ๆ กับ library ชุดนี้จนทำให้เราเสื่อมสิทธิ์ไป เพราะผมเชื่อว่ามีหลายคนทำงานรับงานกันก็มักจะมี library หลายตัวที่มักใช้ซ้ำ ๆ เพื่อความรวดเร็วในการทำงาน ซึ่งบางตัวก็พัฒนาเฉพาะเพื่อให้สะดวกมากขึ้น
  3. ระบุว่า ผู้ว่าจ้าง มีสิทธิในเนื้องานระดับใด เช่น ทำซ้ำ แก้ไข ดัดแปลง เผยแพร่ ต่อยอดผลงาน หรือจำหน่ายจ่ายแจก ได้ไหม ตัวไหนไม่ควรก็ตัดออก เอาเฉพาะที่เค้าควรได้รับสิทธิ์ นอกเหนือจากนั้นก็ตกลงกันว่าจะเพิ่มเงินตามสิทธิ์ที่เพิ่มขึ้น
  4. ในส่วนของตัวซอสโค้ด ตรงนั้นต้องชัดเจนว่าการแก้ไข-ดัดแปลง หากเรารู้สึกว่าควรจะเป็นเราที่ควรแก้ไขได้เพียงผู้เดียวก็ลงไว้ หากมีการแก้ไขโดยไม่ใช่เรา ให้ถือว่าหมดประกัน และละเมิดลิขสิทธิ์
  5. จากข้อที่ 3. – 4. หากในตัวเนื้องานของเรา มีการใช้ library ภายนอกทั้งที่เป็น open source หรือ proprietary นอกเหนือจาก library พื้นฐานของภาษานั้น ๆ ควรตรวจสอบสัญญาณลิขสิทธิ์ให้ดีก่อนใช้งาน และพ่วงสัญญาพวกนี้ลงในสัญญาหลักด้วย เพื่อป้องกันปัญหาในอนาคต หากเจ้าของลิขสิทธิ์ library นั้น ๆ เค้าขอตรวจสอบ ซึ่งตรงนี้สำคัญ แม้จะเกิดขึ้นได้น้อย แต่ก็มีสิทธิ์ที่จะโดนได้หากใช้งานแล้วไม่ได้ตรวจสอบก่อน

จากที่ยกตัวอย่างเพื่อให้เห็นว่ามันสำคัญมาก ๆ กับข้อสัญญาก่อนว่าจ้าง ดูเรื่องมากแต่ไม่ชวนปวดหัวภายหลัง เพราะตอนว่าจ้าง ทำงานก็จากกันด้วยดี แต่ตอนมีปัญหานี่ก็อีกเรื่อง

 

เรื่องเล่า myujikkii.com และ music48voter.com ฉบับปี 2020

สำหรับเว็บ music48voter.com ตัวเว็บใช้บริการ DigitalOcean ด้วยเหตุผลการเข้าถึงได้ดีจากนอกประเทศ ราคาไม่แพง รวมทั้ง 6 อย่างที่เห็น จ่ายเดือนละ 30USD โดยให้ B/W ค่อนข้างเยอะ สเปคก็ค่อนข้างดี เร็วใช้ได้ (ปีก่อนมีปัญหา ตปท เข้าได้ยาก เพราะระบบอยู่ในไทย)

ใช้ VM จำนวน 4 ตัว เป็น LBx1 (HAProxy), Webx2 (docker-compose),DBx1 (MariaDB+Redis) ส่วนไฟล์สลิปไปฝากที่บริการ Spaces คล้ายๆ S3 ของ AWS เก็บแยกไปต่างหาก

เว็บพัฒนาบน .NET Core 3.1 (C#) ต่อยอดจากปีก่อน (ปีที่แล้ว .NET Core 2.0) docker image build บน Docker Hub
เหตุผลที่มี Webx2 เพราะกลัว deploy แก้ไขจะได้ไม่ล่มแบบปีก่อน ทำ auto deploy ร่วมด้วย (ปีก่อนแก้หรือ deploy แล้วเว็บจะ down แป็บนึง เพราะ start/stop Kestrel manual) และเหตุผลที่ไม่ใช้ K8s เพราะเครื่องมันจะเยอะขึ้นอีกตัว บวกกับขี้เกียจ (จบ ?)

myujikkii.com อันนี้อีกทีมนึงทำ ซึ่งด้านการ sync ยอด progress ดึงจาก music48voter อีกที (cache 5 นาที) ทีม myujikkii ก็จะได้ไม่ปวดหัวกับระบบหลังบ้าน เลยรวมที่เดียว และ myujikkii เอา cloudflare ทำ proxy cache ด้วยเพื่อให้โหลดเร็วขึ้นประหยัด B/W

ระบบหลังบ้านในการตรวจสลิป มีทีมตรวจแล้วแจ้งสถานะ approve/reject แล้วส่งอีเมลแจ้งสมาชิกในเว็บ (ปีก่อนไม่มีอีเมลแจ้ง) ถือว่าโอเค เพราะทีมหลังบ้านไม่ต้องคอยแจ้งหรือลุ้นว่าเค้าจะกลับมาแก้ไขไหม และทำให้ทีมงานเช็คยอดง่ายขึ้น (ตัวอีเมลใช้บริการ mailgun ส่งอีเมลฟรี 5,000 ฉบับ)

ส่วนฝั่งโค้ดโหวตก็เหมือนปีก่อน และสุดท้าย last word ตอนแรกจะใช้ Google Form แต่คิดว่าลำบากตรงเอาข้อความมาแปะ และตรวจยอด เลยเขียนเพิ่มให้ทีมงานบริการจัดการได้จากจุดเดียว โดยข้อมูลโดเนทและข้อความก็อยู่ฐานข้อมูลเดียวกันทั้งหมด เอาเวลาไปโฟกัสเรื่องยอดกับบิ้วโหวตดีกว่า

สุดท้าย ปีนี้เราพยายามให้แฟนคลับ ไม่ว่าจะเป็นสมาชิกที่โหวตเอง โดเนทกับกิจกรรม หรือรู้สึกยุ่งยากแต่อยากโดเนทแบบไม่ระบุตัวตน ก็ทำได้หมด เราพยายามให้ทุกคนมีส่วนรวมกับกิจกรรมให้เยอะที่สุด

หวังว่าจะสนุกกับกิจกรรมของทีมงานที่ทำกันทุกคน ^^

 

DNS Propagation Check

เจอปัญหาว่าตอนเช็ค domain ที่ดูแล ว่าตอน client ไป resolve กับ DNS resolver เจ้าต่าง ๆ ได้ผลตอบกลับออกมาเป็น IP อะไรบ้าง เพื่อตรวจสอบว่าแต่ละ DNS resolver เข้าที่ CDN/Server ชุดไหน (ถ้ามีหลายเครื่อง) หรือตรวจสอบว่าเป็นข้อมูลล่าสุดแล้วหรือยัง

หากตรวจสอบแค่ 1-2 ISP หรือ Public DNS หลัก ๆ ก็ไม่ยาก แต่ถ้าเยอะ ๆ สัก 4-5 ตัวขึ้นไป ก็จะลำบากแล้ว ต้องมานั่ง dig ทีละ Nameserver ก็ยุ่งยากเสียเวลา หรือใช้ whatsmydns.net ก็มีไม่ครบสำหรับ ISP ในไทย

ก็เลยเขียน console app ง่าย ๆ เร็ว ๆ ไว้ใช้เอง (เขียนแบบลวกมาก แบบให้มันใช้ได้ก่อน)

แต่ก็เจอปัญหาอยู่บ้างว่ามี DNS resolver พวก ISP หลายที่เค้าไม่เปิด public ให้คนนอก ISP ใช้ ก็ลำบากนิดหน่อยแต่รวม ๆ ก็ทุ่นเวลาไปได้พอสมควรถ้าตรวจสอบกับ DNS resolver หลัก ๆ ได้ (ISP บางเจ้าข้อมูลอาจจะไม่อัพเดท เดี่ยวค่อยปรับรายการอีกที)

โปรแกรมชุดนี้พัฒนาด้วย C# บน .NET Core 3.1 ทำงานได้ทั้งบน Windows และ Linux ซึ่งตอนนี้บน GitHub ได้ build รองรับทั้ง 2 platform

สำหรับ binary ที่ได้จาก build ทดสอบบน Windows 10 (1909) และ Ubuntu 18.04 ซึ่งทำงานได้ตามวัตถุประสงค์

ตัว source code เปิด source อยู่ที่ GitHub นี้ DNS Propagation Check และ binary โหลดได้จากในส่วน Release ของ GitHub