127 lines
3.5 KiB
Dart
127 lines
3.5 KiB
Dart
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();
|
||
}
|
||
}
|