zoom-lecture-publish

Original🇨🇳 Chinese
Translated

End-to-end automated operation for publishing Zoom recordings as lectures on PORSEO LMS / AI PLAY GUILD, then automatically handing off to note membership article creation. It also supports a branch where Zoom recordings are not uploaded as lectures, but only converted into note articles with eye-catching images and screenshots. Responsible for searching unpublished Zoom recordings, matching with lecture candidates, retrieving VTT transcripts and chat logs, creating summaries/lecture data, generating YouTube-style thumbnails and applying Convex Storage, importing to Mux, publishing to production Convex, notifying Discord forums, handing off to note articles, and deleting incorrectly published videos. Used when requested with commands like "Turn this Zoom video into a lecture", "Find and publish unpublished videos", "Create and link lecture thumbnails", "Notify Discord about the video", "Create a note article after publishing", "Turn this Zoom recording into only a note article", "Don't upload it as a lecture", "Include note thumbnails and screenshots", "Delete this lecture video".

15installs
Added on

NPX Install

npx skill4agent add nanameru/zoom-lecture-publish-skill zoom-lecture-publish

SKILL.md Content (Chinese)

View Translation Comparison →

zoom-lecture-publish

Handles Zoom recordings from PORSEO LMS / AI PLAY GUILD end-to-end, including lecture data, thumbnails, Mux, Convex, Discord, and handoff to note articles.

Things to Follow

  • In production operations, always confirm the Convex URL used by the target site. Do not trust the default connections in local
    .vercel/project.json
    or
    npx convex
    .
  • Do not output secrets for Vercel/Convex/Zoom/Discord/Mux. Do not paste them into logs either. If necessary, load them into a temporary
    .env
    file and delete it afterward.
  • Test with only one video in canary before full publishing. Confirm that
    muxPlaybackId
    and
    thumbnailUrl
    are visible via
    getPublishedVideos
    before bulk publishing.
  • Wait for automatic Discord posts, and check active/public archived threads to avoid duplicate posts.
  • After completing LMS publishing, use the same materials to hand off to
    $note-membership-lecture
    , creating an editable draft, paywall, and Light Plan member-only settings for note. Add a one-time purchase price only if explicitly specified by the user. Do not publish note articles without explicit confirmation from the user.
  • If the user requests "Don't upload as a lecture", "Only note", or "Don't publish to LMS", handle it as the note-only branch and skip Convex video creation, Mux import, publishing, and Discord notifications.
  • Even in the note-only branch, do not publish note articles without explicit confirmation. Stop at creating an editable draft, note eye-catching image, body screenshots, and preview. The note-only branch defaults to free publishing; add paywall, member-only, or one-time purchase settings only if explicitly specified by the user.
  • When deleting incorrectly published content, check not only Convex but also Mux assets and Discord forum threads.

References

Read the AI PLAY GUILD production-specific secure connection procedures, verification rules, and Discord/Mux deletion procedures only when needed:
  • AI PLAY GUILD production runbook
  • note membership handoff - Data contract and verification procedures for handing off to note article creation after LMS publishing

Branch Judgment

At the start of work, classify the user's intent into one of the following:
  • LMS Publishing Flow: Purpose includes "Turn into lecture", "Publish", "Upload to LMS", "Notify Discord", "Mux", "Link thumbnail", etc. Use the standard flow below.
  • note-only Branch: Purpose includes "Take Zoom recording and create only note article", "Don't upload as a lecture", "Don't publish to LMS", "Create article without video publishing", etc. Use the
    Zoom recording → note-only branch
    .
If the intent is ambiguous and could lead to external publishing via Convex/Mux/Discord, proceed with the note-only branch up to material collection and draft preparation without performing any publishing operations.

Zoom Recording → note-only Branch

Retrieve a Zoom recording and create only a note article. Do not create LMS video records, Mux assets, Convex Storage thumbnails, or Discord forum posts.
  1. Identify the target recording
    • If the user specifies relative dates like "just now", "today", or "yesterday", list candidates with the current date and time zone clearly stated.
    • Obtain a Zoom Server-to-Server OAuth token and search cloud recordings for the target period.
    • Display only the topic, meeting ID, recording start time, duration, MP4 size, and presence of VTT/Chat for candidates. Do not output download URLs or access tokens.
  2. Save materials locally
    • By default, save to
      ~/Downloads/zoom-recordings/<YYYY-MM-DD-HHmm-slug>/
      . If the user requests saving within the repo, use
      note-drafts/assets/<YYYY-MM-DD-slug>/
      .
    • Retrieve MP4, TRANSCRIPT/VTT, and CHAT (if available), and save them as
      recording.mp4
      ,
      transcript.vtt
      ,
      chat.txt
      , and
      metadata.json
      .
    • Include only title candidates, meeting ID, Zoom recording file ID, recording start time, duration, saved files, and presence of chat in
      metadata.json
      . Do not include secrets or URLs with tokens.
  3. Create article materials
    • Generate a natural Japanese summary, key topics, learning points for readers, and title candidates from the VTT.
    • If Zoom chat is available, extract URLs and questions to reflect in the article's reference links and supplements. Remove query parameters, fragments, and authentication information from URLs.
    • If necessary, create
      note-drafts/handoffs/zoom-note-only-<YYYY-MM-DD-HHmm>.json
      to compile title/date/sourceRecording/transcriptPath/chatPath/videoPath/summary/links/thumbnailPath/screenshotPaths.
  4. Create note eye-catching image and screenshots
    • Even in the note-only branch, create a note eye-catching image and body screenshots as a rule. Do not post only the article text.
    • Use
      note-drafts/assets/<YYYY-MM-DD-slug>/
      as the working directory, and save images as
      eyecatch.png
      and
      screenshots/*.jpg
      .
    • In environments where screenshots can be extracted from MP4, generate candidates at
      t-45s
      ,
      t-20s
      ,
      t
      ,
      t+20s
      ,
      t+45s
      based on key points in the VTT. For short recordings, generate candidates based on real-time positions like 5 seconds, 30%, 60%, 85%.
    • Prioritize using
      ffmpeg -ss <seconds> -i recording.mp4 -frames:v 1 -q:v 2 screenshots/candidate_<seconds>.jpg
      to generate local MP4 candidates. If
      ffmpeg
      is not available, try creating only a representative thumbnail via QuickLook, etc. If that fails, use generated illustrations or official screenshots as alternatives.
    • Always visually check screenshots and only include those that support nearby explanations in the text. Do not use Zoom participant lists, face-only shots, black bars, unrelated screens, or screens with unreadable text.
    • If screenshots do not match UI explanations, do not force them; use screenshots from Zed/official documentation/repositories or conceptual diagrams as body images.
    • Resize the eye-catching image to 1280:670 to meet note requirements. If using a recording screenshot, perform center cropping and resizing; if text becomes distorted, create it via AI generation or design images.
    • When using a generated eye-catching image, use
      $imagegen
      to create a 1280:670 raster image aligned with the article theme. Prioritize readable titles, margins, and cleanliness for note articles, rather than flashy YouTube-style LMS thumbnails.
    • Record the created
      eyecatch.png
      , adopted
      screenshots/*.jpg
      , reasons for rejection, and the heading where the image will be inserted in the handoff.
  5. Handoff to
    $note-membership-lecture
    • Use
      $note-membership-lecture
      to create a note article based on the Zoom recording/VTT/Chat/summary.
    • Since no LMS URL or Mux playback ID exists, do not force a "Hands-on here" link at the start of the article. Instead, use a natural introduction like "This article organizes key points and procedures based on the recording content" if necessary.
    • When creating the note draft, upload
      eyecatch.png
      and insert adopted screenshots near relevant explanations in the text. Confirm the preview with images before reporting completion.
    • Do not generate LMS thumbnails or apply Convex Storage.
    • In the note-only branch, do not include paywall text like "Members-only content below" in the body. Structure the entire article as freely readable.
    • In the note-only branch, do not enable Light Plan member access, one-time purchase,
      limited=True
      , or paywall settings. Switch to the paid setting procedure of
      $note-membership-lecture
      only if the user explicitly requests paid content.
  6. Report results
    • Output the paths of saved MP4/VTT/Chat/metadata, eye-catching image, adopted screenshots, handoff, article key of the draft, preview URL, editor URL, whether it is for free publishing, and whether it is pending note publication.
    • Clearly state that Convex/Mux/Discord/LMS publishing was not performed.

Standard Flow

  1. Organize inputs
    • Confirm the lecture list, existing MD files, thumbnail guidelines, target quantity, and exclusion criteria provided by the user.
    • Prioritize existing compilations in
      thumbnails/summaries/lectures-for-thumbnails.md
      , and per-lecture files in
      thumbnails/summaries/per-lecture/
      .
    • Maintain a correspondence table of lecture titles, recording dates, Zoom meeting IDs, recording times, Mux playback IDs, thumbnail paths, and publishing URLs.
  2. Confirm production connection destination
    • Retrieve
      NEXT_PUBLIC_CONVEX_URL
      and
      CONVEX_DEPLOYMENT
      from the Vercel production environment.
    • Confirm that it matches the Convex URL embedded in the site JS.
    • If the CLI points to a different Convex instance, use
      ConvexHttpClient
      with
      NEXT_PUBLIC_CONVEX_URL
      and
      CONVEX_INTERNAL_SECRET
      for operations.
  3. Search for Zoom recordings
    • Obtain a Zoom Server-to-Server OAuth token and search cloud recordings for the target period by date range.
    • Extract
      MP4
      ,
      TRANSCRIPT
      /
      VTT
      , and
      CHAT
      from
      recording_files
      .
    • Match candidates by recording date, meeting ID, duration, title similarity, and title order in existing MD files.
    • Only create a new record without passing
      recordingFileId
      to avoid duplicate checks if there is a broken, incomplete record with the same
      zoomRecordingId
      in existing Convex. Normally, pass
      recordingFileId
      to maintain idempotency.
  4. Create lecture data
    • If VTT is available, read the content to create a lecture overview, learning points, and chapter candidates.
    • If Zoom chat is available, save it as supplementary information and reflect questions and URLs in the overview.
    • If a per-lecture MD file exists, use it as the source and supplement missing content from the Zoom transcript.
  5. Create thumbnails
    • Maintain the existing tone if specified. For AI PLAY GUILD lecture thumbnails, base them on YouTube-style large text, strong outlines, and right-side character icon-style faces.
    • Do not fix the color to orange every time. Use colors matching the tool or lecture theme: orange for Claude, black/purple/green for Vercel/Slack, white/black/Google colors for Google/Notion, etc.
    • Do not make faces look scary. Keep the balance of eyes, mouth, and hairstyle natural, and use clean hairstyles.
    • Save generated files to
      thumbnails/generated/lecture-thumbnails/
      and write the path back to the correspondence table MD file.
  6. Import to Convex
    • Use
      api.zoom.createZoomManualImportVideo
      to convert the Zoom recording into a video record and start Mux import.
    • Use
      api.videos.updateVideoMetadataServer
      to refine the title/description/summary.
    • Use
      api.videos.generateUploadUrlServer
      to create an upload URL, PUT the PNG, then use
      api.videos.setThumbnailServer
      to set
      customThumbnailStorageId
      .
    • Since
      getPublishedVideos
      does not show unpublished videos, verify pending Mux imports via a publishing canary or admin query. Do not rely solely on
      getZoomVideosForMigration
      for waiting judgments, as it may not return Mux IDs.
  7. Publish and verify
    • Publish only the first video using
      publishVideoServer({ isPublished: true })
      .
    • Confirm that the target
      muxPlaybackId
      and
      thumbnailUrl
      can be retrieved via
      getPublishedVideos
      .
    • If no issues exist, publish the remaining videos and confirm that all have
      published / mux / thumbnail
      status.
    • Use
      curl
      on the representative URL to confirm HTTP 200. However, since the detail page may return 200 via app-side routing immediately after deletion, make the final judgment via the publishing list query.
  8. Notify Discord
    • Let
      discordNotify.postVideoToForum
      handle posting at publish time, then wait a few seconds.
    • Search active threads and public archived threads in the forum channel, and directly create missing titles via the Bot API.
    • Prioritize the summary for the post body; shorten it if too long, and include
      https://aiplayguild.com/videos/<videoId>
      .
  9. Handoff to note articles
    • Read
      references/note-membership-handoff.md
      to create handoff information for each published video.
    • Include title/date/videoId/LMS URL/Mux playback ID/VTT/Zoom chat/summary/thumbnail/shared URL/verification results in the handoff.
    • For single lecture publishing, automatically use
      $note-membership-lecture
      after verifying LMS publishing and Discord notifications, proceeding to create the note draft, 1280:670 eye-catching image, body images, paywall, and Light Plan member-only settings. Add a one-time purchase price only if explicitly specified by the user.
    • For bulk publishing of multiple videos, create note drafts for each lecture unless the user limits the scope. However, wait for explicit confirmation before publishing each note article.
    • At the start of the note article, present it as "Hands-on for note membership users" (not a video), paste the thumbnail image, then add a natural link.
    • When creating a Light Plan note article from a video published as an LMS lecture from a Zoom recording, always add a prominent bold badge stating "Light Plan Exclusive Article" to the note eye-catching image. Do not use a manual overlay that makes Japanese text look messy; regenerate the thumbnail itself via
      $imagegen
      /image 2 if necessary. Use the same image with the badge for the hands-on thumbnail pasted at the start of the article.
  10. Report results
    • Briefly output the quantity, published/unpublished status, Mux ready status, thumbnail ready status, Discord post/skip/error status, and publishing URLs.
    • If note articles were created, output the article key, preview URL, editor URL, paywall status, Light Plan status, access model, one-time price (if applicable), and whether it is pending note publication.
    • Delete temporary env files and logs containing secrets created during the process.

Incorrect Publication Deletion Flow

  1. Extract
    videoId
    from the URL.
  2. Confirm the title, Mux asset ID, and Discord post name via
    getPublishedVideos
    .
  3. Search for the Discord forum thread by name in active/public archived threads and delete it.
  4. Delete the Mux asset via the Mux API if an asset ID exists.
  5. Delete the Convex record if
    api.videos.deleteVideoServer
    is available. If not, set it to unpublished via
    publishVideoServer({ isPublished: false })
    , then either deploy a deletion mutation or confirm with the user.
  6. Confirm that the target ID no longer appears in
    getPublishedVideos
    .

Related Files

  • src/app/api/webhooks/zoom/route.ts
    - Zoom Webhook
  • src/app/api/zoom/import/route.ts
    - Single import
  • src/app/api/zoom/bulk-import/route.ts
    - Bulk import
  • convex/zoom.ts
    - Zoom recording record creation
  • convex/zoomActions.ts
    - Mux import, VTT/Chat saving, AI metadata
  • convex/videos.ts
    - Video publishing, thumbnail application, deletion server mutations
  • convex/discordNotify.ts
    - Discord forum posting
  • scripts/generate-thumbnail-source-md.mjs
    - Lecture MD generation for thumbnails
  • scripts/upload-lecture-thumbnails-to-convex-prod.mjs
    - Bulk thumbnail upload to Convex production