m3u8多码率下载实现
This commit is contained in:
@@ -50,6 +50,7 @@ import com.arialyy.aria.orm.DelegateWrapper;
|
|||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.AriaCrashHandler;
|
import com.arialyy.aria.util.AriaCrashHandler;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -294,13 +295,13 @@ import org.xml.sax.SAXException;
|
|||||||
public void delRecord(int type, String key, boolean removeFile) {
|
public void delRecord(int type, String key, boolean removeFile) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1: // 删除普通任务记录
|
case 1: // 删除普通任务记录
|
||||||
CommonUtil.delTaskRecord(key, RecordHandler.TYPE_DOWNLOAD, removeFile, true);
|
RecordUtil.delTaskRecord(key, RecordHandler.TYPE_DOWNLOAD, removeFile, true);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
CommonUtil.delGroupTaskRecord(key, removeFile);
|
RecordUtil.delGroupTaskRecord(key, removeFile);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
CommonUtil.delTaskRecord(key, RecordHandler.TYPE_UPLOAD);
|
RecordUtil.delTaskRecord(key, RecordHandler.TYPE_UPLOAD);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package com.arialyy.aria.core.common;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.SparseArray;
|
|
||||||
import com.arialyy.aria.core.AriaManager;
|
import com.arialyy.aria.core.AriaManager;
|
||||||
import com.arialyy.aria.core.inf.AbsNormalEntity;
|
import com.arialyy.aria.core.inf.AbsNormalEntity;
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
||||||
@@ -26,6 +25,8 @@ import com.arialyy.aria.core.manager.ThreadTaskManager;
|
|||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
protected Context mContext;
|
protected Context mContext;
|
||||||
protected File mTempFile; //文件
|
protected File mTempFile; //文件
|
||||||
|
|
||||||
private SparseArray<AbsThreadTask> mTask = new SparseArray<>();
|
private Map<Integer, AbsThreadTask> mTask = new HashMap<>();
|
||||||
private ScheduledThreadPoolExecutor mTimer;
|
private ScheduledThreadPoolExecutor mTimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,6 +69,11 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
*/
|
*/
|
||||||
protected abstract void handleTask();
|
protected abstract void handleTask();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置最大下载速度
|
||||||
|
*/
|
||||||
|
protected abstract void setMaxSpeed(int maxSpeed);
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return mTaskWrapper.getKey();
|
return mTaskWrapper.getKey();
|
||||||
}
|
}
|
||||||
@@ -76,7 +82,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
return mEntity;
|
return mEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SparseArray<AbsThreadTask> getTaskList() {
|
public Map<Integer, AbsThreadTask> getTaskList() {
|
||||||
return mTask;
|
return mTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,8 +92,7 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
private void resetState() {
|
private void resetState() {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
if (mTask != null && mTask.size() != 0) {
|
if (mTask != null && mTask.size() != 0) {
|
||||||
for (int i = 0; i < mTask.size(); i++) {
|
for (AbsThreadTask task : mTask.values()) {
|
||||||
AbsThreadTask task = mTask.get(i);
|
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
task.breakTask();
|
task.breakTask();
|
||||||
}
|
}
|
||||||
@@ -203,24 +208,28 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
}
|
}
|
||||||
closeTimer();
|
closeTimer();
|
||||||
isCancel = true;
|
isCancel = true;
|
||||||
for (int i = 0; i < mTask.size(); i++) {
|
onCancel();
|
||||||
AbsThreadTask task = mTask.get(i);
|
for (AbsThreadTask task : mTask.values()) {
|
||||||
if (task != null) {
|
if (task != null && !task.isThreadComplete()) {
|
||||||
task.cancel();
|
task.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onCancel() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
public synchronized void stop() {
|
||||||
if (isStop) {
|
if (isStop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
closeTimer();
|
closeTimer();
|
||||||
isStop = true;
|
isStop = true;
|
||||||
|
onStop();
|
||||||
if (mStateManager.isComplete()) return;
|
if (mStateManager.isComplete()) return;
|
||||||
for (int i = 0; i < mTask.size(); i++) {
|
for (AbsThreadTask task : mTask.values()) {
|
||||||
AbsThreadTask task = mTask.get(i);
|
|
||||||
if (task != null && !task.isThreadComplete()) {
|
if (task != null && !task.isThreadComplete()) {
|
||||||
task.stop();
|
task.stop();
|
||||||
}
|
}
|
||||||
@@ -228,6 +237,10 @@ public abstract class AbsFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER ext
|
|||||||
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
ThreadTaskManager.getInstance().removeTaskThread(mTaskWrapper.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onStop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 直接调用的时候会自动启动线程执行
|
* 直接调用的时候会自动启动线程执行
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -160,13 +160,13 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
protected abstract BaseTaskConfig getTaskConfig();
|
protected abstract BaseTaskConfig getTaskConfig();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置最大下载速度
|
* 设置当前线程最大下载速度
|
||||||
*
|
*
|
||||||
* @param speed 单位为:kb
|
* @param speed 单位为:kb
|
||||||
*/
|
*/
|
||||||
public void setMaxSpeed(int speed) {
|
public void setMaxSpeed(int speed) {
|
||||||
if (mSpeedBandUtil != null) {
|
if (mSpeedBandUtil != null) {
|
||||||
mSpeedBandUtil.setMaxRate(speed / mConfig.startThreadNum);
|
mSpeedBandUtil.setMaxRate(speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,18 +252,20 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
* 停止任务
|
* 停止任务
|
||||||
*/
|
*/
|
||||||
public void stop() {
|
public void stop() {
|
||||||
synchronized (AriaManager.LOCK) {
|
isStop = true;
|
||||||
isStop = true;
|
sendState(IThreadState.STATE_STOP, null);
|
||||||
|
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) {
|
||||||
|
writeConfig(false, getConfig().tempFile.length());
|
||||||
|
ALog.i(TAG, String.format("任务【%s】已停止", getFileName()));
|
||||||
|
} else {
|
||||||
if (mTaskWrapper.isSupportBP()) {
|
if (mTaskWrapper.isSupportBP()) {
|
||||||
final long stopLocation = mChildCurrentLocation;
|
final long stopLocation = mChildCurrentLocation;
|
||||||
sendState(IThreadState.STATE_STOP, null);
|
|
||||||
ALog.d(TAG,
|
ALog.d(TAG,
|
||||||
String.format("任务【%s】thread__%s__停止【当前线程停止位置:%s】", getFileName(),
|
String.format("任务【%s】thread__%s__停止【当前线程停止位置:%s】", getFileName(),
|
||||||
mRecord.threadId, stopLocation));
|
mRecord.threadId, stopLocation));
|
||||||
writeConfig(false, stopLocation);
|
writeConfig(false, stopLocation);
|
||||||
} else {
|
} else {
|
||||||
ALog.i(TAG, String.format("任务【%s】已停止", getFileName()));
|
ALog.i(TAG, String.format("任务【%s】已停止", getFileName()));
|
||||||
sendState(IThreadState.STATE_STOP, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,12 +304,10 @@ public abstract class AbsThreadTask<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
* 取消任务
|
* 取消任务
|
||||||
*/
|
*/
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
synchronized (AriaManager.LOCK) {
|
isCancel = true;
|
||||||
isCancel = true;
|
sendState(IThreadState.STATE_CANCEL, null);
|
||||||
sendState(IThreadState.STATE_CANCEL, null);
|
ALog.d(TAG,
|
||||||
ALog.d(TAG,
|
String.format("任务【%s】thread__%s__取消", getFileName(), mRecord.threadId));
|
||||||
String.format("任务【%s】thread__%s__取消", getFileName(), mRecord.threadId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.arialyy.aria.core.inf.AbsTask;
|
|||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
||||||
import com.arialyy.aria.core.inf.IEntity;
|
import com.arialyy.aria.core.inf.IEntity;
|
||||||
import com.arialyy.aria.core.inf.IEventListener;
|
import com.arialyy.aria.core.inf.IEventListener;
|
||||||
|
import com.arialyy.aria.core.inf.ITaskWrapper;
|
||||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||||
import com.arialyy.aria.core.scheduler.ISchedulers;
|
import com.arialyy.aria.core.scheduler.ISchedulers;
|
||||||
import com.arialyy.aria.core.upload.UploadEntity;
|
import com.arialyy.aria.core.upload.UploadEntity;
|
||||||
@@ -30,6 +31,7 @@ import com.arialyy.aria.exception.BaseException;
|
|||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
import com.arialyy.aria.util.ErrorHelp;
|
import com.arialyy.aria.util.ErrorHelp;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
public abstract class BaseListener<ENTITY extends AbsEntity, TASK_WRAPPER extends AbsTaskWrapper<ENTITY>,
|
public abstract class BaseListener<ENTITY extends AbsEntity, TASK_WRAPPER extends AbsTaskWrapper<ENTITY>,
|
||||||
@@ -139,8 +141,10 @@ public abstract class BaseListener<ENTITY extends AbsEntity, TASK_WRAPPER extend
|
|||||||
}
|
}
|
||||||
mEntity.setSpeed(speed < 0 ? 0 : speed);
|
mEntity.setSpeed(speed < 0 ? 0 : speed);
|
||||||
|
|
||||||
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
|
if (mTaskWrapper.getRequestType() != ITaskWrapper.M3U8_FILE) {
|
||||||
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
|
mEntity.setPercent((int) (mEntity.getFileSize() <= 0 ? 0
|
||||||
|
: mEntity.getCurrentProgress() * 100 / mEntity.getFileSize()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,11 +158,11 @@ public abstract class BaseListener<ENTITY extends AbsEntity, TASK_WRAPPER extend
|
|||||||
handleSpeed(0);
|
handleSpeed(0);
|
||||||
ALog.i(TAG, String.format("任务【%s】完成,将删除线程任务记录", mEntity.getKey()));
|
ALog.i(TAG, String.format("任务【%s】完成,将删除线程任务记录", mEntity.getKey()));
|
||||||
if (mEntity instanceof DownloadGroupEntity) {
|
if (mEntity instanceof DownloadGroupEntity) {
|
||||||
CommonUtil.delGroupTaskRecord((DownloadGroupEntity) mEntity, false, false);
|
RecordUtil.delGroupTaskRecord((DownloadGroupEntity) mEntity, false, false);
|
||||||
} else if (mEntity instanceof DownloadEntity) {
|
} else if (mEntity instanceof DownloadEntity) {
|
||||||
CommonUtil.delTaskRecord(mEntity.getKey(), RecordHandler.TYPE_DOWNLOAD, false, false);
|
RecordUtil.delTaskRecord(mEntity.getKey(), RecordHandler.TYPE_DOWNLOAD, false, false);
|
||||||
} else if (mEntity instanceof UploadEntity) {
|
} else if (mEntity instanceof UploadEntity) {
|
||||||
CommonUtil.delTaskRecord(mEntity.getKey(), RecordHandler.TYPE_UPLOAD, false, false);
|
RecordUtil.delTaskRecord(mEntity.getKey(), RecordHandler.TYPE_UPLOAD, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public abstract class NormalFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
private ThreadStateManager mStateManager;
|
private ThreadStateManager mStateManager;
|
||||||
private Handler mStateHandler;
|
private Handler mStateHandler;
|
||||||
protected int mTotalThreadNum; //总线程数
|
protected int mTotalThreadNum; //总线程数
|
||||||
|
private int mStartThreadNum; //启动的线程数
|
||||||
|
|
||||||
protected NormalFileer(IEventListener listener, TASK_WRAPPER wrapper) {
|
protected NormalFileer(IEventListener listener, TASK_WRAPPER wrapper) {
|
||||||
super(listener, wrapper);
|
super(listener, wrapper);
|
||||||
@@ -52,11 +53,11 @@ public abstract class NormalFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
*
|
*
|
||||||
* @param maxSpeed 单位为:kb
|
* @param maxSpeed 单位为:kb
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setMaxSpeed(int maxSpeed) {
|
public void setMaxSpeed(int maxSpeed) {
|
||||||
for (int i = 0; i < mTotalThreadNum; i++) {
|
for (AbsThreadTask task : getTaskList().values()) {
|
||||||
AbsThreadTask task = getTaskList().get(i);
|
if (task != null && mStartThreadNum > 0) {
|
||||||
if (task != null) {
|
task.setMaxSpeed(maxSpeed / mStartThreadNum);
|
||||||
task.setMaxSpeed(maxSpeed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,10 +113,9 @@ public abstract class NormalFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int startNum = mRecord.threadNum;
|
|
||||||
for (ThreadRecord tr : mRecord.threadRecords) {
|
for (ThreadRecord tr : mRecord.threadRecords) {
|
||||||
if (!tr.isComplete) {
|
if (!tr.isComplete) {
|
||||||
startNum++;
|
mStartThreadNum++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +143,7 @@ public abstract class NormalFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
}
|
}
|
||||||
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", mEntity.getFileName(), i));
|
ALog.d(TAG, String.format("任务【%s】线程__%s__恢复任务", mEntity.getFileName(), i));
|
||||||
|
|
||||||
AbsThreadTask task = createSingThreadTask(tr, startNum);
|
AbsThreadTask task = createSingThreadTask(tr, mStartThreadNum);
|
||||||
if (task == null) return;
|
if (task == null) return;
|
||||||
getTaskList().put(tr.threadId, task);
|
getTaskList().put(tr.threadId, task);
|
||||||
}
|
}
|
||||||
@@ -168,8 +168,8 @@ public abstract class NormalFileer<ENTITY extends AbsNormalEntity, TASK_WRAPPER
|
|||||||
} else {
|
} else {
|
||||||
mListener.onStart(mStateManager.getCurrentProgress());
|
mListener.onStart(mStateManager.getCurrentProgress());
|
||||||
}
|
}
|
||||||
for (int i = 0; i < getTaskList().size(); i++) {
|
for (AbsThreadTask task : getTaskList().values()) {
|
||||||
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), getTaskList().get(i));
|
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.arialyy.aria.orm.DbEntity;
|
|||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
import com.arialyy.aria.util.DbDataHelper;
|
import com.arialyy.aria.util.DbDataHelper;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -42,6 +43,7 @@ public class RecordHandler {
|
|||||||
|
|
||||||
public static final int TYPE_DOWNLOAD = 1;
|
public static final int TYPE_DOWNLOAD = 1;
|
||||||
public static final int TYPE_UPLOAD = 2;
|
public static final int TYPE_UPLOAD = 2;
|
||||||
|
public static final int TYPE_M3U8_FILE = 3;
|
||||||
|
|
||||||
private static final String STATE = "_state_";
|
private static final String STATE = "_state_";
|
||||||
private static final String RECORD = "_record_";
|
private static final String RECORD = "_record_";
|
||||||
@@ -113,6 +115,7 @@ public class RecordHandler {
|
|||||||
DTaskWrapper wrapper = (DTaskWrapper) mTaskWrapper;
|
DTaskWrapper wrapper = (DTaskWrapper) mTaskWrapper;
|
||||||
String cacheDir = wrapper.asM3U8().getCacheDir();
|
String cacheDir = wrapper.asM3U8().getCacheDir();
|
||||||
long currentProgress = 0;
|
long currentProgress = 0;
|
||||||
|
int completeNum = 0;
|
||||||
for (ThreadRecord record : mRecord.threadRecords) {
|
for (ThreadRecord record : mRecord.threadRecords) {
|
||||||
File temp = new File(M3U8FileLoader.getTsFilePath(cacheDir, record.threadId));
|
File temp = new File(M3U8FileLoader.getTsFilePath(cacheDir, record.threadId));
|
||||||
if (!record.isComplete) {
|
if (!record.isComplete) {
|
||||||
@@ -120,17 +123,19 @@ public class RecordHandler {
|
|||||||
temp.delete();
|
temp.delete();
|
||||||
}
|
}
|
||||||
record.startLocation = 0;
|
record.startLocation = 0;
|
||||||
ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId));
|
//ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId));
|
||||||
} else {
|
} else {
|
||||||
if (!temp.exists()) {
|
if (!temp.exists()) {
|
||||||
record.startLocation = 0;
|
record.startLocation = 0;
|
||||||
record.isComplete = false;
|
record.isComplete = false;
|
||||||
ALog.w(TAG, String.format("分片【%s】不存在,将重新下载该分片", record.threadId));
|
ALog.w(TAG, String.format("分片【%s】不存在,将重新下载该分片", record.threadId));
|
||||||
} else {
|
} else {
|
||||||
|
completeNum++;
|
||||||
currentProgress += temp.length();
|
currentProgress += temp.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wrapper.asM3U8().setCompleteNum(completeNum);
|
||||||
wrapper.getEntity().setCurrentProgress(currentProgress);
|
wrapper.getEntity().setCurrentProgress(currentProgress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,7 +323,7 @@ public class RecordHandler {
|
|||||||
endL = mEntity.getFileSize();
|
endL = mEntity.getFileSize();
|
||||||
}
|
}
|
||||||
tr.endLocation = endL;
|
tr.endLocation = endL;
|
||||||
tr.blockLen = CommonUtil.getBlockLen(mEntity.getFileSize(), i, mRecord.threadNum);
|
tr.blockLen = RecordUtil.getBlockLen(mEntity.getFileSize(), i, mRecord.threadNum);
|
||||||
}
|
}
|
||||||
mRecord.threadRecords.add(tr);
|
mRecord.threadRecords.add(tr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.arialyy.aria.core.inf.IEntity;
|
|||||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||||
import com.arialyy.aria.core.scheduler.ISchedulers;
|
import com.arialyy.aria.core.scheduler.ISchedulers;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载监听类
|
* 下载监听类
|
||||||
@@ -55,10 +56,10 @@ public class BaseDListener extends BaseListener<DownloadEntity, DTaskWrapper, Do
|
|||||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||||
mEntity.setComplete(false);
|
mEntity.setComplete(false);
|
||||||
mEntity.setState(IEntity.STATE_WAIT);
|
mEntity.setState(IEntity.STATE_WAIT);
|
||||||
CommonUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_DOWNLOAD,
|
RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_DOWNLOAD,
|
||||||
mTaskWrapper.isRemoveFile(), false);
|
mTaskWrapper.isRemoveFile(), false);
|
||||||
} else {
|
} else {
|
||||||
CommonUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_DOWNLOAD,
|
RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_DOWNLOAD,
|
||||||
mTaskWrapper.isRemoveFile(), true);
|
mTaskWrapper.isRemoveFile(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import com.arialyy.aria.core.queue.DownloadTaskQueue;
|
|||||||
import com.arialyy.aria.orm.DbEntity;
|
import com.arialyy.aria.orm.DbEntity;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CheckUtil;
|
import com.arialyy.aria.util.CheckUtil;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -102,13 +102,31 @@ class DNormalConfigHandler<TARGET extends AbsDTarget> implements IConfigHandler
|
|||||||
if (b) {
|
if (b) {
|
||||||
mEntity.save();
|
mEntity.save();
|
||||||
}
|
}
|
||||||
if (mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_FILE
|
if (mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_FILE) {
|
||||||
&& mEntity.getFileSize() == 0) {
|
checkM3u8();
|
||||||
ALog.e(TAG, "由于m3u8协议的特殊性质,你需要设置文件长度才能获取到正确的下载进度百分比。设置方法:asM3U8().setFileSize(xxx)");
|
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkM3u8() {
|
||||||
|
File file = new File(mTempFilePath);
|
||||||
|
DTaskWrapper wrapper = (DTaskWrapper) mTarget.getTaskWrapper();
|
||||||
|
if (wrapper.getRequestType() == ITaskWrapper.M3U8_FILE) {
|
||||||
|
// 缓存文件夹格式:问文件夹/.文件名_码率
|
||||||
|
wrapper.asM3U8()
|
||||||
|
.setCacheDir(String.format("%s/.%s_%s", file.getPath(), file.getName(),
|
||||||
|
wrapper.asM3U8().getBandWidth()));
|
||||||
|
}
|
||||||
|
if (mEntity.getFileSize() == 0) {
|
||||||
|
ALog.w(TAG, "由于m3u8协议的特殊性质,无法获取到正确到文件长度,因此你需要自行设置文件大小:asM3U8().setFileSize(xxx)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapper.asM3U8().getBandWidthUrlConverter() != null
|
||||||
|
&& wrapper.asM3U8().getBandWidth() == 0) {
|
||||||
|
ALog.w(TAG, "你已经设置了码率url转换器,但是没有设置码率,Aria框架将采用第一个获取到的码率");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override public boolean checkFilePath() {
|
@Override public boolean checkFilePath() {
|
||||||
String filePath = mTempFilePath;
|
String filePath = mTempFilePath;
|
||||||
if (TextUtils.isEmpty(filePath)) {
|
if (TextUtils.isEmpty(filePath)) {
|
||||||
@@ -120,7 +138,8 @@ class DNormalConfigHandler<TARGET extends AbsDTarget> implements IConfigHandler
|
|||||||
}
|
}
|
||||||
File file = new File(filePath);
|
File file = new File(filePath);
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
if (mTarget.getTargetType() == ITargetHandler.D_HTTP) {
|
if (mTarget.getTargetType() == ITargetHandler.D_HTTP
|
||||||
|
|| mTarget.getTaskWrapper().getRequestType() == ITaskWrapper.M3U8_FILE) {
|
||||||
ALog.e(TAG,
|
ALog.e(TAG,
|
||||||
String.format("下载失败,保存路径【%s】不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip", filePath));
|
String.format("下载失败,保存路径【%s】不能为文件夹,路径需要是完整的文件路径,如:/mnt/sdcard/game.zip", filePath));
|
||||||
return false;
|
return false;
|
||||||
@@ -143,13 +162,13 @@ class DNormalConfigHandler<TARGET extends AbsDTarget> implements IConfigHandler
|
|||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath));
|
ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用,当前任务将覆盖该路径的文件", filePath));
|
||||||
CommonUtil.delTaskRecord(filePath, RecordHandler.TYPE_DOWNLOAD);
|
RecordUtil.delTaskRecord(filePath, RecordHandler.TYPE_DOWNLOAD);
|
||||||
mTarget.setTaskWrapper(
|
mTarget.setTaskWrapper(
|
||||||
TaskWrapperManager.getInstance()
|
TaskWrapperManager.getInstance()
|
||||||
.getHttpTaskWrapper(DTaskWrapper.class, mUrl));
|
.getHttpTaskWrapper(DTaskWrapper.class, mUrl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File oldFile = new File(mEntity.getDownloadPath());
|
File oldFile = new File(mEntity.getFilePath());
|
||||||
File newFile = new File(filePath);
|
File newFile = new File(filePath);
|
||||||
mEntity.setFilePath(filePath);
|
mEntity.setFilePath(filePath);
|
||||||
mEntity.setFileName(newFile.getName());
|
mEntity.setFileName(newFile.getName());
|
||||||
@@ -159,11 +178,11 @@ class DNormalConfigHandler<TARGET extends AbsDTarget> implements IConfigHandler
|
|||||||
}
|
}
|
||||||
if (oldFile.exists()) {
|
if (oldFile.exists()) {
|
||||||
// 处理普通任务的重命名
|
// 处理普通任务的重命名
|
||||||
CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());
|
RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());
|
||||||
ALog.i(TAG, String.format("将任务重命名为:%s", newFile.getName()));
|
ALog.i(TAG, String.format("将任务重命名为:%s", newFile.getName()));
|
||||||
} else if (CommonUtil.blockTaskExists(oldFile.getPath())) {
|
} else if (RecordUtil.blockTaskExists(oldFile.getPath())) {
|
||||||
// 处理分块任务的重命名
|
// 处理分块任务的重命名
|
||||||
CommonUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());
|
RecordUtil.modifyTaskRecord(oldFile.getPath(), newFile.getPath());
|
||||||
ALog.i(TAG, String.format("将分块任务重命名为:%s", newFile.getName()));
|
ALog.i(TAG, String.format("将分块任务重命名为:%s", newFile.getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ import com.arialyy.aria.core.inf.GroupSendParams;
|
|||||||
import com.arialyy.aria.core.inf.IEntity;
|
import com.arialyy.aria.core.inf.IEntity;
|
||||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||||
import com.arialyy.aria.core.scheduler.ISchedulers;
|
import com.arialyy.aria.core.scheduler.ISchedulers;
|
||||||
import com.arialyy.aria.core.upload.UploadEntity;
|
|
||||||
import com.arialyy.aria.exception.BaseException;
|
import com.arialyy.aria.exception.BaseException;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
import com.arialyy.aria.util.ErrorHelp;
|
import com.arialyy.aria.util.ErrorHelp;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Aria.Lao on 2017/7/20. 任务组下载事件
|
* Created by Aria.Lao on 2017/7/20. 任务组下载事件
|
||||||
@@ -117,7 +117,7 @@ class DownloadGroupListener
|
|||||||
subEntity.setConvertSpeed("0kb/s");
|
subEntity.setConvertSpeed("0kb/s");
|
||||||
subEntity.setSpeed(0);
|
subEntity.setSpeed(0);
|
||||||
ALog.i(TAG, String.format("任务【%s】完成,将删除线程任务记录", mEntity.getKey()));
|
ALog.i(TAG, String.format("任务【%s】完成,将删除线程任务记录", mEntity.getKey()));
|
||||||
CommonUtil.delTaskRecord(subEntity.getKey(), RecordHandler.TYPE_DOWNLOAD, false, false);
|
RecordUtil.delTaskRecord(subEntity.getKey(), RecordHandler.TYPE_DOWNLOAD, false, false);
|
||||||
}
|
}
|
||||||
subEntity.update();
|
subEntity.update();
|
||||||
}
|
}
|
||||||
@@ -156,9 +156,9 @@ class DownloadGroupListener
|
|||||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||||
mEntity.setComplete(false);
|
mEntity.setComplete(false);
|
||||||
mEntity.setState(IEntity.STATE_WAIT);
|
mEntity.setState(IEntity.STATE_WAIT);
|
||||||
CommonUtil.delGroupTaskRecord(mEntity, mTaskWrapper.isRemoveFile(), false);
|
RecordUtil.delGroupTaskRecord(mEntity, mTaskWrapper.isRemoveFile(), false);
|
||||||
} else {
|
} else {
|
||||||
CommonUtil.delGroupTaskRecord(mEntity, mTaskWrapper.isRemoveFile(), true);
|
RecordUtil.delGroupTaskRecord(mEntity, mTaskWrapper.isRemoveFile(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.arialyy.aria.core.common.RequestEnum;
|
|||||||
import com.arialyy.aria.orm.DbEntity;
|
import com.arialyy.aria.orm.DbEntity;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -161,8 +162,8 @@ class HttpGroupConfigHandler extends AbsGroupConfigHandler<DownloadGroupTarget>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommonUtil.modifyTaskRecord(oldPath, newPath);
|
RecordUtil.modifyTaskRecord(oldPath, newPath);
|
||||||
entity.setDownloadPath(newPath);
|
entity.setFilePath(newPath);
|
||||||
entity.setFileName(newName);
|
entity.setFileName(newName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.arialyy.aria.exception.TaskException;
|
|||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CheckUtil;
|
import com.arialyy.aria.util.CheckUtil;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -280,7 +281,7 @@ public class HttpFileInfoThread implements Runnable {
|
|||||||
}
|
}
|
||||||
mEntity.setFileName(newName);
|
mEntity.setFileName(newName);
|
||||||
mEntity.setFilePath(newPath);
|
mEntity.setFilePath(newPath);
|
||||||
CommonUtil.modifyTaskRecord(oldFile.getPath(), newPath);
|
RecordUtil.modifyTaskRecord(oldFile.getPath(), newPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,6 +304,7 @@ public class HttpFileInfoThread implements Runnable {
|
|||||||
mEntity.setRedirect(true);
|
mEntity.setRedirect(true);
|
||||||
mEntity.setRedirectUrl(newUrl);
|
mEntity.setRedirectUrl(newUrl);
|
||||||
String cookies = conn.getHeaderField("Set-Cookie");
|
String cookies = conn.getHeaderField("Set-Cookie");
|
||||||
|
conn.disconnect();
|
||||||
URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate);
|
URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate);
|
||||||
conn = ConnectionHelp.handleConnection(url, mTaskDelegate);
|
conn = ConnectionHelp.handleConnection(url, mTaskDelegate);
|
||||||
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的,
|
||||||
|
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||||
|
*/
|
||||||
|
public interface IBandWidthUrlConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换码率地址为可用的http地址,对于某些服务器,返回的切片信息有可能是相对地址,也可能是处理过的,
|
||||||
|
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||||
|
*
|
||||||
|
* @param bandWidthUrl 原始码率地址
|
||||||
|
* @return 可正常访问的http地址
|
||||||
|
*/
|
||||||
|
String convert(String bandWidthUrl);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ts文件合并处理,如果你希望使用自行处理ts文件的合并,你可以实现该接口
|
||||||
|
*/
|
||||||
|
public interface ITsMergeHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并ts文件
|
||||||
|
*
|
||||||
|
* @param tsPath ts文件列表
|
||||||
|
* @return {@code true} 合并成功
|
||||||
|
*/
|
||||||
|
boolean merge(List<String> tsPath);
|
||||||
|
}
|
||||||
@@ -18,15 +18,17 @@ package com.arialyy.aria.core.download.m3u8;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* M3U8 #EXTINF 信息处理器
|
* M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的
|
||||||
|
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||||
*/
|
*/
|
||||||
public interface IM3U8UrlExtInfHandler {
|
public interface ITsUrlConverter {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接
|
* 处理#EXTINF信息,对于某些服务器,返回的切片信息有可能是相对地址,因此,你需要自行转换为可下载http连接
|
||||||
*
|
*
|
||||||
* @param extInf #EXTINF 切片信息列表
|
* @param m3u8Url m3u8文件下载地址
|
||||||
|
* @param tsUrls ts文件下载地址
|
||||||
* @return 根据切片信息转换后的http连接列表,如果你的切片信息是可以直接下载的http连接,直接返回extInf便可
|
* @return 根据切片信息转换后的http连接列表,如果你的切片信息是可以直接下载的http连接,直接返回extInf便可
|
||||||
*/
|
*/
|
||||||
List<String> handler(List<String> extInf);
|
List<String> convert(String m3u8Url, List<String> tsUrls);
|
||||||
}
|
}
|
||||||
@@ -17,11 +17,9 @@ package com.arialyy.aria.core.download.m3u8;
|
|||||||
|
|
||||||
import com.arialyy.aria.core.common.BaseDelegate;
|
import com.arialyy.aria.core.common.BaseDelegate;
|
||||||
import com.arialyy.aria.core.download.DTaskWrapper;
|
import com.arialyy.aria.core.download.DTaskWrapper;
|
||||||
import com.arialyy.aria.core.download.DownloadEntity;
|
|
||||||
import com.arialyy.aria.core.inf.AbsTarget;
|
import com.arialyy.aria.core.inf.AbsTarget;
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* m3u8 委托
|
* m3u8 委托
|
||||||
@@ -33,15 +31,29 @@ public class M3U8Delegate<TARGET extends AbsTarget> extends BaseDelegate<TARGET>
|
|||||||
super(target);
|
super(target);
|
||||||
mTaskWrapper = (DTaskWrapper) mTarget.getTaskWrapper();
|
mTaskWrapper = (DTaskWrapper) mTarget.getTaskWrapper();
|
||||||
mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_FILE);
|
mTaskWrapper.setRequestType(AbsTaskWrapper.M3U8_FILE);
|
||||||
String filePath = mTaskWrapper.getEntity().getFilePath();
|
|
||||||
File file = new File(filePath);
|
|
||||||
if (!file.isDirectory()) {
|
|
||||||
mTaskWrapper.asM3U8().setCacheDir(file.getParent() + "/." + file.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置文件长度,由于m3u8协议的特殊性质,你需要设置文件长度才能获取到正确的下载进度百分比
|
* 是否合并ts文件,默认合并ts
|
||||||
|
*
|
||||||
|
* @param merge {@code true}合并所有ts文件为一个
|
||||||
|
*/
|
||||||
|
public M3U8Delegate merge(boolean merge) {
|
||||||
|
mTaskWrapper.asM3U8().setMergeFile(merge);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果你希望使用自行处理ts文件的合并,可以使用{@link ITsMergeHandler}处理ts文件的合并
|
||||||
|
* 需要注意的是:只有{@link #merge(boolean)}设置合并ts文件,该方法才会生效
|
||||||
|
*/
|
||||||
|
public M3U8Delegate setMergeHandler(ITsMergeHandler handler) {
|
||||||
|
mTaskWrapper.asM3U8().setMergeHandler(handler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 由于m3u8协议的特殊性质,无法获取到正确到文件长度,因此你需要自行设置文件大小
|
||||||
*
|
*
|
||||||
* @param fileSize 文件长度
|
* @param fileSize 文件长度
|
||||||
*/
|
*/
|
||||||
@@ -55,21 +67,34 @@ public class M3U8Delegate<TARGET extends AbsTarget> extends BaseDelegate<TARGET>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如果你的#EXTINF信息是相对路径或有其它需求,你需要设置extinf处理器,将#EXTINF中的切片信息转换为可下载的http连接
|
* M3U8 ts 文件url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的
|
||||||
|
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||||
*
|
*
|
||||||
* @param handler {@link IM3U8UrlExtInfHandler}
|
* @param converter {@link ITsUrlConverter}
|
||||||
*/
|
*/
|
||||||
public M3U8Delegate setExtInfHandler(IM3U8UrlExtInfHandler handler) {
|
public M3U8Delegate setTsUrlConvert(ITsUrlConverter converter) {
|
||||||
mTaskWrapper.asM3U8().setExtInfHandler(handler);
|
mTaskWrapper.asM3U8().setTsUrlConverter(converter);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择需要下载的码率,默认下载最大码率
|
* 选择需要下载的码率,默认下载的码率
|
||||||
*
|
*
|
||||||
* @param bandWidth 指定的码率
|
* @param bandWidth 指定的码率
|
||||||
*/
|
*/
|
||||||
public M3U8Delegate setBandWidth(int bandWidth) {
|
public M3U8Delegate setBandWidth(int bandWidth) {
|
||||||
|
mTaskWrapper.asM3U8().setBandWidth(bandWidth);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M3U8 bandWidth 码率url转换器,对于某些服务器,返回的ts地址可以是相对地址,也可能是处理过的,
|
||||||
|
* 对于这种情况,你需要使用url转换器将地址转换为可正常访问的http地址
|
||||||
|
*
|
||||||
|
* @param converter {@link IBandWidthUrlConverter}
|
||||||
|
*/
|
||||||
|
public M3U8Delegate setBandWidthUrlConverter(IBandWidthUrlConverter converter) {
|
||||||
|
mTaskWrapper.asM3U8().setBandWidthUrlConverter(converter);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +105,10 @@ public class M3U8Delegate<TARGET extends AbsTarget> extends BaseDelegate<TARGET>
|
|||||||
return new M3U8LiveDelegate<>(mTarget);
|
return new M3U8LiveDelegate<>(mTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
///**
|
||||||
* 处理需要解码的ts文件
|
// * 处理需要解码的ts文件
|
||||||
*/
|
// */
|
||||||
public M3U8Delegate setDecodeAdapter() {
|
//public M3U8Delegate setDecodeAdapter() {
|
||||||
return this;
|
// return this;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import com.arialyy.aria.exception.M3U8Exception;
|
|||||||
import com.arialyy.aria.exception.TaskException;
|
import com.arialyy.aria.exception.TaskException;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CheckUtil;
|
import com.arialyy.aria.util.CheckUtil;
|
||||||
|
import com.arialyy.aria.util.Regular;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
@@ -36,12 +37,14 @@ import java.net.HttpURLConnection;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解析url中获取到到m3u信息
|
* 解析url中获取到到m3u信息
|
||||||
* https://www.cnblogs.com/renhui/p/10351870.html
|
* https://www.cnblogs.com/renhui/p/10351870.html
|
||||||
|
* https://blog.csdn.net/Guofengpu/article/details/54922865
|
||||||
*/
|
*/
|
||||||
public class M3U8FileInfoThread implements Runnable {
|
public class M3U8FileInfoThread implements Runnable {
|
||||||
private final String TAG = "M3U8FileInfoThread";
|
private final String TAG = "M3U8FileInfoThread";
|
||||||
@@ -70,7 +73,6 @@ public class M3U8FileInfoThread implements Runnable {
|
|||||||
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
||||||
conn.setConnectTimeout(mConnectTimeOut);
|
conn.setConnectTimeout(mConnectTimeOut);
|
||||||
conn.connect();
|
conn.connect();
|
||||||
Map<String, List<String>> head = conn.getHeaderFields();
|
|
||||||
handleConnect(conn);
|
handleConnect(conn);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -91,20 +93,28 @@ public class M3U8FileInfoThread implements Runnable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> extInf = new ArrayList<>();
|
List<String> extInf = new ArrayList<>();
|
||||||
boolean isDes = false;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
if (line.startsWith("#EXT-X-ENDLIST")) {
|
if (line.startsWith("#EXT-X-ENDLIST")) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (line.startsWith("#EXTINF")) {
|
if (line.startsWith("#EXTINF")) {
|
||||||
isDes = true;
|
extInf.add(reader.readLine());
|
||||||
} else if (isDes) {
|
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
|
||||||
extInf.add(line);
|
int setBand = mTaskWrapper.asM3U8().getBandWidth();
|
||||||
isDes = false;
|
int bandWidth = getBandWidth(line);
|
||||||
|
if (setBand == 0) {
|
||||||
|
handleBandWidth(conn, reader.readLine());
|
||||||
|
} else if (bandWidth == setBand) {
|
||||||
|
handleBandWidth(conn, reader.readLine());
|
||||||
|
} else {
|
||||||
|
failDownload(String.format("【%s】码率不存在", bandWidth), false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extInf.isEmpty()) {
|
if (extInf.isEmpty()) {
|
||||||
failDownload("获取M3U8下载地址列表失败", false);
|
failDownload(String.format("获取M3U8下载地址列表失败,url: %s", mEntity.getUrl()), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CompleteInfo info = new CompleteInfo();
|
CompleteInfo info = new CompleteInfo();
|
||||||
@@ -123,6 +133,20 @@ public class M3U8FileInfoThread implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取bandwidth
|
||||||
|
*/
|
||||||
|
private int getBandWidth(String line) {
|
||||||
|
Pattern p = Pattern.compile(Regular.BANDWIDTH);
|
||||||
|
|
||||||
|
Matcher m = p.matcher(line);
|
||||||
|
|
||||||
|
if (m.find()) {
|
||||||
|
return Integer.parseInt(m.group());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理30x跳转
|
* 处理30x跳转
|
||||||
*/
|
*/
|
||||||
@@ -143,6 +167,7 @@ public class M3U8FileInfoThread implements Runnable {
|
|||||||
mEntity.setRedirect(true);
|
mEntity.setRedirect(true);
|
||||||
mEntity.setRedirectUrl(newUrl);
|
mEntity.setRedirectUrl(newUrl);
|
||||||
String cookies = conn.getHeaderField("Set-Cookie");
|
String cookies = conn.getHeaderField("Set-Cookie");
|
||||||
|
conn.disconnect(); // 关闭上一个连接
|
||||||
URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate);
|
URL url = ConnectionHelp.handleUrl(newUrl, mTaskDelegate);
|
||||||
conn = ConnectionHelp.handleConnection(url, mTaskDelegate);
|
conn = ConnectionHelp.handleConnection(url, mTaskDelegate);
|
||||||
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
||||||
@@ -153,6 +178,35 @@ public class M3U8FileInfoThread implements Runnable {
|
|||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理码率
|
||||||
|
*/
|
||||||
|
private void handleBandWidth(HttpURLConnection conn, String bandWidthM3u8Url) throws IOException {
|
||||||
|
IBandWidthUrlConverter converter = mTaskWrapper.asM3U8().getBandWidthUrlConverter();
|
||||||
|
if (converter != null) {
|
||||||
|
bandWidthM3u8Url = converter.convert(bandWidthM3u8Url);
|
||||||
|
if (converter.getClass().isAnonymousClass()) {
|
||||||
|
mTaskWrapper.asM3U8().setBandWidthUrlConverter(null);
|
||||||
|
}
|
||||||
|
if (!bandWidthM3u8Url.startsWith("http")) {
|
||||||
|
failDownload(String.format("码率转换器转换后的url地址无效,转换后的url:%s", bandWidthM3u8Url), false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTaskWrapper.asM3U8().setBandWidthUrl(bandWidthM3u8Url);
|
||||||
|
ALog.d(TAG, String.format("新码率url:%s", bandWidthM3u8Url));
|
||||||
|
String cookies = conn.getHeaderField("Set-Cookie");
|
||||||
|
conn.disconnect(); // 关闭上一个连接
|
||||||
|
URL url = ConnectionHelp.handleUrl(bandWidthM3u8Url, mTaskDelegate);
|
||||||
|
conn = ConnectionHelp.handleConnection(url, mTaskDelegate);
|
||||||
|
ConnectionHelp.setConnectParam(mTaskDelegate, conn);
|
||||||
|
conn.setRequestProperty("Cookie", cookies);
|
||||||
|
conn.setConnectTimeout(mConnectTimeOut);
|
||||||
|
conn.connect();
|
||||||
|
handleConnect(conn);
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
private void failDownload(String errorInfo, boolean needRetry) {
|
private void failDownload(String errorInfo, boolean needRetry) {
|
||||||
onFileInfoCallback.onFail(mEntity, new M3U8Exception(TAG, errorInfo), needRetry);
|
onFileInfoCallback.onFail(mEntity, new M3U8Exception(TAG, errorInfo), needRetry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
private ReentrantLock LOCK = new ReentrantLock();
|
private ReentrantLock LOCK = new ReentrantLock();
|
||||||
private Condition mCondition = LOCK.newCondition();
|
private Condition mCondition = LOCK.newCondition();
|
||||||
|
|
||||||
protected M3U8FileLoader(IEventListener listener, DTaskWrapper wrapper) {
|
M3U8FileLoader(IEventListener listener, DTaskWrapper wrapper) {
|
||||||
super(listener, wrapper);
|
super(listener, wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
new Thread(new Runnable() {
|
new Thread(new Runnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
String cacheDir = getCacheDir();
|
||||||
while (!isBreak()) {
|
while (!isBreak()) {
|
||||||
try {
|
try {
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
@@ -104,7 +105,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
M3U8ThreadTask task = createThreadTask(getCacheDir(), tr);
|
M3U8ThreadTask task = createThreadTask(cacheDir, tr);
|
||||||
getTaskList().put(tr.threadId, task);
|
getTaskList().put(tr.threadId, task);
|
||||||
mFlagQueue.offer(startThreadTask(task));
|
mFlagQueue.offer(startThreadTask(task));
|
||||||
}
|
}
|
||||||
@@ -121,6 +122,21 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override protected void setMaxSpeed(int maxSpeed) {
|
||||||
|
// TODO: 2019-06-05 暂不支持
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
//notifyLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected void onCancel() {
|
||||||
|
super.onCancel();
|
||||||
|
//notifyLock();
|
||||||
|
}
|
||||||
|
|
||||||
private void notifyLock() {
|
private void notifyLock() {
|
||||||
try {
|
try {
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
@@ -209,6 +225,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
case STATE_STOP:
|
case STATE_STOP:
|
||||||
mStopNum++;
|
mStopNum++;
|
||||||
if (isStop()) {
|
if (isStop()) {
|
||||||
|
ALog.d(TAG, "任务停止");
|
||||||
mListener.onStop(mProgress);
|
mListener.onStop(mProgress);
|
||||||
quitLooper();
|
quitLooper();
|
||||||
}
|
}
|
||||||
@@ -216,7 +233,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
case STATE_CANCEL:
|
case STATE_CANCEL:
|
||||||
mCancelNum++;
|
mCancelNum++;
|
||||||
if (isCancel()) {
|
if (isCancel()) {
|
||||||
ALog.d(TAG, "icCancel");
|
ALog.d(TAG, "任务取消");
|
||||||
mListener.onCancel();
|
mListener.onCancel();
|
||||||
quitLooper();
|
quitLooper();
|
||||||
}
|
}
|
||||||
@@ -232,11 +249,11 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
break;
|
break;
|
||||||
case STATE_COMPLETE:
|
case STATE_COMPLETE:
|
||||||
mCompleteNum++;
|
mCompleteNum++;
|
||||||
ALog.d(TAG, "完成");
|
handlerPercent();
|
||||||
notifyLock();
|
notifyLock();
|
||||||
if (isComplete()) {
|
if (isComplete()) {
|
||||||
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
||||||
if (mTaskRecord.isBlock) {
|
if (mTaskWrapper.asM3U8().isMergeFile()) {
|
||||||
if (mergeFile()) {
|
if (mergeFile()) {
|
||||||
mListener.onComplete();
|
mListener.onComplete();
|
||||||
} else {
|
} else {
|
||||||
@@ -255,9 +272,21 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置进度
|
||||||
|
*/
|
||||||
|
private void handlerPercent() {
|
||||||
|
int completeNum = mTaskWrapper.asM3U8().getCompleteNum();
|
||||||
|
completeNum++;
|
||||||
|
mTaskWrapper.asM3U8().setCompleteNum(completeNum);
|
||||||
|
int percent = completeNum * 100 / mTaskRecord.threadRecords.size();
|
||||||
|
mEntity.setPercent(percent);
|
||||||
|
mEntity.update();
|
||||||
|
}
|
||||||
|
|
||||||
@Override public boolean isStop() {
|
@Override public boolean isStop() {
|
||||||
printInfo("isStop");
|
printInfo("isStop");
|
||||||
return mStartThreadNum == mStopNum + mFailNum + mCompleteNum;
|
return mStopNum == mFlagQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean isFail() {
|
@Override public boolean isFail() {
|
||||||
@@ -272,7 +301,7 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
|
|
||||||
@Override public boolean isCancel() {
|
@Override public boolean isCancel() {
|
||||||
printInfo("isCancel");
|
printInfo("isCancel");
|
||||||
return mStartThreadNum == mCancelNum + mFailNum + mCompleteNum;
|
return mCancelNum == mFlagQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public long getCurrentProgress() {
|
@Override public long getCurrentProgress() {
|
||||||
@@ -293,27 +322,32 @@ public class M3U8FileLoader extends AbsFileer<DownloadEntity, DTaskWrapper> {
|
|||||||
* @return {@code true} 合并成功,{@code false}合并失败
|
* @return {@code true} 合并成功,{@code false}合并失败
|
||||||
*/
|
*/
|
||||||
private boolean mergeFile() {
|
private boolean mergeFile() {
|
||||||
|
ITsMergeHandler mergeHandler = mTaskWrapper.asM3U8().getMergeHandler();
|
||||||
String cacheDir = getCacheDir();
|
String cacheDir = getCacheDir();
|
||||||
|
|
||||||
List<String> partPath = new ArrayList<>();
|
List<String> partPath = new ArrayList<>();
|
||||||
|
|
||||||
for (ThreadRecord tr : mTaskRecord.threadRecords) {
|
for (ThreadRecord tr : mTaskRecord.threadRecords) {
|
||||||
partPath.add(getTsFilePath(cacheDir, tr.threadId));
|
partPath.add(getTsFilePath(cacheDir, tr.threadId));
|
||||||
}
|
}
|
||||||
|
boolean isSuccess;
|
||||||
boolean isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
if (mergeHandler != null) {
|
||||||
|
isSuccess = mergeHandler.merge(partPath);
|
||||||
|
if (mergeHandler.getClass().isAnonymousClass()) {
|
||||||
|
mTaskWrapper.asM3U8().setMergeHandler(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isSuccess = FileUtil.mergeFile(mTaskRecord.filePath, partPath);
|
||||||
|
}
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
|
// 合并成功,删除缓存文件
|
||||||
for (String pp : partPath) {
|
for (String pp : partPath) {
|
||||||
File f = new File(pp);
|
File f = new File(pp);
|
||||||
if (f.exists()) {
|
if (f.exists()) {
|
||||||
f.delete();
|
f.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File targetFile = new File(mTaskRecord.filePath);
|
File cDir = new File(cacheDir);
|
||||||
if (targetFile.exists() && targetFile.length() > mTaskRecord.fileLength) {
|
if (cDir.exists()) {
|
||||||
ALog.e(TAG, String.format("任务【%s】分块文件合并失败,下载长度超出文件真实长度,downloadLen: %s,fileSize: %s",
|
cDir.delete();
|
||||||
targetFile.getName(), targetFile.length(), mTaskRecord.fileLength));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.arialyy.aria.core.download.m3u8;
|
package com.arialyy.aria.core.download.m3u8;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
import com.arialyy.aria.core.common.CompleteInfo;
|
import com.arialyy.aria.core.common.CompleteInfo;
|
||||||
import com.arialyy.aria.core.common.IUtil;
|
import com.arialyy.aria.core.common.IUtil;
|
||||||
import com.arialyy.aria.core.common.OnFileInfoCallback;
|
import com.arialyy.aria.core.common.OnFileInfoCallback;
|
||||||
@@ -44,10 +45,12 @@ public class M3U8FileUtil implements IUtil {
|
|||||||
private IDownloadListener mListener;
|
private IDownloadListener mListener;
|
||||||
private boolean isStop = false, isCancel = false;
|
private boolean isStop = false, isCancel = false;
|
||||||
private List<String> mUrls = new ArrayList<>();
|
private List<String> mUrls = new ArrayList<>();
|
||||||
|
private M3U8FileLoader mLoader;
|
||||||
|
|
||||||
public M3U8FileUtil(DTaskWrapper wrapper, IDownloadListener listener) {
|
public M3U8FileUtil(DTaskWrapper wrapper, IDownloadListener listener) {
|
||||||
mWrapper = wrapper;
|
mWrapper = wrapper;
|
||||||
mListener = listener;
|
mListener = listener;
|
||||||
|
mLoader = new M3U8FileLoader(mListener, mWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String getKey() {
|
@Override public String getKey() {
|
||||||
@@ -63,15 +66,17 @@ public class M3U8FileUtil implements IUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean isRunning() {
|
@Override public boolean isRunning() {
|
||||||
return false;
|
return mLoader.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void cancel() {
|
@Override public void cancel() {
|
||||||
isCancel = true;
|
isCancel = true;
|
||||||
|
mLoader.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void stop() {
|
@Override public void stop() {
|
||||||
isStop = true;
|
isStop = true;
|
||||||
|
mLoader.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void start() {
|
@Override public void start() {
|
||||||
@@ -88,18 +93,23 @@ public class M3U8FileUtil implements IUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setMaxSpeed(int speed) {
|
@Override public void setMaxSpeed(int speed) {
|
||||||
|
mLoader.setMaxSpeed(speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable createInfoThread() {
|
private Runnable createInfoThread() {
|
||||||
if (mWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) {
|
if (mWrapper.getRequestType() == ITaskWrapper.M3U8_FILE) {
|
||||||
return new M3U8FileInfoThread(mWrapper, new OnFileInfoCallback() {
|
return new M3U8FileInfoThread(mWrapper, new OnFileInfoCallback() {
|
||||||
@Override public void onComplete(String key, CompleteInfo info) {
|
@Override public void onComplete(String key, CompleteInfo info) {
|
||||||
IM3U8UrlExtInfHandler handler = mWrapper.asM3U8().getExtInfHandler();
|
ITsUrlConverter handler = mWrapper.asM3U8().getTsUrlConverter();
|
||||||
if (handler != null) {
|
if (handler != null) {
|
||||||
mUrls.addAll(handler.handler((List<String>) info.obj));
|
if (TextUtils.isEmpty(mWrapper.asM3U8().getBandWidthUrl())) {
|
||||||
|
mUrls.addAll(handler.convert(mWrapper.getEntity().getUrl(), (List<String>) info.obj));
|
||||||
|
} else {
|
||||||
|
mUrls.addAll(
|
||||||
|
handler.convert(mWrapper.asM3U8().getBandWidthUrl(), (List<String>) info.obj));
|
||||||
|
}
|
||||||
if (handler.getClass().isAnonymousClass()) {
|
if (handler.getClass().isAnonymousClass()) {
|
||||||
mWrapper.asM3U8().setExtInfHandler(null);
|
mWrapper.asM3U8().setTsUrlConverter(null);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mUrls.addAll((Collection<? extends String>) info.obj);
|
mUrls.addAll((Collection<? extends String>) info.obj);
|
||||||
@@ -112,7 +122,7 @@ public class M3U8FileUtil implements IUtil {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mWrapper.asM3U8().setUrls(mUrls);
|
mWrapper.asM3U8().setUrls(mUrls);
|
||||||
new M3U8FileLoader(mListener, mWrapper).start();
|
mLoader.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
|
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
|
||||||
|
|||||||
@@ -30,19 +30,111 @@ public class M3U8TaskConfig {
|
|||||||
/**
|
/**
|
||||||
* #EXTINF 标签信息处理器
|
* #EXTINF 标签信息处理器
|
||||||
*/
|
*/
|
||||||
private IM3U8UrlExtInfHandler extInfHandler;
|
private ITsUrlConverter tsUrlConverter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存目录
|
* 缓存目录
|
||||||
*/
|
*/
|
||||||
private String cacheDir;
|
private String cacheDir;
|
||||||
|
|
||||||
public IM3U8UrlExtInfHandler getExtInfHandler() {
|
/**
|
||||||
return extInfHandler;
|
* 是否合并ts文件 {@code true} 合并ts文件为一个
|
||||||
|
*/
|
||||||
|
private boolean mergeFile = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并处理器
|
||||||
|
*/
|
||||||
|
private ITsMergeHandler mergeHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成的ts分片数量
|
||||||
|
*/
|
||||||
|
private int completeNum = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频时长,单位s
|
||||||
|
*/
|
||||||
|
private long duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 码率
|
||||||
|
*/
|
||||||
|
private int bandWidth = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 码率url转换器
|
||||||
|
*/
|
||||||
|
private IBandWidthUrlConverter bandWidthUrlConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 码率地址
|
||||||
|
*/
|
||||||
|
private String bandWidthUrl;
|
||||||
|
|
||||||
|
public String getBandWidthUrl() {
|
||||||
|
return bandWidthUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExtInfHandler(IM3U8UrlExtInfHandler extInfHandler) {
|
public void setBandWidthUrl(String bandWidthUrl) {
|
||||||
this.extInfHandler = extInfHandler;
|
this.bandWidthUrl = bandWidthUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBandWidthUrlConverter getBandWidthUrlConverter() {
|
||||||
|
return bandWidthUrlConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBandWidthUrlConverter(
|
||||||
|
IBandWidthUrlConverter bandWidthUrlConverter) {
|
||||||
|
this.bandWidthUrlConverter = bandWidthUrlConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBandWidth() {
|
||||||
|
return bandWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBandWidth(int bandWidth) {
|
||||||
|
this.bandWidth = bandWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDuration(long duration) {
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCompleteNum() {
|
||||||
|
return completeNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCompleteNum(int completeNum) {
|
||||||
|
this.completeNum = completeNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMergeFile() {
|
||||||
|
return mergeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMergeFile(boolean mergeFile) {
|
||||||
|
this.mergeFile = mergeFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITsMergeHandler getMergeHandler() {
|
||||||
|
return mergeHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMergeHandler(ITsMergeHandler mergeHandler) {
|
||||||
|
this.mergeHandler = mergeHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITsUrlConverter getTsUrlConverter() {
|
||||||
|
return tsUrlConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTsUrlConverter(ITsUrlConverter tsUrlConverter) {
|
||||||
|
this.tsUrlConverter = tsUrlConverter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getUrls() {
|
public List<String> getUrls() {
|
||||||
|
|||||||
@@ -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.m3u8;
|
|
||||||
//
|
|
||||||
//import android.os.Bundle;
|
|
||||||
//import android.os.Looper;
|
|
||||||
//import android.os.Message;
|
|
||||||
//import com.arialyy.aria.core.common.IThreadState;
|
|
||||||
//import com.arialyy.aria.core.common.TaskRecord;
|
|
||||||
//import com.arialyy.aria.core.download.DTaskWrapper;
|
|
||||||
//import com.arialyy.aria.core.inf.IEventListener;
|
|
||||||
//import com.arialyy.aria.exception.BaseException;
|
|
||||||
//import com.arialyy.aria.util.ALog;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * M3U8线程状态管理
|
|
||||||
// */
|
|
||||||
//public class M3U8ThreadStateManager implements IThreadState {
|
|
||||||
// private final String TAG = "M3U8ThreadStateManager";
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 任务状态回调
|
|
||||||
// */
|
|
||||||
// private IEventListener mListener;
|
|
||||||
// private int mThreadNum; // 启动的线程总数
|
|
||||||
// private int mCancelNum = 0; // 已经取消的线程的数
|
|
||||||
// private int mStopNum = 0; // 已经停止的线程数
|
|
||||||
// private int mFailNum = 0; // 失败的线程数
|
|
||||||
// private int mCompleteNum = 0; // 完成的线程数
|
|
||||||
// private long mProgress; //当前总进度
|
|
||||||
// private TaskRecord mTaskRecord; // 任务记录
|
|
||||||
// private Looper mLooper;
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * @param taskRecord 任务记录
|
|
||||||
// * @param listener 任务事件
|
|
||||||
// */
|
|
||||||
// M3U8ThreadStateManager(Looper looper, TaskRecord taskRecord, IEventListener listener) {
|
|
||||||
// mLooper = looper;
|
|
||||||
// mTaskRecord = taskRecord;
|
|
||||||
// mThreadNum = mTaskRecord.threadNum;
|
|
||||||
// mListener = listener;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * 退出looper循环
|
|
||||||
// */
|
|
||||||
// private void quitLooper() {
|
|
||||||
// mLooper.quit();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public boolean handleMessage(Message msg) {
|
|
||||||
// switch (msg.what) {
|
|
||||||
// case STATE_STOP:
|
|
||||||
// mStopNum++;
|
|
||||||
// if (isStop()) {
|
|
||||||
// mListener.onStop(mProgress);
|
|
||||||
// quitLooper();
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case STATE_CANCEL:
|
|
||||||
// mCancelNum++;
|
|
||||||
// if (isCancel()) {
|
|
||||||
// ALog.d(TAG, "icCancel");
|
|
||||||
// mListener.onCancel();
|
|
||||||
// quitLooper();
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case STATE_FAIL:
|
|
||||||
// mFailNum++;
|
|
||||||
// if (isFail()) {
|
|
||||||
// Bundle b = msg.getData();
|
|
||||||
// mListener.onFail(b.getBoolean(KEY_RETRY, true),
|
|
||||||
// (BaseException) b.getSerializable(KEY_ERROR_INFO));
|
|
||||||
// quitLooper();
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case STATE_COMPLETE:
|
|
||||||
// mCompleteNum++;
|
|
||||||
// if (isComplete()) {
|
|
||||||
// ALog.d(TAG, "isComplete, completeNum = " + mCompleteNum);
|
|
||||||
// if (mTaskRecord.isBlock) {
|
|
||||||
// if (mergeFile()) {
|
|
||||||
// mListener.onComplete();
|
|
||||||
// } else {
|
|
||||||
// mListener.onFail(false, null);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// mListener.onComplete();
|
|
||||||
// }
|
|
||||||
// quitLooper();
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// case STATE_RUNNING:
|
|
||||||
// mProgress += (long) msg.obj;
|
|
||||||
// break;
|
|
||||||
// case STATE_UPDATE_PROGRESS:
|
|
||||||
// if (msg.obj == null) {
|
|
||||||
// mProgress = updateBlockProgress();
|
|
||||||
// } else {
|
|
||||||
// mProgress = (long) msg.obj;
|
|
||||||
// }
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public boolean isStop() {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public boolean isFail() {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public boolean isComplete() {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public boolean isCancel() {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override public long getCurrentProgress() {
|
|
||||||
// return mProgress;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@@ -35,6 +35,7 @@ import com.arialyy.aria.core.scheduler.UploadSchedulers;
|
|||||||
import com.arialyy.aria.core.upload.UTaskWrapper;
|
import com.arialyy.aria.core.upload.UTaskWrapper;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -78,9 +79,9 @@ public abstract class AbsTarget<TARGET extends AbsTarget> implements ITargetHand
|
|||||||
cancel();
|
cancel();
|
||||||
} else {
|
} else {
|
||||||
if (mEntity instanceof AbsNormalEntity) {
|
if (mEntity instanceof AbsNormalEntity) {
|
||||||
CommonUtil.delTaskRecord((AbsNormalEntity) mEntity, mTaskWrapper.isRemoveFile());
|
RecordUtil.delTaskRecord((AbsNormalEntity) mEntity, mTaskWrapper.isRemoveFile());
|
||||||
} else if (mEntity instanceof DownloadGroupEntity) {
|
} else if (mEntity instanceof DownloadGroupEntity) {
|
||||||
CommonUtil.delGroupTaskRecord(((DownloadGroupEntity) mEntity), mTaskWrapper.isRemoveFile(), true);
|
RecordUtil.delGroupTaskRecord(((DownloadGroupEntity) mEntity), mTaskWrapper.isRemoveFile(), true);
|
||||||
}
|
}
|
||||||
TaskWrapperManager.getInstance().removeTaskWrapper(mEntity.getKey());
|
TaskWrapperManager.getInstance().removeTaskWrapper(mEntity.getKey());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ package com.arialyy.aria.core.inf;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import com.arialyy.aria.core.Aria;
|
|
||||||
import com.arialyy.aria.core.AriaManager;
|
|
||||||
import com.arialyy.aria.core.common.IUtil;
|
import com.arialyy.aria.core.common.IUtil;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.CommonUtil;
|
||||||
@@ -167,11 +165,7 @@ public abstract class AbsTask<ENTITY extends AbsEntity, TASK_WRAPPER extends Abs
|
|||||||
* @return 返回百分比进度,如果文件长度为0,返回0
|
* @return 返回百分比进度,如果文件长度为0,返回0
|
||||||
*/
|
*/
|
||||||
public int getPercent() {
|
public int getPercent() {
|
||||||
if (mTaskWrapper.getEntity().getFileSize() == 0) {
|
return mEntity.getPercent();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return (int) (mTaskWrapper.getEntity().getCurrentProgress() * 100 / mTaskWrapper.getEntity()
|
|
||||||
.getFileSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,8 +174,7 @@ public abstract class AbsTask<ENTITY extends AbsEntity, TASK_WRAPPER extends Abs
|
|||||||
* @return {@link IEntity}
|
* @return {@link IEntity}
|
||||||
*/
|
*/
|
||||||
public int getState() {
|
public int getState() {
|
||||||
return mTaskWrapper.getEntity() == null ? IEntity.STATE_OTHER
|
return mTaskWrapper.getState();
|
||||||
: mTaskWrapper.getEntity().getState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import com.arialyy.aria.core.common.RecordHandler;
|
|||||||
import com.arialyy.aria.core.inf.IEntity;
|
import com.arialyy.aria.core.inf.IEntity;
|
||||||
import com.arialyy.aria.core.inf.IUploadListener;
|
import com.arialyy.aria.core.inf.IUploadListener;
|
||||||
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
import com.arialyy.aria.core.inf.TaskSchedulerType;
|
||||||
import com.arialyy.aria.util.CommonUtil;
|
import com.arialyy.aria.util.RecordUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下载监听类
|
* 下载监听类
|
||||||
@@ -38,10 +38,10 @@ class BaseUListener extends BaseListener<UploadEntity, UTaskWrapper, UploadTask>
|
|||||||
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
if (sType == TaskSchedulerType.TYPE_CANCEL_AND_NOT_NOTIFY) {
|
||||||
mEntity.setComplete(false);
|
mEntity.setComplete(false);
|
||||||
mEntity.setState(IEntity.STATE_WAIT);
|
mEntity.setState(IEntity.STATE_WAIT);
|
||||||
CommonUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD,
|
RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD,
|
||||||
mTaskWrapper.isRemoveFile(), false);
|
mTaskWrapper.isRemoveFile(), false);
|
||||||
} else {
|
} else {
|
||||||
CommonUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD,
|
RecordUtil.delTaskRecord(mEntity.getFilePath(), RecordHandler.TYPE_UPLOAD,
|
||||||
mTaskWrapper.isRemoveFile(), true);
|
mTaskWrapper.isRemoveFile(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,17 +31,8 @@ import com.arialyy.aria.core.command.group.AbsGroupCmd;
|
|||||||
import com.arialyy.aria.core.command.group.GroupCmdFactory;
|
import com.arialyy.aria.core.command.group.GroupCmdFactory;
|
||||||
import com.arialyy.aria.core.command.normal.AbsNormalCmd;
|
import com.arialyy.aria.core.command.normal.AbsNormalCmd;
|
||||||
import com.arialyy.aria.core.command.normal.NormalCmdFactory;
|
import com.arialyy.aria.core.command.normal.NormalCmdFactory;
|
||||||
import com.arialyy.aria.core.common.RecordHandler;
|
|
||||||
import com.arialyy.aria.core.common.RecordWrapper;
|
|
||||||
import com.arialyy.aria.core.common.TaskRecord;
|
|
||||||
import com.arialyy.aria.core.common.ThreadRecord;
|
|
||||||
import com.arialyy.aria.core.download.DownloadEntity;
|
|
||||||
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
|
||||||
import com.arialyy.aria.core.inf.AbsGroupTaskWrapper;
|
import com.arialyy.aria.core.inf.AbsGroupTaskWrapper;
|
||||||
import com.arialyy.aria.core.inf.AbsNormalEntity;
|
|
||||||
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
import com.arialyy.aria.core.inf.AbsTaskWrapper;
|
||||||
import com.arialyy.aria.core.upload.UploadEntity;
|
|
||||||
import com.arialyy.aria.orm.DbEntity;
|
|
||||||
import dalvik.system.DexFile;
|
import dalvik.system.DexFile;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
@@ -92,15 +83,7 @@ public class CommonUtil {
|
|||||||
return new String(str.getBytes(charSet), SERVER_CHARSET);
|
return new String(str.getBytes(charSet), SERVER_CHARSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查分块任务是否存在
|
|
||||||
*
|
|
||||||
* @param filePath 文件保存路径
|
|
||||||
* @return {@code true} 分块文件存在
|
|
||||||
*/
|
|
||||||
public static boolean blockTaskExists(String filePath) {
|
|
||||||
return new File(String.format(RecordHandler.SUB_PATH, filePath, 0)).exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除文件
|
* 删除文件
|
||||||
@@ -198,19 +181,6 @@ public class CommonUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取分块文件的快大小
|
|
||||||
*
|
|
||||||
* @param fileLen 文件总长度
|
|
||||||
* @param blockId 分块id
|
|
||||||
* @param blockNum 分块数量
|
|
||||||
* @return 分块长度
|
|
||||||
*/
|
|
||||||
public static long getBlockLen(long fileLen, int blockId, int blockNum) {
|
|
||||||
final long averageLen = fileLen / blockNum;
|
|
||||||
return blockId == blockNum - 1 ? (fileLen - blockId * averageLen) : averageLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查SD内存空间是否充足
|
* 检查SD内存空间是否充足
|
||||||
*
|
*
|
||||||
@@ -601,186 +571,6 @@ public class CommonUtil {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务组记录
|
|
||||||
*
|
|
||||||
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
|
||||||
* {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
|
||||||
*/
|
|
||||||
public static void delGroupTaskRecord(String groupHash, boolean removeFile) {
|
|
||||||
if (TextUtils.isEmpty(groupHash)) {
|
|
||||||
ALog.e(TAG, "删除下载任务组记录失败,groupHash为null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DownloadGroupEntity groupEntity = DbDataHelper.getDGEntity(groupHash);
|
|
||||||
|
|
||||||
delGroupTaskRecord(groupEntity, removeFile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务组记录
|
|
||||||
*
|
|
||||||
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
|
||||||
* {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
|
||||||
*/
|
|
||||||
public static void delGroupTaskRecord(DownloadGroupEntity groupEntity, boolean removeFile,
|
|
||||||
boolean removeEntity) {
|
|
||||||
if (groupEntity == null) {
|
|
||||||
ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<RecordWrapper> records =
|
|
||||||
DbEntity.findRelationData(RecordWrapper.class, "dGroupHash=?", groupEntity.getGroupHash());
|
|
||||||
|
|
||||||
if (records == null || records.isEmpty()) {
|
|
||||||
ALog.w(TAG, "组任务记录删除失败,记录为null");
|
|
||||||
} else {
|
|
||||||
for (RecordWrapper record : records) {
|
|
||||||
if (record == null || record.taskRecord == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 删除分块文件
|
|
||||||
if (record.taskRecord.isBlock) {
|
|
||||||
for (int i = 0, len = record.taskRecord.threadNum; i < len; i++) {
|
|
||||||
File partFile =
|
|
||||||
new File(String.format(RecordHandler.SUB_PATH, record.taskRecord.filePath, i));
|
|
||||||
if (partFile.exists()) {
|
|
||||||
partFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DbEntity.deleteData(ThreadRecord.class, "key=?", record.taskRecord.filePath);
|
|
||||||
record.taskRecord.deleteData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DownloadEntity> subs = groupEntity.getSubEntities();
|
|
||||||
if (subs != null) {
|
|
||||||
for (DownloadEntity sub : subs) {
|
|
||||||
File file = new File(sub.getFilePath());
|
|
||||||
if (file.exists() && (removeFile || !sub.isComplete())) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除文件夹
|
|
||||||
if (!TextUtils.isEmpty(groupEntity.getDirPath())) {
|
|
||||||
File dir = new File(groupEntity.getDirPath());
|
|
||||||
if (dir.exists() && (removeFile || !groupEntity.isComplete())) {
|
|
||||||
dir.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (removeEntity) {
|
|
||||||
DbEntity.deleteData(DownloadEntity.class, "groupHash=?", groupEntity.getGroupHash());
|
|
||||||
DbEntity.deleteData(DownloadGroupEntity.class, "groupHash=?", groupEntity.getGroupHash());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务记录,默认删除任务实体
|
|
||||||
*
|
|
||||||
* @param removeFile{@code true} 无论任务是否完成,都会删除记录和文件;
|
|
||||||
* {@code false} 如果是下载任务,并且任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
|
||||||
* 如果是上传任务,无论任务是否完成,都只删除记录
|
|
||||||
*/
|
|
||||||
public static void delTaskRecord(AbsNormalEntity dEntity, boolean removeFile) {
|
|
||||||
if (dEntity == null) return;
|
|
||||||
String filePath;
|
|
||||||
int type;
|
|
||||||
if (dEntity instanceof DownloadEntity) {
|
|
||||||
type = RecordHandler.TYPE_DOWNLOAD;
|
|
||||||
filePath = ((DownloadEntity) dEntity).getDownloadPath();
|
|
||||||
} else if (dEntity instanceof UploadEntity) {
|
|
||||||
type = RecordHandler.TYPE_UPLOAD;
|
|
||||||
filePath = ((UploadEntity) dEntity).getFilePath();
|
|
||||||
} else {
|
|
||||||
ALog.w(TAG, "删除记录失败,未知类型");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delTaskRecord(filePath, type, removeFile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务记录,默认删除文件
|
|
||||||
*
|
|
||||||
* @param filePath 文件路径
|
|
||||||
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
|
||||||
* {@code false} 如果是下载任务,并且任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
|
||||||
* 如果是上传任务,无论任务是否完成,都只删除记录
|
|
||||||
* @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD}
|
|
||||||
* 上传任务的记录
|
|
||||||
* @param removeEntity {@code true} 删除任务实体,
|
|
||||||
*/
|
|
||||||
public static void delTaskRecord(String filePath, int type, boolean removeFile,
|
|
||||||
boolean removeEntity) {
|
|
||||||
if (TextUtils.isEmpty(filePath)) {
|
|
||||||
throw new NullPointerException("删除记录失败,文件路径为空");
|
|
||||||
}
|
|
||||||
if (type != 1 && type != 2) {
|
|
||||||
throw new IllegalArgumentException("任务记录类型错误");
|
|
||||||
}
|
|
||||||
List<RecordWrapper> recordWrapper =
|
|
||||||
DbEntity.findRelationData(RecordWrapper.class, "filePath=?", filePath);
|
|
||||||
|
|
||||||
File file = new File(filePath);
|
|
||||||
if (recordWrapper == null
|
|
||||||
|| recordWrapper.isEmpty()
|
|
||||||
|| recordWrapper.get(0) == null
|
|
||||||
|| recordWrapper.get(0).taskRecord == null) {
|
|
||||||
ALog.w(TAG, String.format("记录为空,filePath: %s", filePath));
|
|
||||||
} else {
|
|
||||||
TaskRecord record = recordWrapper.get(0).taskRecord;
|
|
||||||
// 删除分块文件
|
|
||||||
if (record.isBlock) {
|
|
||||||
for (int i = 0, len = record.threadNum; i < len; i++) {
|
|
||||||
File partFile = new File(String.format(RecordHandler.SUB_PATH, record.filePath, i));
|
|
||||||
if (partFile.exists()) {
|
|
||||||
partFile.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == RecordHandler.TYPE_DOWNLOAD) { // 处理单线程任务没有完成的情况
|
|
||||||
if (record.threadRecords != null && !record.threadRecords.isEmpty()) {
|
|
||||||
ThreadRecord tr = record.threadRecords.get(0);
|
|
||||||
if (!tr.isComplete && file.exists()) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (file.exists()) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // 处理上传的情况
|
|
||||||
if (file.exists() && removeFile) {
|
|
||||||
file.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除任务记录
|
|
||||||
DbEntity.deleteData(ThreadRecord.class, "key=?", filePath); // 必须先获取完成数据再删除线程记录
|
|
||||||
record.deleteData();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (removeEntity) {
|
|
||||||
if (type == 1) {
|
|
||||||
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath);
|
|
||||||
} else {
|
|
||||||
DbEntity.deleteData(UploadEntity.class, "filePath=?", filePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除任务记录,默认删除文件,删除任务实体
|
|
||||||
*
|
|
||||||
* @param filePath 文件路径
|
|
||||||
* @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD}
|
|
||||||
* 上传任务的记录
|
|
||||||
*/
|
|
||||||
public static void delTaskRecord(String filePath, int type) {
|
|
||||||
delTaskRecord(filePath, type, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取CPU核心数
|
* 获取CPU核心数
|
||||||
*/
|
*/
|
||||||
@@ -1198,46 +988,6 @@ public class CommonUtil {
|
|||||||
: AriaManager.UPLOAD_TEMP_DIR) + fileName + ".properties";
|
: AriaManager.UPLOAD_TEMP_DIR) + fileName + ".properties";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改任务路径,修改文件路径和任务记录信息。如果是分块任务,则修改分块文件的路径。
|
|
||||||
*
|
|
||||||
* @param oldPath 旧的文件路径
|
|
||||||
* @param newPath 新的文件路径
|
|
||||||
*/
|
|
||||||
public static void modifyTaskRecord(String oldPath, String newPath) {
|
|
||||||
if (oldPath.equals(newPath)) {
|
|
||||||
ALog.w(TAG, "修改任务记录失败,新文件路径和旧文件路径一致");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TaskRecord record = DbDataHelper.getTaskRecord(oldPath);
|
|
||||||
if (record == null) {
|
|
||||||
if (new File(oldPath).exists()) {
|
|
||||||
ALog.w(TAG, "修改任务记录失败,文件【" + oldPath + "】对应的任务记录不存在");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!record.isBlock) {
|
|
||||||
File oldFile = new File(oldPath);
|
|
||||||
if (oldFile.exists()) {
|
|
||||||
oldFile.renameTo(new File(newPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
record.filePath = newPath;
|
|
||||||
record.update();
|
|
||||||
// 修改线程记录
|
|
||||||
if (record.threadRecords != null && !record.threadRecords.isEmpty()) {
|
|
||||||
for (ThreadRecord tr : record.threadRecords) {
|
|
||||||
tr.key = newPath;
|
|
||||||
File blockFile = new File(String.format(RecordHandler.SUB_PATH, oldPath, tr.threadId));
|
|
||||||
if (blockFile.exists()) {
|
|
||||||
blockFile.renameTo(new File(String.format(RecordHandler.SUB_PATH, newPath, tr.threadId)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DbEntity.updateManyData(record.threadRecords);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 读取下载配置文件
|
* 读取下载配置文件
|
||||||
*/
|
*/
|
||||||
|
|||||||
380
Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java
Normal file
380
Aria/src/main/java/com/arialyy/aria/util/RecordUtil.java
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import com.arialyy.aria.core.common.RecordHandler;
|
||||||
|
import com.arialyy.aria.core.common.RecordWrapper;
|
||||||
|
import com.arialyy.aria.core.common.TaskRecord;
|
||||||
|
import com.arialyy.aria.core.common.ThreadRecord;
|
||||||
|
import com.arialyy.aria.core.download.DownloadEntity;
|
||||||
|
import com.arialyy.aria.core.download.DownloadGroupEntity;
|
||||||
|
import com.arialyy.aria.core.download.m3u8.M3U8FileLoader;
|
||||||
|
import com.arialyy.aria.core.inf.AbsEntity;
|
||||||
|
import com.arialyy.aria.core.inf.AbsNormalEntity;
|
||||||
|
import com.arialyy.aria.core.upload.UploadEntity;
|
||||||
|
import com.arialyy.aria.orm.DbEntity;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务记录处理工具
|
||||||
|
*/
|
||||||
|
public class RecordUtil {
|
||||||
|
private static final String TAG = "RecordUtil";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务组记录
|
||||||
|
*
|
||||||
|
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
||||||
|
* {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
||||||
|
*/
|
||||||
|
public static void delGroupTaskRecord(String groupHash, boolean removeFile) {
|
||||||
|
if (TextUtils.isEmpty(groupHash)) {
|
||||||
|
ALog.e(TAG, "删除下载任务组记录失败,groupHash为null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DownloadGroupEntity groupEntity = DbDataHelper.getDGEntity(groupHash);
|
||||||
|
|
||||||
|
delGroupTaskRecord(groupEntity, removeFile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务组记录
|
||||||
|
*
|
||||||
|
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
||||||
|
* {@code false} 如果任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
||||||
|
*/
|
||||||
|
public static void delGroupTaskRecord(DownloadGroupEntity groupEntity, boolean removeFile,
|
||||||
|
boolean removeEntity) {
|
||||||
|
if (groupEntity == null) {
|
||||||
|
ALog.e(TAG, "删除下载任务组记录失败,任务组实体为null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<RecordWrapper> records =
|
||||||
|
DbEntity.findRelationData(RecordWrapper.class, "dGroupHash=?", groupEntity.getGroupHash());
|
||||||
|
|
||||||
|
if (records == null || records.isEmpty()) {
|
||||||
|
ALog.w(TAG, "组任务记录删除失败,记录为null");
|
||||||
|
} else {
|
||||||
|
for (RecordWrapper record : records) {
|
||||||
|
if (record == null || record.taskRecord == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 删除分块文件
|
||||||
|
if (record.taskRecord.isBlock) {
|
||||||
|
for (int i = 0, len = record.taskRecord.threadNum; i < len; i++) {
|
||||||
|
File partFile =
|
||||||
|
new File(String.format(RecordHandler.SUB_PATH, record.taskRecord.filePath, i));
|
||||||
|
if (partFile.exists()) {
|
||||||
|
partFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DbEntity.deleteData(ThreadRecord.class, "key=?", record.taskRecord.filePath);
|
||||||
|
record.taskRecord.deleteData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<DownloadEntity> subs = groupEntity.getSubEntities();
|
||||||
|
if (subs != null) {
|
||||||
|
for (DownloadEntity sub : subs) {
|
||||||
|
File file = new File(sub.getFilePath());
|
||||||
|
if (file.exists() && (removeFile || !sub.isComplete())) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件夹
|
||||||
|
if (!TextUtils.isEmpty(groupEntity.getDirPath())) {
|
||||||
|
File dir = new File(groupEntity.getDirPath());
|
||||||
|
if (dir.exists() && (removeFile || !groupEntity.isComplete())) {
|
||||||
|
dir.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removeEntity) {
|
||||||
|
DbEntity.deleteData(DownloadEntity.class, "groupHash=?", groupEntity.getGroupHash());
|
||||||
|
DbEntity.deleteData(DownloadGroupEntity.class, "groupHash=?", groupEntity.getGroupHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务记录,默认删除文件
|
||||||
|
*
|
||||||
|
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
||||||
|
* {@code false} 如果是下载任务,并且任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
||||||
|
* 如果是上传任务,无论任务是否完成,都只删除记录
|
||||||
|
*/
|
||||||
|
public static void delTaskRecord(AbsNormalEntity entity, boolean removeFile) {
|
||||||
|
if (entity == null) return;
|
||||||
|
String filePath;
|
||||||
|
int type;
|
||||||
|
if (entity instanceof DownloadEntity) {
|
||||||
|
type = RecordHandler.TYPE_DOWNLOAD;
|
||||||
|
filePath = ((DownloadEntity) entity).getDownloadPath();
|
||||||
|
} else if (entity instanceof UploadEntity) {
|
||||||
|
type = RecordHandler.TYPE_UPLOAD;
|
||||||
|
filePath = ((UploadEntity) entity).getFilePath();
|
||||||
|
} else {
|
||||||
|
ALog.w(TAG, "删除记录失败,未知类型");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File targetFile = new File(filePath);
|
||||||
|
TaskRecord record = getTaskRecord(filePath);
|
||||||
|
if (record == null) {
|
||||||
|
if (removeFile) {
|
||||||
|
removeTargetFile(targetFile);
|
||||||
|
}
|
||||||
|
removeRecord(filePath);
|
||||||
|
removeEntity(type, filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 处理任务未完成的情况
|
||||||
|
*/
|
||||||
|
if (!entity.isComplete()) {
|
||||||
|
if (record.taskType == TaskRecord.TYPE_M3U8) { // 删除ts分片文件
|
||||||
|
String cacheDir = null;
|
||||||
|
if (!targetFile.isDirectory()) {
|
||||||
|
cacheDir = targetFile.getParent() + "/." + targetFile.getName();
|
||||||
|
}
|
||||||
|
removeTsCache(record, cacheDir);
|
||||||
|
} else if (record.isBlock) { // 删除分块文件
|
||||||
|
removeBlockFile(record);
|
||||||
|
}
|
||||||
|
} else if (removeFile) { // 处理任务完成情况
|
||||||
|
removeTargetFile(targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功与否都将删除记录
|
||||||
|
removeRecord(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务记录,默认删除文件
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param removeFile {@code true} 无论任务是否完成,都会删除记录和文件;
|
||||||
|
* {@code false} 如果是下载任务,并且任务已经完成,则只删除记录,不删除文件;任务未完成,记录和文件都会删除。
|
||||||
|
* 如果是上传任务,无论任务是否完成,都只删除记录
|
||||||
|
* @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD}
|
||||||
|
* 上传任务的记录
|
||||||
|
* @param removeEntity {@code true} 删除任务实体,
|
||||||
|
*/
|
||||||
|
public static void delTaskRecord(String filePath, int type, boolean removeFile,
|
||||||
|
boolean removeEntity) {
|
||||||
|
if (TextUtils.isEmpty(filePath)) {
|
||||||
|
throw new NullPointerException("删除记录失败,文件路径为空");
|
||||||
|
}
|
||||||
|
if (type != RecordHandler.TYPE_DOWNLOAD && type != RecordHandler.TYPE_UPLOAD) {
|
||||||
|
throw new IllegalArgumentException("任务记录类型错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
AbsEntity entity;
|
||||||
|
if (type == RecordHandler.TYPE_DOWNLOAD) {
|
||||||
|
entity = DbEntity.findFirst(DownloadEntity.class, "downloadPath=?", filePath);
|
||||||
|
} else {
|
||||||
|
entity = DbEntity.findFirst(UploadEntity.class, "filePath=?", filePath);
|
||||||
|
}
|
||||||
|
File targetFile = new File(filePath);
|
||||||
|
TaskRecord record = getTaskRecord(filePath);
|
||||||
|
if (entity == null || record == null) {
|
||||||
|
if (removeFile) {
|
||||||
|
removeTargetFile(targetFile);
|
||||||
|
}
|
||||||
|
removeRecord(filePath);
|
||||||
|
removeEntity(type, filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 处理任务未完成的情况
|
||||||
|
*/
|
||||||
|
if (!entity.isComplete()) {
|
||||||
|
if (record.taskType == TaskRecord.TYPE_M3U8) { // 删除ts分片文件
|
||||||
|
String cacheDir = null;
|
||||||
|
if (!targetFile.isDirectory()) {
|
||||||
|
cacheDir = targetFile.getParent() + "/." + targetFile.getName();
|
||||||
|
}
|
||||||
|
removeTsCache(record, cacheDir);
|
||||||
|
} else if (record.isBlock) { // 删除分块文件
|
||||||
|
removeBlockFile(record);
|
||||||
|
}
|
||||||
|
} else if (removeFile) { // 处理任务完成情况
|
||||||
|
removeTargetFile(targetFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 成功与否都将删除记录
|
||||||
|
removeRecord(filePath);
|
||||||
|
|
||||||
|
if (removeEntity) {
|
||||||
|
removeEntity(type, filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeTargetFile(File targetFile) {
|
||||||
|
if (targetFile.exists()) {
|
||||||
|
targetFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeRecord(String filePath) {
|
||||||
|
DbEntity.deleteData(ThreadRecord.class, "key=?", filePath);
|
||||||
|
DbEntity.deleteData(TaskRecord.class, "filePath=?", filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除实体
|
||||||
|
*
|
||||||
|
* @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD}
|
||||||
|
* 上传任务的记录
|
||||||
|
*/
|
||||||
|
private static void removeEntity(int type, String filePath) {
|
||||||
|
if (type == RecordHandler.TYPE_DOWNLOAD) {
|
||||||
|
DbEntity.deleteData(DownloadEntity.class, "downloadPath=?", filePath);
|
||||||
|
} else {
|
||||||
|
DbEntity.deleteData(UploadEntity.class, "filePath=?", filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件路径获取任务记录
|
||||||
|
*/
|
||||||
|
private static TaskRecord getTaskRecord(String filePath) {
|
||||||
|
List<RecordWrapper> recordWrapper =
|
||||||
|
DbEntity.findRelationData(RecordWrapper.class, "filePath=?", filePath);
|
||||||
|
if (recordWrapper == null
|
||||||
|
|| recordWrapper.isEmpty()
|
||||||
|
|| recordWrapper.get(0) == null
|
||||||
|
|| recordWrapper.get(0).taskRecord == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return recordWrapper.get(0).taskRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除多线程分块下载的分块文件
|
||||||
|
*/
|
||||||
|
private static void removeBlockFile(TaskRecord record) {
|
||||||
|
for (int i = 0, len = record.threadNum; i < len; i++) {
|
||||||
|
File partFile = new File(String.format(RecordHandler.SUB_PATH, record.filePath, i));
|
||||||
|
if (partFile.exists()) {
|
||||||
|
partFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除ts文件
|
||||||
|
*/
|
||||||
|
private static void removeTsCache(TaskRecord record, String cacheDir) {
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(cacheDir)) {
|
||||||
|
List<String> partPath = new ArrayList<>();
|
||||||
|
for (ThreadRecord tr : record.threadRecords) {
|
||||||
|
partPath.add(M3U8FileLoader.getTsFilePath(cacheDir, tr.threadId));
|
||||||
|
}
|
||||||
|
for (String pp : partPath) {
|
||||||
|
File f = new File(pp);
|
||||||
|
if (f.exists()) {
|
||||||
|
f.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File cDir = new File(cacheDir);
|
||||||
|
if (cDir.exists()) {
|
||||||
|
cDir.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除任务记录,默认删除文件,删除任务实体
|
||||||
|
*
|
||||||
|
* @param filePath 文件路径
|
||||||
|
* @param type {@link RecordHandler#TYPE_DOWNLOAD}下载任务的记录,{@link RecordHandler#TYPE_UPLOAD}
|
||||||
|
* 上传任务的记录
|
||||||
|
*/
|
||||||
|
public static void delTaskRecord(String filePath, int type) {
|
||||||
|
delTaskRecord(filePath, type, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改任务路径,修改文件路径和任务记录信息。如果是分块任务,则修改分块文件的路径。
|
||||||
|
*
|
||||||
|
* @param oldPath 旧的文件路径
|
||||||
|
* @param newPath 新的文件路径
|
||||||
|
*/
|
||||||
|
public static void modifyTaskRecord(String oldPath, String newPath) {
|
||||||
|
if (oldPath.equals(newPath)) {
|
||||||
|
ALog.w(TAG, "修改任务记录失败,新文件路径和旧文件路径一致");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TaskRecord record = DbDataHelper.getTaskRecord(oldPath);
|
||||||
|
if (record == null) {
|
||||||
|
if (new File(oldPath).exists()) {
|
||||||
|
ALog.w(TAG, "修改任务记录失败,文件【" + oldPath + "】对应的任务记录不存在");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!record.isBlock) {
|
||||||
|
File oldFile = new File(oldPath);
|
||||||
|
if (oldFile.exists()) {
|
||||||
|
oldFile.renameTo(new File(newPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record.filePath = newPath;
|
||||||
|
record.update();
|
||||||
|
// 修改线程记录
|
||||||
|
if (record.threadRecords != null && !record.threadRecords.isEmpty()) {
|
||||||
|
for (ThreadRecord tr : record.threadRecords) {
|
||||||
|
tr.key = newPath;
|
||||||
|
File blockFile = new File(String.format(RecordHandler.SUB_PATH, oldPath, tr.threadId));
|
||||||
|
if (blockFile.exists()) {
|
||||||
|
blockFile.renameTo(new File(String.format(RecordHandler.SUB_PATH, newPath, tr.threadId)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DbEntity.updateManyData(record.threadRecords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查分块任务是否存在
|
||||||
|
*
|
||||||
|
* @param filePath 文件保存路径
|
||||||
|
* @return {@code true} 分块文件存在
|
||||||
|
*/
|
||||||
|
public static boolean blockTaskExists(String filePath) {
|
||||||
|
return new File(String.format(RecordHandler.SUB_PATH, filePath, 0)).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分块文件的快大小
|
||||||
|
*
|
||||||
|
* @param fileLen 文件总长度
|
||||||
|
* @param blockId 分块id
|
||||||
|
* @param blockNum 分块数量
|
||||||
|
* @return 分块长度
|
||||||
|
*/
|
||||||
|
public static long getBlockLen(long fileLen, int blockId, int blockNum) {
|
||||||
|
final long averageLen = fileLen / blockNum;
|
||||||
|
return blockId == blockNum - 1 ? (fileLen - blockId * averageLen) : averageLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,4 +25,9 @@ public interface Regular {
|
|||||||
* 匹配window.location.replace
|
* 匹配window.location.replace
|
||||||
*/
|
*/
|
||||||
String REG_WINLOD_REPLACE = "replace\\(\".*\"\\)";
|
String REG_WINLOD_REPLACE = "replace\\(\".*\"\\)";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配BANDWIDTH
|
||||||
|
*/
|
||||||
|
String BANDWIDTH = "[0-9]{3,}";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.arialyy.simple.core.download.m3u8;
|
|||||||
|
|
||||||
import android.arch.lifecycle.Observer;
|
import android.arch.lifecycle.Observer;
|
||||||
import android.arch.lifecycle.ViewModelProviders;
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -30,6 +31,8 @@ import com.arialyy.aria.core.Aria;
|
|||||||
import com.arialyy.aria.core.download.DownloadEntity;
|
import com.arialyy.aria.core.download.DownloadEntity;
|
||||||
import com.arialyy.aria.core.download.DownloadTarget;
|
import com.arialyy.aria.core.download.DownloadTarget;
|
||||||
import com.arialyy.aria.core.download.DownloadTask;
|
import com.arialyy.aria.core.download.DownloadTask;
|
||||||
|
import com.arialyy.aria.core.download.m3u8.IBandWidthUrlConverter;
|
||||||
|
import com.arialyy.aria.core.download.m3u8.ITsUrlConverter;
|
||||||
import com.arialyy.aria.core.inf.IEntity;
|
import com.arialyy.aria.core.inf.IEntity;
|
||||||
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
|
import com.arialyy.aria.core.inf.IHttpFileLenAdapter;
|
||||||
import com.arialyy.aria.util.ALog;
|
import com.arialyy.aria.util.ALog;
|
||||||
@@ -41,6 +44,7 @@ import com.arialyy.simple.common.ModifyPathDialog;
|
|||||||
import com.arialyy.simple.common.ModifyUrlDialog;
|
import com.arialyy.simple.common.ModifyUrlDialog;
|
||||||
import com.arialyy.simple.databinding.ActivityM3u8Binding;
|
import com.arialyy.simple.databinding.ActivityM3u8Binding;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -54,7 +58,7 @@ public class M3U8DownloadActivity extends BaseActivity<ActivityM3u8Binding> {
|
|||||||
@Override
|
@Override
|
||||||
protected void init(Bundle savedInstanceState) {
|
protected void init(Bundle savedInstanceState) {
|
||||||
super.init(savedInstanceState);
|
super.init(savedInstanceState);
|
||||||
setTitle("单任务下载");
|
setTitle(getString(R.string.m3u8_file));
|
||||||
Aria.download(this).register();
|
Aria.download(this).register();
|
||||||
mModule = ViewModelProviders.of(this).get(M3U8Module.class);
|
mModule = ViewModelProviders.of(this).get(M3U8Module.class);
|
||||||
mModule.getHttpDownloadInfo(this).observe(this, new Observer<DownloadEntity>() {
|
mModule.getHttpDownloadInfo(this).observe(this, new Observer<DownloadEntity>() {
|
||||||
@@ -72,9 +76,8 @@ public class M3U8DownloadActivity extends BaseActivity<ActivityM3u8Binding> {
|
|||||||
|
|
||||||
if (entity.getFileSize() != 0) {
|
if (entity.getFileSize() != 0) {
|
||||||
getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize()));
|
getBinding().setFileSize(CommonUtil.formatFileSize(entity.getFileSize()));
|
||||||
getBinding().setProgress(entity.isComplete() ? 100
|
|
||||||
: (int) (entity.getCurrentProgress() * 100 / entity.getFileSize()));
|
|
||||||
}
|
}
|
||||||
|
getBinding().setProgress(entity.getPercent());
|
||||||
getBinding().setUrl(entity.getUrl());
|
getBinding().setUrl(entity.getUrl());
|
||||||
getBinding().setFilePath(entity.getFilePath());
|
getBinding().setFilePath(entity.getFilePath());
|
||||||
mUrl = entity.getUrl();
|
mUrl = entity.getUrl();
|
||||||
@@ -164,13 +167,7 @@ public class M3U8DownloadActivity extends BaseActivity<ActivityM3u8Binding> {
|
|||||||
protected void running(DownloadTask task) {
|
protected void running(DownloadTask task) {
|
||||||
if (task.getKey().equals(mUrl)) {
|
if (task.getKey().equals(mUrl)) {
|
||||||
ALog.d(TAG, "isRunning");
|
ALog.d(TAG, "isRunning");
|
||||||
//Log.d(TAG, task.getKey());
|
getBinding().setProgress(task.getPercent());
|
||||||
long len = task.getFileSize();
|
|
||||||
if (len == 0) {
|
|
||||||
getBinding().setProgress(0);
|
|
||||||
} else {
|
|
||||||
getBinding().setProgress(task.getPercent());
|
|
||||||
}
|
|
||||||
getBinding().setSpeed(task.getConvertSpeed());
|
getBinding().setSpeed(task.getConvertSpeed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,6 +227,7 @@ public class M3U8DownloadActivity extends BaseActivity<ActivityM3u8Binding> {
|
|||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
switch (view.getId()) {
|
switch (view.getId()) {
|
||||||
case R.id.start:
|
case R.id.start:
|
||||||
|
ALog.d(TAG, "isRunning = " + mTarget.isRunning());
|
||||||
if (mTarget.isRunning()) {
|
if (mTarget.isRunning()) {
|
||||||
Aria.download(this).load(mUrl).stop();
|
Aria.download(this).load(mUrl).stop();
|
||||||
} else {
|
} else {
|
||||||
@@ -248,6 +246,24 @@ public class M3U8DownloadActivity extends BaseActivity<ActivityM3u8Binding> {
|
|||||||
.useServerFileName(true)
|
.useServerFileName(true)
|
||||||
.setFilePath(mFilePath, true)
|
.setFilePath(mFilePath, true)
|
||||||
.asM3U8()
|
.asM3U8()
|
||||||
|
.setBandWidthUrlConverter(new IBandWidthUrlConverter() {
|
||||||
|
@Override public String convert(String bandWidthUrl) {
|
||||||
|
int index = mUrl.lastIndexOf("/");
|
||||||
|
return mUrl.substring(0, index + 1) + bandWidthUrl;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setTsUrlConvert(new ITsUrlConverter() {
|
||||||
|
@Override public List<String> convert(String m3u8Url, List<String> tsUrls) {
|
||||||
|
int index = m3u8Url.lastIndexOf("/");
|
||||||
|
String parentUrl = m3u8Url.substring(0, index + 1);
|
||||||
|
List<String> newUrls = new ArrayList<>();
|
||||||
|
for (String url : tsUrls) {
|
||||||
|
newUrls.add(parentUrl + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUrls;
|
||||||
|
}
|
||||||
|
})
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ import java.io.File;
|
|||||||
public class M3U8Module extends BaseViewModule {
|
public class M3U8Module extends BaseViewModule {
|
||||||
private final String M3U8_URL_KEY = "M3U8_URL_KEY";
|
private final String M3U8_URL_KEY = "M3U8_URL_KEY";
|
||||||
private final String M3U8_PATH_KEY = "M3U8_PATH_KEY";
|
private final String M3U8_PATH_KEY = "M3U8_PATH_KEY";
|
||||||
|
// m3u8测试集合:http://www.voidcn.com/article/p-snaliarm-ct.html
|
||||||
private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8";
|
//private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8";
|
||||||
|
// 多码率地址:
|
||||||
|
private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";
|
||||||
private final String filePath =
|
private final String filePath =
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
|
||||||
+ "/1557993797897.m3u8";
|
+ "/1557993797897.ts";
|
||||||
|
|
||||||
private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>();
|
private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>();
|
||||||
private DownloadEntity singDownloadInfo;
|
private DownloadEntity singDownloadInfo;
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<string name="url_hint">url</string>
|
<string name="url_hint">url</string>
|
||||||
<string name="name">NAME: </string>
|
<string name="name">NAME: </string>
|
||||||
<string name="modify_file_path">修改文件路径</string>
|
<string name="modify_file_path">修改文件路径</string>
|
||||||
|
<string name="m3u8_file">M3U8点播文件下载</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="error_file_name_null">文件名为空</string>
|
<string name="error_file_name_null">文件名为空</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user