feat: Add support for progress callback
This commit is contained in:
1 parent
4a8cae998f
commit
abce9d359b
5 files changed
+53
-5
No files matched your search
@@ -73,6 +73,16 @@ class TranscriptionBloc extends Bloc<TranscriptionEvent, TranscriptionState> {
|
||||
final result = await _whisperService.transcribe(
|
||||
videoPath, // Pass video file directly
|
||||
language: event.language ?? 'auto',
|
||||
onProgress: (progress) {
|
||||
if (!emit.isDone) {
|
||||
emit(Transcribing(
|
||||
videoPath: videoPath,
|
||||
fileName: fileName,
|
||||
statusMessage: 'Transcribing...',
|
||||
progress: progress,
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
emit(TranscriptionComplete(
|
||||
|
||||
@@ -65,15 +65,19 @@ class Transcribing extends TranscriptionState {
|
||||
final String videoPath;
|
||||
final String fileName;
|
||||
final String statusMessage;
|
||||
final int progress;
|
||||
final bool? isGpu;
|
||||
|
||||
const Transcribing({
|
||||
required this.videoPath,
|
||||
required this.fileName,
|
||||
this.statusMessage = 'Transcribing...',
|
||||
this.progress = 0,
|
||||
this.isGpu,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [videoPath, fileName, statusMessage];
|
||||
List<Object?> get props => [videoPath, fileName, statusMessage, progress, isGpu];
|
||||
}
|
||||
|
||||
/// Transcription completed successfully.
|
||||
|
||||
@@ -12,21 +12,33 @@ class TranscriptionResult extends Equatable {
|
||||
/// All subtitle segments with timestamps.
|
||||
final List<SubtitleSegment> segments;
|
||||
|
||||
/// Whether GPU acceleration was used.
|
||||
final bool? isGpu;
|
||||
|
||||
/// Time taken for transcription in milliseconds.
|
||||
final int? timingMs;
|
||||
|
||||
const TranscriptionResult({
|
||||
required this.language,
|
||||
required this.duration,
|
||||
required this.segments,
|
||||
this.isGpu,
|
||||
this.timingMs,
|
||||
});
|
||||
|
||||
TranscriptionResult copyWith({
|
||||
String? language,
|
||||
Duration? duration,
|
||||
List<SubtitleSegment>? segments,
|
||||
bool? isGpu,
|
||||
int? timingMs,
|
||||
}) {
|
||||
return TranscriptionResult(
|
||||
language: language ?? this.language,
|
||||
duration: duration ?? this.duration,
|
||||
segments: segments ?? this.segments,
|
||||
isGpu: isGpu ?? this.isGpu,
|
||||
timingMs: timingMs ?? this.timingMs,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,6 +49,8 @@ class TranscriptionResult extends Equatable {
|
||||
segments: (json['segments'] as List<dynamic>)
|
||||
.map((e) => SubtitleSegment.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
isGpu: json['isGpu'] as bool?,
|
||||
timingMs: json['timingMs'] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,9 +59,11 @@ class TranscriptionResult extends Equatable {
|
||||
'language': language,
|
||||
'durationMs': duration.inMilliseconds,
|
||||
'segments': segments.map((e) => e.toJson()).toList(),
|
||||
'isGpu': isGpu,
|
||||
'timingMs': timingMs,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [language, duration, segments];
|
||||
List<Object?> get props => [language, duration, segments, isGpu, timingMs];
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class WhisperService {
|
||||
Future<TranscriptionResult> transcribe(
|
||||
String mediaPath, {
|
||||
String language = 'auto',
|
||||
void Function(int progress)? onProgress,
|
||||
}) async {
|
||||
if (_currentModel == null) {
|
||||
throw StateError('No model loaded. Call loadModel() first.');
|
||||
@@ -60,6 +61,7 @@ class WhisperService {
|
||||
lang: language,
|
||||
withTimestamps: true,
|
||||
convert: true, // Auto-convert via registered FFmpeg converter
|
||||
onProgress: onProgress,
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
@@ -83,6 +85,8 @@ class WhisperService {
|
||||
language: language,
|
||||
duration: segments.isNotEmpty ? segments.last.endTime : Duration.zero,
|
||||
segments: segments,
|
||||
isGpu: result.transcription.gpu,
|
||||
timingMs: result.transcription.timing,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,17 +139,31 @@ class TranscriptionPanel extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (state is Transcribing) {
|
||||
final s = state as Transcribing;
|
||||
String label = l10n.processingTranscription;
|
||||
if (s.isGpu == true) {
|
||||
label += ' (GPU Accel)';
|
||||
}
|
||||
return _buildProgressRow(
|
||||
context,
|
||||
icon: Icons.mic_rounded,
|
||||
label: l10n.processingTranscription,
|
||||
progress: null,
|
||||
label: label,
|
||||
progress: s.progress > 0 ? s.progress / 100.0 : null,
|
||||
color: Colors.purple,
|
||||
);
|
||||
}
|
||||
|
||||
if (state is TranscriptionComplete) {
|
||||
final s = state as TranscriptionComplete;
|
||||
String infoText =
|
||||
l10n.segmentsExtracted(s.result.segments.length, s.result.language);
|
||||
if (s.result.isGpu == true) {
|
||||
infoText += ' • GPU';
|
||||
}
|
||||
if (s.result.timingMs != null) {
|
||||
infoText += ' • ${s.result.timingMs}ms';
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
const Icon(
|
||||
@@ -159,7 +173,7 @@ class TranscriptionPanel extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.segmentsExtracted(s.result.segments.length, s.result.language),
|
||||
infoText,
|
||||
style: theme.textTheme.bodySmall?.copyWith(
|
||||
color: Colors.greenAccent,
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user