mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 08:46:46 +00:00
remove ffmpeg from dependencies to reduce file size
This commit is contained in:
parent
d04828b020
commit
87ddb8ebdb
11 changed files with 95 additions and 105 deletions
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
Feature: Show link in chat if the saved media file contains one
|
||||
Improve: Verification badge for groups
|
||||
Improve: Huge reduction in app size
|
||||
Fix: Crash on older devices when compressing a video
|
||||
|
||||
## 0.0.94
|
||||
|
||||
Fix: Problem with decrypting messages fixed
|
||||
|
||||
## 0.0.93
|
||||
|
|
|
|||
|
|
@ -46,11 +46,6 @@ PODS:
|
|||
- SwiftyGif
|
||||
- emoji_picker_flutter (0.0.1):
|
||||
- Flutter
|
||||
- ffmpeg_kit_flutter_new (1.0.0):
|
||||
- ffmpeg_kit_flutter_new/full-gpl (= 1.0.0)
|
||||
- Flutter
|
||||
- ffmpeg_kit_flutter_new/full-gpl (1.0.0):
|
||||
- Flutter
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
|
|
@ -278,6 +273,8 @@ PODS:
|
|||
- Flutter
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- pro_video_editor (0.0.1):
|
||||
- Flutter
|
||||
- PromisesObjC (2.4.0)
|
||||
- restart_app (0.0.1):
|
||||
- Flutter
|
||||
|
|
@ -329,6 +326,8 @@ PODS:
|
|||
- SwiftyGif (5.4.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_compress (0.3.0):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
|
@ -342,7 +341,6 @@ DEPENDENCIES:
|
|||
- cryptography_flutter_plus (from `.symlinks/plugins/cryptography_flutter_plus/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`)
|
||||
- ffmpeg_kit_flutter_new (from `.symlinks/plugins/ffmpeg_kit_flutter_new/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- Firebase
|
||||
- firebase_core (from `.symlinks/plugins/firebase_core/ios`)
|
||||
|
|
@ -368,6 +366,7 @@ DEPENDENCIES:
|
|||
- no_screenshot (from `.symlinks/plugins/no_screenshot/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- pro_video_editor (from `.symlinks/plugins/pro_video_editor/ios`)
|
||||
- restart_app (from `.symlinks/plugins/restart_app/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
|
|
@ -376,6 +375,7 @@ DEPENDENCIES:
|
|||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
|
||||
- SwiftProtobuf
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_compress (from `.symlinks/plugins/video_compress/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
|
|
@ -428,8 +428,6 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
emoji_picker_flutter:
|
||||
:path: ".symlinks/plugins/emoji_picker_flutter/ios"
|
||||
ffmpeg_kit_flutter_new:
|
||||
:path: ".symlinks/plugins/ffmpeg_kit_flutter_new/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
firebase_core:
|
||||
|
|
@ -470,6 +468,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
pro_video_editor:
|
||||
:path: ".symlinks/plugins/pro_video_editor/ios"
|
||||
restart_app:
|
||||
:path: ".symlinks/plugins/restart_app/ios"
|
||||
sentry_flutter:
|
||||
|
|
@ -484,6 +484,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_compress:
|
||||
:path: ".symlinks/plugins/video_compress/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/darwin"
|
||||
|
||||
|
|
@ -498,7 +500,6 @@ SPEC CHECKSUMS:
|
|||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
emoji_picker_flutter: ece213fc274bdddefb77d502d33080dc54e616cc
|
||||
ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
Firebase: 9a58fdbc9d8655ed7b79a19cf9690bb007d3d46d
|
||||
firebase_core: ee30637e6744af8e0c12a6a1e8a9718506ec2398
|
||||
|
|
@ -540,6 +541,7 @@ SPEC CHECKSUMS:
|
|||
no_screenshot: 5e345998c43ffcad5d6834f249590483fcc037bd
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
pro_video_editor: 44ef9a6d48dbd757ed428cf35396dd05f35c7830
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
restart_app: 9cda5378aacc5000e3f66ee76a9201534e7d3ecf
|
||||
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
|
||||
|
|
@ -554,6 +556,7 @@ SPEC CHECKSUMS:
|
|||
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||
video_compress: f2133a07762889d67f0711ac831faa26f956980e
|
||||
video_player_avfoundation: dd410b52df6d2466a42d28550e33e4146928280a
|
||||
|
||||
PODFILE CHECKSUM: ae041999f13ba7b2285ff9ad9bc688ed647bbcb7
|
||||
|
|
|
|||
|
|
@ -31,9 +31,6 @@ class UserData {
|
|||
@JsonKey(defaultValue: false)
|
||||
bool isDeveloper = false;
|
||||
|
||||
@JsonKey(defaultValue: false)
|
||||
bool disableVideoCompression = false;
|
||||
|
||||
@JsonKey(defaultValue: 0)
|
||||
int deviceId = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ UserData _$UserDataFromJson(Map<String, dynamic> json) => UserData(
|
|||
..appVersion = (json['appVersion'] as num?)?.toInt() ?? 0
|
||||
..avatarCounter = (json['avatarCounter'] as num?)?.toInt() ?? 0
|
||||
..isDeveloper = json['isDeveloper'] as bool? ?? false
|
||||
..disableVideoCompression =
|
||||
json['disableVideoCompression'] as bool? ?? false
|
||||
..deviceId = (json['deviceId'] as num?)?.toInt() ?? 0
|
||||
..subscriptionPlanIdStore = json['subscriptionPlanIdStore'] as String?
|
||||
..lastImageSend = json['lastImageSend'] == null
|
||||
|
|
@ -95,7 +93,6 @@ Map<String, dynamic> _$UserDataToJson(UserData instance) => <String, dynamic>{
|
|||
'appVersion': instance.appVersion,
|
||||
'avatarCounter': instance.avatarCounter,
|
||||
'isDeveloper': instance.isDeveloper,
|
||||
'disableVideoCompression': instance.disableVideoCompression,
|
||||
'deviceId': instance.deviceId,
|
||||
'subscriptionPlan': instance.subscriptionPlan,
|
||||
'subscriptionPlanIdStore': instance.subscriptionPlanIdStore,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
|
||||
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
|
||||
import 'package:flutter_image_compress/flutter_image_compress.dart';
|
||||
import 'package:pro_video_editor/pro_video_editor.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:video_compress/video_compress.dart';
|
||||
|
||||
Future<void> compressImage(
|
||||
File sourceFile,
|
||||
|
|
@ -69,51 +69,47 @@ Future<void> compressAndOverlayVideo(MediaFileService media) async {
|
|||
media.ffmpegOutputPath.deleteSync();
|
||||
}
|
||||
|
||||
if (gUser.disableVideoCompression) {
|
||||
media.originalPath.copySync(media.tempPath.path);
|
||||
return;
|
||||
}
|
||||
|
||||
var overLayCommand = '';
|
||||
if (media.overlayImagePath.existsSync()) {
|
||||
if (Platform.isAndroid) {
|
||||
overLayCommand =
|
||||
'-i "${media.overlayImagePath.path}" -filter_complex "[1:v]format=yuva420p[ovr_in];[0:v]format=yuv420p[base_in];[ovr_in][base_in]scale2ref=w=rw:h=rh[ovr_out][base_out];[base_out][ovr_out]overlay=0:0"';
|
||||
} else {
|
||||
overLayCommand =
|
||||
'-i "${media.overlayImagePath.path}" -filter_complex "[1:v][0:v]scale2ref=w=ref_w:h=ref_h[ovr][base];[base][ovr]overlay=0:0"';
|
||||
}
|
||||
}
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
|
||||
var additionalParams = '';
|
||||
try {
|
||||
final task = VideoRenderData(
|
||||
video: EditorVideo.file(media.originalPath),
|
||||
// qualityPreset: VideoQualityPreset.p720High,
|
||||
imageBytes: media.overlayImagePath.readAsBytesSync(),
|
||||
enableAudio: !media.removeAudio,
|
||||
);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
additionalParams += ' -c:v libx264';
|
||||
}
|
||||
final result = await ProVideoEditor.instance.renderVideo(task);
|
||||
media.ffmpegOutputPath.writeAsBytesSync(result);
|
||||
|
||||
var command =
|
||||
'-i "${media.originalPath.path}" $overLayCommand -map "0:a?" $additionalParams -preset veryfast -crf 28 -c:a aac -b:a 64k "${media.ffmpegOutputPath.path}"';
|
||||
MediaInfo? mediaInfo;
|
||||
try {
|
||||
mediaInfo = await VideoCompress.compressVideo(
|
||||
media.ffmpegOutputPath.path,
|
||||
quality: VideoQuality.Res640x480Quality,
|
||||
includeAudio: true,
|
||||
);
|
||||
Log.info('Video has now size of ${mediaInfo!.filesize} bytes.');
|
||||
} catch (e) {
|
||||
Log.error('during video compression: $e');
|
||||
}
|
||||
|
||||
if (media.removeAudio) {
|
||||
command =
|
||||
'-i "${media.originalPath.path}" $overLayCommand $additionalParams -preset veryfast -crf 28 -an "${media.ffmpegOutputPath.path}"';
|
||||
}
|
||||
if (mediaInfo == null) {
|
||||
Log.error('Could not compress video using original video.');
|
||||
// as a fall back use the non compressed version
|
||||
media.ffmpegOutputPath.renameSync(media.tempPath.path);
|
||||
} else {
|
||||
mediaInfo.file!.renameSync(media.tempPath.path);
|
||||
}
|
||||
|
||||
final session = await FFmpegKit.execute(command);
|
||||
final returnCode = await session.getReturnCode();
|
||||
|
||||
if (ReturnCode.isSuccess(returnCode)) {
|
||||
media.ffmpegOutputPath.copySync(media.tempPath.path);
|
||||
stopwatch.stop();
|
||||
Log.info(
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video',
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to compress the video. Reduced from ${media.ffmpegOutputPath.statSync().size} to ${media.tempPath.statSync().size} bytes.',
|
||||
);
|
||||
} else {
|
||||
Log.info(command);
|
||||
Log.error('Compression failed for the video with exit code $returnCode.');
|
||||
Log.error(await session.getAllLogsAsString());
|
||||
} catch (e) {
|
||||
Log.error(e);
|
||||
// Log.error('Compression failed for the video with exit code $returnCode.');
|
||||
// Log.error(await session.getAllLogsAsString());
|
||||
// This should not happen, but in case "notify" the user that the video was not send... This is absolutely bad, but
|
||||
// better this way then sending an uncompressed media file which potentially is 100MB big :/
|
||||
// Hopefully the user will report the strange behavior <3
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:io';
|
||||
import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit.dart';
|
||||
import 'package:ffmpeg_kit_flutter_new/return_code.dart';
|
||||
import 'dart:ui';
|
||||
import 'package:pro_video_editor/pro_video_editor.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
|
||||
Future<void> createThumbnailsForVideo(
|
||||
|
|
@ -13,22 +13,26 @@ Future<void> createThumbnailsForVideo(
|
|||
return;
|
||||
}
|
||||
|
||||
final command =
|
||||
'-y -i "${sourceFile.path}" -ss 00:00:00 -vframes 1 -vf "scale=iw:ih:flags=lanczos" -c:v libwebp -q:v 100 -compression_level 6 "${destinationFile.path}"';
|
||||
final images = await ProVideoEditor.instance.getThumbnails(
|
||||
ThumbnailConfigs(
|
||||
video: EditorVideo.file(sourceFile),
|
||||
outputFormat: ThumbnailFormat.webp,
|
||||
timestamps: const [
|
||||
Duration.zero,
|
||||
],
|
||||
outputSize: const Size(272, 153),
|
||||
),
|
||||
);
|
||||
|
||||
final session = await FFmpegKit.execute(command);
|
||||
final returnCode = await session.getReturnCode();
|
||||
|
||||
if (ReturnCode.isSuccess(returnCode)) {
|
||||
if (images.isNotEmpty) {
|
||||
stopwatch.stop();
|
||||
destinationFile.writeAsBytesSync(images.first);
|
||||
Log.info(
|
||||
'It took ${stopwatch.elapsedMilliseconds}ms to create the thumbnail.',
|
||||
);
|
||||
} else {
|
||||
Log.info(command);
|
||||
Log.error(
|
||||
'Thumbnail creation failed for the video with exit code $returnCode.',
|
||||
'Thumbnail creation failed for the video with exit code.',
|
||||
);
|
||||
Log.error(await session.getAllLogsAsString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
setState(() {});
|
||||
|
||||
// Make a short delay, so the setState does have its effect...
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
await Future.delayed(const Duration(milliseconds: 80));
|
||||
|
||||
final image = await screenshotController.capture(
|
||||
pixelRatio: pixelRatio,
|
||||
|
|
|
|||
|
|
@ -29,14 +29,6 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
|||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> toggleVideoCompression() async {
|
||||
await updateUserdata((u) {
|
||||
u.disableVideoCompression = !u.disableVideoCompression;
|
||||
return u;
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
|
@ -75,17 +67,6 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
|
|||
}
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Disable ffmpeg'),
|
||||
subtitle: const Text(
|
||||
'If your smartphone crashes, you can disable ffmpeg. This will prevent your videos from being compressed and NO FILTER will be applied to the video! This is a workaround, until the root-cause in ffmpeg is found.',
|
||||
),
|
||||
onTap: toggleVideoCompression,
|
||||
trailing: Switch(
|
||||
value: gUser.disableVideoCompression,
|
||||
onChanged: (a) => toggleVideoCompression(),
|
||||
),
|
||||
),
|
||||
if (!kReleaseMode)
|
||||
ListTile(
|
||||
title: const Text('Automated Testing'),
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:go_router/go_router.dart';
|
||||
import 'package:twonly/src/constants/routes.keys.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/components/loader.dart';
|
||||
|
||||
class DiagnosticsView extends StatefulWidget {
|
||||
|
|
@ -150,14 +151,19 @@ class _LogViewerWidgetState extends State<LogViewerWidget> {
|
|||
}
|
||||
|
||||
TextSpan _formatLineSpan(_LogEntry e) {
|
||||
final tsStyle =
|
||||
TextStyle(color: Colors.grey.shade500, fontFamily: 'monospace');
|
||||
final tsStyle = TextStyle(
|
||||
color: isDarkMode(context) ? Colors.white : Colors.black,
|
||||
fontFamily: 'monospace',
|
||||
);
|
||||
final levelStyle = TextStyle(
|
||||
color: Colors.blueGrey.shade600,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'monospace',
|
||||
);
|
||||
const msgStyle = TextStyle(fontFamily: 'monospace');
|
||||
final msgStyle = TextStyle(
|
||||
color: isDarkMode(context) ? Colors.white : Colors.black,
|
||||
fontFamily: 'monospace',
|
||||
);
|
||||
|
||||
return TextSpan(
|
||||
children: [
|
||||
|
|
|
|||
32
pubspec.lock
32
pubspec.lock
|
|
@ -456,22 +456,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
ffmpeg_kit_flutter_new:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: ffmpeg_kit_flutter_new
|
||||
sha256: d127635f27e93a7f21f0a14ce0a1a148e80919c402dac4a2118d73bfb17ce841
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
ffmpeg_kit_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffmpeg_kit_flutter_platform_interface
|
||||
sha256: addf046ae44e190ad0101b2fde2ad909a3cd08a2a109f6106d2f7048b7abedee
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1513,6 +1497,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.3"
|
||||
pro_video_editor:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pro_video_editor
|
||||
sha256: "0d985f7653c59e2b521d19db49351476eb74eb4001689b33fb8112ab1a9c4330"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.1"
|
||||
protobuf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -1996,6 +1988,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.0"
|
||||
video_compress:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: video_compress
|
||||
sha256: "31bc5cdb9a02ba666456e5e1907393c28e6e0e972980d7d8d619a7beda0d4f20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
video_player:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec
|
|||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.0.94+94
|
||||
version: 0.0.95+95
|
||||
|
||||
environment:
|
||||
sdk: ^3.6.0
|
||||
|
|
@ -83,7 +83,6 @@ dependencies:
|
|||
cached_network_image: ^3.4.1
|
||||
cryptography_flutter_plus: ^2.3.4
|
||||
cryptography_plus: ^2.7.0
|
||||
ffmpeg_kit_flutter_new: ^4.1.0
|
||||
flutter_android_volume_keydown: ^1.0.1
|
||||
flutter_image_compress: ^2.4.0
|
||||
flutter_volume_controller: ^1.3.4
|
||||
|
|
@ -113,6 +112,8 @@ dependencies:
|
|||
flutter_sharing_intent: ^2.0.4
|
||||
no_screenshot: ^0.3.1
|
||||
google_mlkit_face_detection: ^0.13.1
|
||||
pro_video_editor: ^1.6.1
|
||||
video_compress: ^3.1.4
|
||||
|
||||
dependency_overrides:
|
||||
dots_indicator:
|
||||
|
|
|
|||
Loading…
Reference in a new issue