TCBGMHelper.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // TCVideoEditBGMHelper.m
  3. // TXXiaoShiPinDemo
  4. //
  5. // Created by linkzhzhu on 2017/12/7.
  6. // Copyright © 2017年 tencent. All rights reserved.
  7. //
  8. #import "TCBGMHelper.h"
  9. #import "AFHTTPSessionManager.h"
  10. #import "pthread.h"
  11. //#import "TCLoginModel.h"
  12. //#import "Music_manager.h"
  13. @interface TCBGMHelper(){
  14. NSDictionary* _configs;
  15. NSUserDefaults* _userDefaults;
  16. NSString* _userIDKey;
  17. // NSMutableDictionary* _tasks;
  18. NSURLSessionDownloadTask* _currentTask;
  19. TCBGMElement* _currentEle;
  20. NSString* _bgmPath;
  21. }
  22. @property(nonatomic, assign)pthread_mutex_t lock;
  23. @property(nonatomic, assign)pthread_cond_t cond;
  24. @property(nonatomic, strong)dispatch_queue_t queue;
  25. @property(nonatomic)NSMutableDictionary* bgmDict;
  26. @property(nonatomic)NSMutableDictionary* bgmList;//只用来存储路径
  27. @property(nonatomic,weak) id <TCBGMHelperListener>delegate;
  28. @end
  29. static TCBGMHelper* _sharedInstance;
  30. static pthread_mutex_t _instanceLock = PTHREAD_MUTEX_INITIALIZER;
  31. @implementation TCBGMHelper
  32. + (instancetype)sharedInstance {
  33. if(!_sharedInstance){
  34. pthread_mutex_lock(&_instanceLock);
  35. _sharedInstance = [TCBGMHelper new];
  36. pthread_mutex_unlock(&_instanceLock);
  37. }
  38. return _sharedInstance;
  39. }
  40. -(void) setDelegate:(nonnull id<TCBGMHelperListener>) delegate{
  41. _delegate = delegate;
  42. }
  43. -(id) init{
  44. if(self = [super init]){
  45. // if(![[TCLoginModel sharedInstance] isLogin]){
  46. // self = nil;
  47. // return nil;
  48. // }
  49. NSFileManager *fileManager = [NSFileManager defaultManager];
  50. _bgmPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/bgm"];
  51. if(![fileManager fileExistsAtPath:_bgmPath]){
  52. if(![fileManager createDirectoryAtPath:_bgmPath withIntermediateDirectories:YES attributes:nil error:nil]){
  53. BGMLog(@"创建BGM目录失败");
  54. return nil;
  55. }
  56. }
  57. pthread_mutex_init(&_lock, NULL);
  58. pthread_cond_init(&_cond, NULL);
  59. _userDefaults = [[NSUserDefaults alloc] initWithSuiteName:BGM_GROUP];
  60. if (_userDefaults == nil) {
  61. _userDefaults = [NSUserDefaults standardUserDefaults];
  62. }
  63. // _tasks = [[NSMutableDictionary alloc] init];
  64. //[[TCLoginParam shareInstance].identifier stringByAppendingString:@"_bgm"]
  65. _userIDKey = @"bugu_bgm";
  66. _queue = dispatch_queue_create("com.tencent.txcloud.videoedit.bgm.download", NULL);
  67. dispatch_async(_queue, ^{[self loadLocalData];});
  68. }
  69. return self;
  70. }
  71. - (void)dealloc {
  72. pthread_mutex_destroy(&_lock);
  73. pthread_cond_destroy(&_cond);
  74. }
  75. -(void) initBGMListWithJsonFile:(NSString* _Nonnull)url{
  76. if(url == nil)return;
  77. __weak TCBGMHelper* weak = self;
  78. // [Music_manager getMoreMusicWithcategoryid:@"2" andCallback:^(id response) {
  79. // if (response && [response isKindOfClass:[NSArray class]])
  80. // {
  81. // __strong TCBGMHelper* strong = weak;
  82. // strong->_configs =@{@"bgm":response};
  83. // NSArray* nameList = [strong->_configs valueForKeyPath:@"bgm.music_name"];
  84. // if([nameList count]){
  85. // NSArray* urlList = [strong->_configs valueForKeyPath:@"bgm.music_url"];
  86. // for (int i = 0; i < [nameList count]; i++) {
  87. // TCBGMElement* ele = [strong->_bgmDict objectForKey:[urlList objectAtIndex:i]];
  88. // if(ele != nil){
  89. //
  90. // }
  91. // else{
  92. // ele = [TCBGMElement new];
  93. // ele.netUrl = [urlList objectAtIndex:i];
  94. // NSString *musicname =[nameList objectAtIndex:i];
  95. // if ([musicname hasSuffix:@".mp3"])
  96. // {
  97. // ele.name =musicname;
  98. // }else
  99. // {
  100. // ele.name = [NSString stringWithFormat:@"%@.mp3",musicname];
  101. // }
  102. // [strong saveBGMStat:ele];
  103. // }
  104. // }
  105. // }
  106. // [strong->_delegate onBGMListLoad:_bgmDict];
  107. // }
  108. // }];
  109. void (^task)() = ^{
  110. NSString* localListPath = url;
  111. __strong TCBGMHelper* strong = weak;
  112. if([url hasPrefix:@"http"]){
  113. localListPath = [_bgmPath stringByAppendingPathComponent:@"bgm_list.json"];
  114. __block BOOL ret = false;
  115. pthread_mutex_lock(&_lock);
  116. [TCBGMHelper downloadFile:url dstUrl:localListPath callback:^(float percent, NSString* path){
  117. __strong TCBGMHelper* strong = weak;
  118. if(strong){
  119. if(percent < 0){
  120. pthread_cond_signal(&(strong->_cond));
  121. }
  122. else{
  123. if(path != nil){
  124. pthread_mutex_lock(&(strong->_lock));
  125. ret = true;
  126. pthread_cond_signal(&(strong->_cond));
  127. pthread_mutex_unlock(&(strong->_lock));
  128. }
  129. }
  130. }
  131. }];
  132. pthread_cond_wait(&_cond, &_lock);
  133. pthread_mutex_unlock(&_lock);
  134. }
  135. NSData *data = [[NSFileManager defaultManager] contentsAtPath:localListPath];
  136. strong->_configs = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
  137. if(strong->_configs == nil){
  138. [strong->_delegate onBGMListLoad:nil];
  139. }
  140. else{
  141. // NSArray* nameList = [strong->_configs valueForKeyPath:@"bgm.list.name"];
  142. // if([nameList count]){
  143. // NSArray* urlList = [strong->_configs valueForKeyPath:@"bgm.list.url"];
  144. // for (int i = 0; i < [nameList count]; i++) {
  145. // TCBGMElement* ele = [strong->_bgmDict objectForKey:[urlList objectAtIndex:i]];
  146. // if(ele != nil){
  147. //
  148. // }
  149. // else{
  150. // ele = [TCBGMElement new];
  151. // ele.netUrl = [urlList objectAtIndex:i];
  152. // ele.name = [nameList objectAtIndex:i];
  153. // [strong saveBGMStat:ele];
  154. // }
  155. // }
  156. // }
  157. }
  158. [strong->_delegate onBGMListLoad:_bgmDict];
  159. };
  160. dispatch_async(_queue, task);
  161. return;
  162. }
  163. -(void) loadLocalData{
  164. _bgmDict = [NSMutableDictionary new];
  165. _bgmList = [[_userDefaults objectForKey:[_userIDKey stringByAppendingString:@".tc.bgm.list"]] mutableCopy];
  166. if(_bgmList == nil){
  167. _bgmList = [NSMutableDictionary new];
  168. }
  169. for (id it in _bgmList) {
  170. TCBGMElement* ele = [NSKeyedUnarchiver unarchiveObjectWithData:[_userDefaults objectForKey:[_userIDKey stringByAppendingString:it]]];
  171. if(ele)[_bgmDict setObject:ele forKey:[ele netUrl]];
  172. }
  173. }
  174. -(void) saveBGMStat:(TCBGMElement*) ele{
  175. [_bgmDict setObject:ele forKey:ele.netUrl];
  176. [_bgmList setObject:[ele netUrl] forKey:[ele netUrl]];
  177. NSData *udObject = [NSKeyedArchiver archivedDataWithRootObject:ele];
  178. [_userDefaults setObject:udObject forKey:[_userIDKey stringByAppendingString:[ele netUrl]]];
  179. [_userDefaults setObject:_bgmList forKey:[_userIDKey stringByAppendingString:@".tc.bgm.list"]];
  180. }
  181. -(void) downloadBGM:(TCBGMElement*) current{
  182. TCBGMElement* bgm = [_bgmDict objectForKey:[current netUrl]];
  183. __block bool needOverride = true;
  184. // if(bgm && [[bgm isValid] boolValue]){
  185. // if(([[bgm netUrl] isEqualToString:[current netUrl]])){
  186. // return;
  187. // }
  188. // else needOverride = false;
  189. // return;
  190. // }
  191. __weak TCBGMHelper* weak = self;
  192. dispatch_async(_queue, ^(){
  193. __strong TCBGMHelper* strong = weak;
  194. if(strong != nil){
  195. if([[_currentEle netUrl] isEqualToString:[current netUrl]]){
  196. if([_currentTask state] == NSURLSessionTaskStateRunning){
  197. BGMLog(@"暂停:%@", [current name]);
  198. [_currentTask suspend];
  199. return;
  200. }
  201. else if([_currentTask state] == NSURLSessionTaskStateSuspended){
  202. BGMLog(@"恢复:%@", [current name]);
  203. [_currentTask resume];
  204. return;
  205. }
  206. }
  207. else{
  208. if(_currentTask){
  209. if([_currentTask state] != NSURLSessionTaskStateCompleted){
  210. [_currentTask cancel];
  211. [strong.delegate onBGMDownloading:_currentEle percent:0];
  212. }
  213. _currentTask = nil;
  214. }
  215. }
  216. NSString* localListPath = nil;
  217. NSString* url = [current netUrl];
  218. __block NSString* justName = [current name];
  219. if(needOverride){
  220. localListPath = [_bgmPath stringByAppendingPathComponent:justName];
  221. }
  222. else{
  223. justName = [NSString stringWithFormat:@"%@1.%@", [justName stringByDeletingPathExtension], [[current name] pathExtension]];
  224. localListPath = [_bgmPath stringByAppendingPathComponent:justName];
  225. }
  226. NSURLSessionDownloadTask* task = [TCBGMHelper downloadFile:url dstUrl:localListPath callback:^(float percent, NSString* path){
  227. __strong TCBGMHelper* strong = weak;
  228. if(strong){
  229. if(percent < 0){
  230. dispatch_async(_queue, ^{
  231. [strong.delegate onBGMDownloadDone:current];
  232. });
  233. }
  234. else{
  235. TCBGMElement* ele = [strong->_bgmDict objectForKey:[current netUrl]];
  236. if (!ele)
  237. {
  238. ele =current;
  239. }
  240. if(path != nil){
  241. ele.localUrl = [NSString stringWithFormat:@"Documents/bgm/%@", justName];
  242. ele.isValid = [NSNumber numberWithBool:true];
  243. dispatch_async(_queue, ^{
  244. [strong.delegate onBGMDownloadDone:ele];
  245. });
  246. [strong saveBGMStat:ele];
  247. }else{
  248. dispatch_async(_queue, ^{
  249. [strong.delegate onBGMDownloading:ele percent:percent];
  250. });
  251. }
  252. }
  253. }
  254. }];
  255. _currentTask = task;
  256. _currentEle = current;
  257. }
  258. });
  259. }
  260. //-(void) pauseAllTasks{
  261. // __weak TCBGMHelper* weak = self;
  262. // dispatch_async(_queue, ^(){
  263. // __strong TCBGMHelper* strong = weak;
  264. // for (id item in strong->_tasks) {
  265. // if([item state] == NSURLSessionTaskStateRunning)[item suspend];
  266. // }
  267. // });
  268. //}
  269. //
  270. //-(void) resumeAllTasks{
  271. // __weak TCBGMHelper* weak = self;
  272. // dispatch_async(_queue, ^(){
  273. // __strong TCBGMHelper* strong = weak;
  274. // for (id item in strong->_tasks) {
  275. // if([item state] == NSURLSessionTaskStateSuspended)[item resume];
  276. // }
  277. // });
  278. //}
  279. /**
  280. 下载函数回调
  281. @param percent 下载进度 < 0 出错并终止
  282. @param url 最终文件地址 nil != url则下载完成
  283. */
  284. typedef void(^DownLoadCallback)(float percent, NSString* url);
  285. +(NSURLSessionDownloadTask*) downloadFile:(NSString*)srcUrl dstUrl:(NSString*)dstUrl callback:(DownLoadCallback)callback{
  286. __weak __typeof(self) weakSelf = self;
  287. NSURLRequest *downloadReq = [NSURLRequest requestWithURL:[NSURL URLWithString:srcUrl] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:300.f];
  288. AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
  289. //注意这里progress/destination是异步线程 completionHandler是main-thread
  290. NSURLSessionDownloadTask* task = [manager downloadTaskWithRequest:downloadReq progress:^(NSProgress * _Nonnull downloadProgress) {
  291. if (callback != nil) {
  292. callback(downloadProgress.completedUnitCount / (float)downloadProgress.totalUnitCount, nil);
  293. }
  294. } destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath_, NSURLResponse * _Nonnull response) {
  295. return [NSURL fileURLWithPath:dstUrl];
  296. } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
  297. if (error) {
  298. if(callback){
  299. callback(-1, nil);
  300. }
  301. return;
  302. }
  303. else{
  304. if(callback){
  305. callback(0, dstUrl);
  306. }
  307. }
  308. }];
  309. [task resume];
  310. return task;
  311. }
  312. @end
  313. @implementation TCBGMElement
  314. - (id) initWithCoder: (NSCoder *)coder
  315. {
  316. if (self = [super init])
  317. {
  318. self.name = [coder decodeObjectForKey:@"name"];
  319. self.netUrl = [coder decodeObjectForKey:@"netUrl"];
  320. self.localUrl = [coder decodeObjectForKey:@"localUrl"];
  321. self.author = [coder decodeObjectForKey:@"author"];
  322. self.title = [coder decodeObjectForKey:@"title"];
  323. self.isValid = [coder decodeObjectForKey:@"isValid"];
  324. self.duration = [coder decodeObjectForKey:@"duration"];
  325. }
  326. return self;
  327. }
  328. - (void) encodeWithCoder: (NSCoder *)coder
  329. {
  330. [coder encodeObject:_name forKey:@"name"];
  331. [coder encodeObject:_netUrl forKey:@"netUrl"];
  332. [coder encodeObject:_localUrl forKey:@"localUrl"];
  333. [coder encodeObject:_author forKey:@"author"];
  334. [coder encodeObject:_title forKey:@"title"];
  335. [coder encodeObject:_isValid forKey:@"isValid"];
  336. [coder encodeObject:_duration forKey:@"duration"];
  337. }
  338. @end