add spotify logic to send to the websocket the currently music data

This commit is contained in:
StarAppeal
2024-11-25 00:35:49 +01:00
parent f9f072021c
commit b60125dff5
10 changed files with 197 additions and 65 deletions
+27 -1
View File
@@ -9,7 +9,8 @@ export interface IUser {
uuid: string,
id: ObjectId,
config: UserConfig,
lastState: MatrixState
lastState: MatrixState,
spotifyConfig: SpotifyConfig
}
export interface UserConfig {
@@ -41,6 +42,13 @@ export interface MatrixState {
};
}
export interface SpotifyConfig {
accessToken: string;
refreshToken: string;
expirationDate: Date;
scope: string;
}
const userSchema = new mongoose.Schema<IUser>({
name: {
type: String,
@@ -116,6 +124,24 @@ const userSchema = new mongoose.Schema<IUser>({
}
}
},
spotifyConfig: {
accessToken: {
type: String,
required: true,
},
refreshToken: {
type: String,
required: true,
},
expirationDate: {
type: Date,
required: true,
},
scope: {
type: String,
required: true,
},
},
});
+6 -1
View File
@@ -15,10 +15,15 @@ export class UserService {
return this._instance;
}
public async updateUser(id: string, user: Partial<IUser>): Promise<IUser | null> {
public async updateUserById(id: string, user: Partial<IUser>): Promise<IUser | null> {
return await UserModel.findByIdAndUpdate(id, user, {new: true}).exec();
}
public async updateUser(user: IUser): Promise<IUser | null> {
const {id, ...rest} = user;
return this.updateUserById(id.toString(), rest);
}
public async getAllUsers(): Promise<IUser[]> {
return await UserModel.find().exec();
}
+45
View File
@@ -0,0 +1,45 @@
import axios from "axios";
export interface CurrentlyPlaying {
timestamp: number;
context?: {
type: string;
uri: string;
};
progress_ms?: number;
item?: {
name: string;
artists: {
name: string;
uri: string;
}[];
album: {
name: string;
uri: string;
};
duration_ms: number;
};
is_playing: boolean;
}
export async function getCurrentlyPlaying(accessToken: string) {
try {
const response = await axios.get<CurrentlyPlaying>("https://api.spotify.com/v1/me/player/currently-playing", {
headers: {
Authorization: `Bearer ${accessToken}`
}, params: {
additional_types: "episode"
}
});
if (response.status === 204) {
console.log("Es wird gerade nichts abgespielt.");
return null;
}
return response.data;
} catch (error: any) {
console.error("Fehler bei der Anfrage:", error.response?.status, error.response?.data);
}
}
-1
View File
@@ -6,7 +6,6 @@ const clientId = process.env.SPOTIFY_CLIENT_ID;
const clientSecret = process.env.SPOTIFY_CLIENT_SECRET;
export class SpotifyTokenService {
public async refreshToken(refreshToken: string) {
console.log("refreshToken")
const response = await axios.post(
+1
View File
@@ -6,4 +6,5 @@ export interface ExtendedWebSocket extends WebSocket {
payload: DecodedToken;
isAlive: boolean;
user:IUser;
spotifyUpdate?: NodeJS.Timeout
}
+2 -1
View File
@@ -25,10 +25,11 @@ export class RestUser {
});
router.put("/:id", async (req, res) => {
console.log("PUT /user/:id")
const userService = await UserService.create();
const id = req.params.id;
const user = req.body as IUser;
const result = await userService.updateUser(id, user);
const result = await userService.updateUserById(id, user);
result
? res.status(200).send(result)
+1
View File
@@ -1,5 +1,6 @@
import express from "express";
import {SpotifyTokenService} from "../db/services/spotifyTokenService";
import {UserService} from "../db/services/db/UserService";
export class SpotifyTokenGenerator {
+59 -5
View File
@@ -1,4 +1,7 @@
import {ExtendedWebSocket} from "../../interfaces/extendedWebsocket";
import {getCurrentlyPlaying} from "../../db/services/spotifyApiService";
import {SpotifyTokenService} from "../../db/services/spotifyTokenService";
import {UserService} from "../../db/services/db/UserService";
export class WebsocketEventHandler {
constructor(private webSocket: ExtendedWebSocket) {
@@ -20,17 +23,68 @@ export class WebsocketEventHandler {
this.webSocket.onclose = (event) => {
console.log("WebSocket closed:", event.code, event.reason, event.wasClean, event.type);
console.log(`User: ${this.webSocket.payload.name} disconnected`);
clearInterval(this.webSocket.spotifyUpdate);
};
}
public enableMessageEvent() {
this.webSocket.on("message", (data) => {
const message = data.toString();
console.log("Received message:", message);
const message = data.toString();
console.log("Received message:", message);
if (message === "GET_STATE") {
this.webSocket.send(JSON.stringify(this.webSocket.user.lastState), {binary: false});
if (message === "GET_STATE") {
const messageToSend = {
type: "STATE",
payload: this.webSocket.user.lastState,
};
this.webSocket.send(JSON.stringify(messageToSend), {binary: false});
}
if (message === "GET_SPOTIFY_UPDATES") {
console.log("Starting Spotify updates");
// first execute the function once
this.spotifyUpdates()
.then(() => {
// then set the interval
this.webSocket.spotifyUpdate = setInterval(() => {
this.spotifyUpdates();
}, 1000);
});
}
if (message === "STOP_SPOTIFY_UPDATES") {
if (this.webSocket.spotifyUpdate) {
clearInterval(this.webSocket.spotifyUpdate);
console.log("Spotify updates stopped");
}
}
}
});
);
}
private async spotifyUpdates() {
console.log("Checking Spotify")
const user = this.webSocket.user;
const spotifyConfig = user.spotifyConfig;
if (Date.now() > spotifyConfig.expirationDate.getTime()) {
console.log("Token expired");
const token = await new SpotifyTokenService().refreshToken(spotifyConfig.refreshToken);
user.spotifyConfig = {
accessToken: token.access_token,
refreshToken: token.refresh_token,
expirationDate: new Date(Date.now() + token.expires_in * 1000),
scope: token.scope,
};
const userService = await UserService.create();
await userService.updateUser(user);
console.log("Token refreshed and database updated");
}
const musicData = await getCurrentlyPlaying(user.spotifyConfig.accessToken);
this.webSocket.send(JSON.stringify({
type: "SPOTIFY_UPDATE",
payload: musicData,
}), {binary: false});
}
}
@@ -17,7 +17,7 @@ export class WebsocketServerEventHandler {
this.webSocketServer.on(
"connection",
async (ws: ExtendedWebSocket, request: ExtendedIncomingMessage) => {
const user = await (await UserService.create()).getUserByUUID(ws.payload._id);
const user = await (await UserService.create()).getUserByUUID(request.payload._id);
ws.user = user!;
+55 -55
View File
@@ -1,68 +1,68 @@
import { Server } from "http";
import { WebSocket, Server as WebSocketServer } from "ws";
import { verifyClient } from "./utils/verifyClient";
import { ExtendedWebSocket } from "./interfaces/extendedWebsocket";
import { DecodedToken } from "./interfaces/decodedToken";
import { WebsocketServerEventHandler } from "./utils/websocket/websocketServerEventHandler";
import { WebsocketEventHandler } from "./utils/websocket/websocketEventHandler";
import {Server} from "http";
import {Server as WebSocketServer, WebSocket} from "ws";
import {verifyClient} from "./utils/verifyClient";
import {ExtendedWebSocket} from "./interfaces/extendedWebsocket";
import {DecodedToken} from "./interfaces/decodedToken";
import {WebsocketServerEventHandler} from "./utils/websocket/websocketServerEventHandler";
import {WebsocketEventHandler} from "./utils/websocket/websocketEventHandler";
export class ExtendedWebSocketServer {
private readonly _wss: WebSocketServer;
private readonly _wss: WebSocketServer;
constructor(server: Server) {
this._wss = new WebSocketServer({
server,
verifyClient: (info, callback) => verifyClient(info.req, callback),
});
constructor(server: Server) {
this._wss = new WebSocketServer({
server,
verifyClient: (info, callback) => verifyClient(info.req, callback),
});
this.setupWebSocket();
}
this.setupWebSocket();
}
private setupWebSocket() {
const serverEventHandler = new WebsocketServerEventHandler(this.wss);
serverEventHandler.enableConnectionEvent((ws) => {
const socketEventHandler = new WebsocketEventHandler(ws);
private get wss(): WebSocketServer {
return this._wss;
}
console.log("WebSocket client connected");
public broadcast(message: string) {
this.wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message, {binary: false});
}
});
}
socketEventHandler.enableErrorEvent();
socketEventHandler.enableDisconnectEvent();
socketEventHandler.enablePongEvent();
socketEventHandler.enableMessageEvent();
});
public sendMessageToUser(_id: string, message: string) {
this.wss.clients.forEach(
(client: WebSocket & { payload?: DecodedToken }) => {
if (
client.payload?._id === _id &&
client.readyState === WebSocket.OPEN
) {
client.send(message, {binary: false});
}
},
);
}
const interval = serverEventHandler.enableHeartbeat(30000);
serverEventHandler.enableCloseEvent(() => {
clearInterval(interval);
});
}
public getConnectedClients(): Set<ExtendedWebSocket> {
return this.wss.clients as Set<ExtendedWebSocket>;
}
public broadcast(message: string) {
this.wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message, { binary: false });
}
});
}
private setupWebSocket() {
const serverEventHandler = new WebsocketServerEventHandler(this.wss);
serverEventHandler.enableConnectionEvent((ws) => {
const socketEventHandler = new WebsocketEventHandler(ws);
public sendMessageToUser(_id: string, message: string) {
this.wss.clients.forEach(
(client: WebSocket & { payload?: DecodedToken }) => {
if (
client.payload?._id === _id &&
client.readyState === WebSocket.OPEN
) {
client.send(message, { binary: false });
}
},
);
}
console.log("WebSocket client connected");
public getConnectedClients(): Set<ExtendedWebSocket> {
return this.wss.clients as Set<ExtendedWebSocket>;
}
socketEventHandler.enableErrorEvent();
socketEventHandler.enableDisconnectEvent();
socketEventHandler.enablePongEvent();
socketEventHandler.enableMessageEvent();
});
private get wss(): WebSocketServer {
return this._wss;
}
const interval = serverEventHandler.enableHeartbeat(30000);
serverEventHandler.enableCloseEvent(() => {
clearInterval(interval);
});
}
}