OBS Course Media
Use this skill to turn a course flow sheet into local media assets plus OBS scene collection JSON files. It supports Google Drive and YouTube media, Google Sheets/xlsx flow tables with hyperlinks, and multi-day courses that need both per-day and all-in-one OBS imports.
Workflow
- Identify the Google Sheet URL, target worksheet name, media category, and output folder.
- Target worksheet names may differ from the user's shorthand. Export or inspect workbook sheet names when needed.
- Default media category is
影片. - If the user gives a Google Sheet URL, test xlsx export access first. If export fails, ask the user to set sharing to link-viewable or provide an exported
.xlsx.
- Run
scripts/course_media_obs.pyto export/read the sheet, find the header row containing類別and內容, download rows whose category matches, and generate:media-manifest.jsonOBS-<sheet>-<category>場景.json- The script reads cell hyperlinks from
.xlsx; do not rely on CSV export. - If
內容has no hyperlink but a檔案column exists, the script can use that linked file as a fallback. - YouTube links are downloaded with
yt-dlp --no-playlistso a single linked video does not accidentally download an entire playlist.
- Verify every downloaded file is real media, not a Google Drive HTML warning page.
- The script records
filecommand output in the manifest. - If a file validates as HTML, retry Drive download using the virus-warning confirm flow or rerun with
--overwrite.
- The script records
- For multi-day courses, optionally collect the per-day OBS JSON files into one central import folder and merge them into a single all-in-one OBS JSON.
- Import the generated JSON in OBS using
Scene Collection > Import.
Script Usage
For a Google Sheet:
python3 /path/to/obs-course-media/scripts/course_media_obs.py \
--sheet-url "https://docs.google.com/spreadsheets/d/.../edit?gid=..." \
--sheet-name "Day 1" \
--output-dir "/absolute/path/to/day-1/media" \
--category "影片" \
--collection-name "Course Day 1 Media"
For an already exported .xlsx:
python3 /path/to/obs-course-media/scripts/course_media_obs.py \
--xlsx "/absolute/path/to/流程表.xlsx" \
--sheet-name "Day 1" \
--output-dir "/absolute/path/to/day-1/media"
When a sheet stores the actual linked media in a 檔案 column rather than 內容, keep the default fallback behavior or specify another header:
python3 /path/to/obs-course-media/scripts/course_media_obs.py \
--xlsx "/absolute/path/to/流程表.xlsx" \
--sheet-name "Day 2" \
--output-dir "/absolute/path/to/day-2/media" \
--fallback-link-header "檔案"
If a flow row has no direct hyperlink but the correct local media is known, append it to the OBS JSON:
--extra-media "Closing song=/absolute/path/to/closing-song.mov"
Use --no-download to parse the sheet and build JSON from existing local files without downloading again. Use --overwrite when replacing partial or HTML files.
Multi-Day / Combined OBS Imports
For a course with multiple per-day OBS JSON files, put the final import JSON files in one folder such as OBS匯入檔/.
The media files do not need to move because OBS JSON paths are absolute.
To create a single all-in-one import file:
python3 /path/to/obs-course-media/scripts/merge_obs_collections.py \
--name "Course Complete Media" \
--output "/absolute/path/to/obs-imports/OBS-complete-media.json" \
"D0=/absolute/path/to/day-0/media/OBS-day-0-media.json" \
"D1=/absolute/path/to/day-1/media/OBS-day-1-media.json" \
"D2=/absolute/path/to/day-2/media/OBS-day-2-media.json" \
"D3=/absolute/path/to/day-3/media/OBS-day-3-media.json"
Keep the per-day JSON files too. The all-in-one file is convenient for one-time setup; per-day files are easier to recover or re-import if only one day changes.
Google Drive Notes
- Google Sheets CSV export loses cell hyperlinks; prefer
.xlsxexport. - Google Drive large files often return a virus-scan warning page on first request. The script detects the warning form and follows the confirm link.
- Some rows may describe a needed asset without linking it. In that case, inspect same-row columns such as
檔案and資訊備註, search nearby detail worksheets, download the correct file, then include it with--extra-mediaor a project-specific plan.
YouTube Notes
yt-dlpandffmpegmust be available for YouTube downloads.- Always use
--no-playlistfor YouTube links from flow sheets; links often include playlist parameters. - If a YouTube download creates
.partfiles or fails with 403, remove partial files and rerun. Prefer a Google Drive copy for live-course reliability when available.
OBS JSON Conventions
- The script creates one OBS scene per media file.
- Each scene contains a single
ffmpeg_sourcemedia source. - Media is bounded to 1920x1080 and
restart_on_activateis enabled so switching to the scene starts playback from the beginning. - Generated source paths are absolute. If the media folder moves, regenerate the JSON.
- Quick transitions include
Cmd+Pfor fade andCmd+Bfor fade-to-black. - Scene names should be operationally clear. If a row groups several videos, include the actual media title in the label, e.g.
Video 1 - Opening Story.
Verification Checklist
Before responding to the user:
- Confirm the OBS JSON is valid JSON with
python3 -m json.tool. - Confirm all paths referenced by
ffmpeg_source.settings.local_fileexist. - Confirm
file <media>reports MP4/MOV/audio/image as expected, not HTML. - Confirm YouTube links resolved to local
.mp4files, not web URLs. - Confirm centralized/merged import files still reference valid absolute media paths.
- Confirm expected hotkeys are present in
quick_transitions. - Report any rows with missing direct links and explain how they were handled.