From e6f1a27f618492d153961f30210e1f3f54c27e42 Mon Sep 17 00:00:00 2001 From: three Date: Mon, 29 Jan 2024 23:39:22 +0800 Subject: [PATCH] improve photo backup and fix crashes --- Pod/Classes/SeafConnection.h | 12 +- Pod/Classes/SeafConnection.m | 292 ++----------- Pod/Classes/SeafDataTaskManager.h | 4 +- Pod/Classes/SeafDataTaskManager.m | 11 +- Pod/Classes/SeafPhotoBackupTool.h | 49 +++ Pod/Classes/SeafPhotoBackupTool.m | 395 ++++++++++++++++++ Pod/Classes/SeafTaskQueue.h | 2 +- Pod/Classes/SeafTaskQueue.m | 17 +- Pod/Classes/SeafUploadFile.h | 2 +- Pod/Classes/SeafUploadFile.m | 12 +- seafile/SeafFileViewController.m | 4 +- seafile/SeafSettingsViewController.m | 31 +- seafile/SeafSyncInfoViewController.m | 3 +- .../en.lproj/Localizable.strings | Bin 50234 -> 50360 bytes .../zh-Hans.lproj/Localizable.strings | 2 + 15 files changed, 524 insertions(+), 312 deletions(-) create mode 100644 Pod/Classes/SeafPhotoBackupTool.h create mode 100644 Pod/Classes/SeafPhotoBackupTool.m diff --git a/Pod/Classes/SeafConnection.h b/Pod/Classes/SeafConnection.h index 346d6c24..c005e632 100644 --- a/Pod/Classes/SeafConnection.h +++ b/Pod/Classes/SeafConnection.h @@ -10,6 +10,7 @@ #import "AFNetworking.h" #import "SeafCacheProvider.h" #import "SeafBase.h" +#import "SeafPhotoBackupTool.h" #define HTTP_ERR_UNAUTHORIZED 401 #define HTTP_ERR_LOGIN_INCORRECT_PASSWORD 400 @@ -39,10 +40,6 @@ typedef void (^CompletionBlock)(BOOL success, NSError * _Nullable error); BOOL SeafServerTrustIsValid(SecTrustRef _Nonnull serverTrust); -@protocol SeafPhotoSyncWatcherDelegate -- (void)photoSyncChanged:(long)remain; -@end - @protocol SeafLoginDelegate - (void)loginSuccess:(SeafConnection *_Nonnull)connection; - (void)loginFailed:(SeafConnection *_Nonnull)connection response:(NSHTTPURLResponse *_Nonnull)response error:(NSError *_Nullable)error; @@ -85,7 +82,8 @@ BOOL SeafServerTrustIsValid(SecTrustRef _Nonnull serverTrust); @property (readwrite, nonatomic, getter=isAutoSync) BOOL autoSync; @property (readwrite, nonatomic, getter=isVideoSync) BOOL videoSync; @property (readwrite, nonatomic, getter=isBackgroundSync) BOOL backgroundSync; -@property (assign, nonatomic) BOOL uploadHeicEnabled; +@property (readwrite, nonatomic, getter=isFirstTimeSync) BOOL firstTimeSync; +@property (assign, nonatomic, getter=isUploadHeicEnabled) BOOL uploadHeicEnabled; @property (readwrite, nonatomic) NSString * _Nullable autoSyncRepo; @@ -94,11 +92,12 @@ BOOL SeafServerTrustIsValid(SecTrustRef _Nonnull serverTrust); @property (readwrite, nonatomic) BOOL touchIdEnabled; @property (readonly) NSURLCredential *_Nullable clientCred; -@property (weak) id _Nullable photSyncWatcher; @property (readonly) BOOL inAutoSync; @property (readonly) NSString *_Nullable avatar; +@property (nonatomic, strong) SeafPhotoBackupTool * _Nullable photoBackup; + - (id _Nonnull)initWithUrl:(NSString *_Nonnull)url cacheProvider:(id _Nullable )cacheProvider; - (id _Nonnull)initWithUrl:(NSString * _Nonnull)url cacheProvider:(id _Nullable )cacheProvider username:(NSString * _Nonnull)username ; @@ -181,6 +180,7 @@ BOOL SeafServerTrustIsValid(SecTrustRef _Nonnull serverTrust); - (void)checkAutoSync; - (NSUInteger)photosInSyncing; +- (BOOL)isCheckingPhotoLibrary; - (void)checkSyncDst:(SeafDir *_Nonnull)dir; - (void)photosDidChange:(NSNotification *_Nullable)note; diff --git a/Pod/Classes/SeafConnection.m b/Pod/Classes/SeafConnection.m index fe63798e..56a53e88 100644 --- a/Pod/Classes/SeafConnection.m +++ b/Pod/Classes/SeafConnection.m @@ -98,11 +98,8 @@ @interface SeafConnection () @property NSDate *avatarLastUpdate; @property NSMutableDictionary *settings; -@property BOOL inCheckPhotoss; @property BOOL inCheckCert; -@property NSMutableArray *photosArray; -@property NSMutableArray *uploadingArray; @property SeafDir *syncDir; @property (readonly) NSString *localUploadDir; @property (readonly) id cacheProvider; @@ -110,9 +107,6 @@ @interface SeafConnection () @property (readonly) NSString *platformVersion; @property (readonly) NSString *tagDataKey; -@property (readwrite, nonatomic, getter=isFirstTimeSync) BOOL firstTimeSync; -@property (nonatomic, strong) dispatch_queue_t photoCheckQueue; - @end @implementation SeafConnection @@ -138,6 +132,9 @@ - (id)initWithUrl:(NSString *)url cacheProvider:(id)cacheProv _avatarLastUpdate = [NSDate dateWithTimeIntervalSince1970:0]; _syncDir = nil; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; +#ifdef Debug +// configuration.protocolClasses = @[NSClassFromString(@"SeafURLProtocol")]; +#endif //configuration.TLSMaximumSupportedProtocol = kTLSProtocol12; configuration.TLSMinimumSupportedProtocol = kTLSProtocol1; _sessionMgr = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:self.address] sessionConfiguration:configuration]; @@ -196,6 +193,7 @@ - (id)initWithUrl:(NSString *)url cacheProvider:(id)cacheProv [self clearRepoPasswords]; } [_rootFolder loadContent:NO]; + self.photoBackup = [[SeafPhotoBackupTool alloc] initWithConnection:self andLocalUploadDir:self.localUploadDir]; return self; } @@ -369,7 +367,7 @@ - (void)setVideoSync:(BOOL)videoSync if (self.isVideoSync == videoSync) return; [self setAttribute:[NSNumber numberWithBool:videoSync] forKey:@"videoSync"]; if (!videoSync) { - [self clearUploadingVideos]; + [self.photoBackup clearUploadingVideos]; } else { [self checkPhotos:true]; } @@ -393,7 +391,7 @@ - (void)setFirstTimeSync:(BOOL)firstTimeSync [self setAttribute:[NSNumber numberWithBool:firstTimeSync] forKey:@"firstTimeSync"]; } -- (BOOL)uploadHeicEnabled { +- (BOOL)isUploadHeicEnabled { return [[self getAttribute:@"uploadHeicEnabled"] booleanValue:false]; } @@ -1114,70 +1112,6 @@ - (void)saveCertificate:(NSURLProtectionSpace *)protectionSpace return _requestSerializer; } -- (void)pickPhotosForUpload -{ - SeafDir *dir = _syncDir; - if (!_inAutoSync || !dir || !self.photosArray || self.photosArray.count == 0) return; - if (self.wifiOnly && ![[AFNetworkReachabilityManager sharedManager] isReachableViaWiFi]) { - Debug("wifiOnly=%d, isReachableViaWiFi=%d, for server %@", self.wifiOnly, [[AFNetworkReachabilityManager sharedManager] isReachableViaWiFi], _address); - return; - } - - Debug("Current %u, %u photos need to upload, dir=%@", (unsigned)self.photosArray.count, (unsigned)self.uploadingArray.count, dir.path); - - int count = 0; - while (_uploadingArray.count < 5 && count++ < 5) { - NSString *localIdentifier = [self popUploadPhotoIdentifier]; - if (!localIdentifier) break; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - //Crash: Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d - PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; - PHAsset *asset = [result firstObject]; - if (asset) { - SeafPhotoAsset *photoAsset = [[SeafPhotoAsset alloc] initWithAsset:asset isCompress:!self.uploadHeicEnabled]; - - NSString *path = [self.localUploadDir stringByAppendingPathComponent:photoAsset.name]; - SeafUploadFile *file = [[SeafUploadFile alloc] initWithPath:path]; - file.retryable = false; - file.autoSync = true; - file.overwrite = true; - [file setPHAsset:asset url:photoAsset.ALAssetURL]; - file.udir = dir; - [file setCompletionBlock:^(SeafUploadFile *file, NSString *oid, NSError *error) { - [self autoSyncFileUploadComplete:file error:error]; - }]; - - Debug("Add file %@ to upload list: %@ current %u %u", photoAsset.name, dir.path, (unsigned)self.photosArray.count, (unsigned)self.uploadingArray.count); - [SeafDataTaskManager.sharedObject addUploadTask:file]; - } else { - [[SeafRealmManager shared] deletePhotoWithIdentifier:[self.accountIdentifier stringByAppendingString:localIdentifier] forAccount:self.accountIdentifier]; - } - }); - } - if (self.photosArray.count == 0) { - Debug("Force check if there are new photos after all synced."); - [self checkPhotos:true]; - } -} - -- (void)autoSyncFileUploadComplete:(SeafUploadFile *)ufile error:(NSError *)error -{ - if (!error) { - [self pickPhotosForUpload]; - [self setPhotoUploadedIdentifier:ufile.assetIdentifier]; - [self removeUploadingPhoto:ufile.assetIdentifier]; - Debug("Autosync file %@ %@, remain %u %u", ufile.name, ufile.assetURL, (unsigned)_photosArray.count, (unsigned)_uploadingArray.count); - - if (_photSyncWatcher) [_photSyncWatcher photoSyncChanged:self.photosInSyncing]; - } else { - Warning("Failed to upload photo %@: %@", ufile.name, error); - // Add photo to the end of queue - [self removeUploadingPhoto:ufile.assetIdentifier]; - [self addUploadPhoto:ufile.assetIdentifier]; - } -} - - (NSUInteger)autoSyncedNum { return [[SeafRealmManager shared] numOfCachedPhotosWhithAccount:self.accountIdentifier]; @@ -1185,51 +1119,8 @@ - (NSUInteger)autoSyncedNum - (void)resetUploadedPhotos { - _uploadingArray = [[NSMutableArray alloc] init]; [self clearCache:ENTITY_UPLOAD_PHOTO]; - [[SeafRealmManager shared] clearAllCachedPhotosInAccount:self.accountIdentifier]; -} - -- (BOOL)IsPhotoUploading:(SeafPhotoAsset *)asset { - if (!asset) { - return false; - } - @synchronized(_photosArray) { - if ([_photosArray containsObject:asset.localIdentifier]) return true; - } - @synchronized(_uploadingArray) { - if ([_uploadingArray containsObject:asset.localIdentifier]) return true; - } - return false; -} - -- (void)addUploadingPhotoIdentifier:(NSString *)localIdentifier { - @synchronized(_uploadingArray) { - [_uploadingArray addObject:localIdentifier]; - } -} - -- (void)removeUploadingPhoto:(NSString *)localIdentifier { - @synchronized(_uploadingArray) { - [_uploadingArray removeObject:localIdentifier]; - } -} - -- (void)addUploadPhoto:(NSString *)localIdentifier { - @synchronized(_photosArray) { - [_photosArray addObject:localIdentifier]; - } -} - -- (NSString *)popUploadPhotoIdentifier{ - @synchronized(self.photosArray) { - if (!self.photosArray || self.photosArray.count == 0) return nil; - NSString *localIdentifier = self.photosArray.firstObject; - [self addUploadingPhotoIdentifier:localIdentifier]; - [self.photosArray removeObject:localIdentifier]; - Debug("Picked photo identifier: %@ remain: %u %u", localIdentifier, (unsigned)_photosArray.count, (unsigned)_uploadingArray.count); - return localIdentifier; - } + [self.photoBackup resetUploadedPhotos]; } - (void)firstTimeSyncUpdateSyncedPhotos:(SeafDir *)uploaddir @@ -1239,113 +1130,7 @@ - (void)firstTimeSyncUpdateSyncedPhotos:(SeafDir *)uploaddir - (void)checkPhotos:(BOOL)force { - if (self.photoCheckQueue == nil) { - self.photoCheckQueue = dispatch_queue_create("com.seafile.checkPhotos", DISPATCH_QUEUE_CONCURRENT); - } - dispatch_async(self.photoCheckQueue, ^{ - [self backGroundCheckPhotos:[NSNumber numberWithBool:force]]; - }); -} - -- (void)backGroundCheckPhotos:(NSNumber *)forceNumber { - bool force = [forceNumber boolValue]; - SeafDir *uploadDir = _syncDir; - bool shouldSkip = !_inAutoSync || (self.firstTimeSync && !uploadDir); - if (shouldSkip) { - return; - } - - if (force || [self photosInSyncing] == 0) { - NSArray *photos = [self filterOutUploadedPhotos]; - [photos enumerateObjectsUsingBlock:^(SeafPhotoAsset *photoAsset, NSUInteger idx, BOOL * _Nonnull stop) { - if (![self IsPhotoUploaded:photoAsset] && ![self IsPhotoUploading:photoAsset]) { - [self addUploadPhoto:photoAsset.localIdentifier]; - } - }]; - - long num = [[SeafRealmManager shared] numOfCachedPhotosWhithAccount:self.accountIdentifier]; - Debug("Filter out %ld photos, cached : %ld photos", (long)photos.count, num); - - [self checkPhotosNeedUploadInRealm]; - } - - if (self.firstTimeSync) { - self.firstTimeSync = false; - } - - Debug("GroupAll Total %ld photos need to upload: %@", (long)_photosArray.count, _address); - - if (_photSyncWatcher) [_photSyncWatcher photoSyncChanged:self.photosInSyncing]; - _inCheckPhotoss = false; - - [self pickPhotosForUpload]; -} - -- (void)checkPhotosNeedUploadInRealm { - NSArray *array = [[SeafRealmManager shared] getNeedUploadPhotosWithAccount:self.accountIdentifier]; - if (array == nil || array.count == 0) { - return; - } - [array enumerateObjectsUsingBlock:^(NSString *url, NSUInteger idx, BOOL * _Nonnull stop) { - NSString *localIdentifier = [url stringByReplacingOccurrencesOfString:self.accountIdentifier withString:@""]; - if (localIdentifier != nil && ![_photosArray containsObject:localIdentifier]) { - [self addUploadPhoto:localIdentifier]; - } - }]; -} - -- (NSArray *)filterOutUploadedPhotos { - PHFetchResult *result = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil]; - - NSPredicate *predicate = [self buildAutoSyncPredicte]; - if (!predicate) { - return nil; - } - - @synchronized(self) { - if (_inCheckPhotoss) { - return nil; - } - _inCheckPhotoss = true; - } - - __block NSMutableArray *photos = [[NSMutableArray alloc] init]; - - PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init]; - fetchOptions.predicate = predicate; - PHAssetCollection *collection = result.firstObject; - PHFetchResult *assets = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions]; - - [assets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL * _Nonnull stop) { - SeafPhotoAsset *photoAsset = [[SeafPhotoAsset alloc] initWithAsset:asset isCompress:!self.uploadHeicEnabled]; - if (photoAsset.name == nil) { - return; - } - [self saveNeedUploadPhotoToLocalWithAssetIdentifier:asset.localIdentifier]; - if (self.firstTimeSync) { - if ([self.syncDir nameExist:photoAsset.name]) { - [self setPhotoUploadedIdentifier:asset.localIdentifier]; - Debug("First time sync, skip file %@(%@) which has already been uploaded", photoAsset.name, photoAsset.localIdentifier); - return; - } - } - [photos addObject:photoAsset]; - }]; - - return photos; -} - -- (NSPredicate *)buildAutoSyncPredicte { - NSPredicate *predicate = nil; - NSPredicate *predicateImage = [NSPredicate predicateWithFormat:@"mediaType == %i", PHAssetMediaTypeImage]; - NSPredicate *predicateVideo = [NSPredicate predicateWithFormat:@"mediaType == %i", PHAssetMediaTypeVideo]; - if (self.isAutoSync) { - predicate = predicateImage; - } - if (self.isAutoSync && self.isVideoSync) { - predicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[predicateImage, predicateVideo]]; - } - return predicate; + [self.photoBackup checkPhotos:force]; } - (SeafDir *)getSubdirUnderDir:(SeafDir *)dir withName:(NSString *)name @@ -1370,23 +1155,32 @@ - (void)checkSyncDst:(SeafDir *)dir - (NSUInteger)photosInSyncing { - return [[SeafRealmManager shared] numOfCachedPhotosWithStatus:@"false" forAccount:self.accountIdentifier]; + return self.photoBackup.photosInSyncing; +} + +- (BOOL)isCheckingPhotoLibrary { + return self.photoBackup.inCheckPhotoss && self.isFirstTimeSync; } - (void)updateUploadDir:(SeafDir *)dir { BOOL changed = !_syncDir || ![_syncDir.repoId isEqualToString:dir.repoId] || ![_syncDir.path isEqualToString:dir.path]; - if (changed) + if (changed) { _syncDir = dir; - Debug("%ld photos remain, syncdir: %@ %@", (long)self.photosArray.count, _syncDir.repoId, _syncDir.name); + _photoBackup.syncDir = dir; + } + + Debug("%ld photos remain, syncdir: %@ %@", (long)self.photoBackup.photosArray.count, _syncDir.repoId, _syncDir.name); [self checkPhotos:true]; } - (void)checkPhotosUploadDir:(CompletionBlock)handler { + CompletionBlock completionHandler = ^(BOOL success, NSError * _Nullable error){ if (!success) { self.syncDir = nil; + self.photoBackup.syncDir = nil; } if (handler) { handler(success, error); @@ -1419,7 +1213,7 @@ - (void)photosDidChange:(NSNotification *)note { if (!_inAutoSync) return; - Debug("photos changed %d for server %@, current: %u %u, _syncDir:%@", _inAutoSync, _address, (unsigned)_photosArray.count, (unsigned)_uploadingArray.count, _syncDir); + Debug("photos changed %d for server %@, current: %u %u, _syncDir:%@", _inAutoSync, _address, (unsigned)self.photoBackup.photosArray.count, (unsigned)self.photoBackup.uploadingArray.count, _syncDir); if (!_syncDir) { Warning("Sync dir not exists, create."); [self checkPhotosUploadDir:nil]; @@ -1444,18 +1238,16 @@ - (void)checkAutoSync if (_inAutoSync != value) { if (value) { Debug("Start auto sync for server %@", _address); - _photosArray = [[NSMutableArray alloc] init]; - _uploadingArray = [[NSMutableArray alloc] init];; + [self.photoBackup prepareForBackup]; } else { Debug("Stop auto Sync for server %@", _address); - _photosArray = nil; - _inCheckPhotoss = false; - _uploadingArray = nil; + [self.photoBackup resetAll]; [SeafDataTaskManager.sharedObject cancelAutoSyncTasks:self]; [self clearUploadCache]; } } _inAutoSync = value; + self.photoBackup.inAutoSync = _inAutoSync; if (_inAutoSync) { Debug("start auto sync, check photos for server %@", _address); [self checkPhotosUploadDir:^(BOOL success, NSError * _Nullable error) { @@ -1543,13 +1335,6 @@ - (void)removeVideosFromArray:(NSMutableArray *)arr { } } -- (void)clearUploadingVideos -{ - [SeafDataTaskManager.sharedObject cancelAutoSyncVideoTasks:self]; - [self removeVideosFromArray:_photosArray]; - [self removeVideosFromArray:_uploadingArray]; -} - - (void)downloadDir:(SeafDir *)dir { [dir loadContentSuccess:^(SeafDir *dir) { @@ -1653,35 +1438,6 @@ - (id)getCachedStarredFiles return JSON; } -- (void)saveNeedUploadPhotoToLocalWithAssetIdentifier:(NSString *)assetIdentifier { - NSString *key = [self.accountIdentifier stringByAppendingString:assetIdentifier]; - [[SeafRealmManager shared] savePhotoWithIdentifier:key forAccount:self.accountIdentifier andStatus:@"false"]; -} - -- (void)setPhotoUploadedIdentifier:(NSString *)localIdentifier { - NSString *key = [self.accountIdentifier stringByAppendingString:localIdentifier]; - [[SeafRealmManager shared] updateCachePhotoWithIdentifier:key forAccount:self.accountIdentifier andStatus:@"true"]; -} - -- (BOOL)IsPhotoUploaded:(SeafPhotoAsset *)asset { - NSInteger saveCount = 0; - if (asset.ALAssetURL && [asset.ALAssetURL respondsToSelector:NSSelectorFromString(@"absoluteString")] && asset.ALAssetURL.absoluteString) { - NSString *value = [self getCachedPhotoStatuWithIdentifier:[self.accountIdentifier stringByAppendingString:asset.ALAssetURL.absoluteString]]; - if (value != nil) { - saveCount ++; - } - } - NSString *identifier = [self getCachedPhotoStatuWithIdentifier:[self.accountIdentifier stringByAppendingString:asset.localIdentifier]]; - if (identifier != nil && [identifier isEqualToString:@"true"]) { - saveCount ++; - } - return saveCount > 0; -} - -- (NSString *)getCachedPhotoStatuWithIdentifier:(NSString *)identifier { - return [[SeafRealmManager shared] getPhotoStatusWithIdentifier:identifier forAccount:self.accountIdentifier]; -} - - (void)clearAccountCache { [SeafStorage.sharedObject clearCache]; diff --git a/Pod/Classes/SeafDataTaskManager.h b/Pod/Classes/SeafDataTaskManager.h index 644df05f..a35cf8eb 100644 --- a/Pod/Classes/SeafDataTaskManager.h +++ b/Pod/Classes/SeafDataTaskManager.h @@ -28,7 +28,7 @@ typedef void(^DownLoadFinshBlock)(id _Nonnull task); + (SeafDataTaskManager * _Nonnull)sharedObject; - (void)addFileDownloadTask:(SeafFile * _Nonnull)dfile; -- (void)addUploadTask:(SeafUploadFile * _Nonnull)ufile; +- (BOOL)addUploadTask:(SeafUploadFile * _Nonnull)ufile; - (void)addAvatarTask:(SeafAvatar * _Nonnull)avatar; - (void)addThumbTask:(SeafThumb * _Nonnull)thumb; - (void)removeUploadTask:(SeafUploadFile * _Nonnull)ufile forAccount:(SeafConnection * _Nonnull)conn; @@ -55,7 +55,7 @@ typedef void(^DownLoadFinshBlock)(id _Nonnull task); @property (nonatomic, strong) SeafTaskQueue * _Nonnull uploadQueue; - (void)addFileDownloadTask:(SeafFile * _Nonnull)dfile; -- (void)addUploadTask:(SeafUploadFile * _Nonnull)ufile; +- (BOOL)addUploadTask:(SeafUploadFile * _Nonnull)ufile; - (void)addAvatarTask:(SeafAvatar * _Nonnull)avatar; - (void)addThumbTask:(SeafThumb * _Nonnull)thumb; diff --git a/Pod/Classes/SeafDataTaskManager.m b/Pod/Classes/SeafDataTaskManager.m index af5ddfe7..290f45ea 100644 --- a/Pod/Classes/SeafDataTaskManager.m +++ b/Pod/Classes/SeafDataTaskManager.m @@ -66,15 +66,16 @@ - (void)tick:(id)userInfo { } #pragma mark- upload -- (void)addUploadTask:(SeafUploadFile *)file { +- (BOOL)addUploadTask:(SeafUploadFile *)file { SeafAccountTaskQueue *accountQueue = [self getAccountQueueWithIndentifier:file.accountIdentifier]; - [accountQueue addUploadTask:file]; - if (file.retryable) { + BOOL res = [accountQueue addUploadTask:file]; + if (res && file.retryable) { [self saveUploadFileToTaskStorage:file]; } if (self.trySyncBlock) { self.trySyncBlock(file); } + return res; } - (void)removeUploadTask:(SeafUploadFile * _Nonnull)ufile forAccount:(SeafConnection * _Nonnull)conn @@ -389,8 +390,8 @@ - (void)addAvatarTask:(SeafAvatar * _Nonnull)avatar { [self.avatarQueue addTask:avatar]; } -- (void)addUploadTask:(SeafUploadFile * _Nonnull)ufile { - [self.uploadQueue addTask:ufile]; +- (BOOL)addUploadTask:(SeafUploadFile * _Nonnull)ufile { + return [self.uploadQueue addTask:ufile]; } - (void)removeFileDownloadTask:(SeafFile * _Nonnull)dfile { diff --git a/Pod/Classes/SeafPhotoBackupTool.h b/Pod/Classes/SeafPhotoBackupTool.h new file mode 100644 index 00000000..a41c59d6 --- /dev/null +++ b/Pod/Classes/SeafPhotoBackupTool.h @@ -0,0 +1,49 @@ +// +// SeafPhotoBackupTool.h +// Seafile +// +// Created by three on 2024/1/25. +// + +#import + +@class SeafConnection; +@class SeafDir; + +@protocol SeafPhotoSyncWatcherDelegate +- (void)photoSyncChanged:(long)remain; +@end + +@interface SeafPhotoBackupTool : NSObject + +@property (nonatomic, strong) SeafConnection * _Nonnull connection; + +@property (nonatomic, strong) SeafDir * _Nullable syncDir; + +@property (weak) id _Nullable photSyncWatcher; + +@property (nonatomic, strong) NSMutableArray * _Nullable photosArray; + +@property (nonatomic, strong) NSMutableArray * _Nullable uploadingArray; + +@property (nonatomic, assign) BOOL inAutoSync; + +@property (nonatomic, assign) BOOL inCheckPhotoss; + +- (instancetype _Nonnull )initWithConnection:(SeafConnection * _Nonnull)connection andLocalUploadDir:(NSString * _Nonnull)localUploadDir; + +- (void)checkPhotos:(BOOL)force; + +- (void)prepareForBackup; + +- (void)resetAll; + +- (NSUInteger)photosInSyncing; + +- (void)resetUploadedPhotos; + +- (void)clearUploadingVideos; + +- (NSUInteger)photosInUploadingArray; + +@end diff --git a/Pod/Classes/SeafPhotoBackupTool.m b/Pod/Classes/SeafPhotoBackupTool.m new file mode 100644 index 00000000..d04bd35e --- /dev/null +++ b/Pod/Classes/SeafPhotoBackupTool.m @@ -0,0 +1,395 @@ +// +// SeafPhotoBackupTool.m +// Seafile +// +// Created by three on 2024/1/25. +// + +#import "SeafPhotoBackupTool.h" +#import "SeafRealmManager.h" +#import "SeafPhotoAsset.h" +#import "Debug.h" +#import "SeafConnection.h" +#import "AFNetworkReachabilityManager.h" +#import "SeafDir.h" +#import "SeafUploadFile.h" +#import "SeafDataTaskManager.h" + +#define DEFAULT_UPLOADINGARRAY_INTERVAL 10*60 // 10 min + +@interface SeafPhotoBackupTool() + +@property (nonatomic, copy) NSString * _Nonnull accountIdentifier; + +@property (nonatomic, copy) NSString * _Nonnull localUploadDir; + +@property (nonatomic, strong) dispatch_queue_t photoCheckQueue; + +@property (nonatomic, strong) NSMutableDictionary *uploadingDict; + +@end + +@implementation SeafPhotoBackupTool + +- (instancetype _Nonnull )initWithConnection:(SeafConnection * _Nonnull)connection andLocalUploadDir:(NSString * _Nonnull)localUploadDir { + self = [super init]; + if (self) { + self.connection = connection; + self.accountIdentifier = connection.accountIdentifier; + self.localUploadDir = localUploadDir; + } + return self; +} + +- (instancetype)init { + self = [super init]; + if (self) { + self.inAutoSync = false; + self.inCheckPhotoss = false; + } + return self; +} + +- (void)prepareForBackup { + _photosArray = [[NSMutableArray alloc] init]; + _uploadingArray = [[NSMutableArray alloc] init]; + _uploadingDict = [[NSMutableDictionary alloc] init]; +} + +- (void)resetAll { + _photosArray = nil; + _inCheckPhotoss = false; + _uploadingArray = nil; + _uploadingDict = nil; +} + +- (void)checkPhotos:(BOOL)force { + if (self.photoCheckQueue == nil) { + self.photoCheckQueue = dispatch_queue_create("com.seafile.checkPhotos", DISPATCH_QUEUE_CONCURRENT); + } + dispatch_async(self.photoCheckQueue, ^{ + [self backGroundCheckPhotos:[NSNumber numberWithBool:force]]; + }); +} + +- (void)backGroundCheckPhotos:(NSNumber *)forceNumber { + bool force = [forceNumber boolValue]; + SeafDir *uploadDir = _syncDir; + bool shouldSkip = !_inAutoSync || (_connection.firstTimeSync && !uploadDir); + if (shouldSkip) { + return; + } + + if (force || [self photosInSyncing] == 0) { + NSArray *photos = [self filterOutUploadedPhotos]; + [photos enumerateObjectsUsingBlock:^(SeafPhotoAsset *photoAsset, NSUInteger idx, BOOL * _Nonnull stop) { + if (![self IsPhotoUploaded:photoAsset] && ![self IsPhotoUploading:photoAsset]) { + [self addUploadPhoto:photoAsset.localIdentifier]; + } + }]; + + long num = [[SeafRealmManager shared] numOfCachedPhotosWhithAccount:self.accountIdentifier]; + Debug("Filter out %ld photos, cached : %ld photos", (long)photos.count, num); + + [self checkPhotosNeedUploadInRealm]; + } + + if (_connection.firstTimeSync) { + _connection.firstTimeSync = false; + } + + Debug("GroupAll Total %ld photos need to upload: %@", (long)_photosArray.count, _connection.address); + if (_photSyncWatcher) [_photSyncWatcher photoSyncChanged:self.photosInSyncing]; + + _inCheckPhotoss = false; + [self pickPhotosForUpload]; +} + +- (void)checkPhotosNeedUploadInRealm { + NSArray *array = [[SeafRealmManager shared] getNeedUploadPhotosWithAccount:self.accountIdentifier]; + if (array == nil || array.count == 0) { + return; + } + NSArray *copyArray = [NSArray arrayWithArray:_photosArray]; + [array enumerateObjectsUsingBlock:^(NSString *url, NSUInteger idx, BOOL * _Nonnull stop) { + NSString *localIdentifier = [url stringByReplacingOccurrencesOfString:self.accountIdentifier withString:@""]; + if (localIdentifier != nil && ![copyArray containsObject:localIdentifier]) { + [self addUploadPhoto:localIdentifier]; + } + }]; +} + +- (void)pickPhotosForUpload { + SeafDir *dir = _syncDir; + if (!_inAutoSync || !dir || !self.photosArray || self.photosArray.count == 0) return; + if (_connection.wifiOnly && ![[AFNetworkReachabilityManager sharedManager] isReachableViaWiFi]) { + Debug("wifiOnly=%d, isReachableViaWiFi=%d, for server %@", _connection.wifiOnly, [[AFNetworkReachabilityManager sharedManager] isReachableViaWiFi], _connection.address); + return; + } + + Debug("Current %u, %u photos need to upload, dir=%@", (unsigned)self.photosArray.count, (unsigned)self.uploadingArray.count, dir.path); + + [self checkAndRemoveFromUploadingArray]; + int count = 0; + while (_uploadingArray.count < 5 && count++ < 5) { + NSString *localIdentifier = [self popUploadPhotoIdentifier]; + if (!localIdentifier) break; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + //Crash: Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d + PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; + PHAsset *asset = [result firstObject]; + if (asset) { + SeafPhotoAsset *photoAsset = [[SeafPhotoAsset alloc] initWithAsset:asset isCompress:!self.connection.isUploadHeicEnabled]; + + NSString *path = [self.localUploadDir stringByAppendingPathComponent:photoAsset.name]; + SeafUploadFile *file = [[SeafUploadFile alloc] initWithPath:path]; + file.retryable = false; + file.autoSync = true; + file.overwrite = true; + [file setPHAsset:asset url:photoAsset.ALAssetURL]; + file.udir = dir; + [file setCompletionBlock:^(SeafUploadFile *file, NSString *oid, NSError *error) { + [self autoSyncFileUploadComplete:file error:error]; + }]; + + Debug("Add file %@ to upload list: %@ current %u %u", photoAsset.name, dir.path, (unsigned)self.photosArray.count, (unsigned)self.uploadingArray.count); + BOOL res = [SeafDataTaskManager.sharedObject addUploadTask:file]; + if (!res) { + [self removeUploadingPhoto:localIdentifier]; + [self removeFromUploadingDictWith:localIdentifier]; + file = nil; + } + } else { + [self removeUploadingPhoto:localIdentifier]; + [self removeFromUploadingDictWith:localIdentifier]; + [[SeafRealmManager shared] deletePhotoWithIdentifier:[self.accountIdentifier stringByAppendingString:localIdentifier] forAccount:self.accountIdentifier]; + } + }); + } + + if (self.photosArray.count == 0) { + Debug("Force check if there are new photos after all synced."); + [self checkPhotos:true]; + } +} + +- (NSString *)popUploadPhotoIdentifier { + @synchronized(self.photosArray) { + if (!self.photosArray || self.photosArray.count == 0) return nil; + NSString *localIdentifier = self.photosArray.firstObject; + [self addUploadingPhotoIdentifier:localIdentifier]; + [self.photosArray removeObject:localIdentifier]; + [self updateUploadingDictWith:localIdentifier]; + Debug("Picked photo identifier: %@ remain: %u %u", localIdentifier, (unsigned)_photosArray.count, (unsigned)_uploadingArray.count); + return localIdentifier; + } +} + +- (void)autoSyncFileUploadComplete:(SeafUploadFile *)ufile error:(NSError *)error { + if (!error) { + [self setPhotoUploadedIdentifier:ufile.assetIdentifier]; + [self removeUploadingPhoto:ufile.assetIdentifier]; + [self removeFromUploadingDictWith:ufile.assetIdentifier]; + Debug("Autosync file %@ %@, remain %u %u", ufile.name, ufile.assetURL, (unsigned)_photosArray.count, (unsigned)_uploadingArray.count); + } else { + Warning("Failed to upload photo %@: %@", ufile.name, error); + // Add photo to the end of queue + [self removeUploadingPhoto:ufile.assetIdentifier]; + [self addUploadPhoto:ufile.assetIdentifier]; + [self removeFromUploadingDictWith:ufile.assetIdentifier]; + } + if (_photSyncWatcher) [_photSyncWatcher photoSyncChanged:self.photosInSyncing]; + [self pickPhotosForUpload]; +} + +- (NSArray *)filterOutUploadedPhotos { + PHFetchResult *result = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil]; + + NSPredicate *predicate = [self buildAutoSyncPredicte]; + if (!predicate) { + return nil; + } + + @synchronized(self) { + if (_inCheckPhotoss) { + return nil; + } + _inCheckPhotoss = true; + } + + __block NSMutableArray *photos = [[NSMutableArray alloc] init]; + + PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init]; + fetchOptions.predicate = predicate; + PHAssetCollection *collection = result.firstObject; + PHFetchResult *assets = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions]; + + [assets enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL * _Nonnull stop) { + SeafPhotoAsset *photoAsset = [[SeafPhotoAsset alloc] initWithAsset:asset isCompress:!self.connection.isUploadHeicEnabled]; + if (photoAsset.name == nil) { + return; + } + [self saveNeedUploadPhotoToLocalWithAssetIdentifier:asset.localIdentifier]; + if (self.connection.isFirstTimeSync) { + if ([self.syncDir nameExist:photoAsset.name]) { + [self setPhotoUploadedIdentifier:asset.localIdentifier]; + Debug("First time sync, skip file %@(%@) which has already been uploaded", photoAsset.name, photoAsset.localIdentifier); + return; + } + } + [photos addObject:photoAsset]; + }]; + + return photos; +} + +- (NSPredicate *)buildAutoSyncPredicte { + NSPredicate *predicate = nil; + NSPredicate *predicateImage = [NSPredicate predicateWithFormat:@"mediaType == %i", PHAssetMediaTypeImage]; + NSPredicate *predicateVideo = [NSPredicate predicateWithFormat:@"mediaType == %i", PHAssetMediaTypeVideo]; + if (_connection.isAutoSync) { + predicate = predicateImage; + } + if (_connection.isAutoSync && _connection.isVideoSync) { + predicate = [NSCompoundPredicate orPredicateWithSubpredicates:@[predicateImage, predicateVideo]]; + } + return predicate; +} + + +- (NSUInteger)photosInSyncing { + return [[SeafRealmManager shared] numOfCachedPhotosWithStatus:@"false" forAccount:self.accountIdentifier]; +} + +- (BOOL)IsPhotoUploaded:(SeafPhotoAsset *)asset { + NSInteger saveCount = 0; + if (asset.ALAssetURL && [asset.ALAssetURL respondsToSelector:NSSelectorFromString(@"absoluteString")] && asset.ALAssetURL.absoluteString) { + NSString *value = [self getCachedPhotoStatuWithIdentifier:[self.accountIdentifier stringByAppendingString:asset.ALAssetURL.absoluteString]]; + if (value != nil) { + saveCount ++; + } + } + NSString *identifier = [self getCachedPhotoStatuWithIdentifier:[self.accountIdentifier stringByAppendingString:asset.localIdentifier]]; + if (identifier != nil && [identifier isEqualToString:@"true"]) { + saveCount ++; + } + return saveCount > 0; +} + +- (BOOL)IsPhotoUploading:(SeafPhotoAsset *)asset { + if (!asset) { + return false; + } + @synchronized(_photosArray) { + if ([_photosArray containsObject:asset.localIdentifier]) return true; + } + @synchronized(_uploadingArray) { + if ([_uploadingArray containsObject:asset.localIdentifier]) return true; + } + return false; +} + +- (void)checkAndRemoveFromUploadingArray { + if (self.uploadingArray == nil || self.uploadingDict == nil) { + return; + } + if (self.uploadingArray == 0 && self.uploadingDict.count != 0) { + [self.uploadingDict removeAllObjects]; + return; + } + NSDictionary *copyDict = [NSDictionary dictionaryWithDictionary:self.uploadingDict]; + NSArray *copyArray = [NSArray arrayWithArray:self.uploadingArray]; + for (NSString *identifier in copyArray) { + if ([copyDict valueForKey:identifier]) { + NSTimeInterval t1 = [[copyDict valueForKey:identifier] doubleValue]; + NSTimeInterval cur = [[NSDate date] timeIntervalSince1970]; + if (cur - t1 > DEFAULT_UPLOADINGARRAY_INTERVAL) { + [self removeUploadingPhoto:identifier]; + [self addUploadPhoto:identifier]; + [self removeFromUploadingDictWith:identifier]; + } + } + } +} + +- (void)clearUploadingVideos { + [SeafDataTaskManager.sharedObject cancelAutoSyncVideoTasks:self.connection]; + [self removeVideosFromArray:_photosArray]; + [self removeVideosFromArray:_uploadingArray]; +} + +- (void)addUploadPhoto:(NSString *)localIdentifier { + @synchronized(_photosArray) { + [_photosArray addObject:localIdentifier]; + } +} + +- (void)addUploadingPhotoIdentifier:(NSString *)localIdentifier { + @synchronized(_uploadingArray) { + [_uploadingArray addObject:localIdentifier]; + } +} + +- (void)removeUploadingPhoto:(NSString *)localIdentifier { + @synchronized(_uploadingArray) { + [_uploadingArray removeObject:localIdentifier]; + } +} + +- (void)updateUploadingDictWith:(NSString *)identifier { + if (identifier) { + @synchronized (self) { + NSTimeInterval cur = [[NSDate date] timeIntervalSince1970]; + [self.uploadingDict setValue:[NSNumber numberWithDouble:cur] forKey:identifier]; + } + } +} + +- (void)removeFromUploadingDictWith:(NSString *)identifier { + if (identifier) { + @synchronized(self.uploadingDict) { + [self.uploadingDict removeObjectForKey:identifier]; + } + } +} + +- (void)removeVideosFromArray:(NSMutableArray *)arr { + if (arr.count == 0) + return; + @synchronized(self) { + NSMutableArray *videos = [[NSMutableArray alloc] init]; + for (NSURL *url in arr) { + if ([Utils isVideoExt:url.pathExtension]) + [videos addObject:url]; + } + [arr removeObjectsInArray:videos]; + } +} + +- (void)resetUploadedPhotos +{ + _uploadingArray = [[NSMutableArray alloc] init]; + [[SeafRealmManager shared] clearAllCachedPhotosInAccount:self.accountIdentifier]; +} + +- (NSUInteger)photosInUploadingArray { + return self.uploadingArray.count; +} + +#pragma mark- cache +- (void)saveNeedUploadPhotoToLocalWithAssetIdentifier:(NSString *)assetIdentifier { + NSString *key = [self.accountIdentifier stringByAppendingString:assetIdentifier]; + [[SeafRealmManager shared] savePhotoWithIdentifier:key forAccount:self.accountIdentifier andStatus:@"false"]; +} + +- (NSString *)getCachedPhotoStatuWithIdentifier:(NSString *)identifier { + return [[SeafRealmManager shared] getPhotoStatusWithIdentifier:identifier forAccount:self.accountIdentifier]; +} + +- (void)setPhotoUploadedIdentifier:(NSString *)localIdentifier { + NSString *key = [self.accountIdentifier stringByAppendingString:localIdentifier]; + [[SeafRealmManager shared] updateCachePhotoWithIdentifier:key forAccount:self.accountIdentifier andStatus:@"true"]; +} + +@end diff --git a/Pod/Classes/SeafTaskQueue.h b/Pod/Classes/SeafTaskQueue.h index d2ffcc1f..612fa84b 100644 --- a/Pod/Classes/SeafTaskQueue.h +++ b/Pod/Classes/SeafTaskQueue.h @@ -47,7 +47,7 @@ typedef void (^TaskProgressBlock)(id _Nonnull task, float progress); - (NSInteger)taskNumber; - (NSArray * _Nonnull)allTasks; -- (void)addTask:(id _Nonnull)task; // Add task if not exist +- (BOOL)addTask:(id _Nonnull)task; // Add task if not exist - (void)removeTask:(id _Nonnull)task;// Remove task, cancel if running - (void)clearTasks; //clear all tasks - (void)tick; //pick available tasks from tasks queue to activate untill it reaches queue concurrency. diff --git a/Pod/Classes/SeafTaskQueue.m b/Pod/Classes/SeafTaskQueue.m index 7dde4bde..f9e68a3f 100644 --- a/Pod/Classes/SeafTaskQueue.m +++ b/Pod/Classes/SeafTaskQueue.m @@ -19,6 +19,7 @@ @interface SeafTaskQueue () @property (nonatomic, copy) TaskCompleteBlock innerQueueTaskCompleteBlock; @property (nonatomic, copy) TaskProgressBlock innerQueueTaskProgressBlock; @property unsigned long failedCount; +@property (nonatomic, strong) dispatch_semaphore_t semaphore; @end @@ -30,6 +31,7 @@ - (instancetype)init { self.concurrency = DEFAULT_CONCURRENCY; self.attemptInterval = DEFAULT_ATTEMPT_INTERVAL; self.failedCount = 0; + self.semaphore = dispatch_semaphore_create(1); __weak typeof(self) weakSelf = self; self.innerQueueTaskCompleteBlock = ^(id task, BOOL result) { __strong __typeof(self) strongSelf = weakSelf; @@ -71,16 +73,20 @@ - (instancetype)init { return self; } -- (void)addTask:(id)task { +- (BOOL)addTask:(id)task { + BOOL res = YES; @synchronized (self.tasks) { if (task != nil && ![self.tasks containsObject:task] && ![self.ongoingTasks containsObject:task]) { task.lastFinishTimestamp = 0; task.retryCount = 0; [self.tasks addObject:task]; Debug("Added task %@: %ld", task.name, (unsigned long)self.tasks.count); + } else { + res = NO; } } [self tick]; + return res; } - (NSInteger)taskNumber { @@ -104,8 +110,9 @@ - (void)runTasks { if (![[AFNetworkReachabilityManager sharedManager] isReachable] || self.tasks.count == 0) { return; } - - while (self.ongoingTasks.count + self.failedCount < self.concurrency) { + + dispatch_semaphore_wait(self.semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))); + while (self.ongoingTasks.count < self.concurrency && self.failedCount < self.concurrency) { id task = [self pickTask]; if (!task) break; @synchronized (self.ongoingTasks) { @@ -113,6 +120,7 @@ - (void)runTasks { } [task run:self.innerQueueTaskCompleteBlock]; } + dispatch_semaphore_signal(self.semaphore); } - (id)pickTask { @@ -149,7 +157,8 @@ - (void)removeTask:(id)task { - (void)removOldCompletedTask { NSMutableArray *tempArray = [NSMutableArray array]; @synchronized (self.completedTasks) { - for (id task in self.completedTasks) { + NSArray *copyArray = [NSArray arrayWithArray:self.completedTasks]; + for (id task in copyArray) { //remove task finished more than 3 min if ([[NSDate date] timeIntervalSince1970] - task.lastFinishTimestamp > DEFAULT_COMPLELE_INTERVAL) { [tempArray addObject:task]; diff --git a/Pod/Classes/SeafUploadFile.h b/Pod/Classes/SeafUploadFile.h index 8340a054..830ae0d9 100644 --- a/Pod/Classes/SeafUploadFile.h +++ b/Pod/Classes/SeafUploadFile.h @@ -43,7 +43,7 @@ typedef void (^SeafUploadCompletionBlock)(SeafUploadFile *file, NSString *oid, N @property (nonatomic) id delegate; @property (nonatomic) SeafUploadCompletionBlock completionBlock; -@property (readwrite) SeafDir *udir; +@property (nonatomic, strong, readwrite) SeafDir *udir; @property (nonatomic, strong) UIImage *previewImage;//NSItemProvider previewImage @property (nonatomic, assign, getter=isStarred) BOOL starred; diff --git a/Pod/Classes/SeafUploadFile.m b/Pod/Classes/SeafUploadFile.m index 8d0a749c..98be41ba 100644 --- a/Pod/Classes/SeafUploadFile.m +++ b/Pod/Classes/SeafUploadFile.m @@ -117,7 +117,7 @@ - (BOOL)editable } - (BOOL)uploadHeic { - return self.udir->connection.uploadHeicEnabled; + return self.udir->connection.isUploadHeicEnabled; } - (NSString *)blockDir @@ -180,11 +180,6 @@ - (void)finishUpload:(BOOL)result oid:(NSString *)oid error:(NSError *)error self.task = nil; [self updateProgress:nil]; } - - if (_starred) { - NSString* rpath = [_udir.path stringByAppendingPathComponent:self.name]; - [_udir->connection setStarred:YES repo:_udir.repoId path:rpath]; - } self.rawblksurl = nil; self.commiturl = nil; @@ -198,6 +193,11 @@ - (void)finishUpload:(BOOL)result oid:(NSString *)oid error:(NSError *)error Debug("result=%d, name=%@, delegate=%@, oid=%@, err=%@\n", result, self.name, _delegate, oid, err); [self uploadComplete:oid error:err]; if (result) { + if (_starred && self.udir) { + NSString* rpath = [_udir.path stringByAppendingPathComponent:self.name]; + [_udir->connection setStarred:YES repo:_udir.repoId path:rpath]; + } + if (!_autoSync) { [Utils linkFileAtPath:self.lpath to:[SeafStorage.sharedObject documentPath:oid] error:nil]; // files.app menory limit 15MB, reSizeImage will use more than 15MB diff --git a/seafile/SeafFileViewController.m b/seafile/SeafFileViewController.m index 506c6642..a1459b1e 100644 --- a/seafile/SeafFileViewController.m +++ b/seafile/SeafFileViewController.m @@ -1609,7 +1609,7 @@ - (void)uploadPickedAssetsIdentifier:(NSArray *)identifiers overwrite:(BOOL)over NSMutableArray *files = [[NSMutableArray alloc] init]; NSString *uploadDir = [self.connection uniqueUploadDir]; NSMutableSet *nameSet = overwrite ? [NSMutableSet new] : [self getExistedNameSet]; - BOOL uploadHeicEnabled = self.connection.uploadHeicEnabled; + BOOL uploadHeicEnabled = self.connection.isUploadHeicEnabled; for (NSString *localIdentifier in identifiers) { PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil]; @@ -1652,7 +1652,7 @@ - (void)qb_imagePickerController:(QBImagePickerController *)imagePickerControlle NSSet *nameSet = [self getExistedNameSet]; NSMutableArray *identifiers = [[NSMutableArray alloc] init]; int duplicated = 0; - BOOL uploadHeicEnabled = self.connection.uploadHeicEnabled; + BOOL uploadHeicEnabled = self.connection.isUploadHeicEnabled; for (PHAsset *asset in assets) { SeafPhotoAsset *photoAsset = [[SeafPhotoAsset alloc] initWithAsset:asset isCompress:!uploadHeicEnabled]; if (photoAsset.localIdentifier) { diff --git a/seafile/SeafSettingsViewController.m b/seafile/SeafSettingsViewController.m index 8cffa77e..ea566060 100644 --- a/seafile/SeafSettingsViewController.m +++ b/seafile/SeafSettingsViewController.m @@ -415,7 +415,7 @@ - (void)configureView _localDecrySwitch.on = _connection.localDecryptionEnabled; _serverCell.detailTextLabel.text = [_connection.address trimUrl]; - self.enableUploadHeic.on = _connection.uploadHeicEnabled; + self.enableUploadHeic.on = _connection.isUploadHeicEnabled; [self updateSyncInfo]; @@ -448,7 +448,7 @@ - (void)updateAccountInfo - (void)setConnection:(SeafConnection *)connection { _connection = connection; - _connection.photSyncWatcher = self; + _connection.photoBackup.photSyncWatcher = self; [self.tableView reloadData]; [self updateAccountInfo]; } @@ -574,25 +574,24 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte if (section < SECTION_ACCOUNT || section > SECTION_LOGOUT) return nil; if (section == SECTION_CAMERA && _connection.inAutoSync) { - NSUInteger num = _connection.photosInSyncing; NSString *remainStr = @""; - if (num == 0) { - remainStr = NSLocalizedString(@"All photos synced", @"Seafile"); - } else if (num == 1) { - remainStr = NSLocalizedString(@"1 photo remain", @"Seafile"); + if (_connection.isCheckingPhotoLibrary) { + remainStr = NSLocalizedString(@"Prepare for backup", @"Seafile"); } else { - remainStr = [NSString stringWithFormat:NSLocalizedString(@"%ld photos remain", @"Seafile"), num]; - } - return [sectionNames[section] stringByAppendingFormat:@"\t %@", remainStr]; - } + NSUInteger num = _connection.photosInSyncing; + if (num == 0) { + remainStr = NSLocalizedString(@"All photos synced", @"Seafile"); + } else if (num == 1) { + remainStr = NSLocalizedString(@"1 photo remain", @"Seafile"); + } else { + remainStr = [NSString stringWithFormat:NSLocalizedString(@"%ld photos remain", @"Seafile"), num]; + } #if DEBUG - else if (section == SECTION_CAMERA) { - NSInteger downloadingNum = [[SeafDataTaskManager.sharedObject accountQueueForConnection:self.connection].fileQueue taskNumber]; - NSInteger uploadingNum = [[SeafDataTaskManager.sharedObject accountQueueForConnection:self.connection].uploadQueue taskNumber]; - NSString *remainStr = [NSString stringWithFormat:@" U:%lu D:%lu",(long)uploadingNum,(long)downloadingNum]; + remainStr = [remainStr stringByAppendingFormat:@" /uploading count is %ld", _connection.photoBackup.photosInUploadingArray]; +#endif + } return [sectionNames[section] stringByAppendingFormat:@"\t %@", remainStr]; } -#endif return sectionNames[section]; } diff --git a/seafile/SeafSyncInfoViewController.m b/seafile/SeafSyncInfoViewController.m index 8a843d5b..5593b576 100644 --- a/seafile/SeafSyncInfoViewController.m +++ b/seafile/SeafSyncInfoViewController.m @@ -81,7 +81,8 @@ - (void)viewDidLoad { WS(weakSelf); SeafDataTaskManager.sharedObject.trySyncBlock = ^(id _Nullable task) { if (![task.accountIdentifier isEqualToString:self.connection.accountIdentifier]) return; - if ([weakSelf.ongongingTasks containsObject:task]) return; + NSArray *copyArray = [NSArray arrayWithArray:weakSelf.ongongingTasks]; + if ([copyArray containsObject:task]) return; @synchronized (weakSelf.ongongingTasks) { [weakSelf.ongongingTasks addObject:task]; } diff --git a/seafile/Supporting Files/en.lproj/Localizable.strings b/seafile/Supporting Files/en.lproj/Localizable.strings index e88eff1509edbafb7d572ea84853533f2ee19582..3de5b93894436a3091adf443207f4025d9f5615d 100644 GIT binary patch delta 98 zcmdnh!MvlBd4o!|e-T3}Ljgk~1Bg~&NMpzcvJ@DSfFj8Z*$kyXIVB+97KoJ?0>G;9 Ps-AqYMt8GPwS*@COS~2D delta 22 ecmdnd$-JwBd4o#z