From f4b7ef19cee0ee0704f61880e8c8ba17673a22eb Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Sat, 8 Feb 2025 03:51:04 +0530 Subject: [PATCH] fix: add backup status item tap action, sorting fix, color space fix --- mobile/lib/db/files_db.dart | 2 +- mobile/lib/services/preview_video_store.dart | 15 + .../ui/settings/backup/backup_item_card.dart | 353 +++++++++--------- 3 files changed, 199 insertions(+), 171 deletions(-) diff --git a/mobile/lib/db/files_db.dart b/mobile/lib/db/files_db.dart index 37d05e0453..4452f12097 100644 --- a/mobile/lib/db/files_db.dart +++ b/mobile/lib/db/files_db.dart @@ -1741,7 +1741,7 @@ class FilesDB { WHERE $columnFileType = ? AND $columnCreationTime > ? AND $columnUploadedFileID != -1 - SORT BY $columnCreationTime DESC + ORDER BY $columnCreationTime DESC ''', [getInt(fileType), beginDate.microsecondsSinceEpoch], ); diff --git a/mobile/lib/services/preview_video_store.dart b/mobile/lib/services/preview_video_store.dart index 5ad1e9a2d2..81c038d43f 100644 --- a/mobile/lib/services/preview_video_store.dart +++ b/mobile/lib/services/preview_video_store.dart @@ -202,7 +202,10 @@ class PreviewVideoStore { ); FFmpegSession? session; + final colorSpace = videoData["color_space"]?.toString().toLowerCase(); + final isColorGood = colorSpace == "bt709"; final codecIsH264 = codec?.contains("h264") ?? false; + if (bitrate != null && bitrate <= 4000 * 1000 && codecIsH264) { // create playlist without compression, as is session = await FFmpegKit.execute( @@ -233,6 +236,18 @@ class PreviewVideoStore { ); } + if (codecIsH264 && (colorSpace == null || isColorGood)) { + session ??= await FFmpegKit.execute( + '-i "${file.path}" ' + '-metadata:s:v:0 rotate=0 ' + '-vf "scale=-2:720,fps=30" ' + '-c:v libx264 -b:v 2000k -preset medium ' + '-c:a aac -b:a 128k -f hls -hls_time 10 -hls_flags single_file ' + '-hls_list_size 0 -hls_key_info_file ${keyinfo.path} ' + '$prefix/output.m3u8', + ); + } + session ??= await FFmpegKit.execute( '-i "${file.path}" ' '-metadata:s:v:0 rotate=0 ' diff --git a/mobile/lib/ui/settings/backup/backup_item_card.dart b/mobile/lib/ui/settings/backup/backup_item_card.dart index a17cdcfd40..dda6273bb3 100644 --- a/mobile/lib/ui/settings/backup/backup_item_card.dart +++ b/mobile/lib/ui/settings/backup/backup_item_card.dart @@ -7,9 +7,11 @@ import "package:photos/models/preview/preview_item.dart"; import "package:photos/models/preview/preview_item_status.dart"; import "package:photos/services/preview_video_store.dart"; import 'package:photos/theme/ente_theme.dart'; +import "package:photos/ui/viewer/file/file_widget.dart"; import "package:photos/ui/viewer/file/thumbnail_widget.dart"; import "package:photos/utils/dialog_util.dart"; import "package:photos/utils/file_uploader.dart"; +import "package:photos/utils/navigation_util.dart"; class BackupItemCard extends StatefulWidget { const BackupItemCard({ @@ -55,199 +57,210 @@ class _BackupItemCardState extends State { final hasError = widget.item.error != null || widget.preview?.status == PreviewItemStatus.failed; - return Container( - height: 60, - margin: const EdgeInsets.symmetric(vertical: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - border: Border.all( - color: colorScheme.fillFaint.withOpacity(0.08), - width: 1, + return GestureDetector( + onTap: () { + routeToPage( + context, + FileWidget( + widget.item.file, + ), + forceCustomPageRoute: true, + ); + }, + child: Container( + height: 60, + margin: const EdgeInsets.symmetric(vertical: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: colorScheme.fillFaint.withOpacity(0.08), + width: 1, + ), ), - ), - child: Row( - children: [ - SizedBox( - width: 60, - height: 60, - child: ClipRRect( - borderRadius: BorderRadius.circular(4), - child: showThumbnail - ? ThumbnailWidget( - widget.item.file, - shouldShowSyncStatus: false, - ) - : Container( - color: colorScheme.fillFaint, // Placeholder color + child: Row( + children: [ + SizedBox( + width: 60, + height: 60, + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: showThumbnail + ? ThumbnailWidget( + widget.item.file, + shouldShowSyncStatus: false, + ) + : Container( + color: colorScheme.fillFaint, // Placeholder color + ), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + widget.item.file.displayName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 16, + height: 20 / 16, + color: Theme.of(context).brightness == Brightness.light + ? const Color(0xFF000000) + : const Color(0xFFFFFFFF), ), - ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.item.file.displayName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 16, - height: 20 / 16, - color: Theme.of(context).brightness == Brightness.light - ? const Color(0xFF000000) - : const Color(0xFFFFFFFF), ), - ), - const SizedBox(height: 4), - Text( - folderName ?? "", - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 14, - height: 17 / 14, - color: Theme.of(context).brightness == Brightness.light - ? const Color.fromRGBO(0, 0, 0, 0.7) - : const Color.fromRGBO(255, 255, 255, 0.7), + const SizedBox(height: 4), + Text( + folderName ?? "", + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 14, + height: 17 / 14, + color: Theme.of(context).brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 0.7) + : const Color.fromRGBO(255, 255, 255, 0.7), + ), ), - ), - ], + ], + ), ), - ), - if (hasError) const SizedBox(width: 12), - if (hasError) + if (hasError) const SizedBox(width: 12), + if (hasError) + SizedBox( + height: 48, + width: 48, + child: IconButton( + icon: Icon( + Icons.error_outline, + color: getEnteColorScheme(context).fillBase, + ), + onPressed: () { + String errorMessage = ""; + if (widget.item.error is Exception) { + final Exception ex = widget.item.error as Exception; + errorMessage = "Error: " + + ex.runtimeType.toString() + + " - " + + ex.toString(); + } else if (widget.item.error != null) { + errorMessage = widget.item.error.toString(); + } else if (widget.preview?.error != null) { + errorMessage = widget.preview!.error!.toString(); + } + showErrorDialog( + context, + 'Upload failed', + errorMessage, + ); + }, + ), + ), + if (hasError) const SizedBox(width: 12), SizedBox( height: 48, width: 48, - child: IconButton( - icon: Icon( - Icons.error_outline, - color: getEnteColorScheme(context).fillBase, - ), - onPressed: () { - String errorMessage = ""; - if (widget.item.error is Exception) { - final Exception ex = widget.item.error as Exception; - errorMessage = "Error: " + - ex.runtimeType.toString() + - " - " + - ex.toString(); - } else if (widget.item.error != null) { - errorMessage = widget.item.error.toString(); - } else if (widget.preview?.error != null) { - errorMessage = widget.preview!.error!.toString(); - } - showErrorDialog( - context, - 'Upload failed', - errorMessage, - ); - }, - ), - ), - if (hasError) const SizedBox(width: 12), - SizedBox( - height: 48, - width: 48, - child: Center( - child: switch (widget.item.status) { - BackupItemStatus.uploading => SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: colorScheme.primary700, + child: Center( + child: switch (widget.item.status) { + BackupItemStatus.uploading => SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: colorScheme.primary700, + ), ), - ), - BackupItemStatus.uploaded => widget.preview != null - ? switch (widget.preview!.status) { - PreviewItemStatus.uploading || - PreviewItemStatus.compressing => - SizedBox( - width: 24, - height: 24, - child: Image.asset( - "assets/processing-video.png", - ), - ), - PreviewItemStatus.failed => GestureDetector( - onTap: () => - PreviewVideoStore.instance.chunkAndUploadVideo( - context, - widget.item.file, - true, - ), - child: SizedBox( + BackupItemStatus.uploaded => widget.preview != null + ? switch (widget.preview!.status) { + PreviewItemStatus.uploading || + PreviewItemStatus.compressing => + SizedBox( width: 24, height: 24, child: Image.asset( - "assets/processing-video-failed.png", + "assets/processing-video.png", ), ), - ), - PreviewItemStatus.retry || - PreviewItemStatus.inQueue => - SizedBox( - width: 24, - height: 24, - child: Image.asset( - "assets/video-processing-queued.png", + PreviewItemStatus.failed => GestureDetector( + onTap: () => PreviewVideoStore.instance + .chunkAndUploadVideo( + context, + widget.item.file, + true, + ), + child: SizedBox( + width: 24, + height: 24, + child: Image.asset( + "assets/processing-video-failed.png", + ), + ), ), - ), - PreviewItemStatus.uploaded => SizedBox( - width: 24, - height: 24, - child: Image.asset( - "assets/processing-video-success.png", + PreviewItemStatus.retry || + PreviewItemStatus.inQueue => + SizedBox( + width: 24, + height: 24, + child: Image.asset( + "assets/video-processing-queued.png", + ), ), + PreviewItemStatus.uploaded => SizedBox( + width: 24, + height: 24, + child: Image.asset( + "assets/processing-video-success.png", + ), + ), + } + : const SizedBox( + width: 24, + height: 24, + child: Icon( + Icons.check, + color: Color(0xFF00B33C), ), - } - : const SizedBox( - width: 24, - height: 24, - child: Icon( - Icons.check, - color: Color(0xFF00B33C), ), + BackupItemStatus.inQueue => SizedBox( + width: 24, + height: 24, + child: Icon( + Icons.history, + color: Theme.of(context).brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, .6) + : const Color.fromRGBO(255, 255, 255, .6), ), - BackupItemStatus.inQueue => SizedBox( - width: 24, - height: 24, - child: Icon( - Icons.history, - color: Theme.of(context).brightness == Brightness.light - ? const Color.fromRGBO(0, 0, 0, .6) - : const Color.fromRGBO(255, 255, 255, .6), ), - ), - BackupItemStatus.retry => IconButton( - icon: const Icon( - Icons.sync, - color: Color(0xFFFDB816), + BackupItemStatus.retry => IconButton( + icon: const Icon( + Icons.sync, + color: Color(0xFFFDB816), + ), + onPressed: () async { + await FileUploader.instance.upload( + widget.item.file, + widget.item.collectionID, + ); + }, ), - onPressed: () async { - await FileUploader.instance.upload( - widget.item.file, - widget.item.collectionID, - ); - }, - ), - BackupItemStatus.inBackground => SizedBox( - width: 16, - height: 16, - child: CircularProgressIndicator( - strokeWidth: 2.0, - color: Theme.of(context).brightness == Brightness.light - ? const Color.fromRGBO(0, 0, 0, .6) - : const Color.fromRGBO(255, 255, 255, .6), + BackupItemStatus.inBackground => SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2.0, + color: Theme.of(context).brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, .6) + : const Color.fromRGBO(255, 255, 255, .6), + ), ), - ), - }, + }, + ), ), - ), - ], + ], + ), ), ); }