Skip to content

Commit a2af64e

Browse files
razznbluek-wilmeth
andauthored
v1.1.4 - Add Audio Endpoints & Upgrade node version (#40)
* version * add tests --------- Co-authored-by: Kaipo Wilmeth <kwilmeth@fanthreesixty.com>
1 parent 777aaa9 commit a2af64e

File tree

16 files changed

+5539
-2815
lines changed

16 files changed

+5539
-2815
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v14.16.0
1+
v18.17.1

package-lock.json

Lines changed: 5012 additions & 2764 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jetsetradio-api",
3-
"version": "1.1.3",
3+
"version": "1.1.4",
44
"description": "A Data Provider relating to the JSR/JSRF universe",
55
"type": "module",
66
"main": "src/app.js",
@@ -15,12 +15,12 @@
1515
"author": "RazzNBlue",
1616
"license": "Apache 2.0",
1717
"dependencies": {
18-
"axios": "^1.3.4",
18+
"axios": "^1.11.0",
1919
"bcrypt": "^5.1.0",
2020
"cors": "^2.8.5",
2121
"dotenv": "^16.0.3",
22-
"ejs": "^3.1.9",
23-
"express": "^4.18.2",
22+
"ejs": "^3.1.10",
23+
"express": "^4.21.2",
2424
"express-list-endpoints": "^6.0.0",
2525
"express-rate-limit": "^6.7.0",
2626
"memory-cache": "^0.2.0",

src/controllers/audioController.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import Constants from "../constants/dbConstants.js";
2+
import {Actions} from "../config/dbActions.js";
3+
import {performDBAction} from "../config/db.js";
4+
import {sortObjects} from "../utils/utility.js";
5+
import {fetchRandom} from "./utilController.js";
6+
import LOGGER from "../utils/logger.js";
7+
8+
const Audio = "Audio";
9+
const {JSR_DB, JSRF_DB} = Constants;
10+
11+
export const getAudio = async (req, res) => {
12+
try {
13+
const sortByValue = req?.query?.sortBy ? req?.query?.sortBy : undefined;
14+
const sortOrder = req?.query?.orderBy ? req?.query?.orderBy : "asc";
15+
const audio = await fetchAudio(req, "ALL");
16+
if (sortByValue) {
17+
return res.send(audio.sort(sortObjects(sortByValue, sortOrder)));
18+
}
19+
res.send(audio);
20+
} catch (err) {
21+
LOGGER.error(`Could not fetch ALL ${Audio}`, err);
22+
res.status(500).send(`Could not fetch ALL ${Audio} due to error:`, err);
23+
}
24+
};
25+
26+
export const getRandomAudio = async (req, res) => {
27+
try {
28+
res.send(await fetchRandom(req, Audio));
29+
} catch (err) {
30+
LOGGER.error(`Could not fetch random ${Audio}`, err);
31+
res.status(500).json({error: `Failed to fetch random ${Audio}`});
32+
}
33+
};
34+
35+
export const getJSRAudio = async (req, res) => {
36+
try {
37+
const jsrAudio = await fetchAudio(req, JSR_DB);
38+
if (jsrAudio) {
39+
return res.send(jsrAudio);
40+
}
41+
res.status(404).send();
42+
} catch (err) {
43+
LOGGER.error(`Could not fetch JSR ${Audio}`, err);
44+
res.status(500).send(`Could not fetch JSR ${Audio}`, err);
45+
}
46+
};
47+
48+
export const getJSRAudioById = async (req, res) => {
49+
try {
50+
const id = req?.params?.id;
51+
const jsrAudio = await performDBAction(
52+
Actions.fetchById,
53+
JSR_DB,
54+
Audio,
55+
id
56+
);
57+
if (jsrAudio) {
58+
return res.send(jsrAudio);
59+
}
60+
res
61+
.status(404)
62+
.send(`JSR ${Audio} Resource could not be found at requested location`);
63+
} catch (err) {
64+
LOGGER.error(`Could not fetch JSR ${Audio} by ID ${req?.params?.id}`, err);
65+
res
66+
.status(500)
67+
.send(`Could not fetch JSR ${Audio} with ID: ${req.params.id}`, err);
68+
}
69+
};
70+
71+
export const getJSRFAudio = async (req, res) => {
72+
try {
73+
const jsrfAudio = await fetchAudio(req, JSRF_DB);
74+
if (jsrfAudio) {
75+
return res.send(jsrfAudio);
76+
}
77+
res.status(404).send();
78+
} catch (err) {
79+
LOGGER.error(`Could not fetch JSRF ${Audio}`, err);
80+
res.status(500).send(`Could not fetch JSRF ${Audio}`, err);
81+
}
82+
};
83+
84+
export const getJSRFAudioById = async (req, res) => {
85+
try {
86+
const id = req?.params?.id;
87+
const jsrfAudio = await performDBAction(
88+
Actions.fetchById,
89+
JSRF_DB,
90+
Audio,
91+
id
92+
);
93+
if (jsrfAudio) {
94+
return res.send(jsrfAudio);
95+
}
96+
res
97+
.status(404)
98+
.send(`JSRF ${Audio} Resource could not be found at requested location`);
99+
} catch (err) {
100+
LOGGER.error(`Could not fetch JSRF ${Audio} by ID`, err);
101+
res
102+
.status(500)
103+
.send(`Could not fetch JSRF ${Audio} with ID: ${req.params.id}`, err);
104+
}
105+
};
106+
107+
export const fetchAudio = async (req, dbName) => {
108+
if (dbName === "ALL") {
109+
const jsrAudio = await fetchAudio(req, JSR_DB);
110+
const jsrfAudio = await fetchAudio(req, JSRF_DB);
111+
const audio = [...jsrAudio, ...jsrfAudio];
112+
return audio;
113+
}
114+
115+
return await performDBAction(
116+
Actions.fetchWithQuery,
117+
dbName,
118+
Audio,
119+
null,
120+
req?.query
121+
);
122+
};

src/controllers/utilController.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ const {JSR_DB, JSRF_DB, BRC_DB, gameMap} = Constants;
88
/* Helper Functions to support all other Controllers */
99
export const fetchRandom = async (req, resource, game) => {
1010
try {
11-
const games = [JSR_DB, JSRF_DB, BRC_DB];
11+
const games =
12+
resource === "Audio" ? [JSR_DB, JSRF_DB] : [JSR_DB, JSRF_DB, BRC_DB];
1213
const selectedGame = req?.query?.game;
1314
const count = Number(req?.query?.count);
1415
const safeCount = Number.isFinite(count) && count > 0 ? count : 1;

src/managers/MiddlewareManager.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,18 @@ const filterPipeRoutes = async (req, endpoints) => {
101101
filteredEndpoints.push(endpoint);
102102
}
103103
if (
104-
(jsrCollections.includes(model) && endpoint.includes("jsr")) ||
104+
((jsrCollections.includes(model) ||
105+
jsrCollections.includes(model + "s")) &&
106+
endpoint.includes("jsr")) ||
105107
endpoint.includes("levels")
106108
) {
107109
filteredEndpoints.push(endpoint);
108110
}
109-
if (jsrfCollections.includes(model) && endpoint.includes("jsrf")) {
111+
if (
112+
(jsrfCollections.includes(model) ||
113+
jsrCollections.includes(model + "s")) &&
114+
endpoint.includes("jsrf")
115+
) {
110116
filteredEndpoints.push(endpoint);
111117
}
112118
if (

src/public/docs.html

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ <h2>Graffiti-Tags</h2>
154154
<pre id="graffiti-tag-response" class="expandable code-snippet" style="display: none;"></pre>
155155
</div>
156156

157+
<div class="main-container">
158+
<h2>Collectibles</h2>
159+
<p>A Collectible is a resource describing a location or place from a specific Game. The only supported game currently is Bomb Rush Cyberfunk.</p>
160+
<p>Endpoints:</p>
161+
<ul>
162+
<li><code class="code-snippet">/collectibles</code> ==> Returns all Collectibles</li>
163+
<li><code class="code-snippet">/collectibles/random</code> ==> Returns a Random Collectible</li>
164+
<li><code class="code-snippet">/collectibles/random?count=10</code> ==> Returns 10 random Collectibles</li>
165+
<li><code class="code-snippet">/collectibles/:id</code> ==> Returns a single Collectible by ID</li>
166+
<li><code class="code-snippet">/collectibles?type=CD</code> ==> Returns all CD Collectibles</li>
167+
<li><code class="code-snippet">/collectibles?character.name=Shine</code> ==> Returns Shine's Collectibles</li>
168+
</ul>
169+
<p>Example Request:</p>
170+
<code class="code-snippet">https://jetsetradio-api.onrender.com/v1/api/collectibles?sortBy=name&limit=5</code>
171+
<p>Example Response: <button class="expandable-button">Expand</button> </p>
172+
<pre id="collectible-response" class="expandable code-snippet" style="display: none;"></pre>
173+
</div>
174+
157175
<div class="main-container">
158176
<h2>Songs</h2>
159177
<p>A Song is a resource describing a song from a specific Game.</p>
@@ -194,22 +212,27 @@ <h2>Artists</h2>
194212
</div>
195213

196214
<div class="main-container">
197-
<h2>Collectibles</h2>
198-
<p>A Collectible is a resource describing a location or place from a specific Game. The only supported game currently is Bomb Rush Cyberfunk.</p>
215+
<h2>Audio/Quotes</h2>
216+
<p>An Audio is a resource describing a audio/quote by a character from a specific Game.</p>
199217
<p>Endpoints:</p>
200218
<ul>
201-
<li><code class="code-snippet">/collectibles</code> ==> Returns all Collectibles</li>
202-
<li><code class="code-snippet">/collectibles/random</code> ==> Returns a Random Collectible</li>
203-
<li><code class="code-snippet">/collectibles/random?count=10</code> ==> Returns 10 random Collectibles</li>
204-
<li><code class="code-snippet">/collectibles/:id</code> ==> Returns a single Collectible by ID</li>
205-
<li><code class="code-snippet">/collectibles?type=CD</code> ==> Returns all CD Collectibles</li>
206-
<li><code class="code-snippet">/collectibles?character.name=Shine</code> ==> Returns Shine's Collectibles</li>
219+
<li><code class="code-snippet">/audio</code> ==> Returns all Audio</li>
220+
<li><code class="code-snippet">/audio/random</code> ==> Returns a Random Audio From any Game</li>
221+
<li><code class="code-snippet">/audio/random?game=jsr</code> ==> Returns a Random JSR Audio</li>
222+
<li><code class="code-snippet">/audio/random?game=jsrf</code> ==> Returns a Random JSRF Audio</li>
223+
<li><code class="code-snippet">/audio/random?count=10&game=jsrf</code> ==> Returns 10 random JSRF Audio</li>
224+
<li><code class="code-snippet">/audio/jsr</code> ==> Returns all Jet Set Radio Audio</li>
225+
<li><code class="code-snippet">/audio/jsr/:id</code> ==> Returns a single JSR Audio by ID</li>
226+
<li><code class="code-snippet">/audio/jsrf</code> ==> Returns all Jet Set Radio Future Audio</li>
227+
<li><code class="code-snippet">/audio/jsrf/:id</code> ==> Returns a single JSRF Audio by ID</li>
228+
<li><code class="code-snippet">/audio/jsrf?speaker=DJ Professor K</code> ==> Returns DJ Professor K quotes from JSRF</li>
207229
</ul>
208230
<p>Example Request:</p>
209-
<code class="code-snippet">https://jetsetradio-api.onrender.com/v1/api/collectibles?sortBy=name&limit=5</code>
231+
<code class="code-snippet">https://jetsetradio-api.onrender.com/v1/api/songs?gameId=64285b7918c8a0231136dc5a</code>
210232
<p>Example Response: <button class="expandable-button">Expand</button> </p>
211-
<pre id="collectible-response" class="expandable code-snippet" style="display: none;"></pre>
233+
<pre id="audio-response" class="expandable code-snippet" style="display: none;"></pre>
212234
</div>
235+
___________________________________________________________
213236

214237
<div class="main-container">
215238
<h2>Base URL</h2>

src/public/js/docs.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import graffitiTagResource from "./examples/graffitiTagExample.js";
66
import songResource from "./examples/songExample.js";
77
import artistResource from "./examples/artistExample.js";
88
import collectibleResource from "./examples/collectibleExample.js";
9+
import audioResource from "./examples/audioExample.js";
910

1011
const gameResponse = document.querySelector('#game-response');
1112
if (gameResponse) {
@@ -39,6 +40,10 @@ const collectibleResponse = document.querySelector('#collectible-response');
3940
if (collectibleResponse) {
4041
collectibleResponse.textContent = JSON.stringify(collectibleResource, null, 4);
4142
}
43+
const audioResponse = document.querySelector('#audio-response');
44+
if (audioResponse) {
45+
audioResponse.textContent = JSON.stringify(audioResource, null, 4);
46+
}
4247

4348
const expandableButtons = document.querySelectorAll(".expandable-button");
4449
if (expandableButtons) {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const audioResource = {
2+
_id: "68be5885b4a1c9bd51b61a2c",
3+
key: "dj-professor-k-643c71c18cabe0dcede8691c-https://stat...",
4+
ordinality: 15,
5+
audioUrl: "https://static.wikia.nocookie.net/jetsetradio/image...",
6+
metadata: {
7+
duration: 5.87319727891156,
8+
codec: "PCM",
9+
mimeType: "audio/wave",
10+
sampleRate: 22050,
11+
bitrate: 352800,
12+
numberOfChannels: 1,
13+
},
14+
transcription:
15+
"\"Whoa! Someone's kidnapped the goddess?! That's bad karma, man. Baaaaad..\"",
16+
usage: "When the player reaches the Goddess Statue plinth....",
17+
category: "Chapter 1",
18+
language: "en",
19+
speaker: "DJ Professor K",
20+
gameId: "64285b7918c8a0231136dc5d",
21+
characterId: "643c71c18cabe0dcede8691c",
22+
source: "Jet Set Radio Future",
23+
createdAt: "2025-09-08T04:16:05.199Z",
24+
updatedAt: "2025-09-08T04:16:05.199Z",
25+
};
26+
27+
export default audioResource;

src/public/style/docs.css

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ a {
3232
.main-container p .expandable-button.expanded::after {
3333
content: "-";
3434
vertical-align: middle;
35-
}
35+
}
3636

3737
@font-face {
3838
font-family: jetSet;
39-
src: url('/font/Jet-Set.ttf');
39+
src: url("/font/Jet-Set.ttf");
4040
}
4141

4242
@font-face {
4343
font-family: jetSetItalic;
44-
src: url('/font/Jet-Set-Italic.ttf');
44+
src: url("/font/Jet-Set-Italic.ttf");
4545
}
4646

4747
#img-container {
@@ -63,12 +63,13 @@ a {
6363
position: absolute;
6464
left: 50%;
6565
top: 50%;
66-
transform: translate(-50%,-50%);
66+
transform: translate(-50%, -50%);
6767
font-size: 5.5vw;
6868
text-align: center;
6969
}
7070

71-
#home, #swagger {
71+
#home,
72+
#swagger {
7273
position: absolute;
7374
right: 3%;
7475
top: 3%;
@@ -152,4 +153,4 @@ footer a img {
152153
width: 100%;
153154
height: auto;
154155
min-width: 50px;
155-
}
156+
}

0 commit comments

Comments
 (0)