Skip to content

Commit

Permalink
Merge pull request #221 from takenet/release/0.0.84
Browse files Browse the repository at this point in the history
Created new version
  • Loading branch information
githubdoandre authored Oct 23, 2023
2 parents ce768c5 + 5640d1e commit db2068d
Show file tree
Hide file tree
Showing 20 changed files with 398 additions and 240 deletions.
25 changes: 0 additions & 25 deletions .github/workflows/merge.yml

This file was deleted.

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.0.84

- [DSImageMessageBubble] Improved the performance of the image bubble
- [DSVideoMessageBubble] Improved the video storage

## 0.0.83

- [DSCard] Accept reply messages
Expand Down
9 changes: 5 additions & 4 deletions lib/blip_ds.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export 'src/themes/texts/styles/ds_headline_small_text_style.theme.dart'
export 'src/themes/texts/utils/ds_font_families.theme.dart' show DSFontFamilies;
export 'src/themes/texts/utils/ds_font_weights.theme.dart' show DSFontWeights;
export 'src/utils/ds_animate.util.dart' show DSAnimate;
export 'src/utils/ds_directory_formatter.util.dart' show DSDirectoryFormatter;
export 'src/utils/ds_linkify.util.dart' show DSLinkify;
export 'src/utils/ds_utils.util.dart' show DSUtils;
export 'src/widgets/animations/ds_animated_size.widget.dart'
Expand Down Expand Up @@ -77,10 +78,10 @@ export 'src/widgets/buttons/ds_icon_button.widget.dart' show DSIconButton;
export 'src/widgets/buttons/ds_pause_button.widget.dart' show DSPauseButton;
export 'src/widgets/buttons/ds_play_button.widget.dart' show DSPlayButton;
export 'src/widgets/buttons/ds_primary_button.widget.dart' show DSPrimaryButton;
export 'src/widgets/buttons/ds_secondary_button.widget.dart'
show DSSecondaryButton;
export 'src/widgets/buttons/ds_request_location_button.widget.dart'
show DSRequestLocationButton;
export 'src/widgets/buttons/ds_secondary_button.widget.dart'
show DSSecondaryButton;
export 'src/widgets/buttons/ds_send_button.widget.dart' show DSSendButton;
export 'src/widgets/buttons/ds_tertiary_button.widget.dart'
show DSTertiaryButton;
Expand All @@ -101,6 +102,8 @@ export 'src/widgets/chat/ds_location_message_bubble.widget.dart'
export 'src/widgets/chat/ds_message_bubble.widget.dart' show DSMessageBubble;
export 'src/widgets/chat/ds_message_bubble_detail.widget.dart'
show DSMessageBubbleDetail;
export 'src/widgets/chat/ds_request_location_bubble.widget.dart'
show DSRequestLocationBubble;
export 'src/widgets/chat/ds_survey_message_bubble.widget.dart'
show DSSurveyMessageBubble;
export 'src/widgets/chat/ds_text_message_bubble.widget.dart'
Expand Down Expand Up @@ -155,5 +158,3 @@ export 'src/widgets/utils/ds_group_card.widget.dart' show DSGroupCard;
export 'src/widgets/utils/ds_header.widget.dart' show DSHeader;
export 'src/widgets/utils/ds_progress_bar.widget.dart' show DSProgressBar;
export 'src/widgets/utils/ds_user_avatar.widget.dart' show DSUserAvatar;
export 'src/widgets/chat/ds_request_location_bubble.widget.dart'
show DSRequestLocationBubble;
3 changes: 1 addition & 2 deletions lib/src/controllers/chat/ds_audio_player.controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import 'package:rxdart/rxdart.dart' as rx_dart;
class DSAudioPlayerController extends GetxController {
final audioSpeed = RxDouble(1.0);
final player = AudioPlayer();

bool isInitialized = false;
final isInitialized = RxBool(false);

/// Collects the data useful for displaying in a SeekBar widget.
///
Expand Down
75 changes: 55 additions & 20 deletions lib/src/controllers/chat/ds_image_message_bubble.controller.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,69 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:blip_ds/src/utils/ds_directory_formatter.util.dart';
import 'package:crypto/crypto.dart';
import 'package:get/get.dart';

import '../../services/ds_auth.service.dart';
import '../../services/ds_file.service.dart';

class DSImageMessageBubbleController extends GetxController {
Future<ImageInfo> getImageInfo({
required final String url,
final bool shouldAuthenticate = false,
}) async {
final Image img = Image.network(
url,
headers: shouldAuthenticate ? DSAuthService.httpHeaders : null,
);
final maximumProgress = RxInt(0);
final downloadProgress = RxInt(0);
final localPath = RxnString();

final String url;
final String? mediaType;
final bool shouldAuthenticate;

final completer = Completer<ImageInfo>();
DSImageMessageBubbleController(
this.url, {
this.mediaType,
this.shouldAuthenticate = false,
}) {
_downloadImage();
}

void _onReceiveProgress(final int currentProgress, final int maxProgres) {
downloadProgress.value = currentProgress;
maximumProgress.value = maxProgres;
}

final ImageStream imageStream =
img.image.resolve(const ImageConfiguration());
Future<void> _downloadImage() async {
if (mediaType == null || !url.startsWith('http')) {
localPath.value = url;
return;
}

imageStream.addListener(
ImageStreamListener(
(ImageInfo i, bool _) {
completer.complete(i);
},
onError: (exception, stackTrace) => completer.completeError(exception),
),
final uri = Uri.parse(url);

final fullPath = await DSDirectoryFormatter.getPath(
type: mediaType!,
fileName: md5.convert(utf8.encode(uri.path)).toString(),
);

return completer.future;
if (await File(fullPath).exists()) {
localPath.value = fullPath;
return;
}

final fileName = fullPath.split('/').last;
final path = fullPath.substring(0, fullPath.lastIndexOf('/'));

try {
final savedFilePath = await DSFileService.download(
url,
fileName,
path: path,
onReceiveProgress: _onReceiveProgress,
httpHeaders: shouldAuthenticate ? DSAuthService.httpHeaders : null,
);

localPath.value = savedFilePath;
} catch (_) {
localPath.value = url;
}
}
}
80 changes: 53 additions & 27 deletions lib/src/controllers/chat/ds_video_message_bubble.controller.dart
Original file line number Diff line number Diff line change
@@ -1,34 +1,37 @@
import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_full_gpl/return_code.dart';
import 'package:file_sizes/file_sizes.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';

import '../../models/ds_toast_props.model.dart';
import '../../services/ds_file.service.dart';
import '../../services/ds_toast.service.dart';
import '../../utils/ds_directory_formatter.util.dart';
import '../../widgets/chat/video/ds_video_error.dialog.dart';

class DSVideoMessageBubbleController {
final String uniqueId;
final String url;
final int mediaSize;
final Map<String, String?>? httpHeaders;
final String type;

DSVideoMessageBubbleController({
required this.uniqueId,
required this.url,
required this.mediaSize,
required this.type,
this.httpHeaders,
}) {
setThumbnail();
getStoredVideo();
}

final isDownloading = RxBool(false);
final thumbnail = RxString('');
final hasError = RxBool(false);
final isLoadingThumbnail = RxBool(false);

String size() {
return mediaSize > 0
Expand All @@ -40,32 +43,49 @@ class DSVideoMessageBubbleController {
: 'Download';
}

Future<void> setThumbnail() async {
final thumbnailFile = File(await getFullThumbnailPath());
if (await thumbnailFile.exists()) {
thumbnail.value = thumbnailFile.path;
Future<void> getStoredVideo() async {
try {
isLoadingThumbnail.value = true;
final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString();
final fullPath = await DSDirectoryFormatter.getPath(
type: type,
fileName: fileName,
);
final fullThumbnailPath = await DSDirectoryFormatter.getPath(
type: 'image/png',
fileName: '$fileName-thumbnail',
);
final file = File(fullPath);
final thumbnailfile = File(fullThumbnailPath);
if (await thumbnailfile.exists()) {
thumbnail.value = thumbnailfile.path;
} else if (await file.exists() && thumbnail.value.isEmpty) {
await _generateThumbnail(file.path);
}
} finally {
isLoadingThumbnail.value = false;
}
}

Future<String> getFullThumbnailPath() async {
final temporaryPath = (await getTemporaryDirectory()).path;
return "$temporaryPath/VIDEO-Thumbnail-$uniqueId.png";
final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString();
final mediaPath = await DSDirectoryFormatter.getPath(
type: 'image/png',
fileName: '$fileName-thumbnail',
);
return mediaPath;
}

Future<void> downloadVideo() async {
final fileName = md5.convert(utf8.encode(Uri.parse(url).path)).toString();
isDownloading.value = true;

try {
final path = Uri.parse(url).path;

var fileName = path.substring(path.lastIndexOf('/')).substring(1);

if (fileName.isEmpty) {
fileName = DateTime.now().toIso8601String();
}

final temporaryPath = (await getTemporaryDirectory()).path;
final outputFile = File('$temporaryPath/VIDEO-$uniqueId.mp4');
final fullPath = await DSDirectoryFormatter.getPath(
type: 'video/mp4',
fileName: fileName,
);
final outputFile = File(fullPath);

if (!await outputFile.exists()) {
final inputFilePath = await DSFileService.download(
Expand All @@ -77,6 +97,8 @@ class DSVideoMessageBubbleController {
final session = await FFmpegKit.execute(
'-hide_banner -y -i "$inputFilePath" "${outputFile.path}"');

File(inputFilePath!).delete();

final returnCode = await session.getReturnCode();

if (!ReturnCode.isSuccess(returnCode)) {
Expand All @@ -89,13 +111,7 @@ class DSVideoMessageBubbleController {
}
}

final thumbnailPath = await getFullThumbnailPath();

await FFmpegKit.execute(
'-hide_banner -y -i "${outputFile.path}" -vframes 1 "$thumbnailPath"',
);

thumbnail.value = thumbnailPath;
_generateThumbnail(outputFile.path);
} catch (_) {
hasError.value = true;

Expand All @@ -110,4 +126,14 @@ class DSVideoMessageBubbleController {
isDownloading.value = false;
}
}

Future<void> _generateThumbnail(String path) async {
final thumbnailPath = await getFullThumbnailPath();

await FFmpegKit.execute(
'-hide_banner -y -i "$path" -vframes 1 "$thumbnailPath"',
);

thumbnail.value = thumbnailPath;
}
}
3 changes: 0 additions & 3 deletions lib/src/controllers/ds_video_player.controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ class DSVideoPlayerController extends GetxController {
/// the management of video controls.
DSVideoPlayerController({
required this.url,
required this.uniqueId,
this.shouldAuthenticate = false,
});

// External URL containing the video to be played
final String url;

final String uniqueId;

/// Indicates if the HTTP Requests should be authenticated or not.
final bool shouldAuthenticate;

Expand Down
5 changes: 5 additions & 0 deletions lib/src/services/ds_file.service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ abstract class DSFileService {
final String? path,
final void Function(bool)? onDownloadStateChange,
final Map<String, String?>? httpHeaders,
final Function(int, int)? onReceiveProgress,
}) async {
try {
onDownloadStateChange?.call(true);
Expand All @@ -63,6 +64,10 @@ abstract class DSFileService {
options: Options(
headers: httpHeaders,
),
onReceiveProgress: onReceiveProgress != null
? (currentProgress, maximumProgress) =>
onReceiveProgress(currentProgress, maximumProgress)
: null,
);

if (response.statusCode == 200) return savedFilePath;
Expand Down
42 changes: 42 additions & 0 deletions lib/src/utils/ds_directory_formatter.util.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'dart:io';

import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';

abstract class DSDirectoryFormatter {
static Future<String> getPath({
required final String type,
required final String fileName,
}) async {
final cachePath = (await getApplicationCacheDirectory()).path;

final typeFolder = '${type.split('/').first.capitalizeFirst}';
final extension = type.split('/').last;

final typePrefix = '${typeFolder.substring(0, 3).toUpperCase()}-';

final newFileName =
'${!fileName.startsWith(typePrefix) ? typePrefix : ''}$fileName';

final path = await _formatDirectory(
type: typeFolder,
directory: cachePath,
);

return '$path/$newFileName.$extension';
}

static Future<String> _formatDirectory({
required final String type,
required final String directory,
}) async {
final formattedDirectory = '$directory/$type';
final directoryExists = await Directory(formattedDirectory).exists();

if (!directoryExists) {
await Directory(formattedDirectory).create(recursive: true);
}

return formattedDirectory;
}
}
Loading

0 comments on commit db2068d

Please sign in to comment.