parent
7ee1347ab4
commit
2a92321785
@ -0,0 +1,44 @@ |
|||||||
|
/* |
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one |
||||||
|
* or more contributor license agreements. See the NOTICE file |
||||||
|
* distributed with this work for additional information |
||||||
|
* regarding copyright ownership. The ASF licenses this file |
||||||
|
* to you under the Apache License, Version 2.0 (the |
||||||
|
* "License"); you may not use this file except in compliance |
||||||
|
* with the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package com.arialyy.aria.core.command; |
||||||
|
|
||||||
|
import com.arialyy.aria.core.download.AbsGroupTaskWrapper; |
||||||
|
import com.arialyy.aria.core.task.ITask; |
||||||
|
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; |
||||||
|
|
||||||
|
public class CmdHelper { |
||||||
|
/** |
||||||
|
* 创建任务命令 |
||||||
|
* |
||||||
|
* @param taskType {@link ITask#DOWNLOAD}、{@link ITask#DOWNLOAD_GROUP}、{@link ITask#UPLOAD} |
||||||
|
*/ |
||||||
|
public static <T extends AbsTaskWrapper> AbsNormalCmd createNormalCmd(T entity, int cmd, |
||||||
|
int taskType) { |
||||||
|
return NormalCmdFactory.getInstance().createCmd(entity, cmd, taskType); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建任务组命令 |
||||||
|
* |
||||||
|
* @param childUrl 子任务url |
||||||
|
*/ |
||||||
|
public static <T extends AbsGroupTaskWrapper> AbsGroupCmd createGroupCmd(T entity, int cmd, |
||||||
|
String childUrl) { |
||||||
|
return GroupCmdFactory.getInstance().createCmd(entity, cmd, childUrl); |
||||||
|
} |
||||||
|
} |
@ -1,457 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.common; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.config.Configuration; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.m3u8.BaseM3U8Loader; |
|
||||||
import com.arialyy.aria.core.inf.AbsNormalEntity; |
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.ITaskWrapper; |
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
|
||||||
import com.arialyy.aria.orm.DbEntity; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.CommonUtil; |
|
||||||
import com.arialyy.aria.util.DbDataHelper; |
|
||||||
import com.arialyy.aria.util.FileUtil; |
|
||||||
import com.arialyy.aria.util.RecordUtil; |
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.HashSet; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Properties; |
|
||||||
import java.util.Set; |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理任务记录,分配线程区间 |
|
||||||
*/ |
|
||||||
public class RecordHandler { |
|
||||||
private final String TAG = "RecordHandler"; |
|
||||||
|
|
||||||
public static final int TYPE_DOWNLOAD = 1; |
|
||||||
public static final int TYPE_UPLOAD = 2; |
|
||||||
public static final int TYPE_M3U8_VOD = 3; |
|
||||||
public static final int TYPE_M3U8_LIVE = 4; |
|
||||||
|
|
||||||
private static final String STATE = "_state_"; |
|
||||||
private static final String RECORD = "_record_"; |
|
||||||
/** |
|
||||||
* 小于1m的文件不启用多线程 |
|
||||||
*/ |
|
||||||
private static final long SUB_LEN = 1024 * 1024; |
|
||||||
|
|
||||||
/** |
|
||||||
* 分块文件路径 |
|
||||||
*/ |
|
||||||
public static final String SUB_PATH = "%s.%s.part"; |
|
||||||
|
|
||||||
@Deprecated private File mConfigFile; |
|
||||||
private TaskRecord mTaskRecord; |
|
||||||
private AbsTaskWrapper mTaskWrapper; |
|
||||||
private AbsNormalEntity mEntity; |
|
||||||
|
|
||||||
RecordHandler(AbsTaskWrapper wrapper) { |
|
||||||
mTaskWrapper = wrapper; |
|
||||||
mEntity = (AbsNormalEntity) mTaskWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取任务记录,如果任务记录存在,检查任务记录 |
|
||||||
* 检查记录 对于分块任务: 子分块不存在或被删除,子线程将重新下载 |
|
||||||
* 对于普通任务: 预下载文件不存在,则任务任务呗删除 |
|
||||||
* 如果任务记录不存在或线程记录不存在,初始化记录 |
|
||||||
* |
|
||||||
* @return 任务记录 |
|
||||||
*/ |
|
||||||
TaskRecord getRecord() { |
|
||||||
mConfigFile = new File(CommonUtil.getFileConfigPath(false, mEntity.getFileName())); |
|
||||||
if (mConfigFile.exists()) { |
|
||||||
convertDb(); |
|
||||||
} else { |
|
||||||
mTaskRecord = DbDataHelper.getTaskRecord(getFilePath()); |
|
||||||
if (mTaskRecord == null) { |
|
||||||
initRecord(true); |
|
||||||
} else { |
|
||||||
if (mTaskRecord.threadRecords == null || mTaskRecord.threadRecords.isEmpty()) { |
|
||||||
initRecord(false); |
|
||||||
} |
|
||||||
|
|
||||||
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { |
|
||||||
handleM3U8Record(); |
|
||||||
} else if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { |
|
||||||
ALog.i(TAG, "直播下载不处理历史记录"); |
|
||||||
} else { |
|
||||||
if (mTaskRecord.isBlock) { |
|
||||||
handleBlockRecord(); |
|
||||||
} else if (!mTaskWrapper.isSupportBP()) { |
|
||||||
handleNoSupportBPRecord(); |
|
||||||
} else if (!mTaskRecord.isBlock && mTaskRecord.threadNum > 1) { |
|
||||||
handleNoBlockMultiThreadRecord(); |
|
||||||
} else { |
|
||||||
handleSingleThreadRecord(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
saveRecord(); |
|
||||||
return mTaskRecord; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理m3u8记录, |
|
||||||
* 1、如果分片文件存在,并且分片文件的记录没有完成,则需要删除该分片文件 |
|
||||||
* 2、如果记录显示已完成,但是分片文件不存在,则重新开始该分片 |
|
||||||
* 3、如果记录显示已完成,并且文件存在,记录当前任务进度 |
|
||||||
*/ |
|
||||||
private void handleM3U8Record() { |
|
||||||
DTaskWrapper wrapper = (DTaskWrapper) mTaskWrapper; |
|
||||||
String cacheDir = wrapper.asM3U8().getCacheDir(); |
|
||||||
long currentProgress = 0; |
|
||||||
int completeNum = 0; |
|
||||||
for (ThreadRecord record : mTaskRecord.threadRecords) { |
|
||||||
File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); |
|
||||||
if (!record.isComplete) { |
|
||||||
if (temp.exists()) { |
|
||||||
temp.delete(); |
|
||||||
} |
|
||||||
record.startLocation = 0; |
|
||||||
//ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId));
|
|
||||||
} else { |
|
||||||
if (!temp.exists()) { |
|
||||||
record.startLocation = 0; |
|
||||||
record.isComplete = false; |
|
||||||
ALog.w(TAG, String.format("分片【%s】不存在,将重新下载该分片", record.threadId)); |
|
||||||
} else { |
|
||||||
completeNum++; |
|
||||||
currentProgress += temp.length(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
wrapper.asM3U8().setCompleteNum(completeNum); |
|
||||||
wrapper.getEntity().setCurrentProgress(currentProgress); |
|
||||||
mTaskRecord.bandWidth = wrapper.asM3U8().getBandWidth(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理不支持断点的记录 |
|
||||||
*/ |
|
||||||
private void handleNoSupportBPRecord() { |
|
||||||
ThreadRecord tr = mTaskRecord.threadRecords.get(0); |
|
||||||
tr.startLocation = 0; |
|
||||||
tr.endLocation = mEntity.getFileSize(); |
|
||||||
tr.taskKey = mTaskRecord.filePath; |
|
||||||
tr.blockLen = tr.endLocation; |
|
||||||
tr.isComplete = false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理为不分块的多线程任务 |
|
||||||
*/ |
|
||||||
private void handleNoBlockMultiThreadRecord() { |
|
||||||
File file = new File(mTaskRecord.filePath); |
|
||||||
if (!file.exists()) { |
|
||||||
ALog.w(TAG, String.format("文件【%s】不存在,重新分配线程区间", mTaskRecord.filePath)); |
|
||||||
DbEntity.deleteData(ThreadRecord.class, "taskKey=?", mTaskRecord.filePath); |
|
||||||
initRecord(false); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理单线程的任务的记录 |
|
||||||
*/ |
|
||||||
private void handleSingleThreadRecord() { |
|
||||||
File file = new File(mTaskRecord.filePath); |
|
||||||
ThreadRecord tr = mTaskRecord.threadRecords.get(0); |
|
||||||
if (!file.exists()) { |
|
||||||
ALog.w(TAG, String.format("文件【%s】不存在,任务将重新开始", file.getPath())); |
|
||||||
tr.startLocation = 0; |
|
||||||
tr.isComplete = false; |
|
||||||
tr.endLocation = mEntity.getFileSize(); |
|
||||||
} else if (mTaskRecord.isOpenDynamicFile) { |
|
||||||
if (file.length() > mEntity.getFileSize()) { |
|
||||||
ALog.i(TAG, String.format("文件【%s】错误,任务重新开始", file.getPath())); |
|
||||||
file.delete(); |
|
||||||
tr.startLocation = 0; |
|
||||||
tr.isComplete = false; |
|
||||||
tr.endLocation = mEntity.getFileSize(); |
|
||||||
} else if (file.length() == mEntity.getFileSize()) { |
|
||||||
tr.isComplete = true; |
|
||||||
} else { |
|
||||||
if (file.length() != tr.startLocation) { |
|
||||||
ALog.i(TAG, String.format("修正【%s】的进度记录为:%s", file.getPath(), file.length())); |
|
||||||
tr.startLocation = file.length(); |
|
||||||
tr.isComplete = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
mTaskWrapper.setNewTask(false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 处理分块任务的记录,分块文件(blockFileLen)长度必须需要小于等于线程区间(threadRectLen)的长度 |
|
||||||
*/ |
|
||||||
private void handleBlockRecord() { |
|
||||||
// 默认线程分块长度
|
|
||||||
long normalRectLen = mEntity.getFileSize() / mTaskRecord.threadRecords.size(); |
|
||||||
for (ThreadRecord tr : mTaskRecord.threadRecords) { |
|
||||||
long threadRect = tr.blockLen; |
|
||||||
|
|
||||||
File temp = new File(String.format(SUB_PATH, mTaskRecord.filePath, tr.threadId)); |
|
||||||
if (!temp.exists()) { |
|
||||||
ALog.i(TAG, String.format("分块文件【%s】不存在,该分块将重新开始", temp.getPath())); |
|
||||||
tr.isComplete = false; |
|
||||||
tr.startLocation = tr.threadId * normalRectLen; |
|
||||||
} else { |
|
||||||
if (!tr.isComplete) { |
|
||||||
ALog.i(TAG, String.format( |
|
||||||
"startLocation = %s; endLocation = %s; block = %s; tempLen = %s; threadId = %s", |
|
||||||
tr.startLocation, tr.endLocation, threadRect, temp.length(), tr.threadId)); |
|
||||||
|
|
||||||
long blockFileLen = temp.length(); // 磁盘中的分块文件长度
|
|
||||||
/* |
|
||||||
* 检查磁盘中的分块文件 |
|
||||||
*/ |
|
||||||
if (blockFileLen > threadRect) { |
|
||||||
ALog.i(TAG, String.format("分块【%s】错误,分块长度【%s】 > 线程区间长度【%s】,将重新开始该分块", |
|
||||||
tr.threadId, blockFileLen, threadRect)); |
|
||||||
temp.delete(); |
|
||||||
tr.startLocation = tr.threadId * threadRect; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
long realLocation = |
|
||||||
tr.threadId * normalRectLen + blockFileLen; //正常情况下,该线程的startLocation的位置
|
|
||||||
/* |
|
||||||
* 检查记录文件 |
|
||||||
*/ |
|
||||||
if (blockFileLen == threadRect) { |
|
||||||
ALog.i(TAG, String.format("分块【%s】已完成,更新记录", temp.getPath())); |
|
||||||
tr.startLocation = blockFileLen; |
|
||||||
tr.isComplete = true; |
|
||||||
} else if (tr.startLocation != realLocation) { // 处理记录小于分块文件长度的情况
|
|
||||||
ALog.i(TAG, String.format("修正分块【%s】的进度记录为:%s", temp.getPath(), realLocation)); |
|
||||||
tr.startLocation = realLocation; |
|
||||||
} |
|
||||||
} else { |
|
||||||
ALog.i(TAG, String.format("分块【%s】已完成", temp.getPath())); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
mTaskWrapper.setNewTask(false); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* convertDb 是兼容性代码 从3.4.1开始,线程配置信息将存储在数据库中。 将配置文件的内容复制到数据库中,并将配置文件删除 |
|
||||||
*/ |
|
||||||
private void convertDb() { |
|
||||||
List<RecordWrapper> records = |
|
||||||
DbEntity.findRelationData(RecordWrapper.class, "TaskRecord.filePath=?", |
|
||||||
getFilePath()); |
|
||||||
if (records == null || records.size() == 0) { |
|
||||||
Properties pro = FileUtil.loadConfig(mConfigFile); |
|
||||||
if (pro.isEmpty()) { |
|
||||||
ALog.d(TAG, "老版本的线程记录为空,任务为新任务"); |
|
||||||
initRecord(true); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
Set<Object> keys = pro.keySet(); |
|
||||||
// 老版本记录是5s存一次,但是5s中内,如果线程执行完成,record记录是没有的,只有state记录...
|
|
||||||
// 第一步应该是record 和 state去重取正确的线程数
|
|
||||||
Set<Integer> set = new HashSet<>(); |
|
||||||
for (Object key : keys) { |
|
||||||
String str = String.valueOf(key); |
|
||||||
int i = Integer.parseInt(str.substring(str.length() - 1)); |
|
||||||
set.add(i); |
|
||||||
} |
|
||||||
int threadNum = set.size(); |
|
||||||
if (threadNum == 0) { |
|
||||||
ALog.d(TAG, "线程数为空,任务为新任务"); |
|
||||||
initRecord(true); |
|
||||||
return; |
|
||||||
} |
|
||||||
mTaskWrapper.setNewTask(false); |
|
||||||
mTaskRecord = createTaskRecord(threadNum); |
|
||||||
mTaskRecord.isOpenDynamicFile = false; |
|
||||||
mTaskRecord.isBlock = false; |
|
||||||
File tempFile = new File(getFilePath()); |
|
||||||
for (int i = 0; i < threadNum; i++) { |
|
||||||
ThreadRecord tRecord = new ThreadRecord(); |
|
||||||
tRecord.taskKey = mTaskRecord.filePath; |
|
||||||
Object state = pro.getProperty(tempFile.getName() + STATE + i); |
|
||||||
Object record = pro.getProperty(tempFile.getName() + RECORD + i); |
|
||||||
if (state != null && Integer.parseInt(String.valueOf(state)) == 1) { |
|
||||||
tRecord.isComplete = true; |
|
||||||
continue; |
|
||||||
} |
|
||||||
if (record != null) { |
|
||||||
long temp = Long.parseLong(String.valueOf(record)); |
|
||||||
tRecord.startLocation = temp > 0 ? temp : 0; |
|
||||||
} else { |
|
||||||
tRecord.startLocation = 0; |
|
||||||
} |
|
||||||
mTaskRecord.threadRecords.add(tRecord); |
|
||||||
} |
|
||||||
mConfigFile.delete(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 初始化任务记录,分配线程区间 |
|
||||||
* |
|
||||||
* @param newRecord {@code true} 需要创建新{@link TaskRecord} |
|
||||||
*/ |
|
||||||
private void initRecord(boolean newRecord) { |
|
||||||
if (newRecord) { |
|
||||||
mTaskRecord = createTaskRecord(getNewTaskThreadNum()); |
|
||||||
} |
|
||||||
mTaskWrapper.setNewTask(true); |
|
||||||
int requestType = mTaskWrapper.getRequestType(); |
|
||||||
if (requestType == ITaskWrapper.M3U8_LIVE) { |
|
||||||
return; |
|
||||||
} |
|
||||||
long blockSize = mEntity.getFileSize() / mTaskRecord.threadNum; |
|
||||||
// 处理线程区间记录
|
|
||||||
for (int i = 0; i < mTaskRecord.threadNum; i++) { |
|
||||||
long startL = i * blockSize, endL = (i + 1) * blockSize; |
|
||||||
ThreadRecord tr; |
|
||||||
tr = new ThreadRecord(); |
|
||||||
tr.taskKey = mTaskRecord.filePath; |
|
||||||
tr.threadId = i; |
|
||||||
tr.startLocation = startL; |
|
||||||
tr.isComplete = false; |
|
||||||
if (requestType == ITaskWrapper.M3U8_VOD) { |
|
||||||
tr.startLocation = 0; |
|
||||||
tr.threadType = TaskRecord.TYPE_M3U8_VOD; |
|
||||||
tr.tsUrl = ((DTaskWrapper) mTaskWrapper).asM3U8().getUrls().get(i); |
|
||||||
} else { |
|
||||||
tr.threadType = TaskRecord.TYPE_HTTP_FTP; |
|
||||||
//最后一个线程的结束位置即为文件的总长度
|
|
||||||
if (i == (mTaskRecord.threadNum - 1)) { |
|
||||||
endL = mEntity.getFileSize(); |
|
||||||
} |
|
||||||
tr.endLocation = endL; |
|
||||||
tr.blockLen = RecordUtil.getBlockLen(mEntity.getFileSize(), i, mTaskRecord.threadNum); |
|
||||||
} |
|
||||||
mTaskRecord.threadRecords.add(tr); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 创建任务记录 |
|
||||||
* |
|
||||||
* @param threadNum 线程总数 |
|
||||||
*/ |
|
||||||
private TaskRecord createTaskRecord(int threadNum) { |
|
||||||
TaskRecord record = new TaskRecord(); |
|
||||||
record.fileName = mEntity.getFileName(); |
|
||||||
record.filePath = getFilePath(); |
|
||||||
record.threadRecords = new ArrayList<>(); |
|
||||||
record.threadNum = threadNum; |
|
||||||
int requestType = mTaskWrapper.getRequestType(); |
|
||||||
if (requestType == ITaskWrapper.M3U8_VOD) { |
|
||||||
record.taskType = TaskRecord.TYPE_M3U8_VOD; |
|
||||||
record.isOpenDynamicFile = true; |
|
||||||
record.bandWidth = ((DTaskWrapper)mTaskWrapper).asM3U8().getBandWidth(); |
|
||||||
} else if (requestType == ITaskWrapper.M3U8_LIVE) { |
|
||||||
record.taskType = TaskRecord.TYPE_M3U8_LIVE; |
|
||||||
record.isOpenDynamicFile = true; |
|
||||||
record.bandWidth = ((DTaskWrapper)mTaskWrapper).asM3U8().getBandWidth(); |
|
||||||
} else { |
|
||||||
if (getRecordType() == TYPE_DOWNLOAD) { |
|
||||||
record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock(); |
|
||||||
// 线程数为1,或者使用了分块,则认为是使用动态长度文件
|
|
||||||
record.isOpenDynamicFile = threadNum == 1 || record.isBlock; |
|
||||||
} else { |
|
||||||
record.isBlock = false; |
|
||||||
} |
|
||||||
record.taskType = TaskRecord.TYPE_HTTP_FTP; |
|
||||||
record.isGroupRecord = mEntity.isGroupChild(); |
|
||||||
if (record.isGroupRecord) { |
|
||||||
if (mEntity instanceof DownloadEntity) { |
|
||||||
record.dGroupHash = ((DownloadEntity) mEntity).getGroupHash(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return record; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 保存任务记录 |
|
||||||
*/ |
|
||||||
private void saveRecord() { |
|
||||||
mTaskRecord.threadNum = mTaskRecord.threadRecords.size(); |
|
||||||
mTaskRecord.save(); |
|
||||||
if (mTaskRecord.threadRecords != null && !mTaskRecord.threadRecords.isEmpty()) { |
|
||||||
DbEntity.saveAll(mTaskRecord.threadRecords); |
|
||||||
} |
|
||||||
ALog.d(TAG, String.format("保存记录,线程记录数:%s", mTaskRecord.threadRecords.size())); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取记录类型 |
|
||||||
* |
|
||||||
* @return {@link #TYPE_DOWNLOAD}、{@link #TYPE_UPLOAD} |
|
||||||
*/ |
|
||||||
private int getRecordType() { |
|
||||||
if (mEntity instanceof DownloadEntity) { |
|
||||||
return TYPE_DOWNLOAD; |
|
||||||
} else { |
|
||||||
return TYPE_UPLOAD; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取任务路径 |
|
||||||
* |
|
||||||
* @return 任务文件路径 |
|
||||||
*/ |
|
||||||
private String getFilePath() { |
|
||||||
if (mEntity instanceof DownloadEntity) { |
|
||||||
return ((DownloadEntity) mTaskWrapper.getEntity()).getFilePath(); |
|
||||||
} else { |
|
||||||
return ((UploadEntity) mTaskWrapper.getEntity()).getFilePath(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 小于1m的文件或是任务组的子任务、线程数强制为1 |
|
||||||
* 不支持断点或chunked模式的线程数都为,线程数强制为1 |
|
||||||
*/ |
|
||||||
private int getNewTaskThreadNum() { |
|
||||||
if (getRecordType() == TYPE_DOWNLOAD) { |
|
||||||
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { |
|
||||||
return ((DTaskWrapper) mTaskWrapper).asM3U8().getUrls().size(); |
|
||||||
} |
|
||||||
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE) { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
if (!mTaskWrapper.isSupportBP() || mTaskWrapper.asHttp().isChunked()) { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
int threadNum = Configuration.getInstance().downloadCfg.getThreadNum(); |
|
||||||
return mEntity.getFileSize() <= SUB_LEN |
|
||||||
|| mEntity.isGroupChild() |
|
||||||
|| threadNum == 1 |
|
||||||
? 1 |
|
||||||
: threadNum; |
|
||||||
} else { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,129 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.AbsThreadTask; |
|
||||||
import com.arialyy.aria.core.common.NormalFileer; |
|
||||||
import com.arialyy.aria.core.common.RecordHandler; |
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.event.Event; |
|
||||||
import com.arialyy.aria.core.event.SpeedEvent; |
|
||||||
import com.arialyy.aria.core.inf.IDownloadListener; |
|
||||||
import com.arialyy.aria.core.inf.ITaskWrapper; |
|
||||||
import com.arialyy.aria.exception.BaseException; |
|
||||||
import com.arialyy.aria.exception.TaskException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
import com.arialyy.aria.util.BufferedRandomAccessFile; |
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by AriaL on 2017/7/1. 文件下载器 |
|
||||||
*/ |
|
||||||
public class Downloader extends NormalFileer<DownloadEntity, DTaskWrapper> { |
|
||||||
private String TAG = "Downloader"; |
|
||||||
|
|
||||||
public Downloader(IDownloadListener listener, DTaskWrapper taskWrapper) { |
|
||||||
super(listener, taskWrapper); |
|
||||||
mTempFile = new File(mEntity.getFilePath()); |
|
||||||
setUpdateInterval(taskWrapper.getConfig().getUpdateInterval()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override protected boolean handleNewTask() { |
|
||||||
if (!mRecord.isBlock) { |
|
||||||
if (mTempFile.exists()) { |
|
||||||
mTempFile.delete(); |
|
||||||
} |
|
||||||
//CommonUtil.createFile(mTempFile.getPath());
|
|
||||||
} else { |
|
||||||
for (int i = 0; i < mTotalThreadNum; i++) { |
|
||||||
File blockFile = new File(String.format(RecordHandler.SUB_PATH, mTempFile.getPath(), i)); |
|
||||||
if (blockFile.exists()) { |
|
||||||
ALog.d(TAG, String.format("分块【%s】已经存在,将删除该分块", i)); |
|
||||||
blockFile.delete(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
BufferedRandomAccessFile file = null; |
|
||||||
try { |
|
||||||
if (mTotalThreadNum > 1 && !mRecord.isBlock) { |
|
||||||
file = new BufferedRandomAccessFile(new File(mTempFile.getPath()), "rwd", 8192); |
|
||||||
//设置文件长度
|
|
||||||
file.setLength(mEntity.getFileSize()); |
|
||||||
} |
|
||||||
return true; |
|
||||||
} catch (IOException e) { |
|
||||||
failDownload(new TaskException(TAG, |
|
||||||
String.format("下载失败,filePath: %s, url: %s", mEntity.getDownloadPath(), |
|
||||||
mEntity.getUrl()), e)); |
|
||||||
} finally { |
|
||||||
if (file != null) { |
|
||||||
try { |
|
||||||
file.close(); |
|
||||||
} catch (IOException e) { |
|
||||||
e.printStackTrace(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 如果使用"Content-Disposition"中的文件名,需要更新{@link #mTempFile}的路径 |
|
||||||
*/ |
|
||||||
void updateTempFile() { |
|
||||||
if (!mTempFile.getPath().equals(mEntity.getFilePath())) { |
|
||||||
if (!mTempFile.exists()) { |
|
||||||
mTempFile = new File(mEntity.getFilePath()); |
|
||||||
} else { |
|
||||||
boolean b = mTempFile.renameTo(new File(mEntity.getDownloadPath())); |
|
||||||
ALog.d(TAG, String.format("更新tempFile文件名%s", b ? "成功" : "失败")); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Event |
|
||||||
public void setMaxSpeed(SpeedEvent event) { |
|
||||||
setMaxSpeed(event.speed); |
|
||||||
} |
|
||||||
|
|
||||||
@Override protected void onPostPre() { |
|
||||||
super.onPostPre(); |
|
||||||
((IDownloadListener) mListener).onPostPre(mEntity.getFileSize()); |
|
||||||
File file = new File(mEntity.getDownloadPath()); |
|
||||||
if (!file.getParentFile().exists()) { |
|
||||||
file.getParentFile().mkdirs(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override protected AbsThreadTask selectThreadTask(SubThreadConfig<DTaskWrapper> config) { |
|
||||||
switch (mTaskWrapper.getRequestType()) { |
|
||||||
case ITaskWrapper.D_FTP: |
|
||||||
case ITaskWrapper.D_FTP_DIR: |
|
||||||
return new FtpThreadTask(config); |
|
||||||
case ITaskWrapper.D_HTTP: |
|
||||||
return new HttpThreadTask(config); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
private void failDownload(BaseException e) { |
|
||||||
closeTimer(); |
|
||||||
mListener.onFail(false, e); |
|
||||||
} |
|
||||||
} |
|
@ -1,140 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
|
|
||||||
package com.arialyy.aria.core.download.downloader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.common.IUtil; |
|
||||||
import com.arialyy.aria.core.common.OnFileInfoCallback; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.AbsEntity; |
|
||||||
import com.arialyy.aria.core.inf.IDownloadListener; |
|
||||||
import com.arialyy.aria.core.inf.ITaskWrapper; |
|
||||||
import com.arialyy.aria.exception.BaseException; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2015/8/25. |
|
||||||
* D_HTTP\FTP单任务下载工具 |
|
||||||
*/ |
|
||||||
public class SimpleDownloadUtil implements IUtil { |
|
||||||
private String TAG = "SimpleDownloadUtil"; |
|
||||||
private IDownloadListener mListener; |
|
||||||
private Downloader mDownloader; |
|
||||||
private DTaskWrapper mTaskWrapper; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
|
|
||||||
public SimpleDownloadUtil(DTaskWrapper wrapper, IDownloadListener downloadListener) { |
|
||||||
mTaskWrapper = wrapper; |
|
||||||
mListener = downloadListener; |
|
||||||
mDownloader = new Downloader(downloadListener, wrapper); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mTaskWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return mDownloader.getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取当前下载位置 |
|
||||||
*/ |
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return mDownloader.getCurrentLocation(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mDownloader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 取消下载 |
|
||||||
*/ |
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
mDownloader.cancel(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 停止下载 |
|
||||||
*/ |
|
||||||
@Override public void stop() { |
|
||||||
isStop = true; |
|
||||||
mDownloader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 多线程断点续传下载文件,开始下载 |
|
||||||
*/ |
|
||||||
@Override public void start() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onPre(); |
|
||||||
// 如果网址没有变,而服务器端端文件改变,以下代码就没有用了
|
|
||||||
//if (mTaskWrapper.getEntity().getFileSize() <= 1
|
|
||||||
// || mTaskWrapper.isRefreshInfo()
|
|
||||||
// || mTaskWrapper.getRequestType() == AbsTaskWrapper.D_FTP
|
|
||||||
// || mTaskWrapper.getState() == IEntity.STATE_FAIL) {
|
|
||||||
// new Thread(createInfoThread()).create();
|
|
||||||
//} else {
|
|
||||||
// mDownloader.create();
|
|
||||||
//}
|
|
||||||
new Thread(createInfoThread()).start(); |
|
||||||
} |
|
||||||
|
|
||||||
private void failDownload(BaseException e, boolean needRetry) { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onFail(needRetry, e); |
|
||||||
mDownloader.onDestroy(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 通过链接类型创建不同的获取文件信息的线程 |
|
||||||
*/ |
|
||||||
private Runnable createInfoThread() { |
|
||||||
switch (mTaskWrapper.getRequestType()) { |
|
||||||
case ITaskWrapper.D_FTP: |
|
||||||
return new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { |
|
||||||
@Override public void onComplete(String url, CompleteInfo info) { |
|
||||||
mDownloader.updateTempFile(); |
|
||||||
mDownloader.start(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
|
||||||
failDownload(e, needRetry); |
|
||||||
mDownloader.closeTimer(); |
|
||||||
} |
|
||||||
}); |
|
||||||
case ITaskWrapper.D_HTTP: |
|
||||||
return new HttpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { |
|
||||||
@Override public void onComplete(String url, CompleteInfo info) { |
|
||||||
mDownloader.updateTempFile(); |
|
||||||
mDownloader.start(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
|
||||||
failDownload(e, needRetry); |
|
||||||
mDownloader.closeTimer(); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
@ -1,133 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.download.group; |
|
||||||
|
|
||||||
import android.os.Handler; |
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.common.IUtil; |
|
||||||
import com.arialyy.aria.core.common.OnFileInfoCallback; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.DownloadEntity; |
|
||||||
import com.arialyy.aria.core.download.downloader.Downloader; |
|
||||||
import com.arialyy.aria.core.download.downloader.HttpFileInfoThread; |
|
||||||
import com.arialyy.aria.core.inf.AbsEntity; |
|
||||||
import com.arialyy.aria.core.inf.ITaskWrapper; |
|
||||||
import com.arialyy.aria.core.scheduler.ISchedulers; |
|
||||||
import com.arialyy.aria.exception.BaseException; |
|
||||||
import com.arialyy.aria.util.ALog; |
|
||||||
|
|
||||||
/** |
|
||||||
* 子任务下载器,负责创建{@link Downloader} |
|
||||||
*/ |
|
||||||
class SubDLoadUtil implements IUtil { |
|
||||||
private final String TAG = "SubDownloadLoader"; |
|
||||||
|
|
||||||
private Downloader mDownloader; |
|
||||||
private DTaskWrapper mWrapper; |
|
||||||
private Handler mSchedulers; |
|
||||||
private ChildDLoadListener mListener; |
|
||||||
private boolean needGetInfo; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param schedulers 调度器 |
|
||||||
* @param needGetInfo {@code true} 需要获取文件信息。{@code false} 不需要获取文件信息 |
|
||||||
*/ |
|
||||||
SubDLoadUtil(Handler schedulers, DTaskWrapper taskWrapper, boolean needGetInfo) { |
|
||||||
mWrapper = taskWrapper; |
|
||||||
mSchedulers = schedulers; |
|
||||||
this.needGetInfo = needGetInfo; |
|
||||||
mListener = new ChildDLoadListener(mSchedulers, SubDLoadUtil.this); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
public DTaskWrapper getWrapper() { |
|
||||||
return mWrapper; |
|
||||||
} |
|
||||||
|
|
||||||
public DownloadEntity getEntity() { |
|
||||||
return mWrapper.getEntity(); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 重新开始任务 |
|
||||||
*/ |
|
||||||
void reStart() { |
|
||||||
if (mDownloader != null) { |
|
||||||
mDownloader.retryTask(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public Downloader getDownloader() { |
|
||||||
return mDownloader; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return mDownloader == null ? -1 : mDownloader.getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return mDownloader == null ? -1 : mDownloader.getCurrentLocation(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mDownloader != null && mDownloader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
if (mDownloader != null && isRunning()) { |
|
||||||
mDownloader.cancel(); |
|
||||||
} else { |
|
||||||
mSchedulers.obtainMessage(ISchedulers.CANCEL, this).sendToTarget(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
if (mDownloader != null && isRunning()) { |
|
||||||
mDownloader.stop(); |
|
||||||
} else { |
|
||||||
mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void start() { |
|
||||||
if (mWrapper.getRequestType() == ITaskWrapper.D_HTTP) { |
|
||||||
if (needGetInfo) { |
|
||||||
new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() { |
|
||||||
|
|
||||||
@Override public void onComplete(String url, CompleteInfo info) { |
|
||||||
mDownloader = new Downloader(mListener, mWrapper); |
|
||||||
mDownloader.start(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
|
||||||
mSchedulers.obtainMessage(ISchedulers.FAIL, SubDLoadUtil.this).sendToTarget(); |
|
||||||
} |
|
||||||
})).start(); |
|
||||||
} else { |
|
||||||
mDownloader = new Downloader(mListener, mWrapper); |
|
||||||
mDownloader.start(); |
|
||||||
} |
|
||||||
} else if (mWrapper.getRequestType() == ITaskWrapper.D_FTP) { |
|
||||||
mDownloader = new Downloader(mListener, mWrapper); |
|
||||||
mDownloader.start(); |
|
||||||
} else { |
|
||||||
ALog.w(TAG, String.format("不识别的类型,requestType:%s", mWrapper.getRequestType())); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,145 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.download.m3u8; |
|
||||||
|
|
||||||
import android.text.TextUtils; |
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.common.IUtil; |
|
||||||
import com.arialyy.aria.core.common.OnFileInfoCallback; |
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper; |
|
||||||
import com.arialyy.aria.core.download.M3U8Listener; |
|
||||||
import com.arialyy.aria.core.inf.AbsEntity; |
|
||||||
import com.arialyy.aria.exception.BaseException; |
|
||||||
import com.arialyy.aria.exception.M3U8Exception; |
|
||||||
import java.io.File; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Collection; |
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
/** |
|
||||||
* M3U8点播文件下载工具 |
|
||||||
* 工作流程: |
|
||||||
* 1、创建一个和文件同父路径并且同名隐藏文件夹 |
|
||||||
* 2、将所有m3u8的ts文件下载到该文件夹中 |
|
||||||
* 3、完成所有分片下载后,合并ts文件 |
|
||||||
* 4、删除该隐藏文件夹 |
|
||||||
*/ |
|
||||||
public class M3U8VodUtil implements IUtil { |
|
||||||
private final String TAG = "M3U8DownloadUtil"; |
|
||||||
|
|
||||||
private DTaskWrapper mWrapper; |
|
||||||
private M3U8Listener mListener; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
private List<String> mUrls = new ArrayList<>(); |
|
||||||
private M3U8VodLoader mLoader; |
|
||||||
|
|
||||||
public M3U8VodUtil(DTaskWrapper wrapper, M3U8Listener listener) { |
|
||||||
mWrapper = wrapper; |
|
||||||
mListener = listener; |
|
||||||
mLoader = new M3U8VodLoader(mListener, mWrapper); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mLoader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
mLoader.cancel(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
isStop = true; |
|
||||||
mLoader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void start() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onPre(); |
|
||||||
// peer数量小于0,
|
|
||||||
M3U8Entity m3U8Entity = mWrapper.getEntity().getM3U8Entity(); |
|
||||||
if (m3U8Entity.getPeerNum() <= 0 || (m3U8Entity.isGenerateIndexFile() && !new File( |
|
||||||
String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, |
|
||||||
mWrapper.getEntity().getFilePath())).exists())) { |
|
||||||
getVodInfo(); |
|
||||||
} else { |
|
||||||
mLoader.start(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* 获取点播文件信息 |
|
||||||
*/ |
|
||||||
private void getVodInfo() { |
|
||||||
M3U8InfoThread thread = new M3U8InfoThread(mWrapper, new OnFileInfoCallback() { |
|
||||||
@Override public void onComplete(String key, CompleteInfo info) { |
|
||||||
IVodTsUrlConverter converter = mWrapper.asM3U8().getVodUrlConverter(); |
|
||||||
if (converter != null) { |
|
||||||
if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) { |
|
||||||
mUrls.addAll(converter.convert(mWrapper.getEntity().getUrl(), (List<String>) info.obj)); |
|
||||||
} else { |
|
||||||
mUrls.addAll( |
|
||||||
converter.convert(mWrapper.asM3U8().getBandWidthUrl(), (List<String>) info.obj)); |
|
||||||
} |
|
||||||
} else { |
|
||||||
mUrls.addAll((Collection<? extends String>) info.obj); |
|
||||||
} |
|
||||||
if (mUrls.isEmpty()) { |
|
||||||
failDownload(new M3U8Exception(TAG, "获取地址失败"), false); |
|
||||||
return; |
|
||||||
} else if (!mUrls.get(0).startsWith("http")) { |
|
||||||
failDownload(new M3U8Exception(TAG, "地址错误,请使用IM3U8UrlExtInfHandler处理你的url信息"), false); |
|
||||||
return; |
|
||||||
} |
|
||||||
mWrapper.asM3U8().setUrls(mUrls); |
|
||||||
if (isStop) { |
|
||||||
mListener.onStop(mWrapper.getEntity().getCurrentProgress()); |
|
||||||
} else if (isCancel) { |
|
||||||
mListener.onCancel(); |
|
||||||
} else { |
|
||||||
mLoader.start(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
|
||||||
failDownload(e, needRetry); |
|
||||||
} |
|
||||||
}); |
|
||||||
new Thread(thread).start(); |
|
||||||
} |
|
||||||
|
|
||||||
private void failDownload(BaseException e, boolean needRetry) { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onFail(needRetry, e); |
|
||||||
mLoader.onDestroy(); |
|
||||||
} |
|
||||||
} |
|
@ -1,36 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.inf; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/6/3. |
|
||||||
*/ |
|
||||||
public abstract class AbsNormalTask<TASK_WRAPPER extends AbsTaskWrapper> |
|
||||||
extends AbsTask<TASK_WRAPPER> { |
|
||||||
|
|
||||||
/** |
|
||||||
* 最高优先级命令,最高优先级命令有以下属性 |
|
||||||
* 1、在下载队列中,有且只有一个最高优先级任务 |
|
||||||
* 2、最高优先级任务会一直存在,直到用户手动暂停或任务完成 |
|
||||||
* 3、任务调度器不会暂停最高优先级任务 |
|
||||||
* 4、用户手动暂停或任务完成后,第二次重新执行该任务,该命令将失效 |
|
||||||
* 5、如果下载队列中已经满了,则会停止队尾的任务 |
|
||||||
* 6、把任务设置为最高优先级任务后,将自动执行任务,不需要重新调用start()启动任务 |
|
||||||
*/ |
|
||||||
public void setHighestPriority(boolean isHighestPriority) { |
|
||||||
isHeighestTask = isHighestPriority; |
|
||||||
} |
|
||||||
} |
|
@ -1,116 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.upload.uploader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.common.CompleteInfo; |
|
||||||
import com.arialyy.aria.core.common.IUtil; |
|
||||||
import com.arialyy.aria.core.common.OnFileInfoCallback; |
|
||||||
import com.arialyy.aria.core.inf.AbsEntity; |
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.IUploadListener; |
|
||||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
|
||||||
import com.arialyy.aria.exception.BaseException; |
|
||||||
import com.arialyy.aria.util.CheckUtil; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by lyy on 2017/2/9. |
|
||||||
* 简单的文件上传工具 |
|
||||||
*/ |
|
||||||
public class SimpleUploadUtil implements IUtil, Runnable { |
|
||||||
private static final String TAG = "SimpleUploadUtil"; |
|
||||||
|
|
||||||
private UTaskWrapper mTaskWrapper; |
|
||||||
private IUploadListener mListener; |
|
||||||
private Uploader mUploader; |
|
||||||
private boolean isStop = false, isCancel = false; |
|
||||||
|
|
||||||
public SimpleUploadUtil(UTaskWrapper taskWrapper, IUploadListener listener) { |
|
||||||
mTaskWrapper = taskWrapper; |
|
||||||
CheckUtil.checkTaskEntity(taskWrapper); |
|
||||||
if (listener == null) { |
|
||||||
throw new IllegalArgumentException("上传监听不能为空"); |
|
||||||
} |
|
||||||
mListener = listener; |
|
||||||
mUploader = new Uploader(mListener, taskWrapper); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void run() { |
|
||||||
mListener.onPre(); |
|
||||||
switch (mTaskWrapper.getRequestType()) { |
|
||||||
case AbsTaskWrapper.U_FTP: |
|
||||||
FtpFileInfoThread infoThread = |
|
||||||
new FtpFileInfoThread(mTaskWrapper, new OnFileInfoCallback() { |
|
||||||
@Override public void onComplete(String url, CompleteInfo info) { |
|
||||||
if (info.code == FtpFileInfoThread.CODE_COMPLETE) { |
|
||||||
mListener.onComplete(); |
|
||||||
} else { |
|
||||||
mUploader.start(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) { |
|
||||||
failUpload(e, needRetry); |
|
||||||
} |
|
||||||
}); |
|
||||||
new Thread(infoThread).start(); |
|
||||||
break; |
|
||||||
case AbsTaskWrapper.U_HTTP: |
|
||||||
mUploader.start(); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void failUpload(BaseException e, boolean needRetry) { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
mListener.onFail(needRetry, e); |
|
||||||
mUploader.onDestroy(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public String getKey() { |
|
||||||
return mTaskWrapper.getKey(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getFileSize() { |
|
||||||
return mUploader.getFileSize(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public long getCurrentLocation() { |
|
||||||
return mUploader.getCurrentLocation(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public boolean isRunning() { |
|
||||||
return mUploader.isRunning(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void cancel() { |
|
||||||
isCancel = true; |
|
||||||
mUploader.cancel(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void stop() { |
|
||||||
isStop = true; |
|
||||||
mUploader.stop(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override public void start() { |
|
||||||
if (isStop || isCancel) { |
|
||||||
return; |
|
||||||
} |
|
||||||
new Thread(this).start(); |
|
||||||
} |
|
||||||
} |
|
@ -1,61 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria)
|
|
||||||
* |
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
||||||
* you may not use this file except in compliance with the License. |
|
||||||
* You may obtain a copy of the License at |
|
||||||
* |
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
* |
|
||||||
* Unless required by applicable law or agreed to in writing, software |
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, |
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
||||||
* See the License for the specific language governing permissions and |
|
||||||
* limitations under the License. |
|
||||||
*/ |
|
||||||
package com.arialyy.aria.core.upload.uploader; |
|
||||||
|
|
||||||
import com.arialyy.aria.core.AriaManager; |
|
||||||
import com.arialyy.aria.core.common.AbsThreadTask; |
|
||||||
import com.arialyy.aria.core.common.NormalFileer; |
|
||||||
import com.arialyy.aria.core.common.SubThreadConfig; |
|
||||||
import com.arialyy.aria.core.event.Event; |
|
||||||
import com.arialyy.aria.core.event.SpeedEvent; |
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper; |
|
||||||
import com.arialyy.aria.core.inf.IUploadListener; |
|
||||||
import com.arialyy.aria.core.upload.UTaskWrapper; |
|
||||||
import com.arialyy.aria.core.upload.UploadEntity; |
|
||||||
import java.io.File; |
|
||||||
|
|
||||||
/** |
|
||||||
* Created by Aria.Lao on 2017/7/27. |
|
||||||
* 文件上传器 |
|
||||||
*/ |
|
||||||
class Uploader extends NormalFileer<UploadEntity, UTaskWrapper> { |
|
||||||
|
|
||||||
Uploader(IUploadListener listener, UTaskWrapper taskEntity) { |
|
||||||
super(listener, taskEntity); |
|
||||||
mTempFile = new File(mEntity.getFilePath()); |
|
||||||
setUpdateInterval( |
|
||||||
AriaManager.getInstance().getUploadConfig().getUpdateInterval()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override protected boolean handleNewTask() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override protected AbsThreadTask selectThreadTask(SubThreadConfig<UTaskWrapper> config) { |
|
||||||
switch (mTaskWrapper.getRequestType()) { |
|
||||||
case AbsTaskWrapper.U_FTP: |
|
||||||
return new FtpThreadTask(config); |
|
||||||
case AbsTaskWrapper.U_HTTP: |
|
||||||
return new HttpThreadTask(config); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
@Event |
|
||||||
public void setMaxSpeed(SpeedEvent event) { |
|
||||||
setMaxSpeed(event.speed); |
|
||||||
} |
|
||||||
} |
|
@ -1,26 +0,0 @@ |
|||||||
package com.example.ariaftpcomponent; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import androidx.test.platform.app.InstrumentationRegistry; |
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4; |
|
||||||
|
|
||||||
import org.junit.Test; |
|
||||||
import org.junit.runner.RunWith; |
|
||||||
|
|
||||||
import static org.junit.Assert.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* Instrumented test, which will execute on an Android device. |
|
||||||
* |
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|
||||||
*/ |
|
||||||
@RunWith(AndroidJUnit4.class) |
|
||||||
public class ExampleInstrumentedTest { |
|
||||||
@Test |
|
||||||
public void useAppContext() { |
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
|
||||||
|
|
||||||
assertEquals("com.example.ariaftpcomponent.test", appContext.getPackageName()); |
|
||||||
} |
|
||||||
} |
|
@ -1,4 +0,0 @@ |
|||||||
package com.arialyy.aria.ftpcomponent; |
|
||||||
|
|
||||||
public class bb { |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
package com.example.ariaftpcomponent; |
|
||||||
|
|
||||||
import org.junit.Test; |
|
||||||
|
|
||||||
import static org.junit.Assert.*; |
|
||||||
|
|
||||||
/** |
|
||||||
* Example local unit test, which will execute on the development machine (host). |
|
||||||
* |
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|
||||||
*/ |
|
||||||
public class ExampleUnitTest { |
|
||||||
@Test |
|
||||||
public void addition_isCorrect() { |
|
||||||
assertEquals(4, 2 + 2); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,33 @@ |
|||||||
|
apply plugin: 'com.android.library' |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion rootProject.ext.compileSdkVersion |
||||||
|
buildToolsVersion rootProject.ext.buildToolsVersion |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
minSdkVersion rootProject.ext.minSdkVersion |
||||||
|
targetSdkVersion rootProject.ext.targetSdkVersion |
||||||
|
versionCode rootProject.ext.versionCode |
||||||
|
versionName rootProject.ext.versionName |
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
||||||
|
consumerProguardFiles 'consumer-rules.pro' |
||||||
|
} |
||||||
|
|
||||||
|
buildTypes { |
||||||
|
release { |
||||||
|
minifyEnabled false |
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||||
|
|
||||||
|
implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}" |
||||||
|
|
||||||
|
testImplementation 'junit:junit:4.12' |
||||||
|
implementation project(path: ':AriaFtpPlug') |
||||||
|
implementation project(path: ':PublicComponent') |
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue