file_sync_util.dart 6.3 KB

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