Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created new version #221

Merged
merged 28 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
43bf0b2
feat: adding fileName field on controller and bubble
RaulRodrigo06 Oct 2, 2023
7556645
feat: adding a directory formatter to unify media storage
RaulRodrigo06 Oct 3, 2023
2429e82
fix: fixing dir creation
RaulRodrigo06 Oct 3, 2023
fe086a1
fix: set media path on download
RaulRodrigo06 Oct 3, 2023
4b1f1b0
feat: deleting output fle
RaulRodrigo06 Oct 3, 2023
2f3270a
chore: renaming vars
RaulRodrigo06 Oct 4, 2023
c7f7cda
chore: renaming var and folder name
RaulRodrigo06 Oct 4, 2023
f47a7ba
fix: using uniqueId
RaulRodrigo06 Oct 5, 2023
45eb2bc
refactor: video storage refactor
RaulRodrigo06 Oct 6, 2023
a99a51a
fix: removing fileName not used
RaulRodrigo06 Oct 9, 2023
1c721df
build: merge main back to develop
actions-user Oct 10, 2023
8ccde5e
build: remove merge workflow
Oct 10, 2023
562ca55
feat: adding md5 converter
RaulRodrigo06 Oct 13, 2023
f171264
feat: transform isInitialized into an observable in DSAudioPlayerCont…
Oct 13, 2023
bc969d9
fix: fix filename prefix check in DSDirectoryFormatter
Oct 16, 2023
4cf55fb
fix: fixing directory
RaulRodrigo06 Oct 17, 2023
59a981a
fix: removing logic from dscard
RaulRodrigo06 Oct 17, 2023
1f646d2
fix: showing file on gallery
RaulRodrigo06 Oct 17, 2023
56d6b96
feat: improve the performance of the image bubble
Oct 18, 2023
2184eff
feat: change DSDirectoryFormatter path getter to always return Applic…
Oct 19, 2023
4eedd06
refactor: code improvement
RaulRodrigo06 Oct 19, 2023
00e67b6
feat: removed compress of the image
Oct 20, 2023
d6fa47a
chore: get dependencies from sample
Oct 20, 2023
5c489fa
Merge pull request #215 from takenet/feature/531586-improve-video-sto…
RaulRodrigo06 Oct 20, 2023
f43658f
Merge branch 'develop' into feature/540984-improve-image-bubble
Oct 20, 2023
c2ee1a3
Merge pull request #220 from takenet/feature/540984-improve-image-bubble
githubdoandre Oct 20, 2023
e9b82e7
fix: fixing imports
RaulRodrigo06 Oct 20, 2023
5640d1e
chore: created new version
Oct 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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