Test
This commit is contained in:
parent
73c4061533
commit
55fae2b0d4
@ -3,4 +3,5 @@ WORKDIR /opt/prod/app
|
||||
COPY . .
|
||||
RUN npm install
|
||||
EXPOSE 3000
|
||||
RUN mkdir /opt/prod/storage/spotifySeasons
|
||||
CMD [ "node", "dist/start.js" ]
|
||||
348
dist/start.js
vendored
348
dist/start.js
vendored
@ -1,19 +1,343 @@
|
||||
console.log("bob test")
|
||||
const fetch = require("node-fetch");
|
||||
const express = require("express");
|
||||
const bodyParser = require("body-parser");
|
||||
const cors = require("cors");
|
||||
const https = require("https");
|
||||
const fs = require("fs/promises");
|
||||
|
||||
const agent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// Define a simple route
|
||||
app.get('/', (req, res) => {
|
||||
res.send('Hello World from Express testing!');
|
||||
const apiCache = {
|
||||
_: {},
|
||||
get(key) {
|
||||
if (
|
||||
this._[key] &&
|
||||
this._[key].timestamp + (this._[key].expire ?? 180000) > Date.now()
|
||||
) {
|
||||
return this._[key].value;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
set(key, value, expire) {
|
||||
this._[key] = {
|
||||
timestamp: Date.now(),
|
||||
expire: expire,
|
||||
value: value,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
app.get("/getMostRecentSpotifyTrack", async function (req, res) {
|
||||
if (apiCache.get("getMostRecentSpotifyTrack")) {
|
||||
res.send(JSON.stringify(apiCache.get("getMostRecentSpotifyTrack")));
|
||||
} else {
|
||||
const lastfmResponse = await fetch(
|
||||
`http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=jdf221&api_key=${process.env.LASTFM_API_KEY}&format=json`
|
||||
).then((res) => res.json());
|
||||
|
||||
const response = {
|
||||
track: undefined,
|
||||
artist: undefined,
|
||||
};
|
||||
|
||||
const track = lastfmResponse?.["recenttracks"]?.["track"]?.[0];
|
||||
response.track = track?.name;
|
||||
response.artist = track?.artist?.["#text"];
|
||||
|
||||
apiCache.set("getMostRecentSpotifyTrack", response);
|
||||
res.send(JSON.stringify(response));
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/*', (req, res) => {
|
||||
res.send('Hello World from '+req.originalUrl+'!');
|
||||
const CanvasApi = require("canvas");
|
||||
CanvasApi.registerFont("./consumer/ComicSans.ttf", { family: "Comic Sans MS" });
|
||||
const backgroundImage = CanvasApi.loadImage("./consumer/background.png");
|
||||
const arrowImage = CanvasApi.loadImage("./consumer/arrow.png");
|
||||
app.get("/consoomer/getLineGraphImage", async (request, response) => {
|
||||
const config = {
|
||||
maxPoints: 60,
|
||||
lowestX: -80,
|
||||
highestX: 1630,
|
||||
};
|
||||
|
||||
const params = {
|
||||
userId: request.query.userId ?? "-1",
|
||||
nickname: request.query.nickname ?? "Idiot",
|
||||
profileImageUrl:
|
||||
request.query.profileImageUrl ?? "https://i.imgur.com/E7JL7yp.png",
|
||||
points: parseInt(request.query.points) ?? 0,
|
||||
};
|
||||
if (!Object.values(params).every((v) => typeof v !== "undefined")) {
|
||||
response.send("Bad params");
|
||||
return;
|
||||
}
|
||||
if (Number.isNaN(params.points)) {
|
||||
response.send("Bad points");
|
||||
return;
|
||||
} else {
|
||||
params.points = Math.min(Math.abs(params.points), config.maxPoints);
|
||||
}
|
||||
|
||||
const step = Math.floor(
|
||||
(Math.abs(config.lowestX) + config.highestX) / config.maxPoints
|
||||
);
|
||||
const xPosition = Math.floor(step * params.points) + config.lowestX;
|
||||
|
||||
//min x = -80
|
||||
//max x = 1630
|
||||
//y = 135
|
||||
|
||||
const canvas = CanvasApi.createCanvas(1920, 737);
|
||||
const context = canvas.getContext("2d");
|
||||
|
||||
try {
|
||||
context.drawImage(await backgroundImage, 0, 0);
|
||||
|
||||
let profileImage = apiCache.get(
|
||||
"consoomer.profilePic." + params.profileImageUrl
|
||||
);
|
||||
if (!profileImage) {
|
||||
profileImage = await CanvasApi.loadImage(
|
||||
await fetch(params.profileImageUrl).then(async (r) =>
|
||||
Buffer.from(await r.arrayBuffer())
|
||||
)
|
||||
);
|
||||
apiCache.set(
|
||||
"consoomer.profilePic." + params.profileImageUrl,
|
||||
profileImage,
|
||||
300000
|
||||
); // 5 minutes
|
||||
}
|
||||
context.drawImage(profileImage, xPosition + 115, 432);
|
||||
|
||||
context.drawImage(await arrowImage, xPosition, 135);
|
||||
|
||||
context.font = "48px 'Comic Sans MS'";
|
||||
context.fillText("Consoomer Report", 700, 75);
|
||||
context.font = "34px 'Comic Sans MS'";
|
||||
context.fillText(`Results for: ${params.nickname}`, 700, 115);
|
||||
context.fillText(`Total: ${params.points}`, 698, 150);
|
||||
|
||||
response.header("Content-Type", "image/png");
|
||||
response.send(canvas.toBuffer("image/png"));
|
||||
} catch (error) {
|
||||
//throw error
|
||||
response.send("Uknown error");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Start the server
|
||||
app.listen(port, () => {
|
||||
console.log(`Server listening on port ${port}`);
|
||||
});
|
||||
app.listen(3000);
|
||||
|
||||
async function getSpotifyApi() {
|
||||
return {
|
||||
token: await fs
|
||||
.readFile("/opt/prod/storage/spotifySeasons/tokenData.json")
|
||||
.then((b) => JSON.parse(b.toString())),
|
||||
application: {
|
||||
id: process.env.SPOTIFY_API_ID,
|
||||
secret: process.env.SPOTIFY_API_SECRET,
|
||||
},
|
||||
|
||||
async sendRequest(url, method = "get", data, asJson = true) {
|
||||
await new Promise((r) => setTimeout(r, 500));
|
||||
|
||||
if (
|
||||
url !== "https://accounts.spotify.com/api/token" &&
|
||||
Date.now() > this.token.expiresAfterTimestamp
|
||||
) {
|
||||
await this.refreshToken();
|
||||
}
|
||||
|
||||
return await fetch(url, {
|
||||
method: method,
|
||||
body: data
|
||||
? asJson
|
||||
? JSON.stringify(data)
|
||||
: Object.keys(data)
|
||||
.map(
|
||||
(key) =>
|
||||
encodeURIComponent(key) +
|
||||
"=" +
|
||||
encodeURIComponent(data[key])
|
||||
)
|
||||
.join("&")
|
||||
: undefined,
|
||||
headers: {
|
||||
"Content-Type": asJson
|
||||
? "application/json"
|
||||
: "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
Authorization:
|
||||
url == "https://accounts.spotify.com/api/token"
|
||||
? "Basic " +
|
||||
new Buffer.from(
|
||||
this.application.id + ":" + this.application.secret
|
||||
).toString("base64")
|
||||
: "Bearer " + this.token.access_token,
|
||||
},
|
||||
}).then((b) => b.json());
|
||||
},
|
||||
|
||||
async refreshToken() {
|
||||
const response = await this.sendRequest(
|
||||
"https://accounts.spotify.com/api/token",
|
||||
"post",
|
||||
{
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: this.token.refresh_token,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
this.token = response;
|
||||
|
||||
this.token.expiresAfterTimestamp =
|
||||
Date.now() + this.token.expires_in * 1000;
|
||||
this.token.refresh_token = process.env.SPOTIFY_API_REFRESH_TOKEN
|
||||
|
||||
await fs.writeFile(
|
||||
"/opt/prod/storage/spotifySeasons/tokenData.json",
|
||||
JSON.stringify(this.token)
|
||||
);
|
||||
},
|
||||
|
||||
async createPlaylist(name) {
|
||||
return (
|
||||
await this.sendRequest(
|
||||
"https://api.spotify.com/v1/users/jdf221/playlists",
|
||||
"post",
|
||||
{
|
||||
name: name,
|
||||
public: false,
|
||||
}
|
||||
)
|
||||
).id;
|
||||
},
|
||||
|
||||
async search(query) {
|
||||
const queryObject = { q: query, type: ["track"] };
|
||||
const queryString = Object.keys(queryObject)
|
||||
.map(
|
||||
(key) =>
|
||||
encodeURIComponent(key) + "=" + encodeURIComponent(queryObject[key])
|
||||
)
|
||||
.join("&");
|
||||
return this.sendRequest(
|
||||
"https://api.spotify.com/v1/search?" + queryString
|
||||
);
|
||||
},
|
||||
|
||||
async addTracksToPlaylist(playlistId, tracksArray) {
|
||||
return this.sendRequest(
|
||||
`https://api.spotify.com/v1/playlists/${playlistId}/tracks`,
|
||||
"post",
|
||||
{ uris: tracksArray },
|
||||
true
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function weeklySpotifyHandler() {
|
||||
const spotifyApi = await getSpotifyApi();
|
||||
|
||||
const seasons = [
|
||||
"winter", // 0 Jan
|
||||
"winter", // 1 February
|
||||
"spring", // 2 March
|
||||
"spring", // 3 April
|
||||
"spring", // 4 May
|
||||
"summer", // 5 June
|
||||
"summer", // 6 July
|
||||
"summer", // 7 August
|
||||
"fall", // 8 September
|
||||
"fall", // 9 October
|
||||
"fall", // 10 November
|
||||
"winter", // 11 December
|
||||
];
|
||||
const currentDate = new Date();
|
||||
currentDate.setDate(currentDate.getDate() - 7);
|
||||
|
||||
const currentSeason = seasons[currentDate.getUTCMonth()];
|
||||
const currentPlaylistInternalId = `${currentSeason}-${currentDate.getUTCFullYear()}`;
|
||||
|
||||
const createdPlaylists = await fs
|
||||
.readFile("/opt/prod/storage/spotifySeasons/playlists.json")
|
||||
.then((b) => JSON.parse(b.toString()));
|
||||
|
||||
if (!createdPlaylists[currentPlaylistInternalId]) {
|
||||
const newPlaylistId = await spotifyApi.createPlaylist(
|
||||
currentDate.getUTCFullYear() +
|
||||
" " +
|
||||
(currentSeason[0].toUpperCase() + currentSeason.slice(1))
|
||||
);
|
||||
createdPlaylists[currentPlaylistInternalId] = {
|
||||
spotifyPlaylistId: newPlaylistId,
|
||||
tracks: [],
|
||||
};
|
||||
}
|
||||
|
||||
const playlistId =
|
||||
createdPlaylists[currentPlaylistInternalId].spotifyPlaylistId;
|
||||
|
||||
const topTracks = await fetch(
|
||||
"https://ws.audioscrobbler.com/2.0/?method=user.getTopTracks&user=jdf221&period=7day&api_key=5d7730f5457bb3e794f69d7713b884ad&format=json"
|
||||
).then((b) => b.json());
|
||||
const filteredTopTracks = topTracks.toptracks.track.filter(
|
||||
(t) => parseInt(t.playcount) > 2
|
||||
);
|
||||
|
||||
const newTracksToAdd = [];
|
||||
for (const lastfmTrack of filteredTopTracks) {
|
||||
const spotifySearch = await spotifyApi.search(
|
||||
`track:${lastfmTrack.name} artist:${lastfmTrack.artist.name}`
|
||||
);
|
||||
const tracksButExplicitFirst = spotifySearch.tracks.items.sort((a, b) => {
|
||||
if (a.explicit && b.explicit) return 0;
|
||||
if (a.explicit) return -1;
|
||||
if (b.explicit) return 1;
|
||||
});
|
||||
|
||||
for (const track of tracksButExplicitFirst) {
|
||||
if (
|
||||
lastfmTrack.artist.name === track.artists[0].name &&
|
||||
lastfmTrack.name === track.name
|
||||
) {
|
||||
if (
|
||||
!createdPlaylists[currentPlaylistInternalId].tracks.includes(
|
||||
track.uri
|
||||
)
|
||||
) {
|
||||
createdPlaylists[currentPlaylistInternalId].tracks.push(track.uri);
|
||||
newTracksToAdd.push(track.uri);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spotifyApi.addTracksToPlaylist(playlistId, newTracksToAdd);
|
||||
|
||||
await fs.writeFile(
|
||||
"/opt/prod/storage/spotifySeasons/playlists.json",
|
||||
JSON.stringify(createdPlaylists)
|
||||
);
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
const currentDate = new Date();
|
||||
if (
|
||||
currentDate.getDay() === 0 &&
|
||||
currentDate.getUTCHours() === 0 &&
|
||||
currentDate.getUTCMinutes() < 29
|
||||
) {
|
||||
weeklySpotifyHandler();
|
||||
}
|
||||
}, 1000 * 60 * 30); // Every 30 minutes
|
||||
|
||||
11
package.json
11
package.json
@ -1,5 +1,12 @@
|
||||
{
|
||||
"name": "api.jdf2.org",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"express": "^4.21.2"
|
||||
}
|
||||
"body-parser": "^1.19.0",
|
||||
"canvas": "^2.11.2",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user