Transcribe a meeting recording from the Rodecaster SD card, Google Drive, or a local file. Creates a meeting note with summary, decisions, and action items, plus an MP3 archive. Use when the user types /transcribe-meeting or asks to transcribe a recording.
Resources
1Install
npx skillscat add olivoil/om-skills/transcribe-meeting Install via the SkillsCat registry.
Transcribe Meeting
Transcribe a meeting recording and create a structured meeting note in the Obsidian vault with summary, decisions, action items, and full transcript.
Input sources (checked in order):
- Auto-detect — Screen recordings + Rodecaster recordings for the target date (matched by time overlap)
- Google Drive URL — if provided as argument
- Local file path — if provided as argument
Recording modes:
- omarchy+rodecaster — screen recording + Rodecaster WAV matched by start time. Transcribe from Rodecaster (better audio). Upload merged video (Rodecaster audio + screen video) to YouTube + MP3 to Google Drive.
- omarchy-only — screen recording with no matching Rodecaster. Extract audio from MP4 for transcription. Upload original MP4 to YouTube.
- rodecaster-only — Rodecaster recording with no matching screen recording. Upload MP3 to Google Drive only (existing behavior).
Workflow
Phase 0: Setup
- Run
echo $OBSIDIAN_VAULT_PATHto get the vault root. If empty, ask the user for the path. - Determine the target date: use the argument if a date is provided, otherwise use today.
- Determine context: if the user provides additional info (project name, participants, meeting topic), note it. Otherwise, these will be inferred from any surrounding daily note context or left generic.
Phase 1: Discover & Match Recordings
Try sources in order:
Option A: Auto-Detect (Primary)
If no explicit URL or file path was given:
Discover screen recordings:
bash skills/transcribe-meeting/scripts/find-screenrecordings.sh "{date}"Save the JSON output to a temp file (e.g.,
/tmp/screenrecs-{date}.json).Discover Rodecaster recordings:
bash skills/transcribe-meeting/scripts/find-recordings.sh "{date}"Save the JSON output to a temp file (e.g.,
/tmp/rodecaster-{date}.json).Match recordings by time overlap:
bash skills/transcribe-meeting/scripts/match-recordings.sh /tmp/screenrecs-{date}.json /tmp/rodecaster-{date}.jsonThis produces groups with
mode(omarchy+rodecaster, omarchy-only, rodecaster-only),video,audio, andtranscribe_fromfields.Check idempotency — for each group, search for existing meeting notes:
- By
recording:field (for groups with Rodecaster audio):grep -rl 'recording: "{folder}"' "$VAULT/🎙️ Meetings/" - By
video_file:field (for groups with screen recordings):grep -rl 'video_file: "{filename}"' "$VAULT/🎙️ Meetings/"
Skip any group that already has a meeting note.
- By
Present findings to the user with mode info for each group:
Found 2 recording groups:
- omarchy-only: screen recording from 09:36 (30 min) — no matching Rodecaster
- omarchy+rodecaster: screen recording from 11:02 (51 min) + Rodecaster recording 10 (51 min)
Determine audio source for transcription:
omarchy+rodecaster→ audio = Rodecaster WAV (theaudio.pathfield)omarchy-only→ extract audio from screen recording:
Capture the WAV path from stdout.bash skills/transcribe-meeting/scripts/extract-audio.sh "{video.path}"rodecaster-only→ audio = Rodecaster WAV (theaudio.pathfield)
Option B: Google Drive URL
If the input contains drive.google.com:
- Run the download script:
bash skills/transcribe-meeting/scripts/download-gdrive.sh "<url>" - Capture the output — it's the local file path.
- Check idempotency via
audio_urlfield (see Idempotency section).
Option C: Local File Path
If the input is a local file path, use it directly.
Phase 2: Transcribe
- Determine the engine: check
echo $OBSIDIAN_WHISPER_ENGINE— defaults toopenaiif unset. - Run the transcription script:
bash skills/transcribe-meeting/scripts/transcribe.sh "<audio-file>" "<engine>" - Capture the JSON output — an array of
{start, end, text}segments.
Phase 3: Summarize & Create Meeting Note
Using the transcript segments, generate:
- Title: A concise meeting title (inferred from transcript content and any provided context)
- Summary: 2-3 paragraph overview of what was discussed
- Decisions: Key decisions made (bullet list, omit section if none)
- Action Items: Tasks assigned with
@Nameattribution where possible (checklist, omit if none) - Open Questions: Unresolved items (bullet list, omit if none)
Format the transcript with timestamps:
[H:MM:SS] Text of the segment...Create the meeting note at $VAULT/🎙️ Meetings/{date} {Title}.md:
---
date: {YYYY-MM-DD}
project: "[[Project Name]]"
participants:
- "[[Olivier]]"
recording: "{folder}"
audio_url: "{original-url-or-path}"
video_file: "{screenrecording-filename}" # only if video exists, for idempotency
video_url: "{youtube-url}" # only after YouTube upload
recording_mode: "{omarchy+rodecaster|omarchy-only|rodecaster-only}" # informational
duration: {estimated-duration}
tags: [meeting]
---
# {Title}
## Summary
{2-3 paragraph summary}
## Decisions
- Decision 1
- Decision 2
## Action Items
- [ ] @Name: Task description
- [ ] @Name: Task description
## Open Questions
- Question 1
---
## Transcript
[0:00:12] First segment text...
[0:00:45] Next segment text...Frontmatter notes:
- omarchy+rodecaster: set
recording: "{folder}",video_file: "{filename}",recording_mode: "omarchy+rodecaster". Setaudio_urlto the WAV path initially — Phase 4 will replace it with the Google Drive URL after upload. Setvideo_urlafter YouTube upload. - omarchy-only: set
video_file: "{filename}",recording_mode: "omarchy-only". Setaudio_urlto the extracted WAV path initially. Setvideo_urlafter YouTube upload. - rodecaster-only: set
recording: "{folder}",recording_mode: "rodecaster-only". Setaudio_urlto the WAV path initially — Phase 4 will replace it with the Google Drive URL after upload. Omitvideo_fileandvideo_url. - Google Drive source: set
audio_url: "{url}". Omitrecording,video_file,video_urlfields. - Local file source: set
audio_url: "{path}". Omitrecordingfield — Phase 4 will replace it with the Google Drive URL after upload.
Present the summary, decisions, and action items to the user for approval before writing the file.
Phase 3.5: Extract Key Screenshots
Only applies when a video recording exists (omarchy+rodecaster or omarchy-only modes).
Scan transcript for visual moments — look for indicators like:
- Screen sharing or demo segments
- Phrases: "as you can see", "look at this", "let me show you", "on the screen"
- Code references or technical discussions with visual context
- Topic transitions (good for section dividers)
- Architecture diagrams, UI walkthroughs, slide presentations
Select 3-8 key timestamps — spread across the meeting, prioritizing moments with unique visual content. Avoid extracting frames too close together (at least 2 minutes apart).
Extract frames with ffmpeg:
ffmpeg -ss {seconds} -i "{video_path}" -frames:v 1 -q:v 2 -y "$VAULT/🗜️Attachments/{meeting-name}-{MM-SS}.png"Where
{MM-SS}is the timestamp in the recording (e.g.,05-30for 5:30).Embed in meeting note — edit the meeting note to insert screenshots at the corresponding positions in the transcript:
[5:30] Discussion about the new dashboard layout... ![[Meeting Name-05-30.png]] [5:45] Next topic...Skip this phase if no video file exists or if the video file is inaccessible.
Phase 4: Post-Process & Upload
After the meeting note is created, compress audio, merge video if applicable, and upload:
4a. Compress WAV → MP3 (all modes with audio):
bash skills/transcribe-meeting/scripts/compress.sh "<wav-file>"4b. Upload MP3 to Google Drive (all modes with audio):
bash skills/transcribe-meeting/scripts/upload-gdrive.sh "<mp3-file>"Capture the Google Drive URL from stdout. Update audio_url in the meeting note frontmatter.
4c. Merge video + Rodecaster audio (omarchy+rodecaster mode only):
bash skills/transcribe-meeting/scripts/merge-av.sh "<video-file>" "<rodecaster-wav>"Capture the merged MP4 path from stdout. This replaces the screen recording's audio with the higher-quality Rodecaster audio.
4d. Upload video to YouTube (omarchy+rodecaster and omarchy-only modes):
bash skills/transcribe-meeting/scripts/upload-youtube.sh "<video-file>" "<meeting-title>" "<summary>" "<date>"- For
omarchy+rodecaster: upload the merged MP4 (from step 4c) - For
omarchy-only: upload the original screen recording MP4 - Capture the YouTube URL from stdout.
4e. Update meeting note frontmatter:
- Set
audio_urlto the Google Drive URL (from step 4b) - Set
video_urlto the YouTube URL (from step 4d, if applicable)
Skip this phase if the source was already a Google Drive URL.
Phase 5: Link from Daily Note (if applicable)
If a date is known (today by default), check if a daily note exists at $VAULT/📅 Daily Notes/{date}.md.
If the daily note contains the Google Drive URL that was transcribed:
- Replace the
[recording](url)link with a wikilink to the meeting note:[[{date} {Title}]]
If the source was an SD card recording:
- Find the matching time entry line (by time proximity or project match) and append:
- [[{date} {Title}]]
If the daily note doesn't reference this recording but exists:
- Optionally add a reference under the appropriate project section
Phase 6: Link from Project Note (if applicable)
If a project was identified, check if the project note exists in $VAULT/🗂️ Projects/.
If it exists, look for a ## Meetings section:
- If found, append:
- [[{date} {Title}]] - If not found, add a
## Meetingssection with the link
Timestamp Formatting
Convert seconds to H:MM:SS or M:SS format:
- Under 1 hour:
0:12,5:30,45:22 - Over 1 hour:
1:05:30,2:15:00
Idempotency
Before creating a meeting note, check if one already exists:
Rodecaster source — search by recording field:
grep -rl 'recording: "{folder}"' "$VAULT/🎙️ Meetings/"Screen recording source — search by video_file field:
grep -rl 'video_file: "{filename}"' "$VAULT/🎙️ Meetings/"Google Drive / local source — search by audio_url field:
grep -r "audio_url:" "$VAULT/🎙️ Meetings/" | grep "<url-or-path>"If found, skip creation and return the existing note path. A group is considered already processed if either its recording folder or video_file filename matches an existing note.
Error Handling
- If SD card not mounted: fall through to ask for URL or file path
- If
gdownis not installed: tell the user to runpip install gdown - If
ffmpegis not available: error with install instructions - If OpenAI API key is missing: check 1Password, then ask user
- If transcription fails: report the error, suggest trying the other engine