mp4 changed, just a thumbnail

master
gitea 2 months ago
parent 222515a2b5
commit 5a2d287d00

@ -1799,25 +1799,25 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
@override
void initState() {
super.initState();
_initializeVideo();
_extractThumbnail();
}
Future<void> _initializeVideo() async {
Future<void> _extractThumbnail() async {
try {
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl));
await _controller!.initialize();
if (mounted) {
if (mounted && _controller != null) {
// Seek to first frame and pause
await _controller!.seekTo(Duration.zero);
_controller!.pause();
setState(() {
_isInitialized = true;
});
// Play continuously in a loop
_controller!.setLooping(true);
_controller!.play();
}
} catch (e) {
Logger.warning('Failed to initialize video thumbnail: $e');
Logger.warning('Failed to extract video thumbnail: $e');
if (mounted) {
setState(() {
_hasError = true;
@ -1848,6 +1848,21 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
),
),
),
// Transparent play button overlay
Center(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.4),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),
),
],
);
}
@ -1855,23 +1870,22 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
return Stack(
fit: StackFit.expand,
children: [
// Video thumbnail (first frame)
AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller!),
),
// Play icon overlay
Positioned(
top: 4,
right: 4,
// Transparent play button overlay centered
Center(
child: Container(
padding: const EdgeInsets.all(4),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.6),
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_circle_filled,
size: 24,
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),

@ -859,25 +859,25 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
@override
void initState() {
super.initState();
_initializeVideo();
_extractThumbnail();
}
Future<void> _initializeVideo() async {
Future<void> _extractThumbnail() async {
try {
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl));
await _controller!.initialize();
if (mounted) {
if (mounted && _controller != null) {
// Seek to first frame and pause
await _controller!.seekTo(Duration.zero);
_controller!.pause();
setState(() {
_isInitialized = true;
});
// Play continuously in a loop
_controller!.setLooping(true);
_controller!.play();
}
} catch (e) {
Logger.warning('Failed to initialize video thumbnail: $e');
Logger.warning('Failed to extract video thumbnail: $e');
if (mounted) {
setState(() {
_hasError = true;
@ -908,6 +908,21 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
),
),
),
// Transparent play button overlay
Center(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.4),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),
),
],
);
}
@ -915,23 +930,22 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
return Stack(
fit: StackFit.expand,
children: [
// Video thumbnail (first frame)
AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller!),
),
// Play icon overlay
Positioned(
top: 4,
right: 4,
// Transparent play button overlay centered
Center(
child: Container(
padding: const EdgeInsets.all(4),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.6),
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_circle_filled,
size: 24,
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),

@ -1085,25 +1085,25 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
@override
void initState() {
super.initState();
_initializeVideo();
_extractThumbnail();
}
Future<void> _initializeVideo() async {
Future<void> _extractThumbnail() async {
try {
_controller = VideoPlayerController.networkUrl(Uri.parse(widget.videoUrl));
await _controller!.initialize();
if (mounted) {
if (mounted && _controller != null) {
// Seek to first frame and pause
await _controller!.seekTo(Duration.zero);
_controller!.pause();
setState(() {
_isInitialized = true;
});
// Play continuously in a loop
_controller!.setLooping(true);
_controller!.play();
}
} catch (e) {
Logger.warning('Failed to initialize video thumbnail: $e');
Logger.warning('Failed to extract video thumbnail: $e');
if (mounted) {
setState(() {
_hasError = true;
@ -1134,6 +1134,21 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
),
),
),
// Transparent play button overlay
Center(
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.4),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),
),
],
);
}
@ -1141,23 +1156,22 @@ class _VideoThumbnailPreviewState extends State<_VideoThumbnailPreview> {
return Stack(
fit: StackFit.expand,
children: [
// Video thumbnail (first frame)
AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller!),
),
// Play icon overlay
Positioned(
top: 4,
right: 4,
// Transparent play button overlay centered
Center(
child: Container(
padding: const EdgeInsets.all(4),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.6),
color: Colors.black.withValues(alpha: 0.5),
shape: BoxShape.circle,
),
child: const Icon(
Icons.play_circle_filled,
size: 24,
Icons.play_arrow,
size: 48,
color: Colors.white,
),
),

@ -645,7 +645,6 @@ class _RelayManagementScreenState extends State<RelayManagementScreen> {
onTap: () => _setDefaultServer(server),
);
}),
const Divider(),
// Add server button
Padding(
padding: const EdgeInsets.all(16.0),

@ -220,6 +220,59 @@ void main() {
throwsA(isA<Exception>()),
);
});
test('creates a recipe with video URLs', () async {
final recipe = RecipeModel(
id: 'test-recipe-video',
title: 'Recipe with Video',
tags: ['video'],
imageUrls: ['https://example.com/image.jpg'],
videoUrls: ['https://example.com/video.mp4'],
);
final created = await recipeService.createRecipe(recipe);
expect(created.videoUrls, equals(['https://example.com/video.mp4']));
expect(created.videoUrls.length, equals(1));
});
test('updates a recipe with video URLs', () async {
final recipe = RecipeModel(
id: 'test-recipe-video-update',
title: 'Original Recipe',
tags: ['test'],
videoUrls: ['https://example.com/video1.mp4'],
);
await recipeService.createRecipe(recipe);
final updated = recipe.copyWith(
videoUrls: ['https://example.com/video1.mp4', 'https://example.com/video2.mp4'],
);
final result = await recipeService.updateRecipe(updated);
expect(result.videoUrls.length, equals(2));
expect(result.videoUrls, contains('https://example.com/video1.mp4'));
expect(result.videoUrls, contains('https://example.com/video2.mp4'));
});
test('retrieves recipe with video URLs from database', () async {
final recipe = RecipeModel(
id: 'test-recipe-video-retrieve',
title: 'Recipe with Videos',
tags: ['test'],
videoUrls: ['https://example.com/video1.mp4', 'https://example.com/video2.mp4'],
);
await recipeService.createRecipe(recipe);
final retrieved = await recipeService.getRecipe('test-recipe-video-retrieve');
expect(retrieved, isNotNull);
expect(retrieved!.videoUrls.length, equals(2));
expect(retrieved.videoUrls, contains('https://example.com/video1.mp4'));
expect(retrieved.videoUrls, contains('https://example.com/video2.mp4'));
});
});
}

Loading…
Cancel
Save

Powered by TurnKey Linux.