This Cloudflare Worker fetches song lyrics by utilizing the YouTube API (for metadata from video IDs) and the Musixmatch and LrcLib APIs (for actual lyrics). It's designed to infer song details from a YouTube video ID and then retrieve corresponding lyrics, with an option for enhanced, word-by-word synchronized lyrics from Musixmatch.
- YouTube Video ID Input: Primarily uses a YouTube video ID to identify a song.
- Metadata Extraction: Fetches video details from the YouTube API to determine song title, artist, album (if possible), and duration.
- Musixmatch Integration: Communicates with the Musixmatch API to find and retrieve lyrics.
- LrcLib Integration: Uses the LrcLib API as an alternative or supplementary source for lyrics.
- LRC Format Lyrics: Provides lyrics in the standard LRC (timed lyrics) format.
- Word-by-Word Sync: Offers word-by-word synchronized lyrics from Musixmatch.
- Caching: Implements multiple layers of caching (YouTube API responses, Musixmatch API responses, final lyrics responses) to improve performance and reduce external API calls.
- Authentication: Protects the API using Cloudflare Turnstile and JWTs to prevent abuse.
When a request is made to the worker:
- Authentication (
index.ts,auth.ts):- The client first completes a Cloudflare Turnstile challenge.
- The client then sends the Turnstile token to the
/verify-turnstileendpoint. - If the token is valid, the worker generates a JWT and sends it back to the client.
- The client then includes the JWT in the
Authorizationheader for all subsequent requests to the main lyrics endpoint.
- Request Handling (
index.ts): The main worker script receives the incoming HTTP request. It handlesOPTIONSrequests for CORS preflight. - Parameter Processing (
GetLyrics.ts):-
The core logic resides in
GetLyrics.ts. It first checks a cache for a response to the identical request URL. -
A
videoId(YouTube Video ID) is a required query parameter. -
YouTube Metadata Fetch: Using the
videoIdand aGOOGLE_API_KEY(configured inwrangler.toml), it queries the Google YouTube Data API v3 for video details (title, description, duration, channel title). These API responses are cached. -
Song Detail Inference: It attempts to parse the song title, artist, and album from the YouTube video's metadata. It's only works with videos "Auto-generated by YouTube." (These are the songs uploaded by labels to YouTube)
-
If essential song details (song title and artist) cannot be determined, it returns a 400 error if they aren't provided in the API call.
-
- Lyric Fetching (
Musixmatch.ts,LrcLib.ts):- A request is sent to LrcLib to fetch lyrics.
- The
Musixmatch.tsclass manages all communication with the Musixmatch API. - Token Management: It handles the acquisition and refreshing of a
usertokenrequired for Musixmatch API calls. Tokens and API responses are cached. It also manages cookies returned by Musixmatch. - Track Matching: It searches for the track on Musixmatch using the artist, song, and album information (derived from YouTube metadata or provided as optional query parameters).
- Lyric Fetching:
- It fetches word-by-word synchronized lyrics ("richsync") and standard LRC lyrics from Musixmatch.
- If no lyrics are found, the lyrics field in the response will be
null.
- Response Generation (
GetLyrics.ts&index.ts):- A JSON response is constructed containing the determined song
artist,songtitle,album(if found),duration, thevideoId, the original YouTubedescription(if relevant), the fetchedlyrics(as an LRC formatted string ornull), anddebugInfo. - The response will also contain separate fields for Musixmatch and LrcLib lyrics.
- This final response is cached. Responses containing lyrics are cached for up to 3 days (
max-age=259200), while responses without lyrics are cached for 10 minutes (max-age=600). - The
index.tsworker sets appropriate HTTP headers (includingAccess-Control-Allow-Origin) and returns the response.
- A JSON response is constructed containing the determined song
-
URL:
/challenge -
Method:
GET -
Description: Returns a page with a Cloudflare Turnstile challenge.
-
URL:
/verify-turnstile -
Method:
POST -
Description: Verifies a Turnstile token and returns a JWT if valid.
-
Body:
{ "token": "YOUR_TURNSTILE_TOKEN" } -
Success Response (200 OK):
{ "jwt": "YOUR_JWT" }
- URL:
/lyrics(relative to your worker's deployed URL) - Method:
GET - Headers:
Authorization:Bearer YOUR_JWT
videoId(string, required): The YouTube video ID for the song.- Example:
dQw4w9WgXcQ
- Example:
artist(string, optional): The artist of the song. If provided, it can be used as a fallback or to refine the Musixmatch search, though data from the YouTubevideoIdoften takes precedence for inference.song(string, optional): The title of the song. Similar toartist, it can be used as a fallback or to refine search.album(string, optional): The album of the song. Used to improve Musixmatch search accuracy.duration(string, optional): The duration of the song in seconds. Can be inferred fromvideoId.alwaysFetchMetadata(boolean, optional, default:false): If set totrue, the worker will always fetch metadata from YouTube, even if song, artist, and album are provided.
GET /lyrics?song=Almost+Forgot&artist=Against+The+Current&duration=208&videoId=Y4gOQSZg5bQ
{
"song": "Almost Forgot",
"artist": "Against The Current",
"album": "Past Lives",
"duration": "209",
"parsedSongAndArtist": "Almost Forgot · Against The Current",
"videoId": "Y4gOQSZg5bQ",
"description": "Provided to YouTube by Fueled By Ramen...",
"debugInfo": {
"lyricMatchingStats": {
"mean": -0.015,
"variance": 0.000025,
"samples": [
...
],
"diff": [
...
]
}
},
"musixmatchWordByWordLyrics": "[offset:-0.015]\n[00:00.24] Oh, oh, oh...",
"musixmatchSyncedLyrics": "[00:00.24]Oh, oh, oh...",
"lrclibSyncedLyrics": "[00:00.24]Oh, oh, oh...",
"lrclibPlainLyrics": "Oh, oh, oh..."
}*Error Response (e.g., 400 Bad Request):
If videoId is missing or song/artist cannot be inferred:
{
"message": "Invalid Video Id"
}or
{
"message": "A Song wasn't provided and couldn't be inferred"
}- Node.js and npm
- Wrangler CLI (Cloudflare Workers CLI)
-
Clone the repository
-
Install dependencies:
npm install
-
Configure Local Development Secrets (
.dev.vars): For local development usingwrangler dev, secrets should be placed in a.dev.varsfile in the root directory of your project. This file should not be committed to version control.Create a file named
.dev.varsin your project root and add your secrets:GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY_HERE" TURNSTILE_SECRET_KEY="YOUR_TURNSTILE_SECRET_KEY_HERE" JWT_SECRET="YOUR_JWT_SECRET_HERE"
Obtain a
GOOGLE_API_KEYfrom the Google Cloud Console, ensuring the YouTube Data API v3 is enabled for your project. Obtain aTURNSTILE_SECRET_KEYfrom your Cloudflare dashboard.JWT_SECRETcan be any long, random string.
-
Start the development server:
npm run dev
This will typically start a server on
http://localhost:8787. -
Test in your browser or with a tool like curl:
- For local development, you can bypass the authentication by setting the
BYPASS_AUTHconstant totrueinsrc/index.ts. - If you want to test the authentication flow, first, complete the Turnstile challenge by visiting
http://localhost:8787/challenge. - Then, use the obtained Turnstile token to get a JWT from
http://localhost:8787/verify-turnstile. - Finally, use the JWT to make requests to the lyrics endpoint, e.g.,
http://localhost:8787/lyrics?videoId=YOUR_YOUTUBE_VIDEO_ID.
For example (with authentication bypass):
curl "http://localhost:8787/lyrics?videoId=Qd_Zcmlf4g4"For example (with authentication):
curl -H "Authorization: Bearer YOUR_JWT" "http://localhost:8787/lyrics?videoId=Qd_Zcmlf4g4" - For local development, you can bypass the authentication by setting the