fixing compression issue

This commit is contained in:
otsmr 2025-07-11 01:16:02 +02:00
parent e9502c7ce9
commit 59528bf082
11 changed files with 140 additions and 143 deletions

View file

@ -45,7 +45,7 @@ final lockRetransStore = Mutex();
/// It handles errors and does automatically tries to reconnect on
/// errors or network changes.
class ApiService {
final String apiHost = (kDebugMode) ? "192.168.178.89:3030" : "api.twonly.eu";
final String apiHost = (kDebugMode) ? "10.99.0.140:3030" : "api.twonly.eu";
final String apiSecure = (kDebugMode) ? "" : "s";
bool appIsOutdated = false;

View file

@ -299,7 +299,7 @@ Future<File?> getVideoPath(int mediaId) async {
Future<Uint8List?> readMediaFile(int mediaId, String type) async {
String basePath = await getMediaFilePath(mediaId, "received");
File file = File("$basePath.$type");
Log.info("Reading: ${file}");
Log.info("Reading: $file");
if (!await file.exists()) {
return null;
}

View file

@ -200,17 +200,25 @@ Future<bool> addVideoToUpload(int mediaUploadId, File videoFilePath) async {
Future<Uint8List> addOrModifyImageToUpload(
int mediaUploadId, Uint8List imageBytes) async {
Uint8List imageBytesCompressed;
Stopwatch stopwatch = Stopwatch();
stopwatch.start();
Log.info("Raw images size in bytes: ${imageBytes.length}");
try {
imageBytesCompressed = await FlutterImageCompress.compressWithList(
format: CompressFormat.png,
format: CompressFormat.webp,
// minHeight: 0,
// minWidth: 0,
imageBytes,
quality: 90,
);
if (imageBytesCompressed.length >= 2 * 1000 * 1000) {
if (imageBytesCompressed.length >= 1 * 1000 * 1000) {
// if the media file is over 2MB compress it with 60%
imageBytesCompressed = await FlutterImageCompress.compressWithList(
format: CompressFormat.png,
format: CompressFormat.webp,
imageBytes,
quality: 60,
);
@ -223,6 +231,26 @@ Future<Uint8List> addOrModifyImageToUpload(
imageBytesCompressed = imageBytes;
}
stopwatch.stop();
Log.info(
'Compression the image took: ${stopwatch.elapsedMilliseconds} milliseconds');
Log.info("Raw images size in bytes: ${imageBytesCompressed.length}");
// stopwatch.reset();
// stopwatch.start();
// // var helper = MediaUploadHelper();
// try {
// final webpBytes =
// await convertAndCompressImage(pngRawImageBytes: imageBytes);
// Log.info(
// 'Compression the image in rust took: ${stopwatch.elapsedMilliseconds} milliseconds');
// Log.info("Raw images size in bytes using webp: ${webpBytes.length}");
// } catch (e) {
// Log.error("$e");
// }
/// in case the media file was already encrypted of even uploaded
/// remove the data so it will be done again.
await twonlyDB.mediaUploadsDao.updateMediaUpload(

View file

@ -75,14 +75,13 @@ Future createThumbnailsForVideo(File file) async {
}
try {
String? thumbnailFile = await VideoThumbnail.thumbnailFile(
await VideoThumbnail.thumbnailFile(
video: file.path,
imageFormat: ImageFormat.PNG,
thumbnailPath: getThumbnailPath(file).path,
maxWidth: 450,
quality: 75,
);
File(thumbnailFile!).rename(getThumbnailPath(file).path);
} catch (e) {
Log.error("Could not create the video thumbnail: $e");
}
@ -92,6 +91,10 @@ File getThumbnailPath(File file) {
String originalFileName = file.uri.pathSegments.last;
String fileNameWithoutExtension = originalFileName.split('.').first;
String fileExtension = originalFileName.split('.').last;
if (fileExtension == "mp4") {
fileExtension = "png";
}
String newFileName = '$fileNameWithoutExtension.thumbnail.$fileExtension';
Directory(file.parent.path).createSync();
return File(join(file.parent.path, newFileName));
}

View file

@ -54,7 +54,6 @@ class _ChatListViewState extends State<ChatListView> {
_contacts = contacts.where((x) => !x.pinned).toList();
_pinnedContacts = contacts.where((x) => x.pinned).toList();
});
;
});
tutorial = Timer(Duration(seconds: 1), () async {

View file

@ -170,9 +170,8 @@ class _ChatMessagesViewState extends State<ChatMessagesView> {
.reversed
.toList();
final items = await MemoryItem.convertFromMessages(filteredMediaFiles);
setState(() {
galleryItems = items.values.toList();
});
setState(() {});
});
}

View file

@ -48,9 +48,11 @@ class _ChatMediaEntryState extends State<ChatMediaEntry> {
return;
}
if (await received.existsMediaFile(widget.message.messageId, "png")) {
if (mounted) {
setState(() {
canBeReopened = true;
});
}
Future.delayed(Duration(seconds: 1), () {
if (!mounted) return;
showReopenMediaFilesTutorial(context, reopenMediaFile);

View file

@ -52,8 +52,8 @@ class _ChatListEntryState extends State<ChatListEntry> {
if (content == null) return Container();
bool right = widget.message.messageOtherId == null;
return Hero(
tag: "${widget.message.mediaUploadId ?? widget.message.messageId}",
return Container(
// tag: "${widget.message.mediaUploadId ?? widget.message.messageId}",
child: Align(
alignment: right ? Alignment.centerRight : Alignment.centerLeft,
child: Padding(
@ -69,8 +69,7 @@ class _ChatListEntryState extends State<ChatListEntry> {
MessageActions(
message: widget.message,
child: Stack(
alignment:
right ? Alignment.centerRight : Alignment.centerLeft,
alignment: right ? Alignment.centerRight : Alignment.centerLeft,
children: [
(textMessage != null)
? ChatTextEntry(

View file

@ -1,17 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/services/api/media_upload.dart' as send;
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/views/components/message_send_state_icon.dart';
import 'package:twonly/src/database/twonly_database.dart';
import 'package:twonly/src/database/tables/messages_table.dart';
import 'package:twonly/src/model/json/message.dart';
import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/views/memories/memories_item_thumbnail.dart';
import 'package:twonly/src/views/memories/memories_photo_slider.view.dart';
import 'package:video_player/video_player.dart';
class InChatMediaViewer extends StatefulWidget {
const InChatMediaViewer({
@ -34,26 +28,48 @@ class InChatMediaViewer extends StatefulWidget {
}
class _InChatMediaViewerState extends State<InChatMediaViewer> {
File? image;
File? video;
bool isMounted = true;
bool mirrorVideo = false;
VideoPlayerController? videoController;
int? galleryItemIndex;
StreamSubscription<Message?>? messageStream;
Timer? _timer;
@override
void initState() {
super.initState();
initAsync(widget.message);
loadIndexAsync();
initStream();
}
Future loadIndexAsync() async {
if (!widget.message.mediaStored) return;
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
/// when the galleryItems are updated this widget is not reloaded
/// so using this timer as a workaround
if (loadIndex()) {
timer.cancel();
setState(() {});
}
});
}
bool loadIndex() {
if (widget.message.mediaStored) {
final index = widget.galleryItems.indexWhere((x) =>
x.id == (widget.message.mediaUploadId ?? widget.message.messageId));
if (index != -1) {
galleryItemIndex = index;
return true;
}
}
return false;
}
@override
void dispose() {
super.dispose();
isMounted = false;
messageStream?.cancel();
videoController?.dispose();
_timer?.cancel();
// videoController?.dispose();
}
Future initStream() async {
@ -70,60 +86,20 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
if (updated != null) {
if (updated.mediaStored) {
messageStream?.cancel();
initAsync(updated);
loadIndexAsync();
}
}
});
}
Future initAsync(Message message) async {
if (!message.mediaStored) return;
bool isSend = message.messageOtherId == null;
final basePath = await send.getMediaFilePath(
isSend ? message.mediaUploadId! : message.messageId,
isSend ? "send" : "received",
);
if (!isMounted) return;
final videoPath = File("$basePath.mp4");
final imagePath = File("$basePath.png");
if (videoPath.existsSync() && message.contentJson != null) {
MessageContent? content = MessageContent.fromJson(
MessageKind.media, jsonDecode(message.contentJson!));
if (content is MediaMessageContent) {
mirrorVideo = content.mirrorVideo;
}
videoController = VideoPlayerController.file(
videoPath,
videoPlayerOptions: VideoPlayerOptions(mixWithOthers: true),
);
videoController?.initialize().then((_) {
videoController!.setVolume(0);
videoController!.play();
videoController!.setLooping(true);
});
setState(() {
image = imagePath;
});
}
if (imagePath.existsSync()) {
setState(() {
image = imagePath;
});
} else {
Log.error("file not found: $imagePath");
}
}
Future onTap() async {
if (galleryItemIndex == null) return;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MemoriesPhotoSliderView(
galleryItems: widget.galleryItems,
initialIndex: widget.galleryItems.indexWhere((x) =>
x.id ==
(widget.message.mediaUploadId ?? widget.message.messageId)),
initialIndex: galleryItemIndex!,
scrollDirection: Axis.horizontal,
),
),
@ -132,7 +108,7 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
@override
Widget build(BuildContext context) {
if (image == null && video == null) {
if (galleryItemIndex == null) {
return Container(
constraints: BoxConstraints(
minHeight: 39,
@ -164,20 +140,9 @@ class _InChatMediaViewerState extends State<InChatMediaViewer> {
color: Colors.transparent,
borderRadius: BorderRadius.circular(12.0),
),
child: GestureDetector(
onTap: ((image == null && videoController == null)) ? null : onTap,
child: Stack(
children: [
if (image != null) Image.file(image!),
if (videoController != null)
Positioned.fill(
child: Transform.flip(
flipX: mirrorVideo,
child: VideoPlayer(videoController!),
),
),
],
),
child: MemoriesItemThumbnail(
galleryItem: widget.galleryItems[galleryItemIndex!],
onTap: onTap,
),
);
}

View file

@ -56,10 +56,10 @@ class MemoriesViewState extends State<MemoriesView> {
}
if (fileName.contains(".png")) {
imagePath = file;
thumbnailFile = getThumbnailPath(imagePath);
if (!await thumbnailFile.exists()) {
await createThumbnailsForImage(imagePath);
}
thumbnailFile = file;
// if (!await thumbnailFile.exists()) {
// await createThumbnailsForImage(imagePath);
// }
} else if (fileName.contains(".mp4")) {
videoPath = file;
thumbnailFile = getThumbnailPath(videoPath);

View file

@ -44,9 +44,11 @@ class _MemoriesItemThumbnailState extends State<MemoriesItemThumbnail> {
children: [
Image.file(widget.galleryItem.thumbnailPath),
if (widget.galleryItem.videoPath != null)
Center(
Positioned.fill(
child: Center(
child: FaIcon(FontAwesomeIcons.circlePlay),
)
),
),
],
),
),