file_sync_util.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import 'dart:math';
  2. import 'package:computer/computer.dart';
  3. import 'package:logging/logging.dart';
  4. import 'package:photo_manager/photo_manager.dart';
  5. import 'package:photos/models/file.dart';
  6. final _logger = Logger("FileSyncUtil");
  7. final ignoreSizeConstraint = SizeConstraint(ignoreSize: true);
  8. final assetFetchPageSize = 2000;
  9. Future<List<File>> getDeviceFiles(
  10. int fromTime, int toTime, Computer computer) async {
  11. final pathEntities = await _getGalleryList(fromTime, toTime);
  12. List<File> files = [];
  13. AssetPathEntity recents;
  14. for (AssetPathEntity pathEntity in pathEntities) {
  15. if (pathEntity.name == "Recent" || pathEntity.name == "Recents") {
  16. recents = pathEntity;
  17. } else {
  18. files = await _computeFiles(pathEntity, fromTime, files, computer);
  19. }
  20. }
  21. if (recents != null) {
  22. files = await _computeFiles(recents, fromTime, files, computer);
  23. }
  24. files.sort(
  25. (first, second) => first.creationTime.compareTo(second.creationTime));
  26. return files;
  27. }
  28. Future<List<LocalAsset>> getAllLocalAssets() async {
  29. final filterOptionGroup = FilterOptionGroup();
  30. filterOptionGroup.setOption(
  31. AssetType.image, FilterOption(sizeConstraint: ignoreSizeConstraint));
  32. filterOptionGroup.setOption(
  33. AssetType.video, FilterOption(sizeConstraint: ignoreSizeConstraint));
  34. filterOptionGroup.createTimeCond = DateTimeCond.def().copyWith(ignore: true);
  35. final assetPaths = await PhotoManager.getAssetPathList(
  36. hasAll: true,
  37. type: RequestType.common,
  38. filterOption: filterOptionGroup,
  39. );
  40. final List<LocalAsset> assets = [];
  41. for (final assetPath in assetPaths) {
  42. for (final asset in await _getAllAssetLists(assetPath)) {
  43. assets.add(LocalAsset(asset.id, assetPath.name));
  44. }
  45. }
  46. return assets;
  47. }
  48. Future<List<File>> getUnsyncedFiles(List<LocalAsset> assets,
  49. Set<String> existingIDs, Set<String> invalidIDs, Computer computer) async {
  50. final args = Map<String, dynamic>();
  51. args['assets'] = assets;
  52. args['existingIDs'] = existingIDs;
  53. args['invalidIDs'] = invalidIDs;
  54. final unsyncedAssets =
  55. await computer.compute(_getUnsyncedAssets, param: args);
  56. if (unsyncedAssets.isEmpty) {
  57. return [];
  58. }
  59. return _convertToFiles(unsyncedAssets, computer);
  60. }
  61. List<LocalAsset> _getUnsyncedAssets(Map<String, dynamic> args) {
  62. final List<LocalAsset> assets = args['assets'];
  63. final Set<String> existingIDs = args['existingIDs'];
  64. final Set<String> invalidIDs = args['invalidIDs'];
  65. final List<LocalAsset> unsyncedAssets = [];
  66. for (final asset in assets) {
  67. if (!existingIDs.contains(asset.id) && !invalidIDs.contains(asset.id)) {
  68. unsyncedAssets.add(asset);
  69. }
  70. }
  71. return unsyncedAssets;
  72. }
  73. Future<List<File>> _convertToFiles(
  74. List<LocalAsset> assets, Computer computer) async {
  75. final List<LocalAsset> recents = [];
  76. final List<LocalAssetEntity> entities = [];
  77. for (final asset in assets) {
  78. if (asset.path == "Recent" || asset.path == "Recents") {
  79. recents.add(asset);
  80. } else {
  81. entities.add(
  82. LocalAssetEntity(await AssetEntity.fromId(asset.id), asset.path));
  83. }
  84. }
  85. // Ignore duplicate items in recents
  86. for (final recent in recents) {
  87. bool presentInOthers = false;
  88. for (final entity in entities) {
  89. if (recent.id == entity.entity.id) {
  90. presentInOthers = true;
  91. break;
  92. }
  93. }
  94. if (!presentInOthers) {
  95. entities.add(
  96. LocalAssetEntity(await AssetEntity.fromId(recent.id), recent.path));
  97. }
  98. }
  99. return await computer.compute(_getFilesFromAssets, param: entities);
  100. }
  101. Future<List<AssetPathEntity>> _getGalleryList(
  102. final int fromTime, final int toTime) async {
  103. final filterOptionGroup = FilterOptionGroup();
  104. filterOptionGroup.setOption(AssetType.image,
  105. FilterOption(needTitle: true, sizeConstraint: ignoreSizeConstraint));
  106. filterOptionGroup.setOption(AssetType.video,
  107. FilterOption(needTitle: true, sizeConstraint: ignoreSizeConstraint));
  108. filterOptionGroup.updateTimeCond = DateTimeCond(
  109. min: DateTime.fromMicrosecondsSinceEpoch(fromTime),
  110. max: DateTime.fromMicrosecondsSinceEpoch(toTime),
  111. );
  112. final galleryList = await PhotoManager.getAssetPathList(
  113. hasAll: true,
  114. type: RequestType.common,
  115. filterOption: filterOptionGroup,
  116. );
  117. galleryList.sort((s1, s2) {
  118. return s2.assetCount.compareTo(s1.assetCount);
  119. });
  120. return galleryList;
  121. }
  122. Future<List<File>> _computeFiles(AssetPathEntity pathEntity, int fromTime,
  123. List<File> files, Computer computer) async {
  124. final args = Map<String, dynamic>();
  125. args["pathEntity"] = pathEntity;
  126. args["assetList"] = await _getAllAssetLists(pathEntity);
  127. args["fromTime"] = fromTime;
  128. args["files"] = files;
  129. return await computer.compute(_getFiles, param: args);
  130. }
  131. Future<List<AssetEntity>> _getAllAssetLists(AssetPathEntity pathEntity) async {
  132. List<AssetEntity> result = [];
  133. int currentPage = 0;
  134. List<AssetEntity> currentPageResult = [];
  135. do {
  136. currentPageResult = await pathEntity.getAssetListPaged(
  137. page: currentPage, size: assetFetchPageSize);
  138. result.addAll(currentPageResult);
  139. currentPage = currentPage + 1;
  140. } while (currentPageResult.length >= assetFetchPageSize);
  141. return result;
  142. }
  143. Future<List<File>> _getFiles(Map<String, dynamic> args) async {
  144. final pathEntity = args["pathEntity"];
  145. final assetList = args["assetList"];
  146. final fromTime = args["fromTime"];
  147. final files = args["files"];
  148. for (AssetEntity entity in assetList) {
  149. if (max(entity.createDateTime.microsecondsSinceEpoch,
  150. entity.modifiedDateTime.microsecondsSinceEpoch) >
  151. fromTime) {
  152. try {
  153. final file = await File.fromAsset(pathEntity.name, entity);
  154. if (!files.contains(file)) {
  155. files.add(file);
  156. }
  157. } catch (e) {
  158. _logger.severe(e);
  159. }
  160. }
  161. }
  162. return files;
  163. }
  164. Future<List<File>> _getFilesFromAssets(List<LocalAssetEntity> assets) async {
  165. final List<File> files = [];
  166. for (final asset in assets) {
  167. files.add(await File.fromAsset(
  168. asset.path,
  169. asset.entity,
  170. ));
  171. }
  172. return files;
  173. }
  174. class LocalAsset {
  175. final String id;
  176. final String path;
  177. LocalAsset(
  178. this.id,
  179. this.path,
  180. );
  181. }
  182. class LocalAssetEntity {
  183. final AssetEntity entity;
  184. final String path;
  185. LocalAssetEntity(this.entity, this.path);
  186. }