// // WXYZ_ComicDownloadManager.m // WXReader // // Created by Andrew on 2020/3/29. // Copyright © 2020 Andrew. All rights reserved. // #import "WXYZ_ComicDownloadManager.h" static dispatch_group_t url_session_manager_completion_group() { static dispatch_group_t af_url_session_manager_completion_group; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ af_url_session_manager_completion_group = dispatch_group_create(); }); return af_url_session_manager_completion_group; } @interface WXYZ_ComicDownloadManager () @property (nonatomic, assign) BOOL requesting; @property (nonatomic, strong) NSMutableArray *requestOrderArray; @end @implementation WXYZ_ComicDownloadManager implementation_singleton(WXYZ_ComicDownloadManager) /* 增 **/ // 下载章节 - (void)downloadChapterWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id { [self downloadChaptersWithProductionModel:productionModel production_id:production_id chapter_ids:@[[TFUtilsHelper formatStringWithInteger:chapter_id]]]; } // 下载多个章节 - (void)downloadChaptersWithProductionModel:(TFProductionModel *)productionModel production_id:(NSInteger)production_id chapter_ids:(NSArray *)chapter_ids { // 作品开始下载标记 [[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:productionModel productionType:TFProductionTypeComic]; WS(weakSelf) [TFNetworkTools POST:Comic_Download parameters:@{@"comic_id":[TFUtilsHelper formatStringWithInteger:production_id], @"chapter_id":[chapter_ids componentsJoinedByString:@","]} model:nil success:^(BOOL isSuccess, NSDictionary * _Nullable t_model, TFNetworkRequestModel * _Nonnull requestModel) { if (isSuccess) { dispatch_async(dispatch_get_main_queue(), ^{ // 任务开始 if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionStart, production_id, chapter_ids); } // 任务加入任务队列 NSArray *taskArray = [t_model objectForKey:@"data"]; if (taskArray.count > 0) { for (NSDictionary *t_dic in taskArray) { TFProductionChapterModel *imageCollectionModel = [TFProductionChapterModel modelWithDictionary:t_dic]; if (imageCollectionModel) { // 开始下载 [[self comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:identify_downloading forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)]; dispatch_async(dispatch_get_main_queue(), ^{ if (self.downloadChapterStateChangeBlock) { self.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadStart, imageCollectionModel.production_id, imageCollectionModel.chapter_id); } }); } } [weakSelf.requestOrderArray addObject:taskArray]; if (!weakSelf.requesting) { [TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"已加入下载列表")]; } // 开始下载任务 [weakSelf downloadTaskRequestWithProduction_id:production_id]; } else { // 任务失败 if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids); } } }); } else if (Compare_Json_isEqualTo(requestModel.code, 701)) { // 请求购买 // 任务失败 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionShouldPay, production_id, chapter_ids); } }); } else { [TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:requestModel.msg]; // 任务失败 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids); } }); } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { // 任务失败 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFail, production_id, chapter_ids); } }); }]; } /* 删 **/ // 删除本地整本已下载作品 - (BOOL)removeDownloadProductionWithProduction_id:(NSInteger)production_id { // 删除作品文件夹 [[WXYZ_DownloadHelper sharedManager] removeDownloadProductionFolderWithProduction_id:production_id productionType:TFProductionTypeComic]; // 删除缓存 [[self comicChaptersRecordDictionaryWithProduction_id:production_id] removeAllObjects]; // 写入 [self comicRecordWriteToPlistWithProduction_id:production_id]; return YES; } // 删除本地多个已下载章节 - (void)removeDownloadChaptersWithProduction_id:(NSInteger)production_id chapter_ids:(NSArray *)chapter_ids { if (chapter_ids.count == 0) { return; } // 删除文件夹 [[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:production_id chapter_ids:chapter_ids productionType:TFProductionTypeComic]; // 删除下载记录 NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id]; NSMutableArray *t_delete_arr = [NSMutableArray array]; for (NSString *chapter_id in chapter_ids) { [t_delete_arr addObject:chapterRecordKey(production_id, [chapter_id integerValue])]; } [t_dic removeObjectsForKeys:t_delete_arr]; // 如果全部章节已经删除,则删除整体作品文件夹 if ([self getDownloadChapterCountWithProduction_id:production_id] == 0) { [self removeDownloadProductionWithProduction_id:production_id]; } else { [self comicRecordWriteToPlistWithProduction_id:production_id]; } // 删除回调 dispatch_async(dispatch_get_main_queue(), ^{ if (self.downloadDeleteFinishBlock) { self.downloadDeleteFinishBlock(chapter_ids, @[]); } }); } /* 查 **/ // 获取下载章节数 - (NSInteger)getDownloadChapterCountWithProduction_id:(NSInteger)production_id { return [[WXYZ_DownloadHelper sharedManager] getDownloadChapterCountWithProduction_id:production_id productionType:TFProductionTypeComic]; } // 获取全部已下载章节model - (NSArray *)getDownloadChapterModelArrayWithProduction_id:(NSInteger)production_id { NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id]; NSMutableArray *t_arr = [NSMutableArray array]; for (id value in t_dic.allValues) { if ([value isKindOfClass:[TFProductionChapterModel class]]) { [t_arr addObject:value]; } } return [t_arr copy]; } // 获取已下载章节model - (id)getDownloadChapterModelWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id { NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id]; id value = [t_dic objectForKey:chapterRecordKey(production_id, chapter_id)]; if ([value isKindOfClass:[TFProductionChapterModel class]]) { return value; } return nil; } // 获取下载状态 - (WXYZ_ProductionDownloadState)getChapterDownloadStateWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id { NSMutableDictionary *t_dic = [self comicChaptersRecordDictionaryWithProduction_id:production_id]; if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isEqual:identify_downloading]) { return WXYZ_ProductionDownloadStateDownloading; } if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isEqual:identify_fail]) { return WXYZ_ProductionDownloadStateFail; } if ([[t_dic objectForKey:chapterRecordKey(production_id, chapter_id)] isKindOfClass:[TFProductionChapterModel class]]) { return WXYZ_ProductionDownloadStateDownloaded; } return WXYZ_ProductionDownloadStateNormal; } // 作品章节是否下载 - (BOOL)isChapterDownloadedWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id { return [[WXYZ_DownloadHelper sharedManager] isChapterDownloadedWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeComic]; } - (UIImage *)getDownloadLocalImageWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id image_id:(NSInteger)image_id image_update_time:(NSInteger)image_update_time { UIImage *t_image = [UIImage imageWithContentsOfFile:[self chapterImageFilePathWithProduction_id:production_id chapter_id:chapter_id image_id:image_id image_update_time:image_update_time]]; if (!t_image) { return nil; } return t_image; } - (NSString *)chapterImageFilePathWithProduction_id:(NSInteger)production_id chapter_id:(NSInteger)chapter_id image_id:(NSInteger)image_id image_update_time:(NSInteger)image_update_time { // 章节图片名称 NSString *imageFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@%@", [TFUtilsHelper formatStringWithInteger:production_id], [TFUtilsHelper formatStringWithInteger:chapter_id], [TFUtilsHelper formatStringWithInteger:image_id], [TFUtilsHelper formatStringWithInteger:image_update_time]]]; NSString *imageFilePath = @""; NSArray *extensionArr = @[@"jpeg", @"png", @"gif", @"tiff", @"webp"]; for (NSString *extension in extensionArr) { imageFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:production_id chapter_id:chapter_id productionType:TFProductionTypeComic] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageFileName, extension]]; if (imageFilePath && imageFilePath.length > 0 && [[NSFileManager defaultManager] fileExistsAtPath:imageFilePath]) { return imageFilePath; } } return imageFilePath; } - (void)downloadTaskRequestWithProduction_id:(NSInteger)production_id { if (self.requestOrderArray.count == 0) { return; } if (self.requesting) { return; } self.requesting = YES; NSArray *taskArray = [self.requestOrderArray firstObject]; [self.requestOrderArray removeFirstObject]; WS(weakSelf) dispatch_group_async(url_session_manager_completion_group(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 记录出错章节 NSMutableArray __block *errorChapter = [NSMutableArray array]; dispatch_semaphore_t collection_semaphore = dispatch_semaphore_create(0); // 记录章节model内容 NSMutableArray *t_chapterModelArray = [NSMutableArray array]; for (NSDictionary *t_dic in taskArray) { dispatch_queue_t image_queue = dispatch_queue_create("wxyz_image_queue", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t image_group = dispatch_group_create(); TFProductionChapterModel *imageCollectionModel = [TFProductionChapterModel modelWithDictionary:t_dic]; if (!imageCollectionModel) { // 单个章节下载失败 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadChapterStateChangeBlock) { weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, imageCollectionModel.production_id, imageCollectionModel.chapter_id); } }); continue; } [t_chapterModelArray addObject:imageCollectionModel]; // 解析任务 下载一话内的图片组 for (TFImageListModel *imageModel in imageCollectionModel.image_list) { dispatch_group_async(image_group, image_queue, ^{ dispatch_group_enter(image_group); // 创建图片下载器 AFURLSessionManager *manager = [[AFURLSessionManager alloc] init]; manager.operationQueue.maxConcurrentOperationCount = 2; NSURL *URL = [NSURL URLWithString:imageModel.image]; NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:[NSURLRequest requestWithURL:URL] progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { // 存储文件夹创建 if (![[NSFileManager defaultManager] fileExistsAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic]]) { [[NSFileManager defaultManager] createDirectoryAtPath:[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic] withIntermediateDirectories:YES attributes:nil error:nil]; } // 获取图片名称 NSString *imageFileName = [TFUtilsHelper stringToMD5:[NSString stringWithFormat:@"%@%@%@%@", [TFUtilsHelper formatStringWithInteger:imageCollectionModel.production_id], [TFUtilsHelper formatStringWithInteger:imageCollectionModel.chapter_id], [TFUtilsHelper formatStringWithInteger:imageModel.image_id], [TFUtilsHelper formatStringWithInteger:imageModel.image_update_time]]]; // 图片地址 NSString *imageFilePath = [[[WXYZ_DownloadHelper sharedManager] getDownloadChapterFolderPathWithProduction_id:imageCollectionModel.production_id chapter_id:imageCollectionModel.chapter_id productionType:TFProductionTypeComic] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageFileName, [TFViewHelper imageExtensionWithFormatString:[response MIMEType]]]]; return [NSURL fileURLWithPath:imageFilePath]; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { // 如果某一张出错就取消下载,删除该章节文件夹 if (error) { [errorChapter addObject:[TFUtilsHelper formatStringWithInteger:imageCollectionModel.chapter_id]]; } dispatch_group_leave(image_group); }]; [downloadTask resume]; }); } dispatch_group_notify(image_group, image_queue, ^{ // 删除图片出错章节 if (errorChapter.count > 0) { // 删除出错章节文件夹 [[WXYZ_DownloadHelper sharedManager] removeDownloadChapterFolderWithProduction_id:imageCollectionModel.production_id chapter_ids:errorChapter productionType:TFProductionTypeComic]; [[weakSelf comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:identify_fail forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)]; [weakSelf comicRecordWriteToPlistWithProduction_id:imageCollectionModel.production_id]; // 单个章节下载失败 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadChapterStateChangeBlock) { weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFail, imageCollectionModel.production_id, imageCollectionModel.chapter_id); } }); } else { // 下载成功 覆盖临时下载记录 [[weakSelf comicChaptersRecordDictionaryWithProduction_id:imageCollectionModel.production_id] setObject:imageCollectionModel forKey:chapterRecordKey(imageCollectionModel.production_id, imageCollectionModel.chapter_id)]; [weakSelf comicRecordWriteToPlistWithProduction_id:imageCollectionModel.production_id]; // 单个章节下载完成 dispatch_async(dispatch_get_main_queue(), ^{ if (weakSelf.downloadChapterStateChangeBlock) { weakSelf.downloadChapterStateChangeBlock(WXYZ_DownloadStateChapterDownloadFinished, imageCollectionModel.production_id, imageCollectionModel.chapter_id); } }); } dispatch_semaphore_signal(collection_semaphore); }); dispatch_semaphore_wait(collection_semaphore, DISPATCH_TIME_FOREVER); } dispatch_async(dispatch_get_main_queue(), ^{ weakSelf.requesting = NO; if (weakSelf.requestOrderArray.count == 0) { // 替换已下载记录章节目录内容 TFProductionModel *t_productionModel = [[WXYZ_DownloadHelper sharedManager] getDownloadProductionModelWithProduction_id:production_id productionType:TFProductionTypeComic]; NSMutableArray *t_chapterList = [t_productionModel.chapter_list mutableCopy]; for (int i = 0; i < t_chapterList.count; i++) { TFProductionChapterModel *t_chapterModel = [t_chapterList objectAtIndex:i]; for (TFProductionChapterModel *tt_chapterModel in t_chapterModelArray) { if (tt_chapterModel.chapter_id == t_chapterModel.chapter_id) { [t_chapterList replaceObjectAtIndex:i withObject:tt_chapterModel]; break; } } } t_productionModel.chapter_list = [t_chapterList copy]; [[WXYZ_DownloadHelper sharedManager] recordDownloadProductionWithProductionModel:t_productionModel productionType:TFProductionTypeComic]; // 任务完成 if (weakSelf.downloadMissionStateChangeBlock) { weakSelf.downloadMissionStateChangeBlock(WXYZ_DownloadStateMissionFinished, 0, @[]); } } else { [weakSelf downloadTaskRequestWithProduction_id:production_id]; } }); }); } - (NSMutableArray *)requestOrderArray { if (!_requestOrderArray) { _requestOrderArray = [NSMutableArray array]; } return _requestOrderArray; } - (NSMutableDictionary *)comicChaptersRecordDictionaryWithProduction_id:(NSUInteger)production_id { return [[WXYZ_DownloadHelper sharedManager] chaptersRecordDownloadDictionaryWithProduction_id:production_id productionType:TFProductionTypeComic modelClass:[TFProductionChapterModel class]]; } - (BOOL)comicRecordWriteToPlistWithProduction_id:(NSUInteger)production_id { return [[WXYZ_DownloadHelper sharedManager] writeToChapterPlistFileWithProduction_id:production_id productionType:TFProductionTypeComic modelClass:[TFProductionChapterModel class]]; } @end