diff --git a/lib/core/constants.dart b/lib/core/constants.dart index 5c70e218f..bc59fccc0 100644 --- a/lib/core/constants.dart +++ b/lib/core/constants.dart @@ -68,3 +68,31 @@ const galleryGridSpacing = 2.0; const searchSectionLimit = 7; bool isInternalUser = false; + +const blackThumbnailBase64 = '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB' + + 'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQ' + + 'EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARC' + + 'ACWASwDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF' + + 'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk' + + '6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztL' + + 'W2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAA' + + 'AAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVY' + + 'nLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImK' + + 'kpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oAD' + + 'AMBAAIRAxEAPwD/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAC' + + 'gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' + + 'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAK' + + 'ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' + + 'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/9k='; diff --git a/lib/models/metadata/file_magic.dart b/lib/models/metadata/file_magic.dart index ca08f95f8..d50dc0dd8 100644 --- a/lib/models/metadata/file_magic.dart +++ b/lib/models/metadata/file_magic.dart @@ -11,6 +11,7 @@ const heightKey = 'h'; const latKey = "lat"; const longKey = "long"; const motionVideoIndexKey = "mvi"; +const noThumbKey = "noThumb"; class MagicMetadata { // 0 -> visible @@ -47,6 +48,13 @@ class PubMagicMetadata { // photo int? mvi; + // if true, then the thumbnail is not available + // Note: desktop/web sets hasStaticThumbnail in the file metadata. + // As we don't want to support updating the og file metadata (yet), adding + // this new field to the pub metadata. For static thumbnail, all thumbnails + // should have exact same hash with should match the constant `blackThumbnailBase64` + bool? noThumb; + PubMagicMetadata({ this.editedTime, this.editedName, @@ -57,6 +65,7 @@ class PubMagicMetadata { this.lat, this.long, this.mvi, + this.noThumb, }); factory PubMagicMetadata.fromEncodedJson(String encodedJson) => @@ -77,6 +86,7 @@ class PubMagicMetadata { lat: map[latKey], long: map[longKey], mvi: map[motionVideoIndexKey], + noThumb: map[noThumbKey], ); } } diff --git a/lib/utils/file_uploader.dart b/lib/utils/file_uploader.dart index 13d109b7a..89a12f147 100644 --- a/lib/utils/file_uploader.dart +++ b/lib/utils/file_uploader.dart @@ -71,6 +71,7 @@ class FileUploader { late ProcessType _processType; late bool _isBackground; late SharedPreferences _prefs; + // _hasInitiatedForceUpload is used to track if user attempted force upload // where files are uploaded directly (without adding them to DB). In such // cases, we don't want to clear the stale upload files. See #removeStaleFiles @@ -457,7 +458,13 @@ class FileUploader { encryptedFilePath, key: key, ); - final thumbnailData = mediaUploadData.thumbnail; + late final Uint8List? thumbnailData; + if (mediaUploadData.thumbnail == null && + file.fileType == FileType.video) { + thumbnailData = base64Decode(blackThumbnailBase64); + } else { + thumbnailData = mediaUploadData.thumbnail; + } final EncryptionResult encryptedThumbnailData = await CryptoUtil.encryptChaCha( @@ -519,17 +526,21 @@ class FileUploader { CryptoUtil.bin2base64(encryptedFileKeyData.encryptedData!); final keyDecryptionNonce = CryptoUtil.bin2base64(encryptedFileKeyData.nonce!); + final Map pubMetadata = {}; MetadataRequest? pubMetadataRequest; if ((mediaUploadData.height ?? 0) != 0 && (mediaUploadData.width ?? 0) != 0) { - final pubMetadata = { - heightKey: mediaUploadData.height, - widthKey: mediaUploadData.width, - }; - if (mediaUploadData.motionPhotoStartIndex != null) { - pubMetadata[motionVideoIndexKey] = - mediaUploadData.motionPhotoStartIndex; - } + pubMetadata[heightKey] = mediaUploadData.height; + pubMetadata[widthKey] = mediaUploadData.width; + } + if (mediaUploadData.motionPhotoStartIndex != null) { + pubMetadata[motionVideoIndexKey] = + mediaUploadData.motionPhotoStartIndex; + } + if (mediaUploadData.thumbnail == null) { + pubMetadata[noThumbKey] = true; + } + if (pubMetadata.isNotEmpty) { pubMetadataRequest = await getPubMetadataRequest( file, pubMetadata, diff --git a/lib/utils/file_uploader_util.dart b/lib/utils/file_uploader_util.dart index f877747aa..1455ee0e9 100644 --- a/lib/utils/file_uploader_util.dart +++ b/lib/utils/file_uploader_util.dart @@ -208,6 +208,10 @@ Future _getThumbnailForUpload( quality: thumbnailQuality, ); if (thumbnailData == null) { + // allow videos to be uploaded without thumbnails + if (asset.type == AssetType.video) { + return null; + } throw InvalidFileError( "no thumbnail : ${file.fileType} ${file.tag}", InvalidReason.thumbnailMissing, @@ -227,6 +231,10 @@ Future _getThumbnailForUpload( final String errMessage = "thumbErr for ${file.fileType}, ${extension(file.displayName)} ${file.tag}"; _logger.warning(errMessage, e); + // allow videos to be uploaded without thumbnails + if (asset.type == AssetType.video) { + return null; + } throw InvalidFileError(errMessage, InvalidReason.thumbnailMissing); } }