file_uploader_util.dart 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import 'dart:async';
  2. import 'dart:io' as io;
  3. import 'dart:typed_data';
  4. import 'package:logging/logging.dart';
  5. import 'package:photo_manager/photo_manager.dart';
  6. import 'package:photos/core/constants.dart';
  7. import 'package:photos/core/errors.dart';
  8. import 'package:photos/models/location.dart';
  9. import 'package:photos/models/file.dart' as ente;
  10. import 'file_util.dart';
  11. final _logger = Logger("FileUtil");
  12. const kMaximumThumbnailCompressionAttempts = 2;
  13. class MediaUploadData {
  14. final io.File sourceFile;
  15. final Uint8List thumbnail;
  16. final bool isDeleted;
  17. MediaUploadData(this.sourceFile, this.thumbnail, this.isDeleted);
  18. }
  19. Future<MediaUploadData> getUploadDataFromEnteFile(ente.File file) async {
  20. // todo: add local to get data from either Asset/photoManager or app cache
  21. return await _getMediaUploadDataFromAssetFile(file);
  22. }
  23. Future<MediaUploadData> _getMediaUploadDataFromAssetFile(ente.File file) async {
  24. io.File sourceFile;
  25. Uint8List thumbnailData;
  26. bool isDeleted;
  27. // The timeouts are to safeguard against https://github.com/CaiJingLong/flutter_photo_manager/issues/467
  28. final asset =
  29. await file.getAsset().timeout(Duration(seconds: 3)).catchError((e) async {
  30. if (e is TimeoutException) {
  31. _logger.info("Asset fetch timed out for " + file.toString());
  32. return await file.getAsset();
  33. } else {
  34. throw e;
  35. }
  36. });
  37. if (asset == null) {
  38. throw InvalidFileError();
  39. }
  40. sourceFile = await asset.originFile
  41. .timeout(Duration(seconds: 3))
  42. .catchError((e) async {
  43. if (e is TimeoutException) {
  44. _logger.info("Origin file fetch timed out for " + file.toString());
  45. return await asset.originFile;
  46. } else {
  47. throw e;
  48. }
  49. });
  50. if (!sourceFile.existsSync()) {
  51. throw InvalidFileError();
  52. }
  53. thumbnailData = await asset.thumbDataWithSize(
  54. THUMBNAIL_SMALL_SIZE,
  55. THUMBNAIL_SMALL_SIZE,
  56. quality: THUMBNAIL_QUALITY,
  57. );
  58. int compressionAttempts = 0;
  59. while (thumbnailData.length > THUMBNAIL_DATA_LIMIT &&
  60. compressionAttempts < kMaximumThumbnailCompressionAttempts) {
  61. _logger.info("Thumbnail size " + thumbnailData.length.toString());
  62. thumbnailData = await compressThumbnail(thumbnailData);
  63. _logger
  64. .info("Compressed thumbnail size " + thumbnailData.length.toString());
  65. compressionAttempts++;
  66. }
  67. isDeleted = asset == null || !(await asset.exists);
  68. // h4ck to fetch location data if missing (thank you Android Q+) lazily only during uploads
  69. await _decorateEnteFileData(file, asset);
  70. return MediaUploadData(sourceFile, thumbnailData, isDeleted);
  71. }
  72. Future<void> _decorateEnteFileData(ente.File file, AssetEntity asset) async {
  73. // h4ck to fetch location data if missing (thank you Android Q+) lazily only during uploads
  74. if (file.location == null ||
  75. (file.location.latitude == 0 && file.location.longitude == 0)) {
  76. final latLong = await asset.latlngAsync();
  77. file.location = Location(latLong.latitude, latLong.longitude);
  78. }
  79. if (file.title == null || file.title.isEmpty) {
  80. _logger.severe("Title was missing");
  81. file.title = await asset.titleAsync;
  82. }
  83. }