ソースを参照

lyrics endpoint

Sv443 3 年 前
コミット
19ee1deefa
8 ファイル変更779 行追加36 行削除
  1. 1 0
      .gitignore
  2. 11 1
      .vscode/launch.json
  3. 66 4
      README.md
  4. 652 16
      package-lock.json
  5. 1 0
      package.json
  6. 3 5
      src/index.js
  7. 27 4
      src/lyrics.js
  8. 18 6
      src/server.js

+ 1 - 0
.gitignore

@@ -1,3 +1,4 @@
 node_modules/
+test.js
 .env
 *.log

+ 11 - 1
.vscode/launch.json

@@ -7,12 +7,22 @@
         {
             "type": "pwa-node",
             "request": "launch",
-            "name": "Launch Program",
+            "name": "Launch",
             "skipFiles": [
                 "<node_internals>/**"
             ],
             "program": "${workspaceFolder}\\src\\index.js",
             "console": "integratedTerminal"
+        },
+        {
+            "type": "pwa-node",
+            "request": "launch",
+            "name": "test.js",
+            "skipFiles": [
+                "<node_internals>/**"
+            ],
+            "program": "${workspaceFolder}\\test.js",
+            "console": "integratedTerminal"
         }
     ]
 }

+ 66 - 4
README.md

@@ -6,12 +6,19 @@ Simple "REST proxy" for searching for lyrics on genius.com
 
 
 ## Routes:
+All routes support gzip and deflate compression.  
 
-> ### GET `/search?q=<text>`
-> This endpoint searches the genius API for the specified `<text>`  
-> The text should contain both song and artist name(s) if possible, but the song name alone might sometimes be enough.  
-> If there are special characters, they need to be [percent/URL-encoded.](https://en.wikipedia.org/wiki/Percent-encoding)  
+<br>
+
+> ### GET `/search?q=search_text`
+> This endpoint gives you the top 10 results for a search query specified by `search_text`  
+> The returned data contains various data like the lyrics website URL, song and thumbnail metadata and more (see below).  
 >   
+> The `search_text` should contain both song and artist name(s) if possible (order doesn't matter, separate with a whitespace)
+> Sometimes the song name alone might be enough but the results may vary.  
+> If the search query contains special characters, they need to be [percent/URL-encoded.](https://en.wikipedia.org/wiki/Percent-encoding)  
+> 
+> <br>
 > <details><summary><b>Successful response (click to view)</b></summary>
 > 
 > ```json
@@ -58,3 +65,58 @@ Simple "REST proxy" for searching for lyrics on genius.com
 > 
 > </details>
 > <br>
+
+<br><br>
+
+> ### GET `/lyrics?q=search_text`
+> Use this endpoint to get the lyrics of a specified song and additionally all properties from the top result of the `/search` endpoint.  
+>   
+> The `search_text` should contain both song and artist name(s) if possible (order doesn't matter, separate with a whitespace)
+> Sometimes the song name alone might be enough but the results may vary.  
+> If the search query contains special characters, they need to be [percent/URL-encoded.](https://en.wikipedia.org/wiki/Percent-encoding)  
+>   
+> This feature is powered by [this awesome scraper.](https://github.com/farshed/genius-lyrics-api)
+> 
+> <br>
+> <details><summary><b>Successful response (click to view)</b></summary>
+> 
+> ```json
+> {
+>     "error": false,
+>     "lyrics": "[Verse 1]\nAyy ayy ayy\nYou know who it is ayy\n",
+>     "url": "https://genius.com/Artist-1-song-name-lyrics",
+>     "path": "/Artist-1-song-name-lyrics",
+>     "meta": {
+>         "title": "Song Name",
+>         "fullTitle": "Song Name by Artist 1 (ft. Artist 2)",
+>         "artists": "Artist 1 (ft. Artist 2)",
+>         "primaryArtist": {
+>             "name": "Artist 1",
+>             "url": "https://genius.com/artists/Artist-1"
+>         }
+>     },
+>     "resources": {
+>         "thumbnail": "https://images.genius.com/8485557225af0345d2c550af8bae731b.300x300x1.png",
+>         "image": "https://images.genius.com/13d7b13ef827a9f007a5d24c115b9ebb.1000x1000x1.png"
+>     },
+>     "lyricsState": "complete",
+>     "id": 42069,
+>     "timestamp": 1234567890123
+> }
+> ```
+> 
+> </details>
+> <br>
+>   
+> <details><summary>Errored response (click to view)</summary>
+> 
+> ```json
+> {
+>     "error": true,
+>     "message": "Something went wrong",
+>     "timestamp": 1234567890123
+> }
+> ```
+> 
+> </details>
+> <br>

ファイルの差分が大きいため隠しています
+ 652 - 16
package-lock.json


+ 1 - 0
package.json

@@ -31,6 +31,7 @@
     "compression": "^1.7.4",
     "cors": "^2.8.5",
     "express": "^4.17.3",
+    "genius-lyrics-api": "^3.2.0",
     "helmet": "^5.0.2",
     "kleur": "^4.1.4",
     "rate-limiter-flexible": "^2.3.6",

+ 3 - 5
src/index.js

@@ -1,19 +1,17 @@
 const dotenv = require("dotenv");
 
+dotenv.config();
+
 const server = require("./server");
 const error = require("./error");
 
 
 async function init()
 {
-    let stage = "reading env file";
+    let stage = "initializing server";
 
     try
     {
-        dotenv.config();
-
-        stage = "initializing server";
-
         server.init();
 
         stage = "(done)";

+ 27 - 4
src/lyrics.js

@@ -1,11 +1,12 @@
 const { default: axios } = require("axios");
+const { getLyrics: apiGetLyrics } = require("genius-lyrics-api");
 
-const { env } = process;
+const accessToken = process.env.GENIUS_ACCESS_TOKEN || "ERR_NO_ENV";
 
-async function getLyrics(search)
+async function getMeta(search)
 {
     const { data: { response } } = await axios.get(`https://api.genius.com/search?q=${encodeURIComponent(search)}`, {
-        headers: { "Authorization": `Bearer ${env.GENIUS_ACCESS_TOKEN || "ERR_NO_ENV"}` },
+        headers: { "Authorization": `Bearer ${accessToken}` },
     });
 
     if(Array.isArray(response?.hits))
@@ -39,4 +40,26 @@ async function getLyrics(search)
     }
 }
 
-module.exports = { getLyrics };
+/**
+ * @param {string} title
+ * @param {string} artist
+ * @returns {Promise<string | null>}
+ */
+async function getLyrics(title, artist)
+{
+    try
+    {
+        return await apiGetLyrics({
+            apiKey: accessToken,
+            title,
+            artist,
+            optimizeQuery: true,
+        });
+    }
+    catch(err)
+    {
+        return null;
+    }
+}
+
+module.exports = { getMeta, getLyrics };

+ 18 - 6
src/server.js

@@ -8,9 +8,7 @@ const cors = require("cors");
 
 const packageJson = require("../package.json");
 const error = require("./error");
-const { getLyrics } = require("./lyrics");
-
-const { env } = process;
+const { getMeta, getLyrics } = require("./lyrics");
 
 /** @typedef {import("svcorelib").JSONCompatible} JSONCompatible */
 /** @typedef {import("express").Response} Response */
@@ -30,7 +28,7 @@ const rateLimiter = new RateLimiterMemory({
 
 async function init()
 {
-    const port = parseInt(env.HTTP_PORT);
+    const port = parseInt(process.env.HTTP_PORT);
 
     if(await portUsed(port))
         return error(`TCP port ${port} is already used`, undefined, true);
@@ -83,9 +81,23 @@ function registerEndpoints()
             if(typeof q !== "string" || q.length === 0)
                 return respond(res, "clientError", "No query parameter (?q=...) provided or it is invalid");
 
-            const lyricsInfo = await getLyrics(q);
+            const meta = await getMeta(q);
+
+            return respond(res, "success", meta);
+        });
+
+        app.get("/lyrics", async (req, res) => {
+            const { q } = req.query;
+
+            if(typeof q !== "string" || q.length === 0)
+                return respond(res, "clientError", "No query parameter (?q=...) provided or it is invalid");
+
+            const meta = await getMeta(q);
+            const { top } = meta;
+
+            const lyrics = await getLyrics(top.meta.title, top.meta.primaryArtist.name);
 
-            return respond(res, "success", lyricsInfo);
+            return respond(res, "success", { lyrics, ...meta });
         });
     }
     catch(err)

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません