# Automating a YouTube Video Showcase in a Profile README

> A GitHub Actions workflow that regenerates the video cards in a profile README on a schedule, so the showcase never goes stale: channel ID, marker comments, card options, and the permission settings that trip people up.

Source: https://chanmeng.org/blog/youtube-github-actions-automation
Author: Chan Meng — https://chanmeng.org
Published: 2026-06-09
Tags: DevOps, Open Source

---

*Originally written as internal engineering notes while building the thing it describes. Republished here with light editing — the timeframe is noted where it matters.*

A GitHub profile README that showcases your latest videos has an obvious failure mode: the moment you publish a new one, the README is out of date, and hand-editing it every time is the kind of chore nobody keeps up for long.

The fix is to stop maintaining that section by hand. A scheduled GitHub Actions workflow can fetch the channel's latest uploads, render them as SVG cards, and commit the result back into the README — so the showcase updates itself whether or not I remember it exists.

This is how I set it up, using [`DenverCoder1/github-readme-youtube-cards`](https://github.com/DenverCoder1/github-readme-youtube-cards), plus the parts of the setup that are easy to get wrong.

## How it fits together

Three pieces have to exist before anything runs:

1. A workflow file at `.github/workflows/youtube-cards.yml`.
2. A video showcase section in `README.md`.
3. A pair of HTML comment markers around that section:

```html
<!-- BEGIN YOUTUBE-CARDS -->
<!-- END YOUTUBE-CARDS -->
```

The markers are the load-bearing part. The action does not parse your README or guess where the cards belong — it replaces everything *between* those two comments and leaves the rest of the file alone. Delete or rename one of them and the workflow will run happily and change nothing.

## Finding the channel ID

The workflow is keyed on a channel ID, not a handle. There are three reliable ways to get it:

**From the channel URL.** If it looks like `https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxxxxxxxx`, the `UC…` segment *is* the ID.

**From YouTube Studio.** Avatar → *YouTube Studio* → *Settings* → *Channel* → *Advanced settings*. The ID is in the "Channel ID" field.

**From any video.** Right-click the channel name under a video, copy the link address, and take the `UC…` part.

Then set it in the workflow:

```yaml
channel_id: UCipSxT7a3rn81vGLw9lqRkg   # replace with your own
```

If the README also carries a subscribe button, that one *does* use the handle rather than the ID — the `@name` from your channel page URL:

```html
<a href="https://www.youtube.com/@YourChannelHandle?sub_confirmation=1">
```

## The first run

Scheduled workflows don't fire on commit, so after pushing the workflow and README changes, the first run has to be triggered by hand: repository → *Actions* → *GitHub Readme YouTube Cards* → *Run workflow* → pick the branch.

A few seconds later it fetches the latest videos, generates the SVG cards, rewrites the block between the markers, and commits the change. After that, the schedule takes over.

## The options worth changing

The defaults are sensible, but four settings are worth a look:

```yaml
max_videos: 6          # how many cards to render
card_width: 250        # px per card
max_title_lines: 2     # truncation for long video titles
schedule:
  - cron: "0 */6 * * *"   # every 6 hours
  # - cron: "0 0 * * *"   # once a day
```

Card width and video count interact with how the cards wrap in the README, so it is worth iterating on those two together rather than separately.

On the cron: a frequent schedule costs nothing but generates a commit each time the video list actually changes. Every six hours is a reasonable ceiling for a channel that publishes weekly; daily is plenty for most.

## Optional: showing video duration

Duration isn't in the free feed — it requires the YouTube Data API. If you want it:

1. In the [Google Cloud Console](https://console.cloud.google.com/), create or pick a project, enable **YouTube Data API v3**, and create an API key.
2. Add it to the repository under *Settings* → *Secrets and variables* → *Actions* → *New repository secret*, named `YOUTUBE_API_KEY`.
3. Reference it from the workflow:

```yaml
youtube_api_key: ${{ secrets.YOUTUBE_API_KEY }}
show_duration: true
```

Never inline the key in the workflow file — the workflow is public if the repository is.

## The two things that actually break

**Write permission.** The workflow commits to your repository, so it needs `contents: write`. Without it the run fails at the very last step, after everything else has worked, which makes it look like a rendering problem rather than a permissions one.

**Branch protection.** If the default branch is protected, the Actions bot may be blocked from pushing. The workflow succeeds at generating the cards and then cannot land them.

Beyond those: if the run fails, check the channel ID first and read the Actions log — it is specific about what it could not fetch. If the run *succeeds* but the README doesn't change, the cause is almost always the marker comments (missing, renamed, or the README path misconfigured) rather than the action.

## Why bother

The value here isn't the video cards. It's that a section of a hand-maintained document has been converted into a derived artefact — something regenerated from a source of truth on a schedule, which cannot drift because nothing is asking a human to remember it. That pattern is worth applying to more of a profile README than just this.

## Reference

- [`github-readme-youtube-cards`](https://github.com/DenverCoder1/github-readme-youtube-cards) — the action
- [Wiki and FAQ](https://github.com/DenverCoder1/github-readme-youtube-cards/wiki) — full option list
