| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // Copyright (c) 2019 Tencent. All rights reserved.
- #import "UGCKitBGMHelper.h"
- #import "pthread.h"
- #define BGM_GROUP @"group.com.tencent.bgm.list"
- // percent < 0 失败
- // url 为 nil 是进度
- typedef void(^DownLoadCallback)(float percent, NSString* url);
- @interface TCBGMDownloadTask : NSObject
- @property (strong, nonatomic) NSString *destPath;
- @property (copy, nonatomic) DownLoadCallback callback;
- @end
- @implementation TCBGMDownloadTask
- @end
- @interface UGCKitBGMHelper() <NSURLSessionDownloadDelegate> {
- NSDictionary* _configs;
- NSUserDefaults* _userDefaults;
- NSString* _userIDKey;
- // NSMutableDictionary* _tasks;
- NSURLSessionDownloadTask* _currentTask;
- NSURLSession *_urlSession;
- NSOperationQueue *_sessionDelegateQueue;
- TCBGMElement* _currentEle;
- NSString* _bgmPath;
- NSMutableDictionary<NSURLSessionTask *, TCBGMDownloadTask*> *_taskDictionary;
- }
- @property(nonatomic, assign)pthread_mutex_t lock;
- @property(nonatomic, assign)pthread_cond_t cond;
- @property(nonatomic, strong)dispatch_queue_t queue;
- @property(nonatomic)NSMutableDictionary* bgmDict;
- @property(nonatomic)NSMutableDictionary* bgmList;//只用来存储路径
- @property(nonatomic,weak) id <TCBGMHelperListener>delegate;
- @end
- @implementation UGCKitBGMHelper
- + (instancetype)sharedInstance {
- static UGCKitBGMHelper* _sharedInstance;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _sharedInstance = [UGCKitBGMHelper new];
- });
- return _sharedInstance;
- }
- -(void) setDelegate:(nonnull id<TCBGMHelperListener>) delegate{
- _delegate = delegate;
- }
- -(id) init{
- if(self = [super init]){
- _taskDictionary = [[NSMutableDictionary alloc] init];
- _sessionDelegateQueue = [[NSOperationQueue alloc] init];
- _sessionDelegateQueue.underlyingQueue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0);
- _urlSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]
- delegate:self
- delegateQueue:_sessionDelegateQueue];
- // if(![[TCLoginModel sharedInstance] isLogin]){
- // self = nil;
- // return nil;
- // }
- NSFileManager *fileManager = [NSFileManager defaultManager];
- _bgmPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/bgm"];
- if(![fileManager fileExistsAtPath:_bgmPath]){
- if(![fileManager createDirectoryAtPath:_bgmPath withIntermediateDirectories:YES attributes:nil error:nil]){
- BGMLog(ASLocalizedString(@"创建BGM目录失败"));
- return nil;
- }
- }
- pthread_mutex_init(&_lock, NULL);
- pthread_cond_init(&_cond, NULL);
- _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:BGM_GROUP];
- if (_userDefaults == nil) {
- _userDefaults = [NSUserDefaults standardUserDefaults];
- }
- // _tasks = [[NSMutableDictionary alloc] init];
- _userIDKey = @"_bgm";
- _queue = dispatch_queue_create("com.tencent.txcloud.videoedit.bgm.download", DISPATCH_QUEUE_SERIAL);
- dispatch_async(_queue, ^{[self loadLocalData];});
- }
- return self;
- }
- - (void)dealloc {
- pthread_mutex_destroy(&_lock);
- pthread_cond_destroy(&_cond);
- }
- - (void)_bgmTask:(NSString *)url {
- NSString* localListPath = url;
- __weak __typeof(self) weak = self;
- if([url hasPrefix:@"http"]){
- localListPath = [_bgmPath stringByAppendingPathComponent:@"bgm_list.json"];
- __block BOOL ret = false;
- pthread_mutex_lock(&_lock);
- [self downloadFile:url dstUrl:localListPath callback:^(float percent, NSString* path){
- __strong UGCKitBGMHelper* strong = weak;
- if(strong){
- if(percent < 0){
- pthread_cond_signal(&(strong->_cond));
- }
- else{
- if(path != nil){
- pthread_mutex_lock(&(strong->_lock));
- ret = true;
- pthread_cond_signal(&(strong->_cond));
- pthread_mutex_unlock(&(strong->_lock));
- }
- }
- }
- }];
- pthread_cond_wait(&_cond, &_lock);
- pthread_mutex_unlock(&_lock);
- }
- NSData *data = [[NSFileManager defaultManager] contentsAtPath:localListPath];
- _configs = data ? [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil] : nil;
- if(_configs == nil){
- [_delegate onBGMListLoad:nil];
- }
- else{
- NSArray* nameList = [_configs valueForKeyPath:@"bgm.list.name"];
- if([nameList count]){
- NSArray* urlList = [_configs valueForKeyPath:@"bgm.list.url"];
- for (int i = 0; i < [nameList count]; i++) {
- TCBGMElement* ele = [_bgmDict objectForKey:[urlList objectAtIndex:i]];
- if(ele != nil){
- }
- else{
- ele = [TCBGMElement new];
- ele.netUrl = [urlList objectAtIndex:i];
- ele.name = [nameList objectAtIndex:i];
- [self saveBGMStat:ele];
- }
- }
- }
- }
- [_delegate onBGMListLoad:_bgmDict];
- }
- -(void) initBGMListWithJsonFile:(NSString* _Nonnull)url{
- if(url == nil)return;
- __weak UGCKitBGMHelper* weak = self;
- void (^task)(void) = ^{
- [weak _bgmTask:url];
- };
- dispatch_async(_queue, task);
- return;
- }
- -(void) loadLocalData{
- _bgmDict = [NSMutableDictionary new];
- _bgmList = [[_userDefaults objectForKey:[_userIDKey stringByAppendingString:@".tc.bgm.list"]] mutableCopy];
- if(_bgmList == nil){
- _bgmList = [NSMutableDictionary new];
- }
- for (id it in _bgmList) {
- TCBGMElement* ele = [NSKeyedUnarchiver unarchiveObjectWithData:[_userDefaults objectForKey:[_userIDKey stringByAppendingString:it]]];
- if(ele) {
- [_bgmDict setObject:ele forKey:[ele netUrl]];
- }
- }
- }
- -(void) saveBGMStat:(TCBGMElement*) ele{
- [_bgmDict setObject:ele forKey:ele.netUrl];
- [_bgmList setObject:[ele netUrl] forKey:[ele netUrl]];
- NSData *udObject = [NSKeyedArchiver archivedDataWithRootObject:ele];
- [_userDefaults setObject:udObject forKey:[_userIDKey stringByAppendingString:[ele netUrl]]];
- [_userDefaults setObject:_bgmList forKey:[_userIDKey stringByAppendingString:@".tc.bgm.list"]];
- }
- - (void)_downloadBGMAction:(TCBGMElement*)current {
- const BOOL needOverride = YES;
- __strong UGCKitBGMHelper* strong = self;
- if(strong != nil){
- if([[_currentEle netUrl] isEqualToString:[current netUrl]]){
- if([_currentTask state] == NSURLSessionTaskStateRunning){
- BGMLog(ASLocalizedString(@"暂停:%@"), [current name]);
- [_currentTask suspend];
- return;
- }
- else if([_currentTask state] == NSURLSessionTaskStateSuspended){
- BGMLog(ASLocalizedString(@"恢复:%@"), [current name]);
- [_currentTask resume];
- return;
- }
- }
- else{
- if(_currentTask){
- if([_currentTask state] != NSURLSessionTaskStateCompleted){
- [_currentTask cancel];
- [strong.delegate onBGMDownloading:_currentEle percent:0];
- }
- _currentTask = nil;
- }
- }
- NSString* localListPath = nil;
- NSString* url = [current netUrl];
- __block NSString* justName = [current name];
- if (needOverride) {
- localListPath = [_bgmPath stringByAppendingPathComponent:justName];
- } else {
- justName = [NSString stringWithFormat:@"%@1.%@", [justName stringByDeletingPathExtension], [[current name] pathExtension]];
- localListPath = [_bgmPath stringByAppendingPathComponent:justName];
- }
- __weak __typeof(self) weak = self;
- NSURLSessionDownloadTask* task = [self downloadFile:url dstUrl:localListPath callback:^(float percent, NSString* path){
- __strong UGCKitBGMHelper* strong = weak;
- if(strong){
- dispatch_queue_t queue = strong->_queue;
- if(percent < 0){
- dispatch_async(queue, ^{
- [strong.delegate onBGMDownloadDone:current];
- });
- }
- else{
- TCBGMElement* ele = [strong->_bgmDict objectForKey:[current netUrl]];
- if(path != nil){
- ele.localUrl = [NSString stringWithFormat:@"Documents/bgm/%@", justName];
- ele.isValid = [NSNumber numberWithBool:true];
- dispatch_async(queue, ^{
- [strong.delegate onBGMDownloadDone:ele];
- });
- [strong saveBGMStat:ele];
- }else{
- dispatch_async(queue, ^{
- [weak.delegate onBGMDownloading:ele percent:percent];
- });
- }
- }
- }
- }];
- _currentTask = task;
- _currentEle = current;
- }
- }
- #pragma mark NSURLSessionDownloadDelegate
- - (void)URLSession:(NSURLSession *)session
- task:(NSURLSessionTask *)urlTask
- didCompleteWithError:(NSError *)error {
- TCBGMDownloadTask *task = nil;
- @synchronized (_taskDictionary) {
- task = _taskDictionary[urlTask];
- }
- if (error) {
- if (task.callback) {
- task.callback(-1, nil);
- }
- }
- @synchronized (_taskDictionary) {
- [_taskDictionary removeObjectForKey:urlTask];
- }
- }
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask
- didWriteData:(int64_t)bytesWritten
- totalBytesWritten:(int64_t)totalBytesWritten
- totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
- TCBGMDownloadTask *task = nil;
- @synchronized (_taskDictionary) {
- task = _taskDictionary[downloadTask];
- }
- if (task.callback) {
- task.callback(totalBytesWritten / (float)totalBytesExpectedToWrite, nil);
- }
- }
- - (void)URLSession:(NSURLSession *)session
- downloadTask:(NSURLSessionDownloadTask *)downloadTask
- didFinishDownloadingToURL:(NSURL *)location {
- TCBGMDownloadTask *task = nil;
- @synchronized (_taskDictionary) {
- task = _taskDictionary[downloadTask];
- }
- NSError *fsError = nil;
- NSFileManager *manager = [NSFileManager defaultManager];
- if ([manager fileExistsAtPath:task.destPath]) {
- [manager removeItemAtPath:task.destPath error:nil];
- }
- [[NSFileManager defaultManager] moveItemAtURL:location
- toURL:[NSURL fileURLWithPath: task.destPath]
- error:&fsError];
- if (task.callback) {
- if (fsError) {
- NSLog(@"Error: %@", fsError);
- task.callback(-1, nil);
- } else {
- task.callback(0, task.destPath);
- }
- }
- }
- -(void) downloadBGM:(TCBGMElement*) current{
- __weak UGCKitBGMHelper* weak = self;
- dispatch_async(_queue, ^(){
- [weak _downloadBGMAction:current];
- });
- }
- //-(void) pauseAllTasks{
- // __weak TCBGMHelper* weak = self;
- // dispatch_async(_queue, ^(){
- // __strong TCBGMHelper* strong = weak;
- // for (id item in strong->_tasks) {
- // if([item state] == NSURLSessionTaskStateRunning)[item suspend];
- // }
- // });
- //}
- //
- //-(void) resumeAllTasks{
- // __weak TCBGMHelper* weak = self;
- // dispatch_async(_queue, ^(){
- // __strong TCBGMHelper* strong = weak;
- // for (id item in strong->_tasks) {
- // if([item state] == NSURLSessionTaskStateSuspended)[item resume];
- // }
- // });
- //}
- /**
- 下载函数回调
-
- @param callback 下载进度 < 0 出错并终止
- @param srcUrl 最终文件地址 nil != url则下载完成
- */
- - (NSURLSessionDownloadTask*) downloadFile:(NSString*)srcUrl dstUrl:(NSString*)dstUrl callback:(DownLoadCallback)callback{
- TCBGMDownloadTask *task = [[TCBGMDownloadTask alloc] init];
- task.destPath = dstUrl;
- task.callback = callback;
- NSURL *url = [NSURL URLWithString:srcUrl];
- NSURLRequest *downloadReq = [NSURLRequest requestWithURL:url
- cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
- timeoutInterval:300.f];
- NSURLSessionDownloadTask *urlSessionTask = [_urlSession downloadTaskWithRequest:downloadReq];
- @synchronized (_taskDictionary) {
- _taskDictionary[urlSessionTask] = task;
- }
- [urlSessionTask resume];
- return urlSessionTask;
- }
- @end
- @implementation TCBGMElement
- - (id) initWithCoder: (NSCoder *)coder
- {
- if (self = [super init])
- {
- self.name = [coder decodeObjectForKey:@"name"];
- self.netUrl = [coder decodeObjectForKey:@"netUrl"];
- self.localUrl = [coder decodeObjectForKey:@"localUrl"];
- self.author = [coder decodeObjectForKey:@"author"];
- self.title = [coder decodeObjectForKey:@"title"];
- self.isValid = [coder decodeObjectForKey:@"isValid"];
- self.duration = [coder decodeObjectForKey:@"duration"];
- }
- return self;
- }
- - (void) encodeWithCoder: (NSCoder *)coder
- {
- [coder encodeObject:_name forKey:@"name"];
- [coder encodeObject:_netUrl forKey:@"netUrl"];
- [coder encodeObject:_localUrl forKey:@"localUrl"];
- [coder encodeObject:_author forKey:@"author"];
- [coder encodeObject:_title forKey:@"title"];
- [coder encodeObject:_isValid forKey:@"isValid"];
- [coder encodeObject:_duration forKey:@"duration"];
- }
- @end
|