open ai service
This commit is contained in:
126
books_flutter/lib/bloc/scanner/scanner_bloc.dart
Normal file
126
books_flutter/lib/bloc/scanner/scanner_bloc.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../models/models.dart';
|
||||
import '../../services/camera_service.dart';
|
||||
import '../../services/openai_service.dart';
|
||||
import 'scanner_event.dart';
|
||||
import 'scanner_state.dart';
|
||||
|
||||
class ScannerBloc extends Bloc<ScannerEvent, ScannerState> {
|
||||
final CameraService cameraService;
|
||||
|
||||
ScannerBloc({required this.cameraService}) : super(const ScannerState()) {
|
||||
on<InitializeCamera>(_onInitializeCamera);
|
||||
on<CaptureAndAnalyze>(_onCaptureAndAnalyze);
|
||||
on<SwitchCamera>(_onSwitchCamera);
|
||||
on<DismissError>(_onDismissError);
|
||||
}
|
||||
|
||||
Future<void> _onInitializeCamera(
|
||||
InitializeCamera event,
|
||||
Emitter<ScannerState> emit,
|
||||
) async {
|
||||
try {
|
||||
final initialized = await cameraService.initializeCamera();
|
||||
emit(
|
||||
state.copyWith(
|
||||
isInitialized: initialized,
|
||||
hasPermissionError: !initialized,
|
||||
errorMessage: initialized ? null : 'Нет доступа к камере',
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
hasPermissionError: true,
|
||||
errorMessage: 'Ошибка инициализации камеры: $e',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onCaptureAndAnalyze(
|
||||
CaptureAndAnalyze event,
|
||||
Emitter<ScannerState> emit,
|
||||
) async {
|
||||
if (cameraService.controller == null) return;
|
||||
|
||||
emit(state.copyWith(isCapturing: true));
|
||||
|
||||
try {
|
||||
// Capture image
|
||||
final imagePath = await cameraService.captureImage();
|
||||
if (imagePath == null) {
|
||||
throw Exception('Не удалось сделать снимок');
|
||||
}
|
||||
|
||||
emit(state.copyWith(isAnalyzing: true, isCapturing: false));
|
||||
|
||||
Book? book;
|
||||
|
||||
// Try OpenAI first if available
|
||||
if (event.openaiApiKey != null && event.openaiApiKey!.isNotEmpty) {
|
||||
print('Using OpenAI service for analysis');
|
||||
final openaiService = OpenAIService(
|
||||
apiKey: event.openaiApiKey!,
|
||||
baseUrl: event.openaiBaseUrl,
|
||||
);
|
||||
book = await openaiService.analyzeBookCover(imagePath);
|
||||
}
|
||||
|
||||
// Fall back to Gemini if OpenAI failed or is not configured
|
||||
// if (book == null) {
|
||||
// if (event.geminiApiKey == null || event.geminiApiKey!.isEmpty) {
|
||||
// throw Exception('API ключ не настроен (ни OpenAI, ни Gemini)');
|
||||
// }
|
||||
// print('Using Gemini service for analysis');
|
||||
// final geminiService = GeminiService(apiKey: event.geminiApiKey!);
|
||||
// book = await geminiService.analyzeBookCover(imagePath);
|
||||
// }
|
||||
|
||||
if (book == null) {
|
||||
throw Exception('Не удалось распознать книгу');
|
||||
}
|
||||
|
||||
// Clean up temporary image
|
||||
try {
|
||||
await File(imagePath).delete();
|
||||
} catch (e) {
|
||||
print('Error deleting temporary file: $e');
|
||||
}
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
analyzedBook: book,
|
||||
isAnalyzing: false,
|
||||
isCapturing: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
errorMessage: e.toString(),
|
||||
isCapturing: false,
|
||||
isAnalyzing: false,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSwitchCamera(
|
||||
SwitchCamera event,
|
||||
Emitter<ScannerState> emit,
|
||||
) async {
|
||||
await cameraService.switchCamera();
|
||||
}
|
||||
|
||||
void _onDismissError(DismissError event, Emitter<ScannerState> emit) {
|
||||
emit(state.copyWith(clearError: true));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
cameraService.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
21
books_flutter/lib/bloc/scanner/scanner_event.dart
Normal file
21
books_flutter/lib/bloc/scanner/scanner_event.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:books_flutter/config/api_config.dart';
|
||||
|
||||
sealed class ScannerEvent {}
|
||||
|
||||
class InitializeCamera extends ScannerEvent {}
|
||||
|
||||
class CaptureAndAnalyze extends ScannerEvent {
|
||||
final String? openaiApiKey;
|
||||
final String openaiBaseUrl;
|
||||
final String? geminiApiKey;
|
||||
|
||||
CaptureAndAnalyze({
|
||||
this.openaiApiKey,
|
||||
this.openaiBaseUrl = ApiConfig.openaiBaseUrl,
|
||||
this.geminiApiKey,
|
||||
});
|
||||
}
|
||||
|
||||
class SwitchCamera extends ScannerEvent {}
|
||||
|
||||
class DismissError extends ScannerEvent {}
|
||||
39
books_flutter/lib/bloc/scanner/scanner_state.dart
Normal file
39
books_flutter/lib/bloc/scanner/scanner_state.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import '../../models/models.dart';
|
||||
|
||||
class ScannerState {
|
||||
final bool isInitialized;
|
||||
final bool isCapturing;
|
||||
final bool isAnalyzing;
|
||||
final bool hasPermissionError;
|
||||
final String? errorMessage;
|
||||
final Book? analyzedBook;
|
||||
|
||||
const ScannerState({
|
||||
this.isInitialized = false,
|
||||
this.isCapturing = false,
|
||||
this.isAnalyzing = false,
|
||||
this.hasPermissionError = false,
|
||||
this.errorMessage,
|
||||
this.analyzedBook,
|
||||
});
|
||||
|
||||
ScannerState copyWith({
|
||||
bool? isInitialized,
|
||||
bool? isCapturing,
|
||||
bool? isAnalyzing,
|
||||
bool? hasPermissionError,
|
||||
String? errorMessage,
|
||||
Book? analyzedBook,
|
||||
bool clearError = false,
|
||||
bool clearBook = false,
|
||||
}) {
|
||||
return ScannerState(
|
||||
isInitialized: isInitialized ?? this.isInitialized,
|
||||
isCapturing: isCapturing ?? this.isCapturing,
|
||||
isAnalyzing: isAnalyzing ?? this.isAnalyzing,
|
||||
hasPermissionError: hasPermissionError ?? this.hasPermissionError,
|
||||
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
|
||||
analyzedBook: clearBook ? null : (analyzedBook ?? this.analyzedBook),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user