curate-course

skill
Guvenlik Denetimi
Uyari
Health Uyari
  • License — License: NOASSERTION
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 6 GitHub stars
Code Gecti
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Gecti
  • Permissions — No dangerous permissions requested

Bu listing icin henuz AI raporu yok.

SUMMARY

把 YouTube 上散落的好內容變成結構完整、連結全數驗證、來源可查的課程網站。主題無關,附一門 371 單元的體態矯正課作為範例。

README.md

curate-course

把 YouTube 上散落的好內容,變成一門結構完整、連結全數驗證、來源可查的課程網站。

主題無關。附的範例是一門體態矯正課(371 個單元、406 支影片、423 篇 PubMed 引用),
但框架本身不認識「體態」——換成吉他、統計、烘焙、電銲都一樣跑。

線上範例https://body-course.pages.dev


為什麼不是又一個播放清單

播放清單解決「收藏」,這個框架解決另外三件事:

一、結構。 章節 → 單元 → 項目三層,配額寫進設定檔,數量不對就建置失敗。
不會出現「這章塞了 40 支、那章只有 3 支」的失衡。

二、連結真的活著。 幾百個格子最大的風險是連結是捏造的。所以有兩層關卡:
make audit 離線把設定檔、配額、影片長度、實證欄位全查一遍(確定性,不打網路);
make verify 再對每個 YouTube 連結重打 oEmbed、對每個 PMID 重打 PubMed API。
不信任任何上游宣稱,包括 AI agent 自稱驗證過的。

三、可查證的深度。 每個單元可以掛證據強度與原始文獻。範例課程的 24 個主題全部經
OpenEvidence 查證,其中 9 個被判為 contested——結果照實寫進網站,包含對課程自己不利的部分。


快速開始

需要 uv。建置腳本只用 Python 標準庫,沒有執行期相依。
(策展階段另外會用到 yt-dlp 搜尋影片與抓中繼資料,
建置與稽核則不需要。)

git clone https://github.com/<you>/curate-course.git
cd curate-course

make build     # examples/body/ → dist/body/
make serve     # http://localhost:8899

看到的是範例的體態課。接著開一門你自己的。


換成你的主題

框架本身不內含任何一門課。課程住在自己的目錄裡,COURSE 指定是哪一門:

make new-course NAME=guitar          # → courses/guitar/,產出即可建置
COURSE=courses/guitar make build     # → dist/guitar/
COURSE=courses/guitar make serve

剛產生的骨架是一門零單元的課,但它現在就建得起來——先看到站台跑起來,
再往裡面填內容,而不是先猜哪些欄位是必填的。

courses/guitar/
├── course.config.json   站台設定、章節、配額、所有 UI 文案
├── data/                策展資料(影片、實證、中繼資料)
├── assets/              favicon、og.png、這門課自己的圖示 sprite
└── taxonomy/            選用:主題專屬的詞彙模組

你只需要動這個目錄,其他都是框架。這不再只是一句承諾:CI 同時建置
examples/body 與一門當場產生的骨架課,還有一顆測試會掃 src/ 底下有沒有
出現任何範例課的專屬字串——框架被主題污染時,CI 會紅。

COURSE 沒設而 repo 裡剛好只有一門課時會自動選它;有兩門以上就直接報錯,
要你講清楚是哪一門。猜錯的代價是把 A 課的資料建成 B 課的站台,而且不會有
任何錯誤訊息。DIST 預設是 dist/<課程名>,所以多門課並存不會互相覆蓋產物。

make courses                         # 列出 repo 裡所有課程,以及現在會建哪一門

1. 設定

course.config.json 裡沒有一個字是寫死在程式裡的——分頁名稱、篩選標籤、
統計欄位、證據分級的顯示文字,全部從這裡讀。

{
  "site":  { "name": "…", "title": "…", "url": "https://…" },
  "kinds": [                       // 每個單元底下的項目分幾類
    { "id": "demo",     "label": "示範",   "tone": "accent"  },
    { "id": "slow",     "label": "慢速",   "tone": "success" },
    { "id": "practice", "label": "練習曲", "tone": "danger"  }
  ],
  "chapters": [
    { "code": "CH1", "title": "…", "icon": "guitar", "source": "ch1",
      "units": 4, "drills": 20 }   // 配額:建置時強制檢查
  ]
}

2. 資料

<課程目錄>/data/<source>.json,一章一檔:

{
  "chapter": "CH1",
  "units": [{
    "id": "ch1-u1",
    "name": "單元名稱",
    "assessment": "讀者可以自己做的判斷方法",
    "lesson": { "title": "…", "channel": "…", "url": "https://youtube.com/watch?v=…",
                "why": "為何選這支" },
    "drills": [{ "name": "…", "kind": "demo", "url": "…", "dose": "…" }]
  }]
}

3. 建置與稽核

make build && make audit && make serve

配額不符、URL 格式錯誤、同單元重複影片會讓建置直接失敗。
make audit 再往下查一層——而且不打網路,同樣的輸入永遠得到同樣的報告:

設定檔    schema 拼字與型別、圖示有沒有打包、nav 有沒有漏章
文案      佔位符打錯字、首屏要的文案欄位有沒有漏填、產物裡有沒有殘留的 {{…}}、
          文案裡有沒有寫 HTML(一律逸出,只認得 **粗體**)
結構      各章配額、id 唯一、kind/type 是否已定義、每單元項目數是否失衡
影片      中繼資料覆蓋率、長度是否落在設定區間、宣稱長度與實際的誤差、觀看數低標、
          留空的格子有沒有寫清楚原因
內容深度  自我評估夠不夠具體、evidence_grade 是否合法、PMID 格式、每類文獻篇數

門檻寫在 course.config.jsonaudit 區塊(影片長度區間、最低觀看數、每單元項目數
上下限…),不是寫死在程式裡。--json 給 agent 讀、--strict 讓警告也變成錯誤。


讓 AI 幫你策展

repo 內附一個 Claude Code skill。在 Claude Code 裡開這個專案,輸入:

/curate-course 幫我用這個框架做一門古典吉他入門課

或直接用自己的話說「幫我用這個框架做一門 X 的課程」,agent 會照著 skill 走完整流程:
談結構 → 定配額 → 並行策展 → 驗證連結 → 補中繼資料 → 加引用 → 稽核 → 建置部署。

Skill 本身採漸進揭露,主檔只有流程骨架,細節按需載入:

.claude/skills/curate-course/
  SKILL.md              鐵則、七步流程、驗收清單
  reference/config.md   設定檔欄位、schema、frameworkVersion、圖示、tone、詞彙模組、文案怎麼寫
  reference/curating.md yt-dlp 搜尋與中繼資料、oEmbed 驗證、資料格式、多語言
  reference/evidence.md 單元/類別兩層實證、PubMed E-utilities 用法
  reference/quality.md  audit 與 verify 的分工、門檻怎麼調、踩過的坑
  reference/writing.md  文案的語域、AI 寫作痕跡與怎麼改

裡頭寫死了幾條不可退讓的規則,最重要的是:video ID 必須取自實際搜尋結果,
不可憑記憶拼湊;找不到合格影片就留空並在 note 說明原因
——留空而不說明會被稽核擋下。

挑片與中繼資料是同一個動作:yt-dlp --flat-playlist 的搜尋一次就回 id/秒數/觀看數/
頻道/標題,agent 沒有機會憑記憶寫長度。但影片的死活與可嵌入與否一律不看 yt-dlp——
它被限流時是 exit 0 加空輸出,而且「能播」不等於「能嵌入」,只有 make verify
oEmbed 算數。


指令

make new-course NAME=x   產生一門新課的骨架(→ courses/x/),產出即可 make build
make courses   列出 repo 裡所有課程,以及現在會建哪一門
make build     <課程>/ → dist/<課程名>/,含配額驗證與 SEO 產出
make audit     離線稽核設定檔、配額、影片長度與實證深度(不打網路,可放 CI)
make test      前端純邏輯的單元測試(node:test,零依賴、不需要瀏覽器)
make e2e       paywall 端對端流程並截圖(Playwright,需要 Chrome)
make verify    重驗每個影片連結與每個 PMID(打真實 API)
make serve     本機預覽
make icons     重新下載 Lucide 圖示並打包成內嵌 sprite
make og        重新產生社群預覽圖
make lint      ruff 檢查
make check     lint + test + build + audit,提交前跑這個
make deploy    部署到 Cloudflare Pages

每個指令都吃 COURSE(不設而只有一門課時會自動選它):

COURSE=courses/guitar make check
COURSE=examples/body  make check

網站有什麼

四個檢視:

  • 首頁 — 用法三步驟、立場摘要、章節總覽
  • 課程內容 — 章節樹、自我評估、分面標籤、項目清單、證據註記
  • 上課模式 — 左側嵌入播放(走 youtube-nocookie.com)、右側播放清單,
    滿版高度、欄寬可拖曳、?tab=player&play=12 深連結
  • 立場 — 課程對自身限制的說明與原始文獻

外加:分面篩選、全文搜尋、localStorage 進度追蹤、深淺色主題、
YouTube IFrame API 快捷鍵(按 ? 看清單)。

每支影片一串討論:上課模式的動作列有「討論」按鈕,用
giscus 把留言存進 repo 的 GitHub Discussions,
data-term 綁 video id,所以同一支影片在不同單元共用同一串。
啟用方式:到 https://github.com/apps/giscus 安裝到你的 repo,
再把 repoId / categoryId 填進 course.config.jsondiscussions
面板只在點開時才載入 giscus,不影響首屏。

選用的 0 元 paywallcourse.config.jsonpaywall 區塊,就會多出「加入購物車 →
0 元結帳 → 解鎖」的流程:前幾章免費試看,其餘章節顯示鎖頭、點下去彈出結帳,
結完帳全部解鎖。金額真的是 0,被劃掉的原價是虛構的,介面上有一行字直接講明。
拿掉那個區塊就完全回到全站開放。設計與接真金流要補的東西見
docs/PAYWALL.md

SEOCourse JSON-LD(含 syllabus 與 citation)、OG/Twitter 卡、sitemap、
robots、llms.txt。文案在建置時就注入 HTML,不等 JS 執行,首屏就有真實內容。

首屏零外部請求:Primer 設計語彙用 CSS 變數自行實作,Lucide 圖示打包成內嵌 sprite。
只有按下播放時才連 YouTube、點開討論時才連 giscus。


架構

src/
  build/
    coursepath.py     解析 COURSE/DIST,所有進入點的唯一真相來源
    new_course.py     make new-course 的骨架產生器
    templates/        骨架範本
    build.py          合併、配額驗證、中繼資料套用
    seo.py            JSON-LD / sitemap / robots / llms.txt / 模板注入
    build_icons.py    Lucide sprite 打包(框架一份、每門課各一份)
    audit.py          離線品質稽核(設定檔/配額/長度/實證)
    course.schema.json  設定檔結構,編輯器自動完成 + 稽核擋拼字
    verify_links.py   YouTube oEmbed 驗證
    verify_refs.py    PubMed 引用驗證
  web/
    index.html        模板,{{token}} 於建置時替換
    css/  js/         前端
    js/paywall-core.js  0 元 paywall 的純邏輯,被 node --test 直接載入
tests/
  paywall-core.test.js  單元測試(零依賴)
  e2e-paywall.cjs       Playwright 端對端 + 截圖
  decoupling.test.js    框架與主題脫鉤(src/ 不得夾帶課程專屬字串)
  framework-version.test.js  跳主版號就必須有對應的遷移指南章節
docs/MIGRATION.md     框架跳主版號時,既有課程要改什麼
docs/PAYWALL.md       paywall 設計與接真金流的待辦
examples/body/        ← 隨框架附的範例課
courses/<你的課>/      ← 你的課程(make new-course 產生)
dist/<課程名>/         ← 建置產物(gitignored,每門課各自獨立)

框架不 import 任何主題詞彙。<課程>/taxonomy/ 是可插拔的:
提供 extract() 就有分面篩選,提供 classify() 就能把引用掛在類別上,
兩個都不給也能跑。

examples/courses/ 底下的課程結構完全相同——範例課沒有任何特權,
框架也不為它留任何例外,這是「框架真的與主題無關」唯一站得住腳的證明方式。

圖示 sprite 也是每門課一份src/web/js/icons.js 只裝框架介面自己用的圖示,
內容與課程無關;課程的章節圖示產在 <課程>/assets/js/icons.js,建置時覆蓋掉
框架那一份。以前只有一份全域 sprite,兩門課並存時後跑 make icons 的那門會把
前一門的圖示洗掉,而且不會報錯——只有打開網站才看得到一排空白方塊。


範例課程:體態矯正

examples/body/ 的內容。做它的過程順便驗證了框架的每個環節:

單元 371(37 堂主課 + 334 支跟練)
影片 406 個欄位、344 支不重複、26 小時 3 分
多語言 35 個單元有繁中/英文兩版
證據查核 24 個主題 + 3 個核心觀念(OpenEvidence)
文獻 55 個動作類別、423 篇 PubMed 引用
驗證 稽核零錯誤、連結 100% 有效、PMID 100% 存在且標題相符

查證結果沒有很好看,而這正是重點:

  • 靜態體態與疼痛的因果關係沒有共識(Swain 2020 涵蓋 41 篇系統性回顧的傘狀回顧)
  • Janda 交叉症候群沒有任何評分者間信度資料,也沒有 EMG/影像研究能重現
  • 矯正運動有效,但不是因為它把體態調正了——沒有中介分析支持,
    頭對頭試驗中也不優於一般運動

這些全部寫在網站上,contested 標籤直接顯示在單元標題列。


授權

程式碼採 MIT,見 LICENSE

影片著作權屬原 YouTube 頻道,本專案只存連結與公開中繼資料,不重製也不代管。
Lucide 圖示為 ISC。範例課程的內容為衛教與運動指引,不構成醫療建議。

Yorumlar (0)

Sonuc bulunamadi