Implement proper youtube support
This commit is contained in:
@ -1,18 +1,5 @@
|
||||
const roomController = require('./room');
|
||||
|
||||
const SONGS = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Black Steam",
|
||||
artist: "Carrot Quest GmbH",
|
||||
coverUrl: "https://mir-s3-cdn-cf.behance.net/project_modules/1400/fe529a64193929.5aca8500ba9ab.jpg"
|
||||
},
|
||||
{id: 2, title: "Sunset Dreams", artist: "Ocean Waves", coverUrl: "https://place-hold.it/500x500/"},
|
||||
{id: 3, title: "Neon Nights", artist: "Electric Avenue", coverUrl: "https://place-hold.it/500x500/"},
|
||||
{id: 4, title: "Mountain Echo", artist: "Wild Terrain", coverUrl: "https://place-hold.it/500x500/"},
|
||||
{id: 5, title: "Urban Jungle", artist: "City Dwellers", coverUrl: "https://place-hold.it/500x500/"},
|
||||
{id: 6, title: "Cosmic Journey", artist: "Stargazers", coverUrl: "https://place-hold.it/500x500/"}
|
||||
];
|
||||
const youtubeService = require('../services/youtubeService');
|
||||
|
||||
const gameStates = {};
|
||||
|
||||
@ -44,7 +31,7 @@ const initializeGameState = (roomId) => {
|
||||
return gameStates[roomId];
|
||||
};
|
||||
|
||||
const startNewRound = (roomId) => {
|
||||
const startNewRound = async (roomId) => {
|
||||
const gameState = gameStates[roomId];
|
||||
if (!gameState) return false;
|
||||
|
||||
@ -63,9 +50,7 @@ const startNewRound = (roomId) => {
|
||||
gameState.roles[user.id] = user.id === gameState.currentComposer ? 'composer' : 'guesser';
|
||||
});
|
||||
|
||||
selectSongAndOptions(gameState);
|
||||
|
||||
return true;
|
||||
return await selectSongAndOptions(gameState);
|
||||
};
|
||||
|
||||
const determineNextComposer = (roomId, gameState, users) => {
|
||||
@ -82,16 +67,35 @@ const determineNextComposer = (roomId, gameState, users) => {
|
||||
return users[(currentIndex + 1) % users.length].id;
|
||||
};
|
||||
|
||||
const selectSongAndOptions = (gameState) => {
|
||||
gameState.selectedSong = SONGS[Math.floor(Math.random() * SONGS.length)];
|
||||
|
||||
const songOptions = [...SONGS].sort(() => Math.random() - 0.5).slice(0, 5);
|
||||
|
||||
if (!songOptions.includes(gameState.selectedSong)) {
|
||||
songOptions[Math.floor(Math.random() * songOptions.length)] = gameState.selectedSong;
|
||||
const selectSongAndOptions = async (gameState) => {
|
||||
try {
|
||||
const availableIds = await youtubeService.getAvailableSongIds();
|
||||
|
||||
if (!availableIds || availableIds.length === 0) {
|
||||
console.error("No song IDs available for selection");
|
||||
return false;
|
||||
}
|
||||
|
||||
gameState.songOptions = songOptions;
|
||||
const selectedId = availableIds[Math.floor(Math.random() * availableIds.length)];
|
||||
gameState.selectedSong = { id: selectedId };
|
||||
|
||||
const optionIds = new Set([selectedId]);
|
||||
const attempts = Math.min(20, availableIds.length);
|
||||
|
||||
let count = 0;
|
||||
while (optionIds.size < 5 && count < attempts) {
|
||||
const randomId = availableIds[Math.floor(Math.random() * availableIds.length)];
|
||||
optionIds.add(randomId);
|
||||
count++;
|
||||
}
|
||||
|
||||
gameState.songOptions = Array.from(optionIds).map(id => ({ id }));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error selecting songs and options:", error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const getTimeRemaining = (roomId) => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
const roomController = require("../controller/room");
|
||||
const gameController = require("../controller/game");
|
||||
const youtubeService = require('../services/youtubeService');
|
||||
|
||||
module.exports = (io) => (socket) => {
|
||||
let currentRoomId = null;
|
||||
@ -147,7 +148,7 @@ module.exports = (io) => (socket) => {
|
||||
currentRoomId = roomId;
|
||||
});
|
||||
|
||||
socket.on("start-game", () => {
|
||||
socket.on("start-game", async () => {
|
||||
const roomId = roomController.getUserRoom(socket.id);
|
||||
if (!roomId || !roomController.isUserHost(socket.id)) {
|
||||
return socket.emit("not-authorized");
|
||||
@ -162,10 +163,19 @@ module.exports = (io) => (socket) => {
|
||||
|
||||
if (!roomController.startGame(roomId)) return;
|
||||
gameController.initializeGameState(roomId);
|
||||
if (!gameController.startNewRound(roomId)) return;
|
||||
|
||||
io.to(roomId).emit("game-started");
|
||||
handleRoundStart(roomId);
|
||||
try {
|
||||
const success = await gameController.startNewRound(roomId);
|
||||
if (!success) {
|
||||
return socket.emit("error", { message: "Failed to start game - could not load songs" });
|
||||
}
|
||||
|
||||
io.to(roomId).emit("game-started");
|
||||
handleRoundStart(roomId);
|
||||
} catch (error) {
|
||||
console.error("Error starting game:", error);
|
||||
socket.emit("error", { message: "Failed to start game due to an error" });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("send-message", (messageData) => {
|
||||
@ -233,7 +243,7 @@ module.exports = (io) => (socket) => {
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("next-round", () => {
|
||||
socket.on("next-round", async () => {
|
||||
const roomId = roomController.getUserRoom(socket.id);
|
||||
if (!roomId || !roomController.isUserHost(socket.id)) return;
|
||||
|
||||
@ -244,8 +254,16 @@ module.exports = (io) => (socket) => {
|
||||
return socket.emit("error", { message: "At least 2 players are required" });
|
||||
}
|
||||
|
||||
if (gameController.startNewRound(roomId)) {
|
||||
handleRoundStart(roomId);
|
||||
try {
|
||||
const success = await gameController.startNewRound(roomId);
|
||||
if (success) {
|
||||
handleRoundStart(roomId);
|
||||
} else {
|
||||
socket.emit("error", { message: "Failed to start next round" });
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error starting next round:", error);
|
||||
socket.emit("error", { message: "Failed to start next round due to an error" });
|
||||
}
|
||||
});
|
||||
|
||||
@ -257,4 +275,14 @@ module.exports = (io) => (socket) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("get-playlist-songs", async () => {
|
||||
try {
|
||||
const songs = await youtubeService.fetchPlaylistSongs();
|
||||
socket.emit("playlist-songs", { songs });
|
||||
} catch (error) {
|
||||
console.error("Error sending playlist songs:", error);
|
||||
socket.emit("playlist-songs", { songs: youtubeService.getDefaultSongs() });
|
||||
}
|
||||
});
|
||||
};
|
71
server/services/youtubeService.js
Normal file
71
server/services/youtubeService.js
Normal file
@ -0,0 +1,71 @@
|
||||
const YOUTUBE_API_KEY = process.env.YOUTUBE_API_KEY;
|
||||
const PLAYLIST_ID = "PLmXxqSJJq-yXrCPGIT2gn8b34JjOrl4Xf";
|
||||
|
||||
let cachedSongs = null;
|
||||
let lastFetchTime = 0;
|
||||
const CACHE_TTL = 3600000; // 1 hour
|
||||
|
||||
/**
|
||||
* Fetches songs from YouTube playlist and returns them
|
||||
*/
|
||||
const fetchPlaylistSongs = async () => {
|
||||
const now = Date.now();
|
||||
if (cachedSongs && cachedSongs.length > 0 && (now - lastFetchTime) < CACHE_TTL) {
|
||||
console.log("Returning cached YouTube songs:", cachedSongs.length);
|
||||
return cachedSongs;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("Fetching songs from YouTube API...");
|
||||
const playlistUrl = `https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=50&playlistId=${PLAYLIST_ID}&key=${YOUTUBE_API_KEY}`;
|
||||
const response = await fetch(playlistUrl);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
console.error("YouTube API error:", data.error);
|
||||
throw new Error(data.error.message);
|
||||
}
|
||||
|
||||
if (!data.items || !data.items.length) {
|
||||
console.error("No items found in YouTube playlist");
|
||||
throw new Error("No songs found in the playlist");
|
||||
}
|
||||
|
||||
const songs = data.items.map((item, index) => ({
|
||||
id: index + 1,
|
||||
youtubeId: item.contentDetails.videoId,
|
||||
title: item.snippet.title,
|
||||
artist: item.snippet.videoOwnerChannelTitle || "Unknown Artist",
|
||||
coverUrl: item.snippet.thumbnails.high?.url || item.snippet.thumbnails.default?.url ||
|
||||
"https://place-hold.it/500x500/333/fff?text=No%20Thumbnail",
|
||||
}));
|
||||
|
||||
cachedSongs = songs;
|
||||
lastFetchTime = now;
|
||||
console.log("Fetched and cached YouTube songs:", songs.length);
|
||||
|
||||
return songs;
|
||||
} catch (error) {
|
||||
console.error("Error fetching YouTube playlist:", error);
|
||||
|
||||
if (cachedSongs && cachedSongs.length > 0) {
|
||||
console.log("Using last cached songs due to API error");
|
||||
return cachedSongs;
|
||||
}
|
||||
|
||||
return [{
|
||||
id: 1,
|
||||
title: "Could not load songs",
|
||||
artist: "API Error",
|
||||
coverUrl: "https://place-hold.it/500x500/f00/fff?text=Error",
|
||||
youtubeId: "dQw4w9WgXcQ"
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
const getAvailableSongIds = async () => {
|
||||
const songs = await fetchPlaylistSongs();
|
||||
return songs.map(song => song.id);
|
||||
};
|
||||
|
||||
module.exports = {fetchPlaylistSongs, getAvailableSongIds};
|
Reference in New Issue
Block a user