Files
bookshelf/books_flutter/lib/services/gemini_service.dart
2026-02-08 12:04:45 +06:00

116 lines
3.6 KiB
Dart

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<Book?> 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<String, dynamic> 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;
}
}