mcpbeat

Agency Meetup Publish

glebis/agency-meetup-publish

End-to-end pipeline for publishing AGENCY Community meetup recordings to YouTube. Downloads Zoom recording, adds intro/outro, generates thumbnail, creates description with timecodes, uploads to YouTube, sets thumbnail, and adds to the AGENCY Community playlist. Use this skill when the user wants to publish a meetup, says "upload the meetup", "publish the recording", "process the Zoom recording for YouTube", or mentions uploading an AGENCY Community session. Also triggers on requests to add intro/outro to a meeting recording and upload it.

3k tokens
context cost
the whole folder, loaded on every use
3
files
instructions only
0
copies elsewhere
how many repositories repackaged it
337
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/glebis/claude-skills --skill agency-meetup-publish

What comes with it

5 520 bytes besides the instruction
references/description-template.md
references/thumbnail-template.html

The instruction itself

20 sections, as written by the author

AGENCY Meetup Publish

Publish AGENCY Community meetup recordings to YouTube with intro, thumbnail, description, and timecodes.

Prerequisites

  • ~/ai_projects/youtube-uploader/ — YouTube upload scripts with OAuth credentials
  • ~/ai_projects/my-video/out/agency-swarm-intro.mp4 — AGENCY intro animation (6s, 1920x1080)
  • Zoom OAuth configured at ~/.zoom_credentials/
  • ffmpeg and ffprobe installed
  • Playwright or Chrome installed (for thumbnail rendering)

Pipeline Overview

1. Identify meeting → 2. Download from Zoom → 3. Add intro/outro
→ 4. Generate timecodes → 5. Write description → 6. Create thumbnail
→ 7. Upload to YouTube → 8. Set thumbnail → 9. Add to playlist

Step 1: Identify the Meeting

Ask the user for:

  • Meeting name or Zoom ID — if unknown, list recent recordings:
  python3 ~/.claude/skills/zoom/scripts/zoom_meetings.py recordings --start YYYY-MM-DD
  • Speaker name — for description and thumbnail
  • Topic summary — or derive from transcript

Get recording details:

python3 ~/.claude/skills/zoom/scripts/zoom_meetings.py recording MEETING_ID

This returns MP4 download URL, duration, transcript URL, and other files.

Step 2: Download from Zoom

Download MP4 and VTT transcript in parallel:

# Video (run in background — large file)
curl -L -o ~/Brains/brain/YYYYMMDD-meeting-slug.mp4 "DOWNLOAD_URL"

# Transcript
curl -L -o ~/Brains/brain/YYYYMMDD-meeting-slug.vtt "TRANSCRIPT_URL"

Naming convention: YYYYMMDD-meeting-slug.mp4 where slug is a kebab-case topic summary.

Step 3: Add Intro (and Outro if Available)

The intro and meeting likely have different specs. Check both:

ffprobe -v quiet -print_format json -show_streams INTRO.mp4
ffprobe -v quiet -print_format json -show_streams MEETING.mp4

Key parameters to match: resolution, fps, audio sample rate, audio channels.

Re-encode intro to match meeting

ffmpeg -y -i ~/ai_projects/my-video/out/agency-swarm-intro.mp4 \
  -vf "scale=WIDTH:HEIGHT:force_original_aspect_ratio=decrease,pad=WIDTH:HEIGHT:(ow-iw)/2:(oh-ih)/2:black" \
  -r FPS -c:v libx264 -preset fast -crf 18 \
  -ar SAMPLE_RATE -ac CHANNELS -c:a aac \
  /tmp/intro-matched.mp4

Trim meeting start

Review the VTT to find where real content begins. Typically trim 1-3 seconds of silence:

ffmpeg -y -ss TRIM_SECONDS -i MEETING.mp4 \
  -c:v libx264 -preset fast -crf 18 \
  -ar SAMPLE_RATE -ac CHANNELS -c:a aac \
  -r FPS -vf "scale=WIDTH:HEIGHT" \
  /tmp/meeting-trimmed.mp4

This re-encode is necessary for concat compatibility. Run in background — it takes several minutes for long recordings.

Concatenate

echo "file '/tmp/intro-matched.mp4'" > /tmp/concat-list.txt
echo "file '/tmp/meeting-trimmed.mp4'" >> /tmp/concat-list.txt
# If outro exists:
# echo "file '/tmp/outro-matched.mp4'" >> /tmp/concat-list.txt

ffmpeg -y -f concat -safe 0 -i /tmp/concat-list.txt -c copy \
  ~/ai_projects/my-video/out/YYYYMMDD-meeting-slug-final.mp4

Step 4: Generate Timecodes from Transcript

Read the VTT transcript and identify up to 20 topic transitions. Look for:

  • Speaker changes (especially when the main speaker starts)
  • Topic shifts signaled by phrases like "давайте", "следующий", "перейдем к"
  • Q&A segments
  • Demo/screen-share moments
  • Conclusion/wrap-up

Important: Timecodes must account for the intro offset. Add the intro duration (typically 6s) to all VTT timestamps.

Format: MM:SS Topic description or H:MM:SS for videos over 1 hour.

Step 5: Write Description

Follow the template from previous AGENCY Community uploads. See references/description-template.md for the full template.

Key sections:

  • Title lineAGENCY Community Meetup: [Topic]
  • Summary paragraph — 2-3 sentences about the speaker and topic
  • Bullet points — 5-7 key topics covered (use •)
  • Timecodes — prefixed with ⏱ Таймкоды:
  • Community lineAGENCY Community — сообщество практиков AI-автоматизации.
  • Links — prefixed with 🔗, using → arrows
  • Hashtags — topic-relevant, always include #AGENCY #Community

Save description to /tmp/youtube-description.txt.

Step 6: Create Thumbnail

Use the AGENCY thumbnail HTML template rendered to 1280x720 PNG.

Template approach

Write an HTML file at /tmp/thumbnail.html based on references/thumbnail-template.html. Customize:

  • Title text — short, punchy (2-4 words max per line)
  • Subtitle — in EB Garamond italic
  • Speaker lineSpeaker Name × AGENCY
  • Speaker photo — if available, use with mix-blend-mode: hard-light

Render to PNG

Preferred: headless Chrome (no install needed):

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --headless --screenshot=/tmp/thumbnail.png \
  --window-size=1280,720 --hide-scrollbars \
  "file:///tmp/thumbnail.html"

Fallback: Playwright:

from playwright.sync_api import sync_playwright
with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={'width': 1280, 'height': 720})
    page.goto('file:///tmp/thumbnail.html')
    page.wait_for_timeout(2000)
    page.screenshot(path='/tmp/thumbnail.png')
    browser.close()

Step 7: Upload to YouTube

cd ~/ai_projects/youtube-uploader && python3 youtube_upload.py \
  --video ~/ai_projects/my-video/out/FINAL.mp4 \
  --title "TITLE" \
  --description "$(cat /tmp/youtube-description.txt)" \
  --category education \
  --privacy public \
  --tags "TAG1,TAG2,..." \
  --yes

The upload script outputs the Video ID and Video URL on success. Capture these for the next steps.

Title guidelines: Keep under 100 chars. Format: Topic — Speaker Name. Include key terms for search.

Step 8: Set Thumbnail

cd ~/ai_projects/youtube-uploader && \
  python3 youtube_manage.py thumbnails set VIDEO_ID /tmp/thumbnail.png

Step 9: Add to Playlist

The AGENCY Community playlist ID is PLZNP0SKU2SqjHOy01UjDxhSRtcluvuw0m.

cd ~/ai_projects/youtube-uploader && \
  python3 youtube_manage.py playlists add-item PLZNP0SKU2SqjHOy01UjDxhSRtcluvuw0m VIDEO_ID

Parallelization Strategy

For speed, run these in parallel where possible:

| Phase | Can parallelize with |

|-------|---------------------|

| Download MP4 | Download VTT |

| Re-encode meeting (bg) | Read transcript, draft timecodes |

| Upload to YouTube (bg) | Generate thumbnail |

| Set thumbnail | Add to playlist |

Output

Report to the user:

  • Video URL: https://www.youtube.com/watch?v=VIDEO_ID
  • Studio URL: https://studio.youtube.com/video/VIDEO_ID/edit
  • Playlist: AGENCY Community

Existing Skills Used

This pipeline builds on:

  • zoom~/.claude/skills/zoom/ for Zoom recording access
  • video-youtube-upload~/.claude/skills/video-youtube-upload/ for upload reference
  • youtube~/.claude/skills/youtube/ for post-upload management
  • agency-socials~/.claude/skills/agency-socials/ for cover design system reference

How to use it

Copy the folder

Take glebis/agency-meetup-publish from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.