Skip to content

Commit 73f4fa8

Browse files
ChanMeng666claude
andcommitted
feat: initial commit — github-readme-suno-cards v0.1
GitHub Action + Vercel-hosted service that renders your Suno AI music as animated SVG cards for your GitHub profile README. - Handle-based auto-discovery via Suno's public API - Spotify-style equalizer animation - Dark/light theme auto-switching - Smart 9-bucket tag classification - Multi-language support (en, zh, ja) - Service mode (live) + Local mode (static SVG) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 parents  commit 73f4fa8

116 files changed

Lines changed: 39757 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
* text=auto eol=lf
2+
*.ts text eol=lf
3+
*.tsx text eol=lf
4+
*.js text eol=lf
5+
*.mjs text eol=lf
6+
*.json text eol=lf
7+
*.yml text eol=lf
8+
*.yaml text eol=lf
9+
*.md text eol=lf
10+
*.css text eol=lf

.github/workflows/ci.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
verify:
15+
name: typecheck · test · lint · bundle
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: pnpm/action-setup@v4
21+
with:
22+
version: 9.12.0
23+
24+
- uses: actions/setup-node@v4
25+
with:
26+
# Node 22+ is needed for `node --experimental-strip-types build.ts`
27+
# to run the esbuild bundle script. The bundled action/dist/index.js
28+
# itself is plain JS and runs on Node 20 at Action runtime.
29+
node-version: '22'
30+
cache: 'pnpm'
31+
32+
- name: Install dependencies
33+
run: pnpm install --frozen-lockfile
34+
35+
- name: Lint (biome)
36+
run: pnpm check
37+
38+
- name: Typecheck all packages
39+
run: pnpm typecheck
40+
41+
- name: Run tests
42+
run: pnpm test
43+
44+
# Action marketplace requires the compiled `dist/index.js` to be committed.
45+
# Rebuild it in CI and fail if the committed bundle drifted from source —
46+
# same safety gate blog-post-workflow uses.
47+
- name: Rebuild action bundle
48+
working-directory: action
49+
run: pnpm build
50+
51+
- name: Verify committed dist matches rebuild
52+
run: |
53+
if ! git diff --quiet action/dist; then
54+
echo "::error::action/dist/ is out of sync with action/src/."
55+
echo "Run \`pnpm --filter @suno-cards/action build\` locally and commit the result."
56+
git diff action/dist
57+
exit 1
58+
fi

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
node_modules/
2+
dist/
3+
# Action marketplace requires the compiled bundle to be committed.
4+
!action/dist/
5+
.next/
6+
coverage/
7+
*.log
8+
.DS_Store
9+
.env
10+
.env.local
11+
.vercel/
12+
.vercel

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Chan Meng
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# github-readme-suno-cards
2+
3+
Display your [Suno AI](https://suno.com/)-generated music as dynamic, animated SVG cards in your GitHub profile README.
4+
5+
[![CI](https://github.com/ChanMeng666/github-readme-suno-cards/actions/workflows/ci.yml/badge.svg)](https://github.com/ChanMeng666/github-readme-suno-cards/actions/workflows/ci.yml)
6+
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
7+
[![Vercel](https://img.shields.io/badge/deployed%20on-Vercel-000)](https://github-readme-suno-cards-lrpl2jknu-chan-mengs-projects.vercel.app/)
8+
9+
> **Give us your Suno handle and do nothing else.** Your GitHub README stays in sync with your music — new songs, play counts, and likes appear automatically.
10+
11+
12+
---
13+
14+
## Live preview
15+
16+
[![My top song](https://github-readme-suno-cards-lrpl2jknu-chan-mengs-projects.vercel.app/api/card?id=a885e43c-6918-456f-a5f0-0e8e29e61066)](https://suno.com/song/a885e43c-6918-456f-a5f0-0e8e29e61066)
17+
18+
[![My Suno profile](https://github-readme-suno-cards-lrpl2jknu-chan-mengs-projects.vercel.app/api/profile?handle=chanmeng)](https://suno.com/@chanmeng)
19+
20+
## Features
21+
22+
- 🎨 **Spotify-style animated equalizer bars** — 4 CSS-animated bars overlay your cover art
23+
- 🎯 **Handle-based auto-discovery** — set your handle once, new songs appear automatically
24+
- 🌓 **Dark/light theme** auto-switches with GitHub's UI theme
25+
- 🏷️ **Smart tag classification** — genres, instruments, moods, vocal types, and tempo rendered as distinct chips
26+
- 🎖️ **Suno-native model badges** — the `v4.5-all` / `v5` badges use the exact theme tokens from Suno's own UI
27+
- 🆕 **"NEW" ribbon** on songs published in the last 7 days
28+
- 📊 **Profile summary card** — avatar, handle, total plays, likes, followers
29+
- 🌐 **Multi-language** — English, 简体中文, 日本語 out of the box
30+
-**Vercel Edge Runtime** — cold start 30–80 ms, warm requests ~20 ms from the edge cache
31+
- 🔒 **Compliance-first** — uses only the public `studio-api-prod.suno.com` endpoints that Suno itself publishes as their oEmbed provider; no cookies, no reverse-engineered auth
32+
33+
## Quick start
34+
35+
### 1. Add the markers to your README
36+
37+
```markdown
38+
## My Suno music
39+
40+
<!-- SUNO-CARDS:START -->
41+
<!-- SUNO-CARDS:END -->
42+
```
43+
44+
### 2. Create `.github/workflows/suno-cards.yml`
45+
46+
```yaml
47+
name: Update Suno cards
48+
49+
on:
50+
schedule:
51+
- cron: '0 */6 * * *' # Every 6 hours
52+
workflow_dispatch:
53+
54+
permissions:
55+
contents: write
56+
57+
jobs:
58+
update:
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v4
62+
- uses: ChanMeng666/github-readme-suno-cards@v0.1
63+
with:
64+
handle: YOUR_SUNO_HANDLE
65+
sort: play_count
66+
max: 6
67+
- uses: EndBug/add-and-commit@v9
68+
with:
69+
message: 'chore(suno-cards): refresh'
70+
add: './README.md'
71+
```
72+
73+
### 3. That's it.
74+
75+
Every 6 hours (or whenever you click "Run workflow"), the Action fetches your public songs from Suno, sorts/filters them, and writes the markdown block between your markers.
76+
77+
## Alternative: embed a single card directly
78+
79+
If you don't want to run a GitHub Action, you can embed a single card URL straight in your README — no setup required:
80+
81+
```markdown
82+
[![](https://github-readme-suno-cards-lrpl2jknu-chan-mengs-projects.vercel.app/api/card?id=YOUR_SONG_UUID)](https://suno.com/song/YOUR_SONG_UUID)
83+
```
84+
85+
## Render modes
86+
87+
### `service` (default)
88+
89+
The Action writes markdown pointing at the hosted Vercel service. Cards auto-update whenever anyone views your README — play counts, NEW badges, and any data change reflects live. No SVG files are committed to your repo.
90+
91+
### `local`
92+
93+
The Action pre-renders SVGs into `.suno-cards/` in your repo and commits them alongside the README update. Zero dependency on any hosted service — your README works offline, in mirrors, and in git history forever. Switch via `render_mode: local` in the workflow inputs.
94+
95+
## Endpoints (for direct embedding)
96+
97+
| Endpoint | Description |
98+
|---|---|
99+
| `/api/card?id=<uuid\|short\|url>` | Single song card |
100+
| `/api/profile?handle=<handle>` | Profile summary card |
101+
| `/api/cards?handle=<handle>&sort=<...>&max=<N>` | Stacked N-card auto-discovery |
102+
| `/song/<uuid>` | Pretty URL alias for `/api/card?id=<uuid>` |
103+
104+
All endpoints accept the same query parameters as the Action inputs below.
105+
106+
## Action inputs
107+
108+
### Data source (pick one)
109+
110+
| Input | Default | Description |
111+
|---|---|---|
112+
| `handle` || Your Suno handle — enables auto-discovery mode |
113+
| `manifest_path` | `./suno-songs.yml` | Path to a YAML manifest of song IDs |
114+
| `song_ids` || Alternative: comma-separated UUIDs inline |
115+
116+
### Filters & ranking
117+
118+
| Input | Default | Description |
119+
|---|---|---|
120+
| `sort` | `created_at` | `created_at` \| `play_count` \| `upvote_count` \| `name` |
121+
| `max` | `6` | Max song cards to render (1–20) |
122+
| `include_tags` || CSV substring filter (case-insensitive) |
123+
| `exclude_tags` || CSV substring filter |
124+
| `min_duration` / `max_duration` || Seconds |
125+
| `min_plays` / `min_likes` || Integer floors |
126+
| `pinned_first` | `true` | Respect `is_pinned` above sort order |
127+
| `featured` || CSV of UUIDs pinned above everything |
128+
| `allow_explicit` | `true` | Include explicit-tagged songs |
129+
| `show_profile_card` | `true` | Emit profile summary card above the song cards |
130+
131+
### Output & styling
132+
133+
| Input | Default | Description |
134+
|---|---|---|
135+
| `render_mode` | `service` | `service` (hosted) or `local` (pre-render SVGs) |
136+
| `local_cards_dir` | `.suno-cards` | Output dir in local mode |
137+
| `readme_path` | `./README.md` | Path to README file |
138+
| `comment_tag_name` | `SUNO-CARDS` | Marker name (allows multiple instances in one README) |
139+
| `output_type` | `markdown` | `markdown` or `html` (with `<picture>` for theme switching) |
140+
| `theme` | `auto` | `auto` / `dark` / `light` |
141+
| `lang` | `en` | `en` / `zh` / `ja` |
142+
| `width` || Card width in px (200–1200) |
143+
| `bg_color` || Card background hex (with or without `#`) |
144+
| `text_color` || Title / primary text color |
145+
| `accent_color` || Accent color (equalizer bars, chips) |
146+
| `base_url` | `https://sunocards.vercel.app` | Self-hosted service override |
147+
| `output_only` | `false` | Skip README write, emit via action outputs only |
148+
149+
### Outputs (for chained workflow steps)
150+
151+
- `profile` — JSON-encoded `SunoProfile`
152+
- `clips` — JSON-encoded array of `SunoSong`
153+
- `cards_block` — the markdown/HTML block written to README
154+
- `rendered_files` — JSON array of SVG paths (local mode only)
155+
156+
## Examples
157+
158+
See [`examples/`](./examples) for ready-to-copy workflow files:
159+
160+
- [`auto-discovery.yml`](./examples/auto-discovery.yml) — the flagship mode, zero config beyond your handle
161+
- [`manifest-mode.yml`](./examples/manifest-mode.yml) — explicit song list via YAML
162+
- [`local-mode.yml`](./examples/local-mode.yml) — pre-render SVGs to your repo
163+
- [`suno-songs.yml`](./examples/suno-songs.yml) — sample manifest file
164+
165+
## Architecture
166+
167+
The project is a pnpm monorepo with four packages:
168+
169+
```
170+
packages/parser — Suno API client + Valibot schemas (zero HTML scraping)
171+
packages/render — SVG card primitives (pure, no network I/O)
172+
apps/web — Next.js 15 on Vercel Edge Runtime (3 route handlers)
173+
action — Node 20 GitHub Action (esbuild-bundled)
174+
```
175+
176+
**Data source**: `https://studio-api-prod.suno.com/api/clip/{uuid}` and `/api/profiles/{handle}` — both are unauthenticated public JSON endpoints that Suno publishes via `<link rel="alternate" type="application/json+oembed">` in their HTML. No cookies, no session tokens, no reverse-engineered internal APIs.
177+
178+
**Rendering**: template-literal SVG strings with `<foreignObject>` for CJK-friendly rich text, CSS `@media (prefers-color-scheme)` for auto theming, and CSS keyframes on HTML `<span>`s inside the foreignObject for the animated equalizer (the same technique [`spotify-github-profile`](https://github.com/kittinan/spotify-github-profile) uses).
179+
180+
**Caching**: dual-layer — Vercel Data Cache hint on upstream Suno calls (`next: { revalidate: 3600 }`), plus HTTP `Cache-Control: s-maxage=3600, stale-while-revalidate=86400` for the downstream GitHub Camo proxy direction.
181+
182+
## Roadmap
183+
184+
### v0.2
185+
186+
- [ ] **Waveform card variant** (Action local mode only) — download MP3 from `cdn1.suno.ai`, compute amplitude samples, render SVG `<path>` like SoundCloud
187+
- [ ] **Lyrics excerpt card** — parse `[Chorus]` from structured prompt, render as card subtitle
188+
- [ ] **Playlist card** — render Suno playlists
189+
- [ ] **JSON API**`/api/song.json` and `/api/profile.json` for third-party tools
190+
- [ ] **PNG export**`/api/card.png` via `@resvg/resvg`
191+
192+
### v0.3
193+
194+
- [ ] **Vercel KV play-count history** — trending arrows (`↑ +15 this week`), weekly summaries
195+
- [ ] **RSS/Atom feed per handle**`/api/feed/{handle}.xml`, consumable by `blog-post-workflow`
196+
- [ ] **Landing page with live configurator** — paste handle → preview → copy snippet
197+
198+
### v0.4+
199+
200+
- [ ] Cover-art color extraction for per-song gradient backgrounds
201+
- [ ] Song-DNA radar chart card
202+
- [ ] Year-in-review card
203+
204+
## Acknowledgements
205+
206+
The card primitives borrow ideas from several excellent projects:
207+
208+
- [`github-readme-medium-recent-article`](https://github.com/omidnikrah/github-readme-medium-recent-article) — Next.js + Vercel architecture
209+
- [`github-readme-youtube-cards`](https://github.com/DenverCoder1/github-readme-youtube-cards) — dual-mode Action + service, theme switching
210+
- [`blog-post-workflow`](https://github.com/gautamkrishnar/blog-post-workflow) — esbuild bundling, marker-replace regex, CI dist-diff verification
211+
- [`spotify-github-profile`](https://github.com/kittinan/spotify-github-profile) — CSS-animated equalizer overlay technique
212+
213+
## License
214+
215+
[MIT](./LICENSE)

0 commit comments

Comments
 (0)