diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 9b4b245..7d86438 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -27,9 +27,6 @@ PODS: - libwebp/sharpyuv - open_filex (0.0.2): - Flutter - - video_thumbnail (0.0.1): - - Flutter - - libwebp - ZIPFoundation (0.9.19) DEPENDENCIES: @@ -39,7 +36,6 @@ DEPENDENCIES: - flutter_native_video_trimmer (from `.symlinks/plugins/flutter_native_video_trimmer/ios`) - get_thumbnail_video (from `.symlinks/plugins/get_thumbnail_video/ios`) - open_filex (from `.symlinks/plugins/open_filex/ios`) - - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) SPEC REPOS: trunk: @@ -59,8 +55,6 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/get_thumbnail_video/ios" open_filex: :path: ".symlinks/plugins/open_filex/ios" - video_thumbnail: - :path: ".symlinks/plugins/video_thumbnail/ios" SPEC CHECKSUMS: ffmpeg_kit_flutter_new: 12426a19f10ac81186c67c6ebc4717f8f4364b7f @@ -70,7 +64,6 @@ SPEC CHECKSUMS: get_thumbnail_video: 1a754d46b860dffefcc57b7290a43089cd5d7e58 libwebp: af5937a13536ef73f3784d69cc342b98961acf33 open_filex: 432f3cd11432da3e39f47fcc0df2b1603854eff1 - video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140 ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c PODFILE CHECKSUM: 1723d4a379f06df5688b6fc71e3a0301b720dee9 diff --git a/lib/pages/home/create_movie/widgets/select_video_from_storage.dart b/lib/pages/home/create_movie/widgets/select_video_from_storage.dart index da4138f..05b31c8 100644 --- a/lib/pages/home/create_movie/widgets/select_video_from_storage.dart +++ b/lib/pages/home/create_movie/widgets/select_video_from_storage.dart @@ -5,7 +5,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; -import 'package:video_thumbnail/video_thumbnail.dart'; +import 'package:get_thumbnail_video/index.dart'; +import 'package:get_thumbnail_video/video_thumbnail.dart'; import '../../../../utils/constants.dart'; import '../../../../utils/date_format_utils.dart'; diff --git a/lib/pages/home/create_movie/widgets/view_movies_page.dart b/lib/pages/home/create_movie/widgets/view_movies_page.dart index db14e37..b200a4b 100644 --- a/lib/pages/home/create_movie/widgets/view_movies_page.dart +++ b/lib/pages/home/create_movie/widgets/view_movies_page.dart @@ -4,9 +4,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; +import 'package:get_thumbnail_video/index.dart'; +import 'package:get_thumbnail_video/video_thumbnail.dart'; import 'package:open_filex/open_filex.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:video_thumbnail/video_thumbnail.dart'; import '../../../../utils/app_paths.dart'; import '../../../../utils/constants.dart'; diff --git a/lib/pages/save_photo/widgets/save_photo_button.dart b/lib/pages/save_photo/widgets/save_photo_button.dart index 74060d8..da17886 100644 --- a/lib/pages/save_photo/widgets/save_photo_button.dart +++ b/lib/pages/save_photo/widgets/save_photo_button.dart @@ -1,12 +1,10 @@ -import 'package:ffmpeg_kit_flutter_new/ffmpeg_kit_config.dart'; -import 'package:ffmpeg_kit_flutter_new/return_code.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; -import 'package:media_store_plus/media_store_plus.dart'; import '../../../controllers/daily_entry_controller.dart'; import '../../../routes/app_pages.dart'; +import '../../../utils/app_paths.dart'; import '../../../utils/constants.dart'; import '../../../utils/custom_dialog.dart'; import '../../../utils/date_format_utils.dart'; @@ -14,6 +12,7 @@ import '../../../utils/ffmpeg_api_wrapper.dart'; import '../../../utils/shared_preferences_util.dart'; import '../../../utils/storage_utils.dart'; import '../../../utils/utils.dart'; +import '../../../utils/video_encoder.dart'; class SavePhotoButton extends StatefulWidget { SavePhotoButton({ @@ -52,7 +51,6 @@ class _SavePhotoButtonState extends State { final String logTag = '[SAVE PHOTO AS VIDEO] - '; String currentProfileName = 'Default'; ValueNotifier saveProgressPercentage = ValueNotifier(0); - final mediaStore = MediaStore(); final DailyEntryController _dayController = Get.find(); @@ -149,27 +147,24 @@ class _SavePhotoButtonState extends State { // Check if user is using a custom profile to determine the output path of the video String getVideoOutputPath() { - String videoOutputPath = ''; final String videoName = DateFormatUtils.getDate(widget.determinedDate); - final String defaultOutputPath = '${SharedPrefsUtil.getString('appPath')}$videoName.mp4'; final selectedProfileIndex = SharedPrefsUtil.getInt('selectedProfileIndex') ?? 0; if (selectedProfileIndex == 0) { - // If this is true, it means we are using the default profile, so the output folder would be the default output path - videoOutputPath = defaultOutputPath; - } else { - // This means we are using a custom profile - final allProfiles = SharedPrefsUtil.getStringList('profiles'); - if (allProfiles != null) { - setState(() { - currentProfileName = allProfiles[selectedProfileIndex]; - }); - - videoOutputPath = - '${SharedPrefsUtil.getString('appPath')}Profiles/$currentProfileName/$videoName.mp4'; - } + // Default profile, videos go straight into the app folder. + return '${AppPaths.videos}$videoName.mp4'; + } + + final allProfiles = SharedPrefsUtil.getStringList('profiles'); + if (allProfiles == null || selectedProfileIndex >= allProfiles.length) { + Utils.logWarning('${logTag}Unknown profile index $selectedProfileIndex, using the default'); + return '${AppPaths.videos}$videoName.mp4'; } - return videoOutputPath; + + setState(() { + currentProfileName = allProfiles[selectedProfileIndex]; + }); + return '${AppPaths.profileVideos(currentProfileName)}$videoName.mp4'; } Future _editWithFFmpeg(bool isGeotaggingEnabled, BuildContext context) async { @@ -221,10 +216,7 @@ class _SavePhotoButtonState extends State { action: () async { Utils.logInfo('${logTag}Video already exists, deleting it to perform edit.'); try { - StorageUtils.deleteFile(finalPath); - } catch (e) { - Utils.logError('${logTag}Error deleting old video: $e, trying MediaStore'); - await StorageUtils.deleteFileWithMediaStore(finalPath); + await StorageUtils.deleteVideo(finalPath); } finally { Get.back(); } @@ -242,7 +234,9 @@ class _SavePhotoButtonState extends State { if (!shouldContinue) return; - await FFmpegKitConfig.setFontDirectory(fontPath); + // The font name map must not be omitted: a null map reaches the iOS side + // of ffmpeg-kit as NSNull, which crashes on [NSNull allKeys]. + await FFmpegKitConfig.setFontDirectory(fontPath, {}); if (isGeotaggingEnabled) { final String locationTextFilePath = await Utils.writeLocationTxt(widget.userLocation); @@ -296,9 +290,10 @@ class _SavePhotoButtonState extends State { ? '-c:s mov_text -map 1:v $audioMap -map 0:s -disposition:s:0 default' : '-map 1:v $audioMap'; - // Apply default edit settings: framerate 30, audio channels 1, audio rate 48000, audio bitrate 256k, video codec libx264, pixel format yuv420p, crf 20, preset slow - const defaultEditSettings = - '-r 30 -ac 1 -ar 48000 -c:a aac -b:a 256k -c:v libx264 -pix_fmt yuv420p -crf 20 -preset slow'; + // Framerate 30, mono 48 kHz aac at 256k, yuv420p, and whichever H.264 + // encoder this build of ffmpeg actually ships (see VideoEncoder). + final String defaultEditSettings = + '-r 30 -ac 1 -ar 48000 -c:a aac -b:a 256k ${VideoEncoder.arguments} -pix_fmt yuv420p'; // Full command to edit and save video final command = diff --git a/lib/pages/save_video/widgets/save_button.dart b/lib/pages/save_video/widgets/save_button.dart index f019b8b..d7bc609 100644 --- a/lib/pages/save_video/widgets/save_button.dart +++ b/lib/pages/save_video/widgets/save_button.dart @@ -248,7 +248,9 @@ class _SaveButtonState extends State { // Caches the default font to save texts in ffmpeg. // The edit may fail unexpectedly in some devices if this is not done. - await FFmpegKitConfig.setFontDirectory(fontPath); + // The font name map must not be omitted: a null map reaches the iOS side + // of ffmpeg-kit as NSNull, which crashes on [NSNull allKeys]. + await FFmpegKitConfig.setFontDirectory(fontPath, {}); // If geotagging is enabled, we can allow the command to render the location text into the video if (isGeotaggingEnabled) { diff --git a/pubspec.lock b/pubspec.lock index ab38868..b2b2f8d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -545,7 +545,7 @@ packages: source: hosted version: "4.7.3" get_thumbnail_video: - dependency: transitive + dependency: "direct main" description: name: get_thumbnail_video sha256: ff61495b42051765d2a9e93bd14dac7ede5853033837bde71c27575a192c53fc @@ -1374,15 +1374,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.0" - video_thumbnail: - dependency: "direct main" - description: - path: "." - ref: master - resolved-ref: e46ccb3b97be758e3610adc5438587fa734eac74 - url: "https://github.com/KyleKun/video_thumbnail" - source: git - version: "0.5.6" video_trimmer: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6de839d..9a087cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,7 @@ dependencies: synchronized: ^3.1.0 timer_builder: ^2.0.0 timezone: ^0.11.0 + get_thumbnail_video: ^0.7.3 url_launcher: ^6.2.1 video_player: ^2.8.1 wakelock_plus: ^1.6.1 @@ -51,10 +52,6 @@ dependencies: git: url: https://github.com/KyleKun/media_store_plus.git ref: main - video_thumbnail: - git: - url: https://github.com/KyleKun/video_thumbnail - ref: master video_trimmer: git: url: https://github.com/KyleKun/video_trimmer