1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 21:09:21 +00:00

Generate video thumbnails

This commit is contained in:
Pierre HUBERT 2021-03-12 19:36:42 +01:00
parent 8f7ca14586
commit b84eba59e3
10 changed files with 100 additions and 12 deletions

View File

@ -312,6 +312,9 @@ class ConversationsHelper {
// Check for file // Check for file
if (message.hasFile) request.addBytesFile("file", message.file); if (message.hasFile) request.addBytesFile("file", message.file);
if (message.hasThumbnail)
request.addBytesFile("thumbnail", message.thumbnail);
//Send the message //Send the message
APIResponse response; APIResponse response;
if (!message.hasFile) if (!message.hasFile)

View File

@ -54,10 +54,16 @@ class ServerConfigurationHelper {
conversationsPolicy: ConversationsPolicy( conversationsPolicy: ConversationsPolicy(
minMessageLen: conversationsPolicy["min_message_len"], minMessageLen: conversationsPolicy["min_message_len"],
maxMessageLen: conversationsPolicy["max_message_len"], maxMessageLen: conversationsPolicy["max_message_len"],
allowedFilesType: conversationsPolicy["allowed_files_type"].cast<String>(), allowedFilesType:
conversationsPolicy["allowed_files_type"].cast<String>(),
filesMaxSize: conversationsPolicy["files_max_size"], filesMaxSize: conversationsPolicy["files_max_size"],
writingEventInterval: conversationsPolicy["writing_event_interval"], writingEventInterval: conversationsPolicy["writing_event_interval"],
writingEventLifetime: conversationsPolicy["writing_event_lifetime"], writingEventLifetime: conversationsPolicy["writing_event_lifetime"],
maxMessageImageWidth: conversationsPolicy["max_message_image_width"],
maxMessageImageHeight:
conversationsPolicy["max_message_image_height"],
maxThumbnailWidth: conversationsPolicy["max_thumbnail_width"],
maxThumbnailHeight: conversationsPolicy["max_thumbnail_height"],
)); ));
} }

View File

@ -11,15 +11,19 @@ class NewConversationMessage {
final int conversationID; final int conversationID;
final String message; final String message;
final BytesFile file; final BytesFile file;
final BytesFile thumbnail;
NewConversationMessage({ NewConversationMessage({
@required this.conversationID, @required this.conversationID,
@required this.message, @required this.message,
this.file, this.file,
this.thumbnail,
}) : assert(conversationID != null), }) : assert(conversationID != null),
assert(file != null || message != null); assert(file != null || message != null);
bool get hasMessage => message != null; bool get hasMessage => message != null;
bool get hasFile => file != null; bool get hasFile => file != null;
bool get hasThumbnail => thumbnail != null;
} }

View File

@ -64,6 +64,10 @@ class ConversationsPolicy {
final int filesMaxSize; final int filesMaxSize;
final int writingEventInterval; final int writingEventInterval;
final int writingEventLifetime; final int writingEventLifetime;
final int maxMessageImageWidth;
final int maxMessageImageHeight;
final int maxThumbnailWidth;
final int maxThumbnailHeight;
const ConversationsPolicy({ const ConversationsPolicy({
@required this.minMessageLen, @required this.minMessageLen,
@ -72,12 +76,20 @@ class ConversationsPolicy {
@required this.filesMaxSize, @required this.filesMaxSize,
@required this.writingEventInterval, @required this.writingEventInterval,
@required this.writingEventLifetime, @required this.writingEventLifetime,
@required this.maxMessageImageWidth,
@required this.maxMessageImageHeight,
@required this.maxThumbnailWidth,
@required this.maxThumbnailHeight,
}) : assert(minMessageLen != null), }) : assert(minMessageLen != null),
assert(maxMessageLen != null), assert(maxMessageLen != null),
assert(allowedFilesType != null), assert(allowedFilesType != null),
assert(filesMaxSize != null), assert(filesMaxSize != null),
assert(writingEventInterval != null), assert(writingEventInterval != null),
assert(writingEventLifetime != null); assert(writingEventLifetime != null),
assert(maxMessageImageWidth != null),
assert(maxMessageImageHeight != null),
assert(maxThumbnailWidth != null),
assert(maxThumbnailHeight != null);
} }
class ServerConfig { class ServerConfig {

View File

@ -77,8 +77,8 @@ Future<BytesFile> showPickFileDialog({
@required BuildContext context, @required BuildContext context,
int maxFileSize, int maxFileSize,
List<String> allowedMimeTypes, List<String> allowedMimeTypes,
double imageMaxWidth, int imageMaxWidth,
double imageMaxHeight, int imageMaxHeight,
}) async { }) async {
assert(allowedMimeTypes != null); assert(allowedMimeTypes != null);
@ -113,8 +113,8 @@ Future<BytesFile> showPickFileDialog({
source: choice == _FileChoices.PICK_IMAGE source: choice == _FileChoices.PICK_IMAGE
? ImageSource.gallery ? ImageSource.gallery
: ImageSource.camera, : ImageSource.camera,
maxWidth: imageMaxWidth, maxWidth: imageMaxWidth.toDouble(),
maxHeight: imageMaxHeight, maxHeight: imageMaxHeight.toDouble(),
); );
if (image == null) return null; if (image == null) return null;

View File

@ -6,6 +6,7 @@ import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/conversation_messages_list.dart'; import 'package:comunic/lists/conversation_messages_list.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/new_conversation_message.dart'; import 'package:comunic/models/new_conversation_message.dart';
@ -15,11 +16,14 @@ import 'package:comunic/ui/tiles/conversation_message_tile.dart';
import 'package:comunic/ui/tiles/server_conversation_message_tile.dart'; import 'package:comunic/ui/tiles/server_conversation_message_tile.dart';
import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/ui/widgets/safe_state.dart';
import 'package:comunic/ui/widgets/scroll_watcher.dart'; import 'package:comunic/ui/widgets/scroll_watcher.dart';
import 'package:comunic/utils/files_utils.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/list_utils.dart'; import 'package:comunic/utils/list_utils.dart';
import 'package:comunic/utils/log_utils.dart'; import 'package:comunic/utils/log_utils.dart';
import 'package:comunic/utils/ui_utils.dart'; import 'package:comunic/utils/ui_utils.dart';
import 'package:comunic/utils/video_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mime/mime.dart';
/// Conversation screen /// Conversation screen
/// ///
@ -227,16 +231,23 @@ class _ConversationScreenState extends SafeState<ConversationScreen> {
context: context, context: context,
maxFileSize: srvConfig.conversationsPolicy.filesMaxSize, maxFileSize: srvConfig.conversationsPolicy.filesMaxSize,
allowedMimeTypes: srvConfig.conversationsPolicy.allowedFilesType, allowedMimeTypes: srvConfig.conversationsPolicy.allowedFilesType,
imageMaxWidth: srvConfig.conversationsPolicy.maxMessageImageWidth,
imageMaxHeight: srvConfig.conversationsPolicy.maxMessageImageHeight,
); );
if (file == null) return; if (file == null) return;
BytesFile thumbnail;
if (isVideo(lookupMimeType(file.filename)))
thumbnail = await generateVideoThumbnail(videoFile: file);
await _submitMessage( await _submitMessage(
NewConversationMessage( NewConversationMessage(
conversationID: widget.conversationID, conversationID: widget.conversationID,
message: null, message: null,
file: file, file: file,
), thumbnail: thumbnail),
); );
} catch (e, s) { } catch (e, s) {
logError(e, s); logError(e, s);

View File

@ -90,7 +90,9 @@ class ConversationMessageTile extends StatelessWidget {
// Update message content // Update message content
PopupMenuItem( PopupMenuItem(
enabled: message.isOwner, enabled: message.isOwner &&
message.message != null &&
message.message.content.isNotEmpty,
value: _MenuChoices.REQUEST_UPDATE_CONTENT, value: _MenuChoices.REQUEST_UPDATE_CONTENT,
child: Text(tr("Update")), child: Text(tr("Update")),
), ),

View File

@ -0,0 +1,40 @@
import 'dart:io';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/utils/log_utils.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:random_string/random_string.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
/// Video utilities
///
/// @author Pierre Hubert
/// Generate a thumbnail for a video. In case of failure, return null
Future<BytesFile> generateVideoThumbnail({
@required BytesFile videoFile,
int maxWidth,
}) async {
File file;
try {
final tempDir = await getTemporaryDirectory();
if (tempDir == null) return null;
file = File(path.join(tempDir.path, randomString(15, from: 65, to: 90)));
await file.writeAsBytes(videoFile.bytes);
return BytesFile(
"thumb.png",
await VideoThumbnail.thumbnailData(
video: file.absolute.path, maxWidth: maxWidth),
);
} catch (e, s) {
logError(e, s);
return null;
} finally {
if (file != null && await file.exists()) await file.delete();
}
}

View File

@ -686,6 +686,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.4+1" version: "0.1.4+1"
video_thumbnail:
dependency: "direct main"
description:
name: video_thumbnail
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.5+1"
wakelock: wakelock:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -109,6 +109,9 @@ dependencies:
# Determine file mime type # Determine file mime type
mime: ^0.9.7 mime: ^0.9.7
# Create video thumbnails
video_thumbnail: ^0.2.5+1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter