Spaces:
Configuration error
Configuration error
| // Fix: This file was previously placeholder text. It is now fully implemented | |
| // to provide the required services for fetching data from the Gemini API, | |
| // resolving all module-related errors. | |
| import { GoogleGenAI, Type } from "@google/genai"; | |
| import { MediaType, Recommendation } from "../types"; | |
| // Initialize the Google Gemini AI client. | |
| // The API key is sourced from the environment variables, as per guidelines. | |
| const ai = new GoogleGenAI({ apiKey: process.env.API_KEY! }); | |
| /** | |
| * Fetches a list of popular titles for a given media type and genres. | |
| * @param mediaType - The type of media (Movies, TV Shows, Video Games). | |
| * @param genres - An array of selected genres. | |
| * @param isGamePassOnly - Optional flag for video games to filter by Game Pass availability. | |
| * @param isMultiplayerOnly - Optional flag for video games to filter by multiplayer availability. | |
| * @returns A promise that resolves to an array of title strings. | |
| */ | |
| export const fetchTitles = async ( | |
| mediaType: MediaType, | |
| genres: string[], | |
| isGamePassOnly?: boolean, | |
| isMultiplayerOnly?: boolean | |
| ): Promise<string[]> => { | |
| const gamePassQuery = isGamePassOnly ? " that are available on PC or Console Game Pass" : ""; | |
| const multiplayerQuery = isMultiplayerOnly ? " multiplayer" : ""; | |
| const prompt = ` | |
| List 20 popular and highly-rated${multiplayerQuery} ${mediaType}${gamePassQuery} from the following genres: ${genres.join(', ')}. | |
| Focus on a diverse mix of classic and recent titles. | |
| Return ONLY a JSON array of strings, where each string is a title. Do not include any other text or explanation. | |
| `; | |
| try { | |
| const response = await ai.models.generateContent({ | |
| model: 'gemini-2.5-flash', | |
| contents: prompt, | |
| config: { | |
| responseMimeType: 'application/json', | |
| responseSchema: { | |
| type: Type.ARRAY, | |
| items: { | |
| type: Type.STRING, | |
| description: "The title of the media.", | |
| } | |
| } | |
| } | |
| }); | |
| const jsonString = response.text.trim(); | |
| // The response is expected to be a JSON string array. | |
| return JSON.parse(jsonString); | |
| } catch (e) { | |
| console.error("Error fetching titles from Gemini API:", e); | |
| // Re-throw a more user-friendly error to be caught by the UI layer. | |
| throw new Error("Failed to fetch title suggestions from the AI. Please try again."); | |
| } | |
| }; | |
| /** | |
| * Fetches personalized recommendations based on user's favorite titles. | |
| * @param mediaType - The type of media (Movies, TV Shows, Video Games). | |
| * @param titles - An array of titles the user likes. | |
| * @param isGamePassOnly - Optional flag for video games to filter by Game Pass availability. | |
| * @param isMultiplayerOnly - Optional flag for video games to filter by multiplayer availability. | |
| * @returns A promise that resolves to an array of Recommendation objects. | |
| */ | |
| export const fetchRecommendations = async ( | |
| mediaType: MediaType, | |
| titles: string[], | |
| isGamePassOnly?: boolean, | |
| isMultiplayerOnly?: boolean, | |
| ): Promise<Recommendation[]> => { | |
| const gamePassQuery = isGamePassOnly ? " that are available on PC or Console Game Pass" : ""; | |
| const multiplayerQuery = isMultiplayerOnly ? " multiplayer" : ""; | |
| const isGames = mediaType === MediaType.VideoGames; | |
| const isStreamable = mediaType === MediaType.Movies || mediaType === MediaType.TVShows; | |
| const playerCountDescription = isGames | |
| ? "Include an estimated 'playerCount' for recent peak concurrent players and a 'livePlayerCount' for the current live players. These should both be numbers." | |
| : ""; | |
| const streamingDescription = isStreamable | |
| ? "- streamingOn: A list of major streaming services where it's available (e.g., Netflix, Hulu, Disney+)." | |
| : ""; | |
| // Base properties for the JSON schema for all media types. | |
| const properties: { [key: string]: object } = { | |
| title: { type: Type.STRING, description: 'The title of the recommended item.' }, | |
| synopsis: { type: Type.STRING, description: 'A brief, engaging synopsis (2-3 sentences).' }, | |
| genres: { type: Type.ARRAY, items: { type: Type.STRING }, description: 'A list of relevant genres.' }, | |
| rating: { type: Type.NUMBER, description: 'An estimated rating out of 10 (e.g., 8.5).' }, | |
| }; | |
| // Add a game-specific property to the schema if the media type is Video Games. | |
| if (isGames) { | |
| properties.playerCount = { type: Type.NUMBER, description: 'Estimated recent peak concurrent player count.' }; | |
| properties.livePlayerCount = { type: Type.NUMBER, description: 'Estimated current live player count.' }; | |
| } | |
| if (isStreamable) { | |
| properties.streamingOn = { type: Type.ARRAY, items: { type: Type.STRING }, description: 'List of streaming services.' }; | |
| } | |
| const prompt = ` | |
| Based on a user's enjoyment of the following ${mediaType}: ${titles.join(', ')}. | |
| Please recommend 5 other${multiplayerQuery} ${mediaType}${gamePassQuery} they might like. | |
| For each recommendation, provide the following details: | |
| - title: The title of the recommendation. | |
| - synopsis: A brief, 2-3 sentence summary. | |
| - genres: A list of relevant genres. | |
| - rating: An estimated critical and audience rating out of 10 (e.g., 8.5). | |
| ${streamingDescription} | |
| ${playerCountDescription} | |
| Return ONLY a JSON array of objects matching the defined schema. Do not include any other text, markdown, or explanation. | |
| `; | |
| try { | |
| const response = await ai.models.generateContent({ | |
| model: 'gemini-2.5-flash', | |
| contents: prompt, | |
| config: { | |
| responseMimeType: 'application/json', | |
| responseSchema: { | |
| type: Type.ARRAY, | |
| items: { | |
| type: Type.OBJECT, | |
| properties: properties, | |
| required: ['title', 'synopsis', 'genres', 'rating'] | |
| } | |
| } | |
| } | |
| }); | |
| const jsonString = response.text.trim(); | |
| // The response is expected to be a JSON string representing an array of Recommendation objects. | |
| return JSON.parse(jsonString); | |
| } catch (e) { | |
| console.error("Error fetching recommendations from Gemini API:", e); | |
| // Re-throw a more user-friendly error to be caught by the UI layer. | |
| throw new Error("Failed to fetch recommendations from the AI. Please try again."); | |
| } | |
| }; | |