olivoil

transcribe-meeting

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.

olivoil 0 Updated 3mo ago

Resources

1
GitHub

Install

npx skillscat add olivoil/om-skills/transcribe-meeting

Install via the SkillsCat registry.

SKILL.md

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):

  1. Auto-detect — Screen recordings + Rodecaster recordings for the target date (matched by time overlap)
  2. Google Drive URL — if provided as argument
  3. 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

  1. Run echo $OBSIDIAN_VAULT_PATH to get the vault root. If empty, ask the user for the path.
  2. Determine the target date: use the argument if a date is provided, otherwise use today.
  3. 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:

  1. 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).

  2. 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).

  3. Match recordings by time overlap:

    bash skills/transcribe-meeting/scripts/match-recordings.sh /tmp/screenrecs-{date}.json /tmp/rodecaster-{date}.json

    This produces groups with mode (omarchy+rodecaster, omarchy-only, rodecaster-only), video, audio, and transcribe_from fields.

  4. 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.

  5. Present findings to the user with mode info for each group:

    Found 2 recording groups:

    1. omarchy-only: screen recording from 09:36 (30 min) — no matching Rodecaster
    2. omarchy+rodecaster: screen recording from 11:02 (51 min) + Rodecaster recording 10 (51 min)
  6. Determine audio source for transcription:

    • omarchy+rodecaster → audio = Rodecaster WAV (the audio.path field)
    • omarchy-only → extract audio from screen recording:
      bash skills/transcribe-meeting/scripts/extract-audio.sh "{video.path}"
      Capture the WAV path from stdout.
    • rodecaster-only → audio = Rodecaster WAV (the audio.path field)

Option B: Google Drive URL

If the input contains drive.google.com:

  1. Run the download script:
    bash skills/transcribe-meeting/scripts/download-gdrive.sh "<url>"
  2. Capture the output — it's the local file path.
  3. Check idempotency via audio_url field (see Idempotency section).

Option C: Local File Path

If the input is a local file path, use it directly.

Phase 2: Transcribe

  1. Determine the engine: check echo $OBSIDIAN_WHISPER_ENGINE — defaults to openai if unset.
  2. Run the transcription script:
    bash skills/transcribe-meeting/scripts/transcribe.sh "<audio-file>" "<engine>"
  3. Capture the JSON output — an array of {start, end, text} segments.

Phase 3: Summarize & Create Meeting Note

Using the transcript segments, generate:

  1. Title: A concise meeting title (inferred from transcript content and any provided context)
  2. Summary: 2-3 paragraph overview of what was discussed
  3. Decisions: Key decisions made (bullet list, omit section if none)
  4. Action Items: Tasks assigned with @Name attribution where possible (checklist, omit if none)
  5. 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". Set audio_url to the WAV path initially — Phase 4 will replace it with the Google Drive URL after upload. Set video_url after YouTube upload.
  • omarchy-only: set video_file: "{filename}", recording_mode: "omarchy-only". Set audio_url to the extracted WAV path initially. Set video_url after YouTube upload.
  • rodecaster-only: set recording: "{folder}", recording_mode: "rodecaster-only". Set audio_url to the WAV path initially — Phase 4 will replace it with the Google Drive URL after upload. Omit video_file and video_url.
  • Google Drive source: set audio_url: "{url}". Omit recording, video_file, video_url fields.
  • Local file source: set audio_url: "{path}". Omit recording field — 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).

  1. 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
  2. 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).

  3. 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-30 for 5:30).

  4. 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...
  5. 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_url to the Google Drive URL (from step 4b)
  • Set video_url to 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 ## Meetings section 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 gdown is not installed: tell the user to run pip install gdown
  • If ffmpeg is 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