M3U8文件的下载总结

一、简述

    最近的项目有个m3u8文件的下载需求,m3u8文件和其他文件有个根本的区别是它只是一个地址的集合文件,我们如果需要下载m3u8文件的话需要将它列表里面所有的文件都一一下载下来,然后还要生成一个播放的排列文件,最后开始播放。

二、难点

1、多文件下载。

2、播放文件的生成。

3、缓存的存取周期管理。

三、相关代码详解

1、为了方便,我们先写一个下载的管理类 BYM3U3DownLoadManger

//返回下载类对象

+ (AriaM3U8Downloader *)plistGetDownloadWith:(NSString *)name dowloadUrl:(NSString *)url;

//删除下载,单集

+ (void)plistDeleteDwonloadWith:(NSString *)name type:(NSString *)poType;

//删除下载,传入电视剧videoID,删除该电视剧下所有剧集

+ (void)plistDeleteAllTVDownloadWith:(NSString *)videoId;

//添加视频下载信息

+ (void)addVideoDownloadDataWith:(NSDictionary *)dict;

+ (NSArray *)getAllVideoDownloadData;

//检查下本地有无下载信息

+ (BOOL)checkDownloadHaveVideoWith:(NSString *)videoId;

//设置本地视频下载状态为已下载

+ (void)setDownloadVideoCompleteWith:(NSString *)videoId;

//传入字典,返回下载类对象

+ (AriaM3U8Downloader *)plistGetDownloadWith:(NSString *)name dowloadUrl:(NSString *)url {

    //先判断临时缓存里面有没有

    __block AriaM3U8Downloader *cashDownload = [AriaM3U8Downloader new];

    __block BOOL isInList = NO;

    NSArray *cashDownloadList = [BYDownloadHelper sharedDownloadMark].downAriaM3U8Arr;

    [cashDownloadList enumerateObjectsUsingBlock:^(BYCashDownloadModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        NSString *cashName = obj.fileName;

        if ([cashName isEqualToString:name]) {

            cashDownload = obj.downModel;

            isInList = YES;

            *stop = YES;

        }

    }];

    if (isInList) {

        return cashDownload;

    }



    //1.创建主文件夹

    [self creatDir:DownloadFile];

    //创建子文件夹

    NSString *file = [DownloadFile stringByAppendingPathComponent:name];

    [self creatDir:file];

    //2.通过 M3U8 URL 进行初始化

    AriaM3U8Downloader *downloader = [[AriaM3U8Downloader alloc] initWithURLString:url outputPath:file tag:[name integerValue]];

    if (downloader) {

        //添加到临时缓存里面去

        BYCashDownloadModel *downModel = [BYCashDownloadModel new];

        downModel.fileName = name;

        downModel.downModel = downloader;

        [[BYDownloadHelper sharedDownloadMark] addNewModelWith:downModel];

    }


    return downloader;

}

//传入字典,删除本地下载文件夹

+ (void)plistDeleteDwonloadWith:(NSString *)name type:(NSString *)poType {

    //1.创建主文件夹

    [self creatDir:DownloadFile];

    //创建子文件夹

    NSString *file = [DownloadFile stringByAppendingPathComponent:name];

    [self creatDir:file];

    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {

        [fileManager removeItemAtPath:file error:nil];

        NSLog(@"缓存清理成功");

    }


    //清除本地存储的视频信息

    if ([poType isEqualToString:@"1"]) {//电影

        NSArray *videoDownloadList = [kUserDefaults archivedForKey:KVideoDownloadList];

        NSMutableArray *newMuarr = [[NSMutableArray alloc] initWithArray:videoDownloadList];

        [videoDownloadList enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

            if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:name]) {

                [newMuarr removeObject:obj];

                *stop = YES;

            }

        }];

        [kUserDefaults setArchived:newMuarr forKey:KVideoDownloadList];

        [kUserDefaults synchronize];

    } else {

        NSArray *videoTVDownloadList = [kUserDefaults archivedForKey:KVideoTVDownloadList];

        NSMutableArray *newMuarr = [[NSMutableArray alloc] initWithArray:videoTVDownloadList];

        __block NSString *espVideoId;

        [videoTVDownloadList enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

            espVideoId = [obj objectOrNilForKey:@"videoId"];

            if ([[obj objectOrNilForKey:@"videoEpisodeId"] isEqualToString:name]) {

                [newMuarr removeObject:obj];

                *stop = YES;

            }

        }];

        [kUserDefaults setArchived:newMuarr forKey:KVideoTVDownloadList];

        [kUserDefaults synchronize];



        //判断下该视频下还有无剧集在本地,没有的话直接把剧集页删了

        if (![self checkDownloadHaveOtherTVVideoWith:espVideoId]) {

            NSArray *videoDownloadList = [kUserDefaults archivedForKey:KVideoDownloadList];

            NSMutableArray *asdMuArr = [[NSMutableArray alloc] initWithArray:videoDownloadList];

            [videoDownloadList enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

                if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:espVideoId]) {

                    [asdMuArr removeObject:obj];

                    *stop = YES;

                }

            }];

            [kUserDefaults setArchived:asdMuArr forKey:KVideoDownloadList];

            [kUserDefaults synchronize];

        }

    }


    //清除临时缓存内的播放器

    NSArray *cashDownloadList = [BYDownloadHelper sharedDownloadMark].downAriaM3U8Arr;

    __block BYCashDownloadModel *deleteModel = [BYCashDownloadModel new];

    [cashDownloadList enumerateObjectsUsingBlock:^(BYCashDownloadModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        NSString *cashName = obj.fileName;

        if ([cashName isEqualToString:name]) {

            deleteModel = obj;

            [deleteModel.downModel stop];//停止下载

            *stop = YES;

        }

    }];

    [[BYDownloadHelper sharedDownloadMark] deleteModelWith:deleteModel];


}

//删除下载,传入电视剧videoID,删除该电视剧下所有剧集

+ (void)plistDeleteAllTVDownloadWith:(NSString *)videoId {

    NSArray *videoTVDownloadList = [kUserDefaults archivedForKey:KVideoTVDownloadList];

    NSMutableArray *newMuarr = [[NSMutableArray alloc] initWithArray:videoTVDownloadList];

    NSMutableArray *deleteLisst = [[NSMutableArray alloc] init];

    [videoTVDownloadList enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:videoId]) {

            [deleteLisst addObject:obj];

            [newMuarr removeObject:obj];

            *stop = YES;

        }

    }];

    [kUserDefaults setArchived:newMuarr forKey:KVideoTVDownloadList];

    [kUserDefaults synchronize];


    //循环删除本地下载文件

    [deleteLisst enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        NSString *fileName = [obj objectOrNilForKey:@"videoEpisodeId"];

        [self creatDir:DownloadFile];

        //创建子文件夹

        NSString *file = [DownloadFile stringByAppendingPathComponent:fileName];

        [self creatDir:file];

        NSFileManager *fileManager = [NSFileManager defaultManager];

        if ([[NSFileManager defaultManager] fileExistsAtPath:file]) {

            [fileManager removeItemAtPath:file error:nil];

            NSLog(@"缓存清理成功");

        }


        //删除临时缓存的播放器

        NSArray *cashDownloadList = [BYDownloadHelper sharedDownloadMark].downAriaM3U8Arr;

        __block BYCashDownloadModel *deleteModel = [BYCashDownloadModel new];

        [cashDownloadList enumerateObjectsUsingBlock:^(BYCashDownloadModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

            NSString *cashName = obj.fileName;

            if ([cashName isEqualToString:fileName]) {

                deleteModel = obj;

                [deleteModel.downModel stop];//停止下载

                *stop = YES;

            }

        }];

        [[BYDownloadHelper sharedDownloadMark] deleteModelWith:deleteModel];

    }];


    //最后删除视频数组里面的数据

    NSArray *videoDownloadList = [kUserDefaults archivedForKey:KVideoDownloadList];

    NSMutableArray *newMuarrList = [[NSMutableArray alloc] initWithArray:videoDownloadList];

    [videoDownloadList enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:videoId]) {

            [newMuarrList removeObject:obj];

            *stop = YES;

        }

    }];

    [kUserDefaults setArchived:newMuarrList forKey:KVideoDownloadList];

    [kUserDefaults synchronize];

}

/**

创建文件夹

@param dirPath 文件夹名称

@return 创建成功或失败

*/

+ (BOOL)creatDir:(NSString *)dirPath {

    if ([[NSFileManager defaultManager] fileExistsAtPath:dirPath]) {//判断dirPath路径文件夹是否已存在,此处dirPath为需要新建的文件夹的绝对路径

        NSLog(@"沙盒目录已经存在%@",dirPath);

        return NO;

    }else {

        [[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil];//创建文件夹

        NSLog(@"创建成功%@",dirPath);

        return YES;

    }

}

//视频下载信息

+ (void)addVideoDownloadDataWith:(NSDictionary *)dict {

    NSArray *videoDownloadList = [kUserDefaults archivedForKey:KVideoDownloadList];

    NSMutableArray *newMuarr = [[NSMutableArray alloc] initWithArray:videoDownloadList];

    [newMuarr addObject:dict];

    [kUserDefaults setArchived:newMuarr forKey:KVideoDownloadList];

    [kUserDefaults synchronize];

}

+ (NSArray *)getAllVideoDownloadData {

    return [kUserDefaults archivedForKey:KVideoDownloadList];

}

+ (BOOL)checkDownloadHaveVideoWith:(NSString *)videoId {

    __block BOOL isHave = NO;

    NSArray *allDataArr = [kUserDefaults archivedForKey:KVideoDownloadList];

    [allDataArr enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:videoId]) {

            isHave = YES;

            *stop = YES;

        }

    }];

    return isHave;

}

//设置本地视频下载状态为已下载

+ (void)setDownloadVideoCompleteWith:(NSString *)videoId {

    NSArray *allDataArr = [kUserDefaults archivedForKey:KVideoDownloadList];

    NSMutableArray *allDataMuArr = [[NSMutableArray alloc] initWithArray:allDataArr];

    [allDataArr enumerateObjectsUsingBlock:^(NSDictionary *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([[obj objectOrNilForKey:@"videoId"] isEqualToString:videoId]) {

            NSMutableDictionary *muDict = [[NSMutableDictionary alloc] initWithDictionary:obj];

            [muDict setObject:@(1) forKey:@"videoComplete"];

            [allDataMuArr replaceObjectAtIndex:idx withObject:muDict];

            *stop = YES;

        }

    }];

    [kUserDefaults setArchived:allDataMuArr forKey:KVideoDownloadList];

    [kUserDefaults synchronize];

}

2、模型

@interface BYCashDownloadModel : NSObject

@property (nonatomic, copy) NSString *fileName;                    //文件名

@property (nonatomic, strong) AriaM3U8Downloader *downModel;        //下载器对象

@end

//下载单例

@interface BYDownloadHelper : NSObject

+ (instancetype)sharedDownloadMark;

@property (nonatomic, strong) NSArray<BYCashDownloadModel *> *downAriaM3U8Arr;        //下载器

- (void)addNewModelWith:(BYCashDownloadModel *)model;

- (void)deleteModelWith:(BYCashDownloadModel *)model;

- (NSString *)nameWithModel:(AriaM3U8Downloader *)download;

@end

+ (instancetype)sharedDownloadMark{

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        _showWaterMark = [[super allocWithZone:NULL] init];

    });


    return _showWaterMark;

}

+ (id)allocWithZone:(struct _NSZone *)zone {

    return [BYDownloadHelper sharedDownloadMark];

}


- (id)copyWithZone:(struct _NSZone *)zone {

    return [BYDownloadHelper sharedDownloadMark];

}

- (void)addNewModelWith:(BYCashDownloadModel *)model {

    NSMutableArray *dataMuArr = [[NSMutableArray alloc] initWithArray:self.downAriaM3U8Arr];

    [dataMuArr addObject:model];

    self.downAriaM3U8Arr = [dataMuArr copy];

}

- (void)deleteModelWith:(BYCashDownloadModel *)model {

    NSMutableArray *dataMuArr = [[NSMutableArray alloc] initWithArray:self.downAriaM3U8Arr];

    [dataMuArr removeObject:model];

    self.downAriaM3U8Arr = [dataMuArr copy];

}

- (NSString *)nameWithModel:(AriaM3U8Downloader *)download {

    NSMutableArray *dataMuArr = [[NSMutableArray alloc] initWithArray:self.downAriaM3U8Arr];

    __block NSString *strName;

    [dataMuArr enumerateObjectsUsingBlock:^(BYCashDownloadModel *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if (obj.downModel == download) {

            strName = obj.fileName;

            *stop = YES;

        }

    }];

    return strName;

}

3、开始下载

NSArray *defaultList = [[self.allUrlPlayDatArr firstObject] objectOrNilForKey:@"defaultList"];

        BYMovieDownLoadView *downLoadView = [[BYMovieDownLoadView alloc] initWithAlertWithArr:defaultList];

        [downLoadView showAlert];

        downLoadView.clickDownloadBlock = ^(NSDictionary * _Nonnull dict) {

            asyncGlobalBlock(^{

                [self startDownloadVideoWith:dict];

            });

        };

//电影下载

- (void)startDownloadVideoWith:(NSDictionary *)dict {

    NSString *url = [dict objectOrNilForKey:@"value"];

    NSString *videoId = self.post_id;

    AriaM3U8Downloader *downloader = [BYM3U3DownLoadManger plistGetDownloadWith:videoId dowloadUrl:url];

    //3.开始下载

    if (downloader.downloadStatus == AriaDownloadStatusIsDownloading) {

        asyncMainBlock(^{

            [kKeyWindow makeToast:@"已经在后台下载了!"];

        });


        return;

    }

    //4.判断本地有无下载

    if ([BYM3U3DownLoadManger checkDownloadHaveVideoWith:videoId]) {

        asyncMainBlock(^{

            [kKeyWindow makeToast:@"本地已存在下载文件!"];

        });

        return;

    }




    [downloader start];

    downloader.downloadTSSuccessExeBlock = ^(NSString *event) {

        NSLog(@"这里是每一个TS下载完成的回调方法:%@", event);

    };

    downloader.downloadStartExeBlock = ^(void) {

        NSLog(@"这里是开始下载的回调方法");

    };


    //下载影片信息存储在本地

    NSDictionary *videoDict = @{@"videoId": self.post_id,

                                @"displayInfo": [self.dataDict objectOrNilForKey:@"displayInfo"],

                                @"videoTitle": [self.dataDict objectOrNilForKey:@"videoTitle"],

                                @"imgUrl": [self.dataDict objectOrNilForKey:@"videoBigImg"],

                                @"videoDownloadUrl": url,

                                @"videoType": self.post_Type,

                                @"videoComplete":@(0)

    };

    [BYM3U3DownLoadManger addVideoDownloadDataWith:videoDict];

    asyncMainBlock(^{

        [kKeyWindow makeToast:@"已开始后台下载,请到下载列表查看"];

    });

}

其他的就是下载进度的一些展示了,播放的方法在这里

//开始播放

                [AriaM3U8LocalServer.shared startWithPath:DownloadFile port:8080 bonjourName:@"AriaM3U8LocalServer"];

//                [playdownloader createLocalM3U8FileWithTSCount:-1];

                NSString *localServer = [AriaM3U8LocalServer.shared getLocalServerURLString];

                if ( localServer == nil ) {

                    NSLog(@"获取Local Server失败...");

                    [self.view makeToast:@"播放失败"];

                    return;

                }

                NSString *indexM3U8 = [NSString stringWithFormat:@"%@/%@/index.m3u8", localServer,[dict objectOrNilForKey:@"videoId"]];

                NSURL *indexURL = [NSURL URLWithString:indexM3U8];

                NSLog(@"%@", indexURL);

                [self startPlayWith:indexURL];

3、用到的Pod工具

swift 下载

pod 'AriaM3U8Downloader', :git => 'https://github.com/moxcomic/AriaM3U8Downloader.git'

pod 'AriaM3U8Downloader/LocalServer', :git => 'https://github.com/moxcomic/AriaM3U8Downloader.git'

后面的以后再说吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容