# 線上課程觀課進度管理小工具開發日誌

By [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake) · 2025-12-20

---

> 這個小工具把跨課程模組的影音進度，收斂到同一張試算表中追蹤，如果你是：
> 
> 1.  同時在上兩門以上線上課
>     
> 2.  想在同一個管理面板確認「全部買的課總共有多少影片、自己看到哪」
>     
> 3.  不想被各家平台的「學習進度條」綁死，想用自己的規則管理進度
>     
> 
> 那你就可以用這個小工具管理自己的學習進度！

一開始需要在終端機執行指令處理一些資料，後續管理都在 Google Sheet 視覺化呈現。 雖然我是為了解決跨課程模組重複章節而開發，就算你的課程之間沒有重複章節，也可以單純把它當成「所有已購買線上課程的集中版進度面板」來用。 詳細操作步驟可以參考 GitHub 專案說明：

[GitHub repo](https://github.com/gcake119/course-lecture-tracker)

![](https://storage.googleapis.com/papyrus_images/93977e2a718a40aa2cce278fcac552cacb36a428a15965d74fe358e845dde826.png)

![](https://storage.googleapis.com/papyrus_images/d7a3218acb44b07875809669ad386b8cf377291e216f724d75b6a42fa915a94e.png)

會起心動念開始做這個小工具，起點其實很單純：我在六角學院的**正課（無限期不限次數的錄影課）**與**體驗營（直播型課程，有附補充錄影教材**）常常出現「內容相同、章節被重新編排」的情況，導致學習時得在兩個課程頁面之間反覆切換，才能確認「這一段我到底在哪一門課看過了」。

尤其當體驗營需要跟上實作節奏、同時又想用沒有觀看期限的正課把基礎知識補齊時，這種來回比對就變成很高頻的痛點：不只浪費時間，還很容易因為名稱差異而判斷錯誤。

所以我的目標很明確：**在同一個介面管理整體學習進度**，把「要看的影片」和「看完打勾」這件事做得像任務管理一樣順手。

* * *

第一階段：先把課程清單抓下來
--------------

第一步是把「課程影片列表」變成可以處理的資料，而不是只能用眼睛看網頁上的文字內容。

我最後採取的方式是：直接在課程頁開 DevTools，把章節與小節的文字抓下來輸出成 CSV，讓每一列都有 `course / section / lecture / duration` 這些欄位。

當時也遇到六角課程頁面在「個人 Dashboard / 繼續觀課」狀態的 DOM 結構不同，需要調整 selector（例如章節 class、影片標題 class），這讓我一開始就把「01 抓取腳本要可調整 selector」列為設計重點。

* * *

第二階段：最有感的效率提升——從人工 rename map → duration + similarity 全自動
---------------------------------------------------------

一開始我對「同內容不同名字」的處理方式，其實很傳統：先用標題相似度找候選，再人工確認，最後把結果寫進 rename map（例如把「同一支影片在不同課程模組的標題」統一成 canonical\_title）。

這方法可行，但只要課程一多，就會進入一種「每次更新都要再人工審一次」的循環：新增一門課要審、體驗營更新要審、章節標題改幾個字也要審，流程很難真正自動化。

後來我發現一個關鍵訊號：**時長（duration）在這個情境非常有辨識度**。很多重複影片即使標題略改、甚至章節歸類不同，影片長度往往完全一致（或差距非常小），這讓「只靠文字」的 fuzzy match 變成「文字 + 時長」的雙因子判斷。

也就是說，我不需要再把所有疑似重複都丟回人工決策，而是讓程式用 `duration` 先分桶縮小候選集合，再用字串相似度（例如 Levenshtein）做最後判斷，符合閾值就直接視為同一支影片。

這個轉變帶來的差異非常明顯：

*   人工 rename map（早期）
    
    *   優點：準確、可控，遇到特例能硬解。
        
    *   缺點：維護成本高，資料每次更新就要再審一次，工作流很難「一鍵重跑」。
        
*   duration + similarity 全自動（現在）
    
    *   優點：新增課程或重新匯出清單後，直接跑腳本就能產出穩定的 `canonical_key` 與 `watch_from`，Google Sheets 只管顯示與完成觀課後勾選，整體流程變成「可重跑、可擴充」。
        
    *   缺點：本質是 heuristic（啟發式）規則，仍有極少數情況可能誤判，需要保留報表或抽樣檢查的出口。
        

對我自己來說，這就是整個專案「最有感」的一步：把原本的人工判斷，收斂成可以被程式穩定重複執行的規則，讓新增課程模組不再是一個要花半天人工對照的任務，而是幾分鐘內可完成的自動化流程。

* * *

第三階段：在 Google Sheets 做成管理面板
---------------------------

資料處理自動化之後，下一個挑戰是「怎麼管理進度」。 我選擇 Google Sheets 的原因很務實：

*   進度管理本質就是表格與勾選。
    
*   我想要能快速篩選、可視化完成度、又能在手機上看。
    

但我其實一開始對 Sheets 公式很不熟，特別是要讓一整張表「自動長出來」，一開始寫公式會卡在拖拉、填滿、引用錯位。

後來真正救我的關鍵是理解 **陣列公式（ARRAYFORMULA）** 的思維：讓 tasks 由 raw\_all 自動生成，資料更新時只要取代匯入 raw\_all，面板就能跟著更新。

需要時我只要匯入新的 `raw_all.canonical.csv`，tasks 這張表就會自動長出所有唯一影片、對應的 watch\_from，並保留之前打勾過的完成狀態，完全不用再手動調整公式或範圍。

* * *

第四階段：第一次做下拉篩選與 Apps Script
--------------------------

我的面板需求之一是：能夠「只看某個 section 的影片」，不然整張表太長。 我第一次用 Sheets 的資料驗證做下拉式選單，讓 section 清單自動從 raw\_all 產生，再用下拉選單選要看的 section。

接著我第一次寫 Apps Script，想把「切換 section → 自動只顯示該 section」做成一鍵操作。

目前的成果是：切換 section 後，`visible` 欄位會自動更新，確實大幅減少手動操作；但還沒做到完全無感的 UI（我現在仍需要手動在篩選器再點一次 TRUE 才會刷新顯示）。

即便如此，它已經把最煩的「一直捲、一直找、一直比對」替換成「選 section → 勾完成」的流程，實際使用體感差很多。

* * *

其他踩坑與收斂
-------

回頭看這段開發，很多問題其實都在「讓流程可重跑、可驗證」：

*   一開始我同時做「全課程重複」和「體驗營子集重複」兩種報表，才把需求界線釐清：哪些是要用來決策 watch\_from，哪些只是用來 debug。
    
*   也曾經為了少數特例寫 patch 腳本，但最後都被更通用的 canonical\_key + watch\_from 規則取代，整體 pipeline 變得更乾淨。
    

* * *

現在的工作流（我自己用的版本）
---------------

目前我已經把流程收斂成：

1.  01：在課程頁 DevTools 抓清單（CSV）。
    
2.  02：合併成 raw\_all.csv（若分多課程檔）。
    
3.  03：自動產生 raw\_all.canonical.csv（包含 canonical\_key 與 watch\_from）。
    
4.  匯入 Google Sheets 的 raw\_all 工作表（取代內容），tasks 面板自動更新。
    

對我來說，這個小工具最有價值的不是「資料分析」，而是把學習流程變得像產品一樣：**一個介面看全局、知道下一步要看什麼、看完就打勾**。

* * *

額外心得：第一次用 JavaScript 寫這類自動化工具
-----------------------------

這次算是我第一次用 **JavaScript** 認真寫一套「自動化工具腳本＋資料處理 pipeline」，因為過去如果要做資料整理、批次處理、或是類似的自動化任務，我比較習慣用 Python 來完成。

有趣的是，不管是以前用 Python，或是這次改用 JS（尤其是 Node.js + 瀏覽器 DevTools 這種組合），在 AI 的協助下開發體驗其實很相似：先把需求描述清楚，快速產出一個可跑的版本，再透過「反覆跑、反覆驗證輸入輸出、反覆釐清操作步驟」去收斂到穩定可用的流程。

幾週前開始系統性地學前端開發之後，慢慢熟悉 JS 的語感與生態（例如直接在瀏覽器對 DOM 做處理、再用 Node 接後續 pipeline），才發現 **兩種語言都能做出幾乎一樣的工具**，只是切入點不同：Python 很適合快速做資料處理與整理，而 JavaScript 在「直接貼到課程頁 DevTools 抓資料」這件事上因為是瀏覽器原生語言就超級方便，不用再透過 Python 寫爬蟲。

最後把功能做完、流程跑順、文件補齊並真的開始拿來追課，那種「從混亂到可控」的成就感，不管用哪一種語言完成，體驗都非常接近，也讓我更確定：語言只是工具，真正的差異在於能不能把問題拆清楚、把流程做成可重跑的系統。

額外小發現：原來整條自動化鏈都是 JavaScript
---------------------------

做到最後才突然意識到：這個專案從頭到尾其實都是用 **JavaScript** 在寫自動化，只是跑在不同的執行環境。  
在課程頁面上，我用瀏覽器 DevTools 直接操作 DOM 把章節/小節抓成 CSV；回到本機後，用 Node.js 在終端機跑腳本做合併、去重與產生 `canonical_key / watch_from`；最後在 Google Sheets 端，Apps Script 也是用 JavaScript 寫 `onEdit` 來處理下拉篩選、同步勾選狀態。

更有趣的是，Google Apps Script 現在同樣是跑在 V8 runtime（也就是 Chrome 與 Node.js 使用的那個 JavaScript 引擎）上，所以可以用相對現代的 JS 語法來寫自動化邏輯。  
這讓我對「JavaScript 不只是拿來寫網頁互動」有了更具體的體感：同一個語言可以在瀏覽器、命令列、雲端腳本這三個地方各司其職，把一個原本很瑣碎的人工流程串成可重跑的工具鏈。

---

*Originally published on [雞蛋糕的前端修煉屋](https://paragraph.com/@gcake/course-lecture-tracker)*
