From e1713851b3509c88f5af891d67db065db56524c6 Mon Sep 17 00:00:00 2001 From: ashilkn Date: Mon, 5 Aug 2024 15:09:16 +0530 Subject: [PATCH] [mob][photos] Show accurate height and width in file info for a video --- mobile/lib/models/ffmpeg/ffprobe_props.dart | 20 +++---- .../ui/viewer/file/file_details_widget.dart | 2 +- .../lib/ui/viewer/file/video_exif_dialog.dart | 52 ++++++++++++------- .../viewer/file_details/video_exif_item.dart | 11 ++-- 4 files changed, 49 insertions(+), 36 deletions(-) diff --git a/mobile/lib/models/ffmpeg/ffprobe_props.dart b/mobile/lib/models/ffmpeg/ffprobe_props.dart index 302fcef20d..59adba31ad 100644 --- a/mobile/lib/models/ffmpeg/ffprobe_props.dart +++ b/mobile/lib/models/ffmpeg/ffprobe_props.dart @@ -12,7 +12,7 @@ import "package:photos/models/ffmpeg/mp4.dart"; import "package:photos/models/location/location.dart"; class FFProbeProps { - Map? prodData; + Map? propData; Location? location; DateTime? creationTimeUTC; String? bitrate; @@ -72,8 +72,8 @@ class FFProbeProps { @override String toString() { final buffer = StringBuffer(); - for (final key in prodData!.keys) { - final value = prodData![key]; + for (final key in propData!.keys) { + final value = propData![key]; if (value != null) { buffer.writeln('$key: $value'); } @@ -169,33 +169,33 @@ class FFProbeProps { parsedData[key] = result.fps; } else if (key == FFProbeKeys.codedWidth) { final width = stream[key]; - if (width != null || width != "0") { + if (width != null && width != 0) { result._width = width.toString(); parsedData[key] = result._width; } } else if (key == FFProbeKeys.codedHeight) { final height = stream[key]; - if (height != null && height != "0") { + if (height != null && height != 0) { result._height = height.toString(); parsedData[key] = result._height; } } else if (key == FFProbeKeys.width) { final width = stream[key]; - if (width != null || width != "0") { + if (width != null && width != 0) { result._width = width.toString(); parsedData[FFProbeKeys.width] = result._width; } } else if (key == FFProbeKeys.height) { final height = stream[key]; - if (height != null && height != "0") { + if (height != null && height != 0) { result._height = height.toString(); - parsedData[FFProbeKeys.width] = result._height; + parsedData[FFProbeKeys.height] = result._height; } } else if (key == FFProbeKeys.sideDataList) { - // result._rotation = stream[key][0][FFProbeKeys.rotation]; for (Map sideData in stream[key]) { if (sideData["side_data_type"] == "Display Matrix") { result._rotation = sideData[FFProbeKeys.rotation]; + parsedData[FFProbeKeys.rotation] = result._rotation; } } } @@ -205,7 +205,7 @@ class FFProbeProps { newStreams.add(metadata); } parsedData["streams"] = newStreams; - result.prodData = parsedData; + result.propData = parsedData; return result; } diff --git a/mobile/lib/ui/viewer/file/file_details_widget.dart b/mobile/lib/ui/viewer/file/file_details_widget.dart index d726d655f7..fef9428724 100644 --- a/mobile/lib/ui/viewer/file/file_details_widget.dart +++ b/mobile/lib/ui/viewer/file/file_details_widget.dart @@ -128,7 +128,7 @@ class _FileDetailsWidgetState extends State { _videoMetadataNotifier.value = properties; if (kDebugMode) { log("videoCustomProps ${properties.toString()}"); - log("PropData ${properties?.prodData.toString()}"); + log("PropData ${properties?.propData.toString()}"); } setState(() {}); } diff --git a/mobile/lib/ui/viewer/file/video_exif_dialog.dart b/mobile/lib/ui/viewer/file/video_exif_dialog.dart index 8fd713c832..59147378f4 100644 --- a/mobile/lib/ui/viewer/file/video_exif_dialog.dart +++ b/mobile/lib/ui/viewer/file/video_exif_dialog.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import "package:photos/l10n/l10n.dart"; import "package:photos/models/ffmpeg/ffprobe_keys.dart"; +import "package:photos/models/ffmpeg/ffprobe_props.dart"; import "package:photos/theme/ente_theme.dart"; class VideoExifDialog extends StatelessWidget { - final Map probeData; + final FFProbeProps props; - const VideoExifDialog({Key? key, required this.probeData}) : super(key: key); + const VideoExifDialog({Key? key, required this.props}) : super(key: key); @override Widget build(BuildContext context) { @@ -48,23 +49,23 @@ class VideoExifDialog extends StatelessWidget { context.l10n.videoInfo, style: getEnteTextTheme(context).large, ), - _buildInfoRow(context, 'Creation Time', probeData, 'creation_time'), - _buildInfoRow(context, 'Duration', probeData, 'duration'), - _buildInfoRow(context, context.l10n.location, probeData, 'location'), - _buildInfoRow(context, 'Bitrate', probeData, 'bitrate'), - _buildInfoRow(context, 'Frame Rate', probeData, FFProbeKeys.rFrameRate), - _buildInfoRow(context, 'Width', probeData, FFProbeKeys.codedWidth), - _buildInfoRow(context, 'Height', probeData, FFProbeKeys.codedHeight), - _buildInfoRow(context, 'Model', probeData, 'com.apple.quicktime.model'), - _buildInfoRow(context, 'OS', probeData, 'com.apple.quicktime.software'), - _buildInfoRow(context, 'Major Brand', probeData, 'major_brand'), - _buildInfoRow(context, 'Format', probeData, 'format'), + _buildInfoRow(context, 'Creation Time', props, 'creation_time'), + _buildInfoRow(context, 'Duration', props, 'duration'), + _buildInfoRow(context, context.l10n.location, props, 'location'), + _buildInfoRow(context, 'Bitrate', props, 'bitrate'), + _buildInfoRow(context, 'Frame Rate', props, FFProbeKeys.rFrameRate), + _buildInfoRow(context, 'Width', props, null), + _buildInfoRow(context, 'Height', props, null), + _buildInfoRow(context, 'Model', props, 'com.apple.quicktime.model'), + _buildInfoRow(context, 'OS', props, 'com.apple.quicktime.software'), + _buildInfoRow(context, 'Major Brand', props, 'major_brand'), + _buildInfoRow(context, 'Format', props, 'format'), ], ); } Widget _buildStreamsList(BuildContext context) { - final List streams = probeData['streams']; + final List streams = props.propData!['streams']; final List> data = []; for (final stream in streams) { final Map streamData = {}; @@ -113,7 +114,12 @@ class VideoExifDialog extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: stream.entries .map( - (entry) => _buildInfoRow(context, entry.key, stream, entry.key), + (entry) => _buildInfoRow( + context, + entry.key, + FFProbeProps()..propData = stream, + entry.key, + ), ) .toList(), ), @@ -124,15 +130,23 @@ class VideoExifDialog extends StatelessWidget { Widget _buildInfoRow( BuildContext context, String rowName, - Map data, - String dataKey, + FFProbeProps data, + String? dataKey, ) { + final propData = data.propData; rowName = rowName.replaceAll('_', ' '); rowName = rowName[0].toUpperCase() + rowName.substring(1); try { - final value = data[dataKey]; + dynamic value; + + if (rowName == 'Width' || rowName == 'Height') { + rowName == 'Width' ? value = data.width : value = data.height; + } else { + value = propData![dataKey]; + } + if (value == null) { - return Container(); // Return an empty container if there's no data for the key. + return const SizedBox.shrink(); } return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), diff --git a/mobile/lib/ui/viewer/file_details/video_exif_item.dart b/mobile/lib/ui/viewer/file_details/video_exif_item.dart index aa9512d662..a3d51d6c9a 100644 --- a/mobile/lib/ui/viewer/file_details/video_exif_item.dart +++ b/mobile/lib/ui/viewer/file_details/video_exif_item.dart @@ -35,8 +35,7 @@ class _VideoProbeInfoState extends State { return InfoItemWidget( leadingIcon: Icons.text_snippet_outlined, title: S.of(context).videoInfo, - subtitleSection: - _exifButton(context, widget.file, widget.props?.prodData), + subtitleSection: _exifButton(context, widget.file, widget.props), onTap: _onTap, ); } @@ -44,20 +43,20 @@ class _VideoProbeInfoState extends State { Future> _exifButton( BuildContext context, EnteFile file, - Map? exif, + FFProbeProps? props, ) async { late final String label; late final VoidCallback? onTap; - if (exif == null) { + if (props?.propData == null) { label = S.of(context).loadingExifData; onTap = null; - } else if (exif.isNotEmpty) { + } else if (props!.propData!.isNotEmpty) { label = "${widget.props?.videoInfo ?? ''} .."; onTap = () => showBarModalBottomSheet( context: context, builder: (BuildContext context) { return VideoExifDialog( - probeData: exif, + props: props, ); }, shape: const RoundedRectangleBorder(