Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
File size: 6,588 Bytes
2e813e6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
// lib/widgets/video_player/nano_clip_manager.dart
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:aitube2/models/video_result.dart';
import 'package:aitube2/services/clip_queue/video_clip.dart';
import 'package:aitube2/services/clip_queue/clip_states.dart';
import 'package:aitube2/services/websocket_api_service.dart';
import 'package:aitube2/utils/seed.dart';
import 'package:uuid/uuid.dart';
/// Manages a single video clip generation for thumbnail playback
class NanoClipManager {
/// The video result for which the clip is being generated
final VideoResult video;
/// WebSocket service for API communication
final WebSocketApiService _websocketService;
/// Callback for when the clip is updated
final void Function()? onClipUpdated;
/// The generated video clip
VideoClip? _videoClip;
/// Whether the manager is disposed
bool _isDisposed = false;
/// Status text to show during generation
String _statusText = 'Initializing...';
/// Get the current video clip
VideoClip? get videoClip => _videoClip;
/// Get the current status text
String get statusText => _statusText;
/// Constructor
NanoClipManager({
required this.video,
WebSocketApiService? websocketService,
this.onClipUpdated,
}) : _websocketService = websocketService ?? WebSocketApiService();
/// Initialize and generate a single clip
Future<void> initialize({
int? overrideSeed,
Duration timeout = const Duration(seconds: 10),
}) async {
if (_isDisposed) return;
try {
// Use either provided seed, video's seed, or generate a new one
final seed = overrideSeed ??
(video.useFixedSeed && video.seed > 0 ? video.seed : generateSeed());
// Create a video clip
_videoClip = VideoClip(
prompt: "${video.title}\n${video.description}",
seed: seed,
);
_updateStatus('Connecting...');
// Set up WebSocket API service if needed
if (_websocketService.status != ConnectionStatus.connected) {
_updateStatus('Connecting to server...');
await _websocketService.initialize();
if (_isDisposed) return;
if (_websocketService.status != ConnectionStatus.connected) {
_updateStatus('Connection failed');
_videoClip!.state = ClipState.failedToGenerate;
return;
}
}
_updateStatus('Requesting thumbnail...');
// Set up timeout
final completer = Completer<void>();
Timer? timeoutTimer;
timeoutTimer = Timer(timeout, () {
if (!completer.isCompleted) {
_updateStatus('Generation timed out');
completer.complete();
}
});
// Request the thumbnail generation
try {
// Create request for thumbnail generation
final requestId = const Uuid().v4();
// Mark as generating
_videoClip!.state = ClipState.generationInProgress;
// Initiate a request to generate a thumbnail
// Using available methods in WebSocketApiService
_generateThumbnail(seed, requestId).then((thumbnailData) {
if (_isDisposed) return;
if (thumbnailData != null && thumbnailData.isNotEmpty) {
// Successful generation
_videoClip!.base64Data = thumbnailData;
_videoClip!.state = ClipState.generatedAndReadyToPlay;
_updateStatus('Ready');
} else {
// Generation failed
_videoClip!.state = ClipState.failedToGenerate;
_updateStatus('Failed to generate');
}
completer.complete();
}).catchError((error) {
debugPrint('Error generating thumbnail: $error');
_videoClip!.state = ClipState.failedToGenerate;
_updateStatus('Error: $error');
completer.complete();
});
// Wait for completion or timeout
await completer.future;
timeoutTimer.cancel();
} catch (e) {
// Handle any errors
debugPrint('Error in thumbnail generation: $e');
_videoClip!.state = ClipState.failedToGenerate;
_updateStatus('Error generating');
timeoutTimer.cancel();
}
} catch (e) {
debugPrint('Error initializing nano clip: $e');
_updateStatus('Error initializing');
}
}
/// Generate a thumbnail using the WebSocketApiService
Future<String?> _generateThumbnail(int seed, String requestId) async {
if (_isDisposed) return null;
// Show progress updates
_simulateProgress();
// If we're in debug mode and on web, we might need to mock the response
if (kDebugMode && !_websocketService.isConnected) {
await Future.delayed(const Duration(seconds: 3));
return 'data:video/mp4;base64,AAAA'; // Mock base64 data
}
try {
// Create a request to generate the thumbnail
// We'll actually implement this using the VideoResult object since that's
// what the API expects
final result = await _websocketService.generateVideo(
video,
width: 512, // Small size for thumbnail
height: 288, // 16:9 aspect ratio
seed: seed, // Use our specific seed
);
return result;
} catch (e) {
debugPrint('Error generating thumbnail through API: $e');
if (kDebugMode) {
// In debug mode, return mock data so development can continue
return 'data:video/mp4;base64,AAAA';
}
return null;
}
}
/// Simulate generation progress during development or when server is slow
void _simulateProgress() {
if (_isDisposed) return;
const progressSteps = [
{'delay': Duration(milliseconds: 500), 'progress': 20},
{'delay': Duration(seconds: 1), 'progress': 40},
{'delay': Duration(seconds: 2), 'progress': 60},
{'delay': Duration(seconds: 3), 'progress': 80}
];
// Show progress updates
for (final step in progressSteps) {
Future.delayed(step['delay'] as Duration, () {
if (_isDisposed) return;
_updateStatus('Generating (${step['progress']}%)');
});
}
}
/// Update the status text and notify listeners
void _updateStatus(String status) {
if (_isDisposed) return;
_statusText = status;
onClipUpdated?.call();
}
/// Dispose resources
void dispose() {
_isDisposed = true;
}
} |