import 'dart:io'; import 'dart:convert'; import 'package:google_generative_ai/google_generative_ai.dart'; import '../models/models.dart'; class GeminiService { final String apiKey; late final GenerativeModel _model; GeminiService({required this.apiKey}) { _model = GenerativeModel(model: 'gemini-1.5-flash', apiKey: apiKey); } Future analyzeBookCover(String imagePath) async { try { // Read the image file final imageFile = File(imagePath); final imageBytes = await imageFile.readAsBytes(); final base64Image = base64Encode(imageBytes); // Create the prompt for book analysis const prompt = ''' Analyze this book cover image and extract the following information in JSON format: { "title": "book title (required)", "author": "author name (required)", "genre": "fiction/fantasy/science/detective/biography/other", "annotation": "brief description or summary if visible, otherwise generate a generic one" } Rules: - Extract exact text from the cover - If genre is unclear, choose the most appropriate one - If annotation is not visible, create a brief generic description - Return ONLY valid JSON, no additional text - Ensure all required fields are present '''; // Create the image part for the model final imagePart = Content.data('image/jpeg', imageBytes); // Generate content with both text and image final response = await _model.generateContent([ Content.text(prompt), imagePart, ]); final responseText = response.text?.trim(); if (responseText == null || responseText.isEmpty) { print('Empty response from Gemini'); return null; } // Extract JSON from response (handle potential markdown formatting) String jsonString = responseText; if (jsonString.contains('```json')) { jsonString = jsonString.split('```json')[1].split('```')[0].trim(); } else if (jsonString.contains('```')) { jsonString = jsonString.split('```')[1].split('```')[0].trim(); } // Parse JSON response final Map jsonData = json.decode(jsonString); // Create Book object with extracted data final Book book = ( id: DateTime.now().millisecondsSinceEpoch.toString(), title: jsonData['title']?.toString() ?? 'Неизвестная книга', author: jsonData['author']?.toString() ?? 'Неизвестный автор', genre: _normalizeGenre(jsonData['genre']?.toString()), annotation: jsonData['annotation']?.toString() ?? 'Нет описания', coverUrl: null, // Will be set by the caller pages: null, language: 'Russian', publishedYear: DateTime.now().year, rating: 5.0, status: 'want_to_read', progress: null, isFavorite: false, ); return book; } catch (e) { print('Error analyzing book cover: $e'); return null; } } String _normalizeGenre(String? genre) { if (genre == null || genre.isEmpty) return 'other'; final normalized = genre.toLowerCase().trim(); // Map various genre names to our standard genres final genreMap = { 'фантастика': 'fiction', 'fantasy': 'fantasy', 'фэнтези': 'fantasy', 'science': 'science', 'научпоп': 'science', 'научная': 'science', 'biography': 'biography', 'биография': 'biography', 'detective': 'detective', 'детектив': 'detective', 'роман': 'other', 'novel': 'other', 'poetry': 'other', 'поэзия': 'other', }; return genreMap[normalized] ?? normalized; } }