Skip to content

Commit 3dde593

Browse files
feat: add workflow to sync YouTube metadata and report discrepancies
1 parent 25455ce commit 3dde593

4 files changed

Lines changed: 439 additions & 1 deletion

File tree

‎.claude‎

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: Sync YouTube metadata
2+
3+
# Watches the LIVE YouTube Data API v3 for drift between the hand-maintained video
4+
# catalog (src/data/videos.ts) and YouTube, and opens/updates ONE tracking issue when
5+
# something needs attention: blank duration/publishedAt to fill, a title you renamed on
6+
# YouTube, a video the API no longer returns (deleted/private → dead embed), or a channel
7+
# upload not yet in the catalog. It NEVER edits the catalog and NEVER pushes to main —
8+
# you read the issue and edit videos.ts by hand.
9+
#
10+
# Trigger: weekly cron + manual. (YouTube is external to this repo, so a schedule is the
11+
# right shape — `push` would only re-check after unrelated commits. Run "Run workflow"
12+
# anytime.) Needs a YOUTUBE_API_KEY repo secret (read-only key, restricted to YouTube
13+
# Data API v3). Quota per run ~5 units of the 10,000/day default.
14+
#
15+
# Injection-safety: the `run:` step contains no `${{ }}` expansion; the github-script step
16+
# reads every value from `env:` via process.env (never interpolated into a shell), and all
17+
# of those values come from THIS repo's own script — no issue/PR/commit text is consumed.
18+
19+
on:
20+
schedule:
21+
- cron: '0 12 * * 1' # every Monday 12:00 UTC (an hour off the DataWeave check so they don't collide)
22+
workflow_dispatch:
23+
24+
permissions:
25+
contents: read
26+
issues: write
27+
28+
concurrency:
29+
group: sync-youtube-metadata
30+
cancel-in-progress: false
31+
32+
jobs:
33+
sync:
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Checkout
37+
uses: actions/checkout@v4
38+
39+
- name: Set up Node
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: 20
43+
44+
- name: Check catalog vs. YouTube Data API
45+
id: sync
46+
env:
47+
YOUTUBE_API_KEY: ${{ secrets.YOUTUBE_API_KEY }} # passed via env, never on the command line
48+
run: node scripts/sync-youtube-metadata.mjs --discover
49+
50+
- name: Open / update tracking issue
51+
if: steps.sync.outputs.drift == 'true'
52+
uses: actions/github-script@v7
53+
env:
54+
ISSUE_TITLE: ${{ steps.sync.outputs.issue_title }}
55+
ISSUE_BODY: ${{ steps.sync.outputs.issue_body }}
56+
FILL_COUNT: ${{ steps.sync.outputs.fill_count }}
57+
DRIFT_COUNT: ${{ steps.sync.outputs.drift_count }}
58+
with:
59+
script: |
60+
const { ISSUE_TITLE, ISSUE_BODY, FILL_COUNT, DRIFT_COUNT } = process.env;
61+
const label = 'youtube-sync';
62+
63+
// Ensure the label exists (ignore "already exists").
64+
try {
65+
await github.rest.issues.createLabel({
66+
owner: context.repo.owner, repo: context.repo.repo,
67+
name: label, color: 'c4302b',
68+
description: 'Video catalog out of sync with the YouTube Data API',
69+
});
70+
} catch (e) { if (e.status !== 422) throw e; }
71+
72+
// Reuse the existing open tracking issue if there is one (don't spam a new
73+
// issue every week — update the body and ping instead).
74+
const existing = await github.paginate(github.rest.issues.listForRepo, {
75+
owner: context.repo.owner, repo: context.repo.repo,
76+
state: 'open', labels: label, per_page: 100,
77+
});
78+
79+
if (existing.length) {
80+
const issue = existing[0];
81+
await github.rest.issues.update({
82+
owner: context.repo.owner, repo: context.repo.repo,
83+
issue_number: issue.number, title: ISSUE_TITLE, body: ISSUE_BODY,
84+
});
85+
await github.rest.issues.createComment({
86+
owner: context.repo.owner, repo: context.repo.repo,
87+
issue_number: issue.number,
88+
body: `🔁 Re-checked: ${FILL_COUNT} field(s) to fill, ${DRIFT_COUNT} to verify. Details updated above.`,
89+
});
90+
core.info(`Updated existing issue #${issue.number}`);
91+
} else {
92+
const created = await github.rest.issues.create({
93+
owner: context.repo.owner, repo: context.repo.repo,
94+
title: ISSUE_TITLE, body: ISSUE_BODY, labels: [label],
95+
});
96+
core.info(`Opened issue #${created.data.number}`);
97+
}

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"remigrate": "node scripts/remigrate-posts.mjs",
1313
"recover-gists": "node scripts/recover-gists.mjs",
1414
"check-dw-version": "node scripts/check-dataweave-version.mjs",
15+
"sync-youtube": "node scripts/sync-youtube-metadata.mjs",
1516
"astro": "astro"
1617
},
1718
"dependencies": {

0 commit comments

Comments
 (0)