Skip to content

feat(photos): Google 相簿 → 相片簡報/影片/PPTX(Smart Photo-to-Deck 完整功能)#95

Merged
dofliu merged 3 commits into
mainfrom
claude/photo-deck-foundation
Jul 7, 2026
Merged

feat(photos): Google 相簿 → 相片簡報/影片/PPTX(Smart Photo-to-Deck 完整功能)#95
dofliu merged 3 commits into
mainfrom
claude/photo-deck-foundation

Conversation

@dofliu

@dofliu dofliu commented Jul 7, 2026

Copy link
Copy Markdown
Owner

這個 PR 做什麼

新功能「連結 Google 相簿 → AI 選圖+配文 → 相片簡報(影片 + 可匯出 PPTX)」,完整接在現有 eduStudio 架構上(core + 後端路由/job + /app UI)。全 offline-first:mock/fake 可測,CI 不打真 Gemini、不需真 OAuth。

為何走 Picker API:Google 於 2025-04 移除 photoslibrary.readonly,Library API 已讀不到使用者既有相簿。改用官方指定的 Photos Picker API(使用者在 Google 託管選取器自選照片)。

架構(最大化複用既有積木)

google_photos 是一個新 SourceType,走既有 job pipeline:ingest(下載選中照片 → vision 選圖+配文 → deck)→ 既有 render。因此 影片 render / PPTX 匯出(/jobs/{id}/pptx)/ SlideEditor 編 caption / YouTube 上傳全部免費繼承

變更

① 核心(core/

  • photo_deck.py — 複用 infocards.gemini.generate_json(多模態 + response_schema)做品質過濾(模糊/曝光/重複)、逐張 caption、取標題;送前 Pillow 縮 ≤1024px 分批;prompt 硬限「只描述明確可見元素」防幻覺;max_select 依 score 全域挑選 → 組 sections/slides deck。
  • google_photos.py — Photos Picker 薄層(sessions.create/getmediaItems.list 翻頁、baseUrl=d 下載、跳過影片);OAuth 沿用 publish.py 模式但獨立 scope(photospicker.mediaitems.readonly)+ 獨立 photos_token.json;缺 token → 412。
  • tools/photos_auth.py — 一次性 CLI 授權。

② 後端整合(server/

  • SourceType.GOOGLE_PHOTOS + JobSource.session_id + JobOptions.photo_title_hint/photo_max_select
  • runner._run_ingest_photos(下載+deck);render 分支讓 photos 跟 slides 走同一條 SlideRenderer(照片鋪底 + caption 旁白)。
  • routes/google_photos.pyGET /statusPOST /sessionGET /session/{id}POST /generate

/app UI

  • PhotoSourcePanel.tsx:授權檢查 → 開 Picker 選照片 → 輪詢 → 填主題/張數/停審 → 產生。
  • CreateJobFormgoogle_photos 來源;api.ts 四個端點;types/SourceBadge

測試(全 mock/fake,25+ 案綠)

downscale/分析/max_select/deck 組裝/餵 deck_to_pptx;picker session/分頁/下載過濾影片/OAuth 412;ingest(mock) 建 deck/路由 status·session·generate。相關 runner/jobs 套件一併綠。前端 tsc 0 error、vite build 通過。

使用者前置(做一次)

Google Cloud:啟用 Photos Picker API → OAuth 同意畫面加 scope(測試中把自己加 Test users)→ client_secret JSON 放專案根 → 本機跑 python -m tools.photos_auth

使用流程

/app → 新增 Job → google_photos → 開相簿選照片 → 產生 → 影片在 job 頁預覽、「📊 匯出 PPTX」下載、caption 可在 SlideEditor 微調重渲染。

後續(可選)

  • 「資訊圖卡」樣式輸出(照片+caption 卡片)。
  • 部署環境(Docker)補 LibreOffice 與 CJK 字型以支援 PPTX/render。

🤖 Generated with Claude Code

claude added 3 commits July 7, 2026 12:20
…Auth

Google 相簿 → 相片簡報功能的後端核心 (階段一+二)。全 offline-first: mock/fake
可測, 不打真 Gemini / 不需真 OAuth。

- core/photo_deck.py: 一組照片 → Gemini vision (複用 infocards.gemini.generate_json
  多模態 + response_schema) 做品質過濾 / 逐張 caption / 取標題 → 組 sections/slides
  deck (bg_image=照片路徑, narration=caption)。送前用 Pillow 縮到 ≤1024px 分批;
  prompt 硬限「只描述明確可見元素」防幻覺; max_select 依 score 全域挑選。deck 直接
  可餵 slide_pptx.deck_to_pptx / 既有 render / infocards。
- core/google_photos.py: Google Photos Picker API 薄層 (sessions.create/get,
  mediaItems.list 翻頁, baseUrl=d 下載, 只收圖片跳過影片)。OAuth 沿用 publish.py
  的 InstalledAppFlow + token 模式, 但獨立 scope (photospicker.mediaitems.readonly)
  + 獨立 photos_token.json; 缺 token 丟 OAuthBootstrapRequired (route 回 412)。
  HTTP 層 session 可注入 → 單元測試不需真 OAuth。
- tools/photos_auth.py: 一次性 CLI 授權腳本 (python -m tools.photos_auth) 產 token。
- .gitignore: photos_token.json; requirements.txt: 正式宣告 google-api-python-client
  / google-auth-oauthlib (先前已裝未宣告)。
- 測試: downscale / 分析(mock) / max_select / deck 組裝 / 餵 PPTX; picker session /
  分頁 / 下載過濾影片 / OAuth 412 gate。共 13 案綠。

註: 2025-04 Google 移除 photoslibrary.readonly, 故走 Picker API (使用者自選照片)。
路由 + job 整合 + /app UI 為後續 PR。

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGT1mi2KPYyD76U1BKEhMo
把相片簡報接進既有 job pipeline: 新來源 google_photos 走 ingest(下載+deck) →
既有 render, 因此影片 render / PPTX 匯出 / SlideEditor 編輯 / YouTube 上傳全部
免費繼承。

- schemas: SourceType.GOOGLE_PHOTOS; JobSource.session_id; JobOptions.photo_title_hint
  / photo_max_select。
- runner: _run_ingest_photos (下載 Picker 選中照片 → photos_to_deck → 寫 deck.json);
  path-check 豁免 google_photos (無本機檔); render 分支讓 google_photos 跟 slides_pdf
  走同一條 SlideRenderer (照片鋪底 + caption 旁白)。
- routes/google_photos.py: GET /status (是否已授權)、POST /session (建 Picker,
  回 picker_uri)、GET /session/{id} (輪詢選好沒)、POST /generate (建 job)。未授權
  回 412 (對齊 youtube 慣例)。
- main.py 註冊 router。
- 測試: ingest(mock) 建 deck / 無照片 raise; 路由 status(授權/未授權)、session 412、
  generate 建 job。共 6 案; 相關 runner/jobs 套件一併綠 (110 passed)。

影片 = job mp4 artifact; PPTX = 既有 GET /jobs/{id}/pptx; caption 可在 SlideEditor 編。
/app UI (開 Picker → 進度 → 產出) 為下一個 PR。

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGT1mi2KPYyD76U1BKEhMo
/app 新增 google_photos 來源: 授權檢查 → 開 Picker 選照片 → 輪詢 → 產生。

- PhotoSourcePanel.tsx: 三步驟面板 — ① status 檢查 (未授權提示跑 tools.photos_auth);
  ② createPhotoSession → 開 picker_uri 新分頁 + 輪詢 media_items_set; ③ 選好後填
  主題脈絡 / 最多張數 / 是否停審 → generatePhotos 建 job。
- CreateJobForm: 加 google_photos 來源選項; isPhotos 時以面板取代一般 file/url 表單
  與所有選項/送出鈕。
- api.ts: photosStatus / createPhotoSession / pollPhotoSession / generatePhotos。
- types + SourceBadge 加 google_photos。

tsc 0 error; vite build 通過。後端 19 案綠。

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01MGT1mi2KPYyD76U1BKEhMo
@dofliu dofliu changed the title feat(photos): Smart Photo-to-Deck 基礎 — vision 選圖+配文 + Photos Picker OAuth feat(photos): Google 相簿 → 相片簡報/影片/PPTX(Smart Photo-to-Deck 完整功能) Jul 7, 2026
@dofliu
dofliu marked this pull request as ready for review July 7, 2026 12:35
@dofliu
dofliu merged commit 219c7cd into main Jul 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants