22 Commits
3.7.2 ... 3.7.9

Author SHA1 Message Date
laoyuyu
91daecd039 Merge branch 'master' of github.com:AriaLyy/Aria 2019-11-28 19:49:26 +08:00
laoyuyu
297cc99f2b fix bug https://github.com/AriaLyy/Aria/issues/537 2019-11-28 19:48:50 +08:00
lyy
f5f8e9b528 Update Custom.md 2019-11-28 16:39:46 +08:00
lyy
94c3e8bb58 Update Custom.md 2019-11-28 16:37:04 +08:00
laoyuyu
ee5ee6e080 增加m3u8密钥url转换器 2019-11-28 09:59:08 +08:00
laoyuyu
c4cc804c67 增加立即恢复任务的接口,正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。如果希望调用恢复接口,马上进入执行队列,需要调用resume(true)这个重载方法。
修复一个非分块模式下,无法恢复下载的问题
增加M3U8加密密钥的下载地址转换器 https://github.com/AriaLyy/Aria/issues/522
2019-11-27 21:38:25 +08:00
laoyuyu
c9c00c38c6 Merge branch 'master' of github.com:AriaLyy/Aria 2019-11-26 19:32:16 +08:00
laoyuyu
846235a826 fix bug https://github.com/AriaLyy/Aria/issues/533
fix bug https://github.com/AriaLyy/Aria/issues/535
2019-11-26 19:28:06 +08:00
lyy
06dd6b35e0 Update README.md 2019-11-26 15:39:47 +08:00
lyy
944d69a1af Update README.md 2019-11-21 10:37:21 +08:00
laoyuyu
1ee36e0bc1 fix bug https://github.com/AriaLyy/Aria/issues/526 2019-11-20 21:33:56 +08:00
laoyuyu
0765ab67f7 3.7.7 2019-11-20 10:17:08 +08:00
laoyuyu
1b54bfc4ac 3.7.6 2019-11-19 20:03:25 +08:00
laoyuyu
846e6c0720 fix bug https://github.com/AriaLyy/Aria/issues/516
fix bug https://github.com/AriaLyy/Aria/issues/505
增加上传任务强制上传的功能
2019-11-18 19:35:12 +08:00
laoyuyu
5ad485890c fix bug https://github.com/AriaLyy/Aria/issues/500
fix bug https://github.com/AriaLyy/Aria/issues/508
fix bug https://github.com/AriaLyy/Aria/issues/503
修复m3u8创建索引不成功的问题
2019-11-10 13:26:00 +08:00
lyy
012f94511b Update README.md 2019-11-04 09:34:43 +08:00
lyy
e41d2f8578 Add files via upload 2019-11-04 09:23:47 +08:00
laoyuyu
a6ecaadea4 3.7.4 2019-11-02 11:06:49 +08:00
laoyuyu
c1b1cc1390 fix bug https://github.com/AriaLyy/Aria/issues/493 2019-11-02 10:58:49 +08:00
laoyuyu
e8c5a9f08b Merge branch 'master' of github.com:AriaLyy/Aria 2019-11-01 10:13:42 +08:00
laoyuyu
d6993eb159 fix bug https://github.com/AriaLyy/Aria/issues/495
fix bug https://github.com/AriaLyy/Aria/issues/496
2019-11-01 10:13:00 +08:00
lyy
25485538e6 Update README.md 2019-10-30 10:13:21 +08:00
117 changed files with 1777 additions and 653 deletions

View File

@@ -1,17 +1,26 @@
---
name: 问题描述
about: 请详细描述你遇到的问题
about: 提交问题前请先阅读文档和搜索issue
---
## Aria版本
<!-- 提交问题前请先阅读文档和搜索相应问题的issue -->
## 版本
* 框架版本
* 系统版本
## 错误日志
<!-- 请提供详细的错误日志 -->
## 什么问题
## 重现步骤
<!-- 请提供明确的步骤 -->
1.
2.
3.
4.
## 如何复现此问题
## 控制台日志

View File

@@ -98,9 +98,6 @@ import java.util.concurrent.ConcurrentHashMap;
}
public static AriaManager getInstance() {
if (INSTANCE == null) {
throw new NullPointerException("请使用AriaManager.init(context)初始化管理器");
}
return INSTANCE;
}
@@ -401,7 +398,7 @@ import java.util.concurrent.ConcurrentHashMap;
return false;
} else {
String parentName = parentClass.getName();
if (mFragmentClassName.contains(parentName)) {
if (mDialogFragmentClassName.contains(parentName)) {
return true;
} else {
return isDialogFragment(parentClass);

View File

@@ -15,6 +15,7 @@
*/
package com.arialyy.aria.core.command;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
/**
@@ -25,8 +26,7 @@ public abstract class AbsCmdFactory<TASK_ENTITY extends AbsTaskWrapper, CMD exte
/**
* @param entity 下载实体
* @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}、{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}、{@link
* ICmd#TASK_TYPE_UPLOAD}
* {@link ITask#DOWNLOAD}、{@link ITask#DOWNLOAD_GROUP}、{@link ITask#UPLOAD}
*/
public abstract CMD createCmd(TASK_ENTITY entity, int type, int taskType);
}

View File

@@ -157,14 +157,6 @@ public abstract class AbsNormalCmd<T extends AbsTaskWrapper> extends AbsCmd<T> {
mQueue.resumeTask(task);
}
/**
* 启动指定任务
*
* @param task 指定任务
*/
void startTask(AbsTask task) {
mQueue.startTask(task);
}
/**
* 从队列中获取任务

View File

@@ -30,7 +30,7 @@ public class CommandManager {
EventMsgUtil.getDefault().register(this);
}
public static CommandManager init() {
public static void init() {
if (instance == null) {
synchronized (CommandManager.class) {
if (instance == null) {
@@ -38,7 +38,6 @@ public class CommandManager {
}
}
}
return instance;
}
@Event
@@ -51,9 +50,6 @@ public class CommandManager {
@Event
public void start(StartCmd cmd) {
if (CommonUtil.isFastDoubleClick()) {
return;
}
cmd.executeCmd();
}

View File

@@ -16,6 +16,7 @@
package com.arialyy.aria.core.command;
import com.arialyy.aria.core.task.ITask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
/**
@@ -83,8 +84,7 @@ public class NormalCmdFactory extends AbsCmdFactory<AbsTaskWrapper, AbsNormalCmd
* @param entity 下载实体
* @param type 命令类型{@link #TASK_CREATE}、{@link #TASK_START}、{@link #TASK_CANCEL}、{@link
* #TASK_STOP}、{@link #TASK_HIGHEST_PRIORITY}、{@link #TASK_STOP_ALL}、{@link #TASK_RESUME_ALL}
* @param taskType {@link ICmd#TASK_TYPE_DOWNLOAD}、{@link ICmd#TASK_TYPE_DOWNLOAD_GROUP}、{@link
* ICmd#TASK_TYPE_UPLOAD}
* @param taskType {@link ITask#DOWNLOAD}、{@link ITask#DOWNLOAD_GROUP}、{@link ITask#UPLOAD}
*/
public AbsNormalCmd createCmd(AbsTaskWrapper entity, int type, int taskType) {
switch (type) {

View File

@@ -1,25 +1,25 @@
/*
* 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.command;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.task.AbsTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.queue.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.queue.UTaskQueue;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.NetUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by AriaL on 2017/6/13.
@@ -30,7 +30,6 @@ import java.util.List;
* 4.恢复下载的任务规则是停止时间越晚的任务启动越早按照DESC来进行排序
*/
final class ResumeAllCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
private List<AbsTaskWrapper> mWaitList = new ArrayList<>();
ResumeAllCmd(T entity, int taskType) {
super(entity, taskType);
@@ -41,94 +40,7 @@ final class ResumeAllCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
ALog.w(TAG, "恢复任务失败,网络未连接");
return;
}
if (isDownloadCmd) {
findTaskData(1);
findTaskData(2);
} else {
findTaskData(3);
}
resumeWaitTask();
}
/**
* 查找数据库中的所有任务数据
*
* @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务
*/
private void findTaskData(int type) {
if (type == 1) {
List<DownloadEntity> entities =
DbEntity.findDatas(DownloadEntity.class,
"isGroupChild=? AND state!=? ORDER BY stopTime DESC", "false", "1");
if (entities != null && !entities.isEmpty()) {
for (DownloadEntity entity : entities) {
addResumeEntity(TaskWrapperManager.getInstance()
.getNormalTaskWrapper(DTaskWrapper.class, entity.getId()));
}
}
} else if (type == 2) {
List<DownloadGroupEntity> entities =
DbEntity.findDatas(DownloadGroupEntity.class, "state!=? ORDER BY stopTime DESC", "1");
if (entities != null && !entities.isEmpty()) {
for (DownloadGroupEntity entity : entities) {
addResumeEntity(
TaskWrapperManager.getInstance()
.getGroupWrapper(DGTaskWrapper.class, entity.getId()));
}
}
} else if (type == 3) {
List<UploadEntity> entities =
DbEntity.findDatas(UploadEntity.class, "state!=? ORDER BY stopTime DESC", "1");
if (entities != null && !entities.isEmpty()) {
for (UploadEntity entity : entities) {
addResumeEntity(TaskWrapperManager.getInstance()
.getNormalTaskWrapper(UTaskWrapper.class, entity.getId()));
}
}
}
}
/**
* 添加恢复实体
*/
private void addResumeEntity(AbsTaskWrapper te) {
if (te == null || te.getEntity() == null) {
return;
}
if (!mQueue.taskExists(te.getKey())) {
mWaitList.add(te);
}
}
/**
* 处理等待状态的任务
*/
private void resumeWaitTask() {
int maxTaskNum = mQueue.getMaxTaskNum();
if (mWaitList == null || mWaitList.isEmpty()) {
return;
}
List<AbsEntity> resumeEntities = new ArrayList<>();
for (AbsTaskWrapper te : mWaitList) {
if (te instanceof DTaskWrapper) {
mQueue = DTaskQueue.getInstance();
} else if (te instanceof UTaskWrapper) {
mQueue = UTaskQueue.getInstance();
} else if (te instanceof DGTaskWrapper) {
mQueue = DGroupTaskQueue.getInstance();
}
if (mQueue.getCurrentExePoolNum() < maxTaskNum) {
startTask(createTask(te));
} else {
te.getEntity().setState(IEntity.STATE_WAIT);
AbsTask task = createTask(te);
sendWaitState(task);
resumeEntities.add(te.getEntity());
}
}
if (!resumeEntities.isEmpty()) {
DbEntity.updateManyData(resumeEntities);
}
new Thread(new ResumeThread(isDownloadCmd,
String.format("state!=%s", IEntity.STATE_COMPLETE))).start();
}
}

View File

@@ -0,0 +1,200 @@
/*
* 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.command;
import android.text.TextUtils;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.IOptionConstant;
import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.queue.AbsTaskQueue;
import com.arialyy.aria.core.queue.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.queue.UTaskQueue;
import com.arialyy.aria.core.task.AbsTask;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import java.util.ArrayList;
import java.util.List;
/**
* 恢复任务工具
*/
public class ResumeThread implements Runnable {
private String TAG = CommonUtil.getClassName(getClass());
private List<AbsTaskWrapper> mWaitList = new ArrayList<>();
private boolean isDownloadCmd;
private String sqlCondition;
ResumeThread(boolean isDownload, String sqlCondition) {
this.isDownloadCmd = isDownload;
this.sqlCondition = sqlCondition;
}
/**
* 查找数据库中的所有任务数据
*
* @param type {@code 1}单任务下载任务;{@code 2}任务组下载任务;{@code 3} 单任务上传任务
*/
private void findTaskData(int type) {
if (type == 1) {
List<DownloadEntity> entities =
DbEntity.findDatas(DownloadEntity.class,
String.format("NOT(isGroupChild) AND NOT(isComplete) AND %s ORDER BY stopTime DESC",
sqlCondition));
if (entities != null && !entities.isEmpty()) {
for (DownloadEntity entity : entities) {
addResumeEntity(TaskWrapperManager.getInstance()
.getNormalTaskWrapper(DTaskWrapper.class, entity.getId()));
}
}
} else if (type == 2) {
List<DownloadGroupEntity> entities =
DbEntity.findDatas(DownloadGroupEntity.class,
String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC",
sqlCondition));
if (entities != null && !entities.isEmpty()) {
for (DownloadGroupEntity entity : entities) {
addResumeEntity(
TaskWrapperManager.getInstance()
.getGroupWrapper(DGTaskWrapper.class, entity.getId()));
}
}
} else if (type == 3) {
List<UploadEntity> entities =
DbEntity.findDatas(UploadEntity.class,
String.format("NOT(isComplete) AND %s ORDER BY stopTime DESC",
sqlCondition));
if (entities != null && !entities.isEmpty()) {
for (UploadEntity entity : entities) {
addResumeEntity(TaskWrapperManager.getInstance()
.getNormalTaskWrapper(UTaskWrapper.class, entity.getId()));
}
}
}
}
/**
* 添加恢复实体
*/
private void addResumeEntity(AbsTaskWrapper te) {
if (te == null || te.getEntity() == null || TextUtils.isEmpty(te.getKey())) {
return;
}
mWaitList.add(te);
}
/**
* 处理等待状态的任务
*/
private void resumeWaitTask() {
if (mWaitList == null || mWaitList.isEmpty()) {
return;
}
List<AbsEntity> resumeEntities = new ArrayList<>();
for (AbsTaskWrapper wrapper : mWaitList) {
AbsTaskQueue queue = null;
if (wrapper instanceof DTaskWrapper) {
queue = DTaskQueue.getInstance();
} else if (wrapper instanceof UTaskWrapper) {
queue = UTaskQueue.getInstance();
} else if (wrapper instanceof DGTaskWrapper) {
queue = DGroupTaskQueue.getInstance();
}
if (queue == null) {
ALog.e(TAG, "任务类型错误");
continue;
}
if (wrapper.getEntity() == null || TextUtils.isEmpty(wrapper.getKey())) {
ALog.e(TAG, "任务实体为空或key为空");
continue;
}
AbsTask task = queue.getTask(wrapper.getKey());
if (task != null) {
ALog.w(TAG, "任务已存在");
continue;
}
int maxTaskNum = queue.getMaxTaskNum();
task = queue.createTask(wrapper);
if (task == null) {
continue;
}
handleWrapper(wrapper);
if (queue.getCurrentExePoolNum() < maxTaskNum) {
queue.startTask(task);
} else {
wrapper.getEntity().setState(IEntity.STATE_WAIT);
sendWaitState(task);
resumeEntities.add(wrapper.getEntity());
}
}
if (!resumeEntities.isEmpty()) {
DbEntity.updateManyData(resumeEntities);
}
}
/**
* 处理ftp的wrapper
*/
private void handleWrapper(AbsTaskWrapper wrapper) {
int requestType = wrapper.getRequestType();
if (requestType == ITaskWrapper.D_FTP
|| requestType == ITaskWrapper.U_FTP
|| requestType == ITaskWrapper.D_FTP_DIR) {
wrapper.getOptionParams()
.setParams(IOptionConstant.ftpUrlEntity,
CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey()));
}
}
/**
* 发送等待状态
*/
private void sendWaitState(AbsTask task) {
if (task != null) {
task.getTaskWrapper().setState(IEntity.STATE_WAIT);
task.getOutHandler().obtainMessage(ISchedulers.WAIT, task).sendToTarget();
}
}
@Override public void run() {
if (isDownloadCmd) {
findTaskData(1);
findTaskData(2);
} else {
findTaskData(3);
}
resumeWaitTask();
}
}

View File

@@ -18,37 +18,31 @@ package com.arialyy.aria.core.command;
import com.arialyy.aria.core.AriaManager;
import com.arialyy.aria.core.common.QueueMod;
import com.arialyy.aria.core.download.DGTaskWrapper;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.task.AbsTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.IOptionConstant;
import com.arialyy.aria.core.manager.TaskWrapperManager;
import com.arialyy.aria.core.queue.DGroupTaskQueue;
import com.arialyy.aria.core.queue.DTaskQueue;
import com.arialyy.aria.core.queue.UTaskQueue;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.NetUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by lyy on 2016/8/22. 开始命令 队列模型{@link QueueMod#NOW}、{@link QueueMod#WAIT}
*/
final class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
final public class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
private boolean newStart = false;
StartCmd(T entity, int taskType) {
super(entity, taskType);
}
/**
* 立即执行任务
* @param newStart true 立即执行任务,无论执行队列是否满了
*/
public void setNewStart(boolean newStart) {
this.newStart = newStart;
}
@Override public void executeCmd() {
if (!canExeCmd) return;
if (!NetUtils.isConnected(AriaManager.getInstance().getAPP())) {
@@ -87,7 +81,11 @@ final class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
startTask();
}
} else {
sendWaitState(task);
if (newStart){
startTask();
}else {
sendWaitState(task);
}
}
}
} else {
@@ -107,75 +105,7 @@ final class StartCmd<T extends AbsTaskWrapper> extends AbsNormalCmd<T> {
* 当缓冲队列为null时查找数据库中所有等待中的任务
*/
private void findAllWaitTask() {
new Thread(new WaitTaskThread()).start();
}
private class WaitTaskThread implements Runnable {
@Override public void run() {
if (isDownloadCmd) {
handleTask(findWaitData(1));
handleTask(findWaitData(2));
} else {
handleTask(findWaitData(3));
}
}
private List<AbsTaskWrapper> findWaitData(int type) {
List<AbsTaskWrapper> waitList = new ArrayList<>();
TaskWrapperManager tManager = TaskWrapperManager.getInstance();
if (type == 1) { // 普通下载任务
List<DownloadEntity> dEntities = DbEntity.findDatas(DownloadEntity.class,
"isGroupChild=? and state=?", "false", "3");
if (dEntities != null && !dEntities.isEmpty()) {
for (DownloadEntity e : dEntities) {
waitList.add(tManager.getNormalTaskWrapper(DTaskWrapper.class, e.getId()));
}
}
} else if (type == 2) { // 组合任务
List<DownloadGroupEntity> dEntities =
DbEntity.findDatas(DownloadGroupEntity.class, "state=?", "3");
if (dEntities != null && !dEntities.isEmpty()) {
for (DownloadGroupEntity e : dEntities) {
if (e.getTaskType() == ITaskWrapper.DG_HTTP) {
waitList.add(tManager.getGroupWrapper(DGTaskWrapper.class, e.getId()));
} else if (e.getTaskType() == ITaskWrapper.D_FTP_DIR) {
waitList.add(tManager.getGroupWrapper(DGTaskWrapper.class, e.getId()));
}
}
}
} else if (type == 3) { //普通上传任务
List<UploadEntity> dEntities = DbEntity.findDatas(UploadEntity.class, "state=?", "3");
if (dEntities != null && !dEntities.isEmpty()) {
for (UploadEntity e : dEntities) {
waitList.add(tManager.getNormalTaskWrapper(UTaskWrapper.class, e.getId()));
}
}
}
return waitList;
}
private void handleTask(List<AbsTaskWrapper> waitList) {
for (AbsTaskWrapper wrapper : waitList) {
if (wrapper.getEntity() == null) continue;
AbsTask task = getTask(wrapper.getKey());
if (task != null) continue;
if (wrapper instanceof DTaskWrapper) {
if (wrapper.getRequestType() == ITaskWrapper.D_FTP
|| wrapper.getRequestType() == ITaskWrapper.U_FTP) {
wrapper.getOptionParams()
.setParams(IOptionConstant.ftpUrlEntity,
CommonUtil.getFtpUrlInfo(wrapper.getEntity().getKey()));
}
mQueue = DTaskQueue.getInstance();
} else if (wrapper instanceof UTaskWrapper) {
mQueue = UTaskQueue.getInstance();
} else if (wrapper instanceof DGTaskWrapper) {
mQueue = DGroupTaskQueue.getInstance();
}
createTask(wrapper);
}
}
new Thread(
new ResumeThread(isDownloadCmd, String.format("state=%s", IEntity.STATE_WAIT))).start();
}
}

View File

@@ -150,7 +150,17 @@ public abstract class AbsNormalTarget<TARGET extends AbsNormalTarget> extends Ab
*/
@Override
public void resume() {
getController().resume();
resume(false);
}
/**
* 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。
* 如果希望调用恢复接口,马上进入执行队列,需要使用该方法
*
* @param newStart true 立即将任务恢复到执行队列中
*/
@Override public void resume(boolean newStart) {
getController().resume(newStart);
}
/**

View File

@@ -19,6 +19,7 @@ import com.arialyy.aria.core.command.NormalCmdFactory;
import com.arialyy.aria.core.event.EventMsgUtil;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.command.CmdHelper;
import com.arialyy.aria.util.ALog;
/**
* 创建任务时使用的控制器

View File

@@ -30,6 +30,14 @@ public interface INormalFeature {
*/
void resume();
/**
* 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。
* 如果希望调用恢复接口,马上进入执行队列,需要使用该方法
*
* @param newStart true 立即将任务恢复到执行队列中
*/
void resume(boolean newStart);
/**
* 删除任务
*/

View File

@@ -16,11 +16,12 @@
package com.arialyy.aria.core.common.controller;
import com.arialyy.aria.core.command.CancelCmd;
import com.arialyy.aria.core.command.CmdHelper;
import com.arialyy.aria.core.command.NormalCmdFactory;
import com.arialyy.aria.core.command.StartCmd;
import com.arialyy.aria.core.event.EventMsgUtil;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.core.command.CmdHelper;
/**
* 启动控制器
@@ -49,10 +50,23 @@ public final class NormalController extends FeatureController implements INormal
*/
@Override
public void resume() {
resume(false);
}
/**
* 正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。
* 如果希望调用恢复接口,马上进入执行队列,需要使用该方法
*
* @param newStart true 立即将任务恢复到执行队列中
*/
@Override public void resume(boolean newStart) {
if (checkConfig()) {
StartCmd cmd =
(StartCmd) CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START,
checkTaskType());
cmd.setNewStart(newStart);
EventMsgUtil.getDefault()
.post(CmdHelper.createNormalCmd(getTaskWrapper(), NormalCmdFactory.TASK_START,
checkTaskType()));
.post(cmd);
}
}

View File

@@ -16,13 +16,10 @@
package com.arialyy.aria.core.download;
import android.text.TextUtils;
import com.arialyy.aria.core.common.ErrorCode;
import com.arialyy.aria.core.inf.ICheckEntityUtil;
import com.arialyy.aria.core.inf.IOptionConstant;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.inf.ITargetHandler;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.DbEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
@@ -74,17 +71,13 @@ public class CheckDEntityUtil implements ICheckEntityUtil {
mWrapper.getM3U8Params().setParams(IOptionConstant.cacheDir, cacheDir);
M3U8Entity m3U8Entity = mEntity.getM3U8Entity();
Object temp = mWrapper.getM3U8Params().getParam(IOptionConstant.generateIndexFileTemp);
boolean generateIndexFileTemp = temp != null && (boolean) temp;
if (m3U8Entity == null) {
m3U8Entity = new M3U8Entity();
m3U8Entity.setFilePath(mEntity.getFilePath());
m3U8Entity.setPeerIndex(0);
m3U8Entity.setCacheDir(cacheDir);
m3U8Entity.setGenerateIndexFile(generateIndexFileTemp);
m3U8Entity.insert();
} else {
m3U8Entity.setGenerateIndexFile(generateIndexFileTemp);
m3U8Entity.update();
}
if (mWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) {
@@ -141,14 +134,8 @@ public class CheckDEntityUtil implements ICheckEntityUtil {
//设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径
if (!filePath.equals(mEntity.getFilePath())) {
// 检查路径冲突
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) {
if (!mWrapper.isForceDownload()) {
ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用请设置其它保存路径", filePath));
return false;
} else {
ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用当前任务将覆盖该路径的文件", filePath));
RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD);
}
if (!CheckUtil.checkDownloadPathConflicts(mWrapper.isForceDownload(), filePath)) {
return false;
}
File newFile = new File(filePath);

View File

@@ -170,9 +170,8 @@ public class CheckDGEntityUtil implements ICheckEntityUtil {
if (!newName.equals(entity.getFileName())) {
String oldPath = mEntity.getDirPath() + "/" + entity.getFileName();
String newPath = mEntity.getDirPath() + "/" + newName;
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=? or isComplete='true'",
newPath)) {
ALog.w(TAG, String.format("更新文件名失败,路径【%s】已存在或文件已下载", newPath));
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", newPath)) {
ALog.w(TAG, String.format("更新文件名失败,路径【%s】被其它任务占用", newPath));
return;
}

View File

@@ -17,8 +17,8 @@ package com.arialyy.aria.core.download.m3u8;
import com.arialyy.aria.core.common.BaseOption;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.IKeyUrlConverter;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.processor.IVodTsUrlConverter;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.ComponentUtil;
@@ -27,11 +27,12 @@ import com.arialyy.aria.util.ComponentUtil;
*/
public class M3U8Option<OP extends M3U8Option> extends BaseOption {
private boolean generateIndexFileTemp = false;
private boolean mergeFile = false;
private boolean generateIndexFile = false;
private boolean mergeFile = true;
private int bandWidth;
private ITsMergeHandler mergeHandler;
private IBandWidthUrlConverter bandWidthUrlConverter;
private IKeyUrlConverter keyUrlConverter;
M3U8Option() {
super();
@@ -41,9 +42,10 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
/**
* 生成m3u8索引文件
* 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件
* 如果是直播文件下载,创建索引文件的操作将导致只能同时下载一个切片!!
*/
public OP generateIndexFile() {
this.generateIndexFileTemp = true;
this.generateIndexFile = true;
return (OP) this;
}
@@ -88,4 +90,15 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
this.bandWidthUrlConverter = bandWidthUrlConverter;
return (OP) this;
}
/**
* M3U8 密钥url转换器对于某些服务器密钥的下载地址是被加密的因此需要使用该方法将被加密的密钥解密成可被识别的http地址
*
* @param keyUrlConverter {@link IKeyUrlConverter}
*/
public OP setKeyUrlConverter(IKeyUrlConverter keyUrlConverter) {
CheckUtil.checkMemberClass(keyUrlConverter.getClass());
this.keyUrlConverter = keyUrlConverter;
return (OP) this;
}
}

View File

@@ -42,14 +42,17 @@ class DGTaskWrapperFactory implements IGroupWrapperFactory<DownloadGroupEntity,
}
@Override public DGTaskWrapper getGroupWrapper(long taskId) {
DGTaskWrapper wrapper;
if (taskId == -1) {
return new DGTaskWrapper(new DownloadGroupEntity());
}
DownloadGroupEntity entity = getOrCreateHttpDGEntity(taskId);
DGTaskWrapper wrapper = new DGTaskWrapper(entity);
if (entity.getSubEntities() != null && !entity.getSubEntities().isEmpty()) {
wrapper.setSubTaskWrapper(DbDataHelper.createDGSubTaskWrapper(entity));
wrapper = new DGTaskWrapper(new DownloadGroupEntity());
}else {
DownloadGroupEntity entity = getOrCreateHttpDGEntity(taskId);
wrapper = new DGTaskWrapper(entity);
if (entity.getSubEntities() != null && !entity.getSubEntities().isEmpty()) {
wrapper.setSubTaskWrapper(DbDataHelper.createDGSubTaskWrapper(entity));
}
}
wrapper.setRequestType(wrapper.getEntity().getTaskType());
return wrapper;
}

View File

@@ -45,11 +45,14 @@ class DTaskWrapperFactory implements INormalTEFactory<DownloadEntity, DTaskWrapp
* 通过下载地址创建任务实体
*/
@Override public DTaskWrapper create(long taskId) {
DTaskWrapper wrapper;
if (taskId == -1) {
return new DTaskWrapper(new DownloadEntity());
wrapper = new DTaskWrapper(new DownloadEntity());
} else {
wrapper = new DTaskWrapper(getEntity(taskId));
}
return new DTaskWrapper(getEntity(taskId));
wrapper.setRequestType(wrapper.getEntity().getTaskType());
return wrapper;
}
/**

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.core.manager;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.util.ALog;
/**
* Created by Aria.Lao on 2017/11/1. 任务实体工厂
@@ -38,10 +39,15 @@ class UTaskWrapperFactory implements INormalTEFactory<UploadEntity, UTaskWrapper
}
@Override public UTaskWrapper create(long taskId) {
UTaskWrapper wrapper;
if (taskId == -1) {
return new UTaskWrapper(new UploadEntity());
wrapper = new UTaskWrapper(new UploadEntity());
} else {
wrapper = new UTaskWrapper(getUploadEntity(taskId));
}
return new UTaskWrapper(getUploadEntity(taskId));
wrapper.setRequestType(wrapper.getEntity().getTaskType());
return wrapper;
}
/**

View File

@@ -16,7 +16,6 @@
package com.arialyy.aria.core.upload;
import android.text.TextUtils;
import com.arialyy.aria.core.common.ErrorCode;
import com.arialyy.aria.core.inf.ICheckEntityUtil;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CheckUtil;
@@ -60,6 +59,11 @@ public class CheckUEntityUtil implements ICheckEntityUtil {
return false;
}
// 检查路径冲突
if (!CheckUtil.checkUploadPathConflicts(mWrapper.isForceUpload(), filePath)) {
return false;
}
File file = new File(mEntity.getFilePath());
if (!file.exists()) {
ALog.e(TAG, "上传失败,文件【" + filePath + "】不存在");
@@ -69,6 +73,7 @@ public class CheckUEntityUtil implements ICheckEntityUtil {
ALog.e(TAG, "上传失败,文件【" + filePath + "】不能是文件夹");
return false;
}
return true;
}

View File

@@ -19,7 +19,8 @@ import androidx.annotation.CheckResult;
import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.FtpOption;
import com.arialyy.aria.core.inf.Suggest;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.CommonUtil;
/**
@@ -33,7 +34,8 @@ public class FtpBuilderTarget extends AbsBuilderTarget<FtpBuilderTarget> {
FtpBuilderTarget(String filePath) {
mConfigHandler = new UNormalConfigHandler<>(this, -1);
mConfigHandler.setFilePath(filePath);
getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP);
getTaskWrapper().setRequestType(ITaskWrapper.U_FTP);
getTaskWrapper().setNewTask(true);
}
/**
@@ -48,6 +50,14 @@ public class FtpBuilderTarget extends AbsBuilderTarget<FtpBuilderTarget> {
return this;
}
/**
* 如果文件路径被其它任务占用,删除其它任务
*/
public FtpBuilderTarget forceUpload() {
((UTaskWrapper)getTaskWrapper()).setForceUpload(true);
return this;
}
/**
* 设置登陆、字符串编码、ftps等参数
*/

View File

@@ -20,7 +20,7 @@ import com.arialyy.aria.core.common.AbsNormalTarget;
import com.arialyy.aria.core.common.FtpOption;
import com.arialyy.aria.core.inf.Suggest;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.CommonUtil;
/**
@@ -32,7 +32,7 @@ public class FtpNormalTarget extends AbsNormalTarget<FtpNormalTarget> {
FtpNormalTarget(long taskId) {
mConfigHandler = new UNormalConfigHandler<>(this, taskId);
getTaskWrapper().setRequestType(AbsTaskWrapper.U_FTP);
getTaskWrapper().setRequestType(ITaskWrapper.U_FTP);
}
/**

View File

@@ -19,6 +19,7 @@ import androidx.annotation.CheckResult;
import com.arialyy.aria.core.common.AbsBuilderTarget;
import com.arialyy.aria.core.common.HttpOption;
import com.arialyy.aria.core.inf.Suggest;
import com.arialyy.aria.core.upload.UTaskWrapper;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
/**
@@ -60,4 +61,12 @@ public class HttpBuilderTarget extends AbsBuilderTarget<HttpBuilderTarget> {
getTaskWrapper().getOptionParams().setParams(option);
return this;
}
/**
* 如果文件路径被其它任务占用,删除其它任务
*/
public HttpBuilderTarget forceUpload() {
((UTaskWrapper) getTaskWrapper()).setForceUpload(true);
return this;
}
}

View File

@@ -1,5 +1,38 @@
## 开发日志
+ v_3.7
+ v_3.7.9 (2019/11/28)
- fix bug https://github.com/AriaLyy/Aria/issues/537
+ v_3.7.8 (2019/11/28)
- fix bug https://github.com/AriaLyy/Aria/issues/526
- fix bug https://github.com/AriaLyy/Aria/issues/533
- fix bug https://github.com/AriaLyy/Aria/issues/535
- 修复ftp无法完成下载的问题
- 修复一个非分块模式下,调用`updateUrl(newUrl)`后无法恢复下载的问题
- 增加立即恢复任务的接口,正常来说,当执行队列满时,调用恢复任务接口,只能将任务放到缓存队列中。如果希望调用恢复接口,马上进入执行队列,需要调用`resume(true)`这个重载方法。
- 增加M3U8加密密钥的下载地址转换器 https://github.com/AriaLyy/Aria/issues/522
+ v_3.7.7 (2019/11/20)
- 修复ftp无法完成下载的问题
- 修复一个http下载崩溃的问题
+ v_3.7.6 (2019/11/19)
- fix bug https://github.com/AriaLyy/Aria/issues/505
- fix bug https://github.com/AriaLyy/Aria/issues/516
- fix bug https://github.com/AriaLyy/Aria/issues/515
- 增加强制上传的api`forceUpload()`
- 修复for循环上传文件出现的问题
- 移除创建任务的500ms间隔限制
- 修复多线程读写时可能出现的`database is locked`的问题
+ v_3.7.5 (2019/11/10)
- fix bug https://github.com/AriaLyy/Aria/issues/500
- fix bug https://github.com/AriaLyy/Aria/issues/508
- fix bug https://github.com/AriaLyy/Aria/issues/503
- 修复m3u8创建索引不成功的问题
+ v_3.7.4 (2019/11/2)
- 修复一个class被莫名改变的问题
- 修复非分块模式下导致的一个下载失败问题
- fix bug https://github.com/AriaLyy/Aria/issues/493
+ v_3.7.3 (2019/10/31)
- fix bug https://github.com/AriaLyy/Aria/issues/495
- fix bug https://github.com/AriaLyy/Aria/issues/496
+ v_3.7.2 (2019/10/28)
- fix bug https://github.com/AriaLyy/Aria/issues/450
- fix bug https://github.com/AriaLyy/Aria/issues/466
- fix bug https://github.com/AriaLyy/Aria/issues/454
@@ -10,7 +43,7 @@
- fix bug https://github.com/AriaLyy/Aria/issues/482
- fix bug https://github.com/AriaLyy/Aria/issues/473
- 移除隐藏api的反射 https://github.com/AriaLyy/Aria/issues/456
- 新增ftp免证书登陆功能h ttps://github.com/AriaLyy/Aria/issues/455
- 新增ftp免证书登陆功能 https://github.com/AriaLyy/Aria/issues/455
- 适配androidX
- 修复组合任务恢复下载会出现进度显示为0的问题
- m3u8点播下载新增创建ts索引功能

View File

@@ -27,7 +27,6 @@ dependencies {
implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}"
// implementation project(path: ':AriaFtpPlug')
implementation project(path: ':PublicComponent')
}

View File

@@ -53,7 +53,7 @@ import javax.net.ssl.SSLContext;
public abstract class AbsFtpInfoThread<ENTITY extends AbsEntity, TASK_WRAPPER extends AbsTaskWrapper<ENTITY>>
implements Runnable {
private final String TAG = "AbsFtpInfoThread";
private final String TAG = CommonUtil.getClassName(getClass());
protected ENTITY mEntity;
protected TASK_WRAPPER mTaskWrapper;
protected FtpTaskOption mTaskOption;

View File

@@ -25,6 +25,7 @@ import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.inf.OnFileInfoCallback;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.CheckUtil;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.RecordUtil;
import java.nio.charset.Charset;
@@ -72,6 +73,9 @@ public class FtpDirInfoThread extends AbsFtpInfoThread<DownloadGroupEntity, DGTa
entity.setGroupChild(true);
entity.setConvertFileSize(CommonUtil.formatFileSize(ftpFile.getSize()));
entity.setFileSize(ftpFile.getSize());
//if(CheckUtil.checkDownloadPathConflicts(mTaskWrapper.is))
entity.insert();
DTaskWrapper subWrapper = new DTaskWrapper(entity);

View File

@@ -21,9 +21,8 @@ import com.arialyy.aria.core.common.AbsRecordHandlerAdapter;
import com.arialyy.aria.core.common.RecordHelper;
import com.arialyy.aria.core.config.Configuration;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.common.AbsNormalEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.RecordUtil;
import java.util.ArrayList;
@@ -40,8 +39,12 @@ public class FtpRecordAdapter extends AbsRecordHandlerAdapter {
@Override public void handlerTaskRecord(TaskRecord record) {
RecordHelper helper = new RecordHelper(getWrapper(), record);
if (record.isBlock) {
helper.handleBlockRecord();
if (getWrapper().isSupportBP()) {
if (record.isBlock) {
helper.handleBlockRecord();
} else {
helper.handleMultiRecord();
}
} else if (record.threadNum == 1) {
helper.handleSingleThreadRecord();
}
@@ -74,9 +77,7 @@ public class FtpRecordAdapter extends AbsRecordHandlerAdapter {
int requestType = getWrapper().getRequestType();
if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) {
record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock();
// 线程数为1或者使用了分块则认为是使用动态长度文件
record.isOpenDynamicFile = threadNum == 1 || record.isBlock;
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock();
} else {
record.isBlock = false;
}
@@ -104,6 +105,4 @@ public class FtpRecordAdapter extends AbsRecordHandlerAdapter {
return 1;
}
}
}

View File

@@ -16,32 +16,33 @@
package com.arialyy.aria.ftp.download;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.task.AbsNormalLoaderAdapter;
import com.arialyy.aria.core.common.RecordHandler;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.task.AbsNormalLoaderAdapter;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.task.ThreadTask;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.ftp.FtpRecordAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
/**
* @Author lyy
* @Date 2019-09-19
*/
final class FtpDLoaderAdapter extends AbsNormalLoaderAdapter {
public class FtpDLoaderAdapter extends AbsNormalLoaderAdapter {
FtpDLoaderAdapter(ITaskWrapper wrapper) {
public FtpDLoaderAdapter(ITaskWrapper wrapper) {
super(wrapper);
}
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
if (!record.isBlock) {
if (getTempFile().exists()) {
getTempFile().delete();
FileUtil.deleteFile(getTempFile());
}
//CommonUtil.createFile(mTempFile.getPath());
} else {
@@ -50,7 +51,7 @@ final class FtpDLoaderAdapter extends AbsNormalLoaderAdapter {
new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i));
if (blockFile.exists()) {
ALog.d(TAG, String.format("分块【%s】已经存在将删除该分块", i));
blockFile.delete();
FileUtil.deleteFile(blockFile);
}
}
}

View File

@@ -83,7 +83,7 @@ final class FtpDThreadTaskAdapter extends BaseFtpThreadTaskAdapter {
return;
}
if (getThreadConfig().isOpenDynamicFile) {
if (getThreadConfig().isBlock) {
readDynamicFile(is);
} else {
readNormal(is);

View File

@@ -85,13 +85,14 @@ public class FtpDirDLoaderUtil extends AbsGroupUtil {
* @param needCloneInfo 第一次下载,信息已经在{@link FtpDirInfoThread}中clone了
*/
private void startDownload(boolean needCloneInfo) {
// ftp需要获取完成只任务信息才更新只任务数量
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
try {
LOCK.lock();
condition.signalAll();
} finally {
LOCK.unlock();
}
initState();
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
if (needCloneInfo) {
cloneInfo(wrapper);

View File

@@ -60,6 +60,7 @@ class FtpUFileInfoThread extends AbsFtpInfoThread<UploadEntity, UTaskWrapper> {
@Override protected boolean onInterceptor(FTPClient client, FTPFile[] ftpFiles) {
// 旧任务将不做处理,否则断点续传上传将失效
if (!mTaskWrapper.isNewTask()) {
ALog.d(TAG, "任务是旧任务,忽略该拦截器");
return true;
}
try {

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">AriaFtpComponent</string>
</resources>

View File

@@ -38,15 +38,19 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter {
@Override public void onPre() {
super.onPre();
if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP){
if (getWrapper().getRequestType() == ITaskWrapper.U_HTTP) {
RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_UPLOAD);
}
}
@Override public void handlerTaskRecord(TaskRecord record) {
RecordHelper helper = new RecordHelper(getWrapper(), record);
if (record.isBlock) {
helper.handleBlockRecord();
if (getWrapper().isSupportBP()) {
if (record.isBlock) {
helper.handleBlockRecord();
} else {
helper.handleMultiRecord();
}
} else if (!getWrapper().isSupportBP()) {
helper.handleNoSupportBPRecord();
} else {
@@ -81,10 +85,9 @@ public class HttpRecordAdapter extends AbsRecordHandlerAdapter {
record.threadNum = threadNum;
int requestType = getWrapper().getRequestType();
if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR) {
record.isBlock = threadNum > 1 && Configuration.getInstance().downloadCfg.isUseBlock();
// 线程数为1或者使用了分块则认为是使用动态长度文件
record.isOpenDynamicFile = threadNum == 1 || record.isBlock;
if (requestType == ITaskWrapper.D_FTP || requestType == ITaskWrapper.D_FTP_DIR
|| requestType == ITaskWrapper.D_HTTP || requestType == ITaskWrapper.DG_HTTP) {
record.isBlock = Configuration.getInstance().downloadCfg.isUseBlock();
} else {
record.isBlock = false;
}

View File

@@ -73,7 +73,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil {
@Override protected boolean onStart() {
super.onStart();
initState();
getState().setSubSize(getWrapper().getSubTaskWrapper().size());
if (getState().getCompleteNum() == getState().getSubSize()) {
mListener.onComplete();
} else {
@@ -91,6 +91,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil {
return getLenComplete;
} else {
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
cloneHeader(wrapper);
if (wrapper.getState() != IEntity.STATE_COMPLETE) {
startSubLoader(createSubLoader(wrapper, true));
}
@@ -179,7 +180,7 @@ public class DGroupLoaderUtil extends AbsGroupUtil {
*/
private void cloneHeader(DTaskWrapper taskWrapper) {
HttpTaskOption groupOption = (HttpTaskOption) getWrapper().getTaskOption();
HttpTaskOption subOption = (HttpTaskOption) taskWrapper.getTaskOption();
HttpTaskOption subOption = new HttpTaskOption();
// 设置属性
subOption.setFileLenAdapter(groupOption.getFileLenAdapter());
@@ -187,5 +188,6 @@ public class DGroupLoaderUtil extends AbsGroupUtil {
subOption.setHeaders(groupOption.getHeaders());
subOption.setProxy(groupOption.getProxy());
subOption.setParams(groupOption.getParams());
taskWrapper.setTaskOption(subOption);
}
}

View File

@@ -28,6 +28,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.http.HttpRecordAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.IOException;
@@ -43,16 +44,15 @@ final class HttpDLoaderAdapter extends AbsNormalLoaderAdapter {
@Override public boolean handleNewTask(TaskRecord record, int totalThreadNum) {
if (!record.isBlock) {
if (getTempFile().exists()) {
getTempFile().delete();
FileUtil.deleteFile(getTempFile());
}
//CommonUtil.createFile(mTempFile.getPath());
} else {
for (int i = 0; i < totalThreadNum; i++) {
File blockFile =
new File(String.format(IRecordHandler.SUB_PATH, getTempFile().getPath(), i));
if (blockFile.exists()) {
ALog.d(TAG, String.format("分块【%s】已经存在将删除该分块", i));
blockFile.delete();
FileUtil.deleteFile(blockFile);
}
}
}

View File

@@ -15,15 +15,16 @@
*/
package com.arialyy.aria.http.download;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.exception.AriaIOException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.http.BaseHttpThreadTaskAdapter;
import com.arialyy.aria.http.ConnectionHelp;
import com.arialyy.aria.core.common.RequestEnum;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -103,7 +104,7 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (mTaskOption.isChunked()) {
readChunked(is);
} else if (getThreadConfig().isOpenDynamicFile) {
} else if (getThreadConfig().isBlock) {
readDynamicFile(is);
} else {
//创建可设置位置的文件
@@ -123,6 +124,10 @@ final class HttpDThreadTaskAdapter extends BaseHttpThreadTaskAdapter {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), true);
} catch (ArrayIndexOutOfBoundsException e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),
getEntity().getFilePath(), getEntity().getUrl()), e), false);
} catch (Exception e) {
fail(new TaskException(TAG,
String.format("任务【%s】下载失败filePath: %s, url: %s", getFileName(),

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">HttpComponent</string>
</resources>

View File

@@ -68,8 +68,9 @@ public abstract class BaseM3U8Loader extends AbsLoader {
/**
* 创建索引文件
*/
protected boolean generateIndexFile() {
File tempFile = new File(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath());
public boolean generateIndexFile(boolean isLive) {
File tempFile =
new File(String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath()));
if (!tempFile.exists()) {
ALog.e(TAG, "源索引文件不存在");
return false;
@@ -85,21 +86,29 @@ public abstract class BaseM3U8Loader extends AbsLoader {
int i = 0;
while ((line = reader.readLine()) != null) {
byte[] bytes;
if (line.startsWith("EXTINF")) {
if (line.startsWith("#EXTINF")) {
fos.write(line.concat("\r\n").getBytes(Charset.forName("UTF-8")));
String tsPath = getTsFilePath(cacheDir, mRecord.threadRecords.get(i).threadId);
bytes = tsPath.concat("\r\n").getBytes(Charset.forName("UTF-8"));
reader.readLine(); // 继续读一行,避免写入源索引文件的切片地址
i++;
} else if (line.startsWith("EXT-X-KEY")) {
} else if (line.startsWith("#EXT-X-KEY")) {
M3U8Entity m3U8Entity = getEntity().getM3U8Entity();
String keyInfo = String.format("#EXT-X-KEY:METHOD=%s,URI=%s,IV=%s\r\n", m3U8Entity.method,
m3U8Entity.keyPath, m3U8Entity.iv);
bytes = keyInfo.getBytes(Charset.forName("UTF-8"));
} else {
bytes = line.getBytes(Charset.forName("UTF-8"));
bytes = line.concat("\r\n").getBytes(Charset.forName("UTF-8"));
}
fos.write(bytes, 0, bytes.length);
}
// 直播的索引文件需要在结束的时候才写入结束标志
if (isLive) {
fos.write("#EXT-X-ENDLIST".concat("\r\n").getBytes(Charset.forName("UTF-8")));
}
fos.flush();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {

View File

@@ -24,8 +24,9 @@ import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.M3U8Entity;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.inf.OnFileInfoCallback;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.IKeyUrlConverter;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.M3U8Exception;
@@ -39,7 +40,6 @@ import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.Regular;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -55,6 +55,7 @@ import java.util.regex.Pattern;
/**
* 解析url中获取到到m3u8文件信息
* 协议地址https://tools.ietf.org/html/rfc8216
* https://www.cnblogs.com/renhui/p/10351870.html
* https://blog.csdn.net/Guofengpu/article/details/54922865
*/
@@ -72,13 +73,9 @@ final public class M3U8InfoThread implements Runnable {
* 是否停止获取切片信息,{@code true}停止获取切片信息
*/
private boolean isStop = false;
/**
* m3u8文件信息
*/
private List<String> mInfos = new ArrayList<>();
public interface OnGetLivePeerCallback {
void onGetPeer(String url);
void onGetPeer(String url, String extInf);
}
public M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) {
@@ -122,38 +119,51 @@ final public class M3U8InfoThread implements Runnable {
}
List<String> extInf = new ArrayList<>();
boolean isLive = mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_LIVE;
boolean isGenerateIndexFile = mTaskWrapper.getEntity().getM3U8Entity().isGenerateIndexFile();
boolean isGenerateIndexFile =
((M3U8TaskOption) mTaskWrapper.getM3u8Option()).isGenerateIndexFile();
// 写入索引信息的流
FileOutputStream fos = null;
if (isGenerateIndexFile) {
mInfos.add(line);
String indexPath = String.format(M3U8_INDEX_FORMAT, mEntity.getFilePath());
File indexFile = new File(indexPath);
if (!indexFile.exists()) {
FileUtil.createFile(indexPath);
} else {
//FileUtil.deleteFile(indexPath);
}
fos = new FileOutputStream(indexFile);
ALog.d(TAG, line);
addIndexInfo(isGenerateIndexFile, fos, line);
}
while ((line = reader.readLine()) != null) {
if (isStop) {
break;
}
if (isGenerateIndexFile) {
mInfos.add(line);
}
if (line.startsWith("#EXT-X-ENDLIST")) {
break;
}
ALog.d(TAG, line);
if (line.startsWith("#EXTINF")) {
String info = reader.readLine();
mInfos.add(info);
if (line.startsWith("#EXT-X-ENDLIST")) {
// 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
break;
} else if (line.startsWith("#EXTINF")) {
String url = reader.readLine();
if (isLive) {
if (onGetPeerCallback != null) {
onGetPeerCallback.onGetPeer(info);
onGetPeerCallback.onGetPeer(url, line);
}
} else {
extInf.add(info);
extInf.add(url);
}
ALog.d(TAG, url);
addIndexInfo(isGenerateIndexFile && !isLive, fos, line);
addIndexInfo(isGenerateIndexFile && !isLive, fos, url);
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
addIndexInfo(isGenerateIndexFile, fos, line);
int setBand = mM3U8Option.getBandWidth();
int bandWidth = getBandWidth(line);
// 多码率的m3u8配置文件清空信息
if (isGenerateIndexFile && mInfos != null) {
mInfos.clear();
}
//if (isGenerateIndexFile && mInfos != null) {
// mInfos.clear();
//}
if (setBand == 0) {
handleBandWidth(conn, reader.readLine());
} else if (bandWidth == setBand) {
@@ -162,8 +172,11 @@ final public class M3U8InfoThread implements Runnable {
failDownload(String.format("【%s】码率不存在", bandWidth), false);
}
return;
} else if (line.startsWith("EXT-X-KEY")) {
} else if (line.startsWith("#EXT-X-KEY")) {
addIndexInfo(isGenerateIndexFile, fos, line);
getKeyInfo(line);
} else {
addIndexInfo(isGenerateIndexFile, fos, line);
}
}
@@ -177,8 +190,11 @@ final public class M3U8InfoThread implements Runnable {
}
CompleteInfo info = new CompleteInfo();
info.obj = extInf;
generateIndexFile();
onFileInfoCallback.onComplete(mEntity.getKey(), info);
if (fos != null) {
fos.close();
}
} else if (code == HttpURLConnection.HTTP_MOVED_TEMP
|| code == HttpURLConnection.HTTP_MOVED_PERM
|| code == HttpURLConnection.HTTP_SEE_OTHER
@@ -193,40 +209,19 @@ final public class M3U8InfoThread implements Runnable {
}
/**
* 创建索引文件
* 添加切片信息到索引文件
* 直播下载的索引只记录头部信息不记录EXTINF中的信息该信息在onGetPeer的方法中添加。
* 点播下载记录所有信息
*
* @param write true 将信息写入文件
* @param info 切片信息
*/
private void generateIndexFile() {
if (mTaskWrapper.getEntity().getM3U8Entity().isGenerateIndexFile()) {
String indexPath = String.format(M3U8_INDEX_FORMAT, mEntity.getFilePath());
File indexFile = new File(indexPath);
if (indexFile.exists()) {
FileUtil.deleteFile(indexPath);
}
FileUtil.createFile(indexPath);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(indexFile);
for (String str : mInfos) {
byte[] by = str.concat("\r\n").getBytes(Charset.forName("UTF-8"));
fos.write(by, 0, by.length);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void addIndexInfo(boolean write, FileOutputStream fos, String info)
throws IOException {
if (!write) {
return;
}
fos.write(info.concat("\r\n").getBytes(Charset.forName("UTF-8")));
}
/**
@@ -355,11 +350,22 @@ final public class M3U8InfoThread implements Runnable {
File keyF = new File(info.keyPath);
if (!keyF.exists()) {
ALog.d(TAG, "密钥不存在,下载密钥");
FileUtil.createFile(keyF.getPath());
FileUtil.createFile(keyF);
} else {
return;
}
URL url = ConnectionHelp.handleUrl(info.keyUrl, mHttpOption);
IKeyUrlConverter keyUrlConverter = mM3U8Option.getKeyUrlConverter();
String keyUrl = info.keyUrl;
if (keyUrlConverter != null) {
keyUrl = keyUrlConverter.convert(keyUrl);
}
if (TextUtils.isEmpty(keyUrl)){
ALog.e(TAG, "m3u8密钥key url 为空");
return;
}
URL url = ConnectionHelp.handleUrl(keyUrl, mHttpOption);
conn = ConnectionHelp.handleConnection(url, mHttpOption);
ConnectionHelp.setConnectParam(mHttpOption, conn);
conn.setConnectTimeout(mConnectTimeOut);

View File

@@ -24,6 +24,7 @@ import com.arialyy.aria.core.download.M3U8Entity;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil;
import java.io.File;
import java.util.ArrayList;
@@ -42,7 +43,7 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter {
@Override public void onPre() {
super.onPre();
if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE){
if (getWrapper().getRequestType() == ITaskWrapper.M3U8_LIVE) {
RecordUtil.delTaskRecord(getEntity().getFilePath(), IRecordHandler.TYPE_DOWNLOAD);
}
}
@@ -54,18 +55,22 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter {
String cacheDir = mOption.getCacheDir();
long currentProgress = 0;
int completeNum = 0;
File targetFile = new File(mTaskRecord.filePath);
if (!targetFile.exists()) {
FileUtil.createFile(targetFile);
}
M3U8Entity m3U8Entity = ((DownloadEntity) getEntity()).getM3U8Entity();
// 重新下载所有切片
boolean reDownload =
(m3U8Entity.getPeerNum() <= 0 || (m3U8Entity.isGenerateIndexFile() && !new File(
(m3U8Entity.getPeerNum() <= 0 || (mOption.isGenerateIndexFile() && !new File(
String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath())).exists()));
for (ThreadRecord record : mTaskRecord.threadRecords) {
File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId));
if (!record.isComplete || reDownload) {
if (temp.exists()) {
temp.delete();
FileUtil.deleteFile(temp);
}
record.startLocation = 0;
//ALog.d(TAG, String.format("分片【%s】未完成将重新下载该分片", record.threadId));
@@ -112,17 +117,15 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter {
record.filePath = getEntity().getFilePath();
record.threadRecords = new ArrayList<>();
record.threadNum = threadNum;
record.isBlock = true;
int requestType = getWrapper().getRequestType();
if (requestType == ITaskWrapper.M3U8_VOD) {
record.taskType = TaskRecord.TYPE_M3U8_VOD;
record.isOpenDynamicFile = true;
record.bandWidth = mOption.getBandWidth();
} else if (requestType == ITaskWrapper.M3U8_LIVE) {
record.taskType = TaskRecord.TYPE_M3U8_LIVE;
record.isOpenDynamicFile = true;
record.bandWidth = mOption.getBandWidth();
}
record.bandWidth = mOption.getBandWidth();
return record;
}

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.m3u8;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.IKeyUrlConverter;
import com.arialyy.aria.core.processor.ILiveTsUrlConverter;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.processor.IVodTsUrlConverter;
@@ -101,14 +102,27 @@ public class M3U8TaskOption implements ITaskOption {
/**
* 生成索引占位字段
*/
private boolean generateIndexFileTemp = false;
private boolean generateIndexFile = false;
public boolean isGenerateIndexFileTemp() {
return generateIndexFileTemp;
/**
* 加密密钥的解密处理器
*/
private SoftReference<IKeyUrlConverter> keyUrlConverter;
public IKeyUrlConverter getKeyUrlConverter() {
return keyUrlConverter == null ? null : keyUrlConverter.get();
}
public void setGenerateIndexFileTemp(boolean generateIndexFileTemp) {
this.generateIndexFileTemp = generateIndexFileTemp;
public void setKeyUrlConverter(IKeyUrlConverter keyUrlConverter) {
this.keyUrlConverter = new SoftReference<>(keyUrlConverter);
}
public boolean isGenerateIndexFile() {
return generateIndexFile;
}
public void setGenerateIndexFile(boolean generateIndexFile) {
this.generateIndexFile = generateIndexFile;
}
public int getJumpIndex() {

View File

@@ -89,10 +89,18 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
}
}
int code = conn.getResponseCode();
if (code != HttpURLConnection.HTTP_OK) {
fail(new TaskException(TAG,
String.format("连接错误http错误码%surl%s", code, getThreadConfig().url)),
false);
return;
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (mHttpTaskOption.isChunked()) {
readChunked(is);
} else if (getThreadConfig().isOpenDynamicFile) {
} else if (getThreadConfig().isBlock) {
readDynamicFile(is);
}
} catch (MalformedURLException e) {

View File

@@ -24,17 +24,22 @@ import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.IThreadState;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.task.ThreadTask;
import com.arialyy.aria.m3u8.BaseM3U8Loader;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.m3u8.IdGenerator;
import com.arialyy.aria.m3u8.M3U8Listener;
import com.arialyy.aria.m3u8.M3U8TaskOption;
import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
@@ -42,6 +47,8 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import static com.arialyy.aria.m3u8.M3U8InfoThread.M3U8_INDEX_FORMAT;
/**
* M3U8点播文件下载器
*/
@@ -49,15 +56,21 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
/**
* 最大执行数
*/
private static final int EXEC_MAX_NUM = 4;
private static int EXEC_MAX_NUM = 4;
private Handler mStateHandler;
private ArrayBlockingQueue<Long> mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM);
private ReentrantLock LOCK = new ReentrantLock();
private Condition mCondition = LOCK.newCondition();
private LinkedBlockingQueue<String> mPeerQueue = new LinkedBlockingQueue<>();
private LinkedBlockingQueue<ExtInfo> mPeerQueue = new LinkedBlockingQueue<>();
private ExtInfo mCurExtInfo;
private FileOutputStream mIndexFos;
M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) {
super(listener, wrapper);
if (((M3U8TaskOption) wrapper.getM3u8Option()).isGenerateIndexFile()) {
ALog.i(TAG, "直播文件下载,创建索引文件的操作将导致只能同时下载一个切片");
EXEC_MAX_NUM = 1;
}
}
@Override protected IThreadState createStateManager(Looper looper) {
@@ -66,8 +79,8 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return manager;
}
void offerPeer(String peerUrl) {
mPeerQueue.offer(peerUrl);
void offerPeer(ExtInfo extInfo) {
mPeerQueue.offer(extInfo);
}
@Override protected void handleTask() {
@@ -80,13 +93,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
try {
LOCK.lock();
while (mFlagQueue.size() < EXEC_MAX_NUM) {
String url = mPeerQueue.poll();
if (url == null) {
ExtInfo extInfo = mPeerQueue.poll();
if (extInfo == null) {
break;
}
ThreadTask task = createThreadTask(cacheDir, index, url);
mCurExtInfo = extInfo;
ThreadTask task = createThreadTask(cacheDir, index, extInfo.url);
getTaskList().put(index, task);
mFlagQueue.offer(startThreadTask(task));
mFlagQueue.offer(startThreadTask(task, task.getConfig().peerIndex));
index++;
}
if (mFlagQueue.size() > 0) {
@@ -106,11 +120,15 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return mTempFile.length();
}
private void notifyLock() {
private void notifyLock(boolean success, int peerId) {
try {
LOCK.lock();
long id = mFlagQueue.take();
ALog.d(TAG, String.format("线程【%s】完成", id));
if (success) {
ALog.d(TAG, String.format("切片【%s】下载成功", peerId));
} else {
ALog.e(TAG, String.format("切片【%s】下载失败", peerId));
}
mCondition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
@@ -119,13 +137,27 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
}
}
@Override protected void onPostStop() {
super.onPostStop();
if (mIndexFos != null) {
try {
mIndexFos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 启动线程任务
*
* @return 线程唯一id标志
*/
private long startThreadTask(ThreadTask task) {
private long startThreadTask(ThreadTask task, int indexId) {
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task);
((M3U8Listener) mListener).onPeerStart(mTaskWrapper.getKey(),
task.getConfig().tempFile.getPath(),
indexId);
return IdGenerator.getInstance().nextId();
}
@@ -139,18 +171,19 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
record.tsUrl = tsUrl;
record.threadType = TaskRecord.TYPE_M3U8_LIVE;
record.threadId = indexId;
mRecord.threadRecords.add(record);
SubThreadConfig config = new SubThreadConfig();
config.url = tsUrl;
config.tempFile = new File(getTsFilePath(cacheDir, indexId));
config.isBlock = mRecord.isBlock;
config.isOpenDynamicFile = mRecord.isOpenDynamicFile;
config.taskWrapper = mTaskWrapper;
config.record = record;
config.stateHandler = mStateHandler;
config.peerIndex = indexId;
if (!config.tempFile.exists()) {
FileUtil.createFile(config.tempFile.getPath());
FileUtil.createFile(config.tempFile);
}
ThreadTask threadTask = new ThreadTask(config);
M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config);
@@ -163,10 +196,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
*
* @return {@code true} 合并成功,{@code false}合并失败
*/
public boolean mergeFile() {
if (getEntity().getM3U8Entity().isGenerateIndexFile()) {
return generateIndexFile();
}
boolean mergeFile() {
ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler();
String cacheDir = getCacheDir();
List<String> partPath = new ArrayList<>();
@@ -213,7 +243,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
/**
* 任务状态回调
*/
private IEventListener mListener;
private M3U8Listener mListener;
private long mProgress; //当前总进度
private Looper mLooper;
@@ -222,7 +252,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
*/
LiveStateManager(Looper looper, IEventListener listener) {
mLooper = looper;
mListener = listener;
mListener = (M3U8Listener) listener;
}
/**
@@ -234,6 +264,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
}
@Override public boolean handleMessage(Message msg) {
int peerIndex = msg.getData().getInt(ISchedulers.DATA_M3U8_PEER_INDEX);
switch (msg.what) {
case STATE_STOP:
if (isBreak()) {
@@ -248,15 +279,46 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
}
break;
case STATE_COMPLETE:
notifyLock();
notifyLock(true, peerIndex);
if (mM3U8Option.isGenerateIndexFile() && !isBreak()) {
addExtInf(mCurExtInfo.url, mCurExtInfo.extInf);
}
mListener.onPeerComplete(mTaskWrapper.getKey(),
msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex);
break;
case STATE_RUNNING:
mProgress += (long) msg.obj;
break;
case STATE_FAIL:
notifyLock(false, peerIndex);
mListener.onPeerFail(mTaskWrapper.getKey(),
msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex);
break;
}
return false;
}
/**
* 给索引文件添加extInfo信息
*/
private void addExtInf(String url, String extInf) {
File indexFile =
new File(String.format(M3U8_INDEX_FORMAT, getEntity().getFilePath()));
if (!indexFile.exists()) {
ALog.e(TAG, String.format("索引文件【%s】不存在添加peer的extInf失败", indexFile.getPath()));
return;
}
try {
if (mIndexFos == null) {
mIndexFos = new FileOutputStream(indexFile, true);
}
mIndexFos.write(extInf.concat("\r\n").getBytes(Charset.forName("UTF-8")));
mIndexFos.write(url.concat("\r\n").getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override public boolean isFail() {
return false;
}
@@ -269,4 +331,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return mProgress;
}
}
static class ExtInfo {
String url;
String extInf;
ExtInfo(String url, String extInf) {
this.url = url;
this.extInf = extInf;
}
}
}

View File

@@ -27,6 +27,7 @@ import com.arialyy.aria.core.processor.ILiveTsUrlConverter;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.M3U8Exception;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.m3u8.M3U8InfoThread;
import com.arialyy.aria.m3u8.M3U8Listener;
@@ -87,7 +88,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
}
});
infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() {
@Override public void onGetPeer(String url) {
@Override public void onGetPeer(String url, String extInf) {
if (mPeerUrls.contains(url)) {
return;
}
@@ -104,7 +105,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
fail(new M3U8Exception(TAG, String.format("ts地址错误url%s", url)), false);
return;
}
getLoader().offerPeer(url);
getLoader().offerPeer(new M3U8LiveLoader.ExtInfo(url, extInf));
}
});
return infoThread;
@@ -129,7 +130,13 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
if (mInfoThread != null) {
mInfoThread.setStop(true);
closeTimer();
if (mM3U8Option.isMergeFile()) {
if (((M3U8TaskOption) getTaskWrapper().getM3u8Option()).isGenerateIndexFile()) {
if (getLoader().generateIndexFile(true)) {
getListener().onComplete();
} else {
getListener().onFail(false, new TaskException(TAG, "创建索引文件失败"));
}
} else if (mM3U8Option.isMergeFile()) {
if (getLoader().mergeFile()) {
getListener().onComplete();
} else {

View File

@@ -31,14 +31,16 @@ import com.arialyy.aria.core.inf.IThreadState;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.manager.ThreadTaskManager;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.task.ThreadTask;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.m3u8.BaseM3U8Loader;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.m3u8.M3U8Listener;
import com.arialyy.aria.m3u8.M3U8TaskOption;
import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.util.ArrayList;
@@ -219,6 +221,12 @@ public class M3U8VodLoader extends BaseM3U8Loader {
}
}
mManager.updateStateCount();
if (mCompleteNum <= 0){
mListener.onStart(0);
}else {
int percent = mCompleteNum * 100 / mRecord.threadRecords.size();
mListener.onResume(percent);
}
}
/**
@@ -428,13 +436,12 @@ public class M3U8VodLoader extends BaseM3U8Loader {
config.url = record.tsUrl;
config.tempFile = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId));
config.isBlock = mRecord.isBlock;
config.isOpenDynamicFile = mRecord.isOpenDynamicFile;
config.taskWrapper = mTaskWrapper;
config.record = record;
config.stateHandler = mStateHandler;
config.peerIndex = index;
if (!config.tempFile.exists()) {
FileUtil.createFile(config.tempFile.getPath());
FileUtil.createFile(config.tempFile);
}
ThreadTask threadTask = new ThreadTask(config);
M3U8ThreadTaskAdapter adapter = new M3U8ThreadTaskAdapter(config);
@@ -446,7 +453,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
* M3U8线程状态管理
*/
private class VodStateManager implements IThreadState {
private final String TAG = "M3U8ThreadStateManager";
private final String TAG = CommonUtil.getClassName(VodStateManager.class);
/**
* 任务状态回调
@@ -456,7 +463,8 @@ public class M3U8VodLoader extends BaseM3U8Loader {
private int cancelNum = 0; // 已经取消的线程的数
private int stopNum = 0; // 已经停止的线程数
private int failNum = 0; // 失败的线程数
private long progress; //当前总进度
private long percent; //当前总进度,百分比进度
private long progress;
private TaskRecord taskRecord; // 任务记录
private Looper looper;
@@ -473,6 +481,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
}
}
this.listener = listener;
progress = getEntity().getCurrentProgress();
}
private void updateStateCount() {
@@ -559,7 +568,14 @@ public class M3U8VodLoader extends BaseM3U8Loader {
"startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s",
startThreadNum, stopNum, cancelNum, failNum, mCompleteNum, mFlagQueue.size()));
ALog.d(TAG, String.format("vod任务【%s】完成", mTempFile.getName()));
if (mM3U8Option.isMergeFile()) {
if (mM3U8Option.isGenerateIndexFile()) {
if (generateIndexFile(false)){
listener.onComplete();
}else {
listener.onFail(false, new TaskException(TAG, "创建索引文件失败"));
}
} else if (mM3U8Option.isMergeFile()) {
if (mergeFile()) {
listener.onComplete();
} else {
@@ -596,6 +612,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
int percent = completeNum * 100 / taskRecord.threadRecords.size();
getEntity().setPercent(percent);
getEntity().update();
this.percent = percent;
}
@Override public boolean isFail() {
@@ -625,9 +642,6 @@ public class M3U8VodLoader extends BaseM3U8Loader {
* @return {@code true} 合并成功,{@code false}合并失败
*/
private boolean mergeFile() {
if (getEntity().getM3U8Entity().isGenerateIndexFile()) {
return generateIndexFile();
}
ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler();
String cacheDir = getCacheDir();
List<String> partPath = new ArrayList<>();

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">M3U8Component</string>
</resources>

View File

@@ -43,6 +43,8 @@ public class AriaConfig {
public static final String DOWNLOAD_TEMP_DIR = "/Aria/temp/download/";
public static final String UPLOAD_TEMP_DIR = "/Aria/temp/upload/";
public static final String IGNORE_CLASS_KLASS = "shadow$_klass_";
public static final String IGNORE_CLASS_MONITOR = "shadow$_monitor_";
private static volatile AriaConfig Instance;
private static Context APP;

View File

@@ -22,6 +22,7 @@ import com.arialyy.aria.core.processor.FtpInterceptHandler;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.IFtpUploadInterceptor;
import com.arialyy.aria.core.processor.IHttpFileLenAdapter;
import com.arialyy.aria.core.processor.IKeyUrlConverter;
import com.arialyy.aria.core.processor.ILiveTsUrlConverter;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.processor.IVodTsUrlConverter;
@@ -61,6 +62,7 @@ public class TaskOptionParams {
PROCESSORES.add(ILiveTsUrlConverter.class);
PROCESSORES.add(ITsMergeHandler.class);
PROCESSORES.add(IVodTsUrlConverter.class);
PROCESSORES.add(IKeyUrlConverter.class);
}
/**

View File

@@ -78,12 +78,6 @@ public class TaskRecord extends DbEntity {
*/
public boolean isBlock = false;
/**
* 是否是使用虚拟文件下载的
* {@code true}是,{@code false}不是
*/
public boolean isOpenDynamicFile = false;
/**
* 线程类型
* {@link #TYPE_HTTP_FTP}、{@link #TYPE_M3U8_VOD}

View File

@@ -86,8 +86,8 @@ public class RecordHandler implements IRecordHandler {
mTaskRecord.threadNum = mAdapter.initTaskThreadNum();
initRecord(false);
}
mAdapter.handlerTaskRecord(mTaskRecord);
}
mAdapter.handlerTaskRecord(mTaskRecord);
}
saveRecord();
return mTaskRecord;
@@ -125,7 +125,6 @@ public class RecordHandler implements IRecordHandler {
}
mTaskWrapper.setNewTask(false);
mTaskRecord = mAdapter.createTaskRecord(threadNum);
mTaskRecord.isOpenDynamicFile = false;
mTaskRecord.isBlock = false;
File tempFile = new File(getFilePath());
for (int i = 0; i < threadNum; i++) {
@@ -145,7 +144,7 @@ public class RecordHandler implements IRecordHandler {
}
mTaskRecord.threadRecords.add(tRecord);
}
mConfigFile.delete();
FileUtil.deleteFile(mConfigFile);
}
}

View File

@@ -20,7 +20,10 @@ import com.arialyy.aria.core.ThreadRecord;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.FileUtil;
import java.io.File;
import java.io.IOException;
/**
* 任务记录帮助类,用于处理统一的逻辑
@@ -39,6 +42,45 @@ public class RecordHelper {
mTaskRecord = record;
}
/**
* 处理非分块的,多线程任务
*/
public void handleMultiRecord() {
// 默认线程分块长度
long blockSize = mWrapper.getEntity().getFileSize() / mTaskRecord.threadRecords.size();
File temp = new File(mTaskRecord.filePath);
boolean fileExists = false;
if (!temp.exists()) {
BufferedRandomAccessFile tempFile;
try {
tempFile = new BufferedRandomAccessFile(temp, "rw");
tempFile.setLength(mWrapper.getEntity().getFileSize());
} catch (IOException e) {
e.printStackTrace();
}
} else {
fileExists = true;
}
// 处理文件被删除的情况
if (!fileExists) {
ALog.w(TAG, String.format("文件【%s】被删除重新分配线程区间", mTaskRecord.filePath));
for (int i = 0; i < mTaskRecord.threadNum; i++) {
long startL = i * blockSize, endL = (i + 1) * blockSize;
ThreadRecord tr = mTaskRecord.threadRecords.get(i);
tr.startLocation = startL;
tr.isComplete = false;
//最后一个线程的结束位置即为文件的总长度
if (tr.threadId == (mTaskRecord.threadNum - 1)) {
endL = mWrapper.getEntity().getFileSize();
}
tr.endLocation = endL;
}
}
mWrapper.setNewTask(false);
}
/**
* 处理分块任务的记录分块文件blockFileLen长度必须需要小于等于线程区间threadRectLen的长度
*/
@@ -104,10 +146,10 @@ public class RecordHelper {
tr.startLocation = 0;
tr.isComplete = false;
tr.endLocation = mWrapper.getEntity().getFileSize();
} else if (mTaskRecord.isOpenDynamicFile) {
} else if (mTaskRecord.isBlock) {
if (file.length() > mWrapper.getEntity().getFileSize()) {
ALog.i(TAG, String.format("文件【%s】错误任务重新开始", file.getPath()));
file.delete();
FileUtil.deleteFile(file);
tr.startLocation = 0;
tr.isComplete = false;
tr.endLocation = mWrapper.getEntity().getFileSize();

View File

@@ -35,8 +35,6 @@ public class SubThreadConfig {
public ThreadRecord record;
// 状态处理器
public Handler stateHandler;
// 动态文件
public boolean isOpenDynamicFile;
// m3u8切片索引
public int peerIndex;
}

View File

@@ -16,6 +16,7 @@
package com.arialyy.aria.core.download;
import android.os.Parcel;
import android.text.TextUtils;
import com.arialyy.aria.core.common.AbsGroupEntity;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.annotation.Ignore;
@@ -48,7 +49,12 @@ public class DownloadGroupEntity extends AbsGroupEntity {
}
@Override public int getTaskType() {
return getKey().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR : ITaskWrapper.DG_HTTP;
if (getSubEntities() == null || getSubEntities().isEmpty() || TextUtils.isEmpty(
getSubEntities().get(0).getUrl())) {
return ITaskWrapper.ERROR;
}
return getSubEntities().get(0).getUrl().startsWith("ftp") ? ITaskWrapper.D_FTP_DIR
: ITaskWrapper.DG_HTTP;
}
public DownloadGroupEntity() {

View File

@@ -58,11 +58,6 @@ public class M3U8Entity extends DbEntity implements Parcelable {
*/
private String cacheDir;
/**
* 生成索引
*/
private boolean generateIndexFile;
/**
* 加密key保存地址
*/
@@ -115,14 +110,6 @@ public class M3U8Entity extends DbEntity implements Parcelable {
this.iv = iv;
}
public boolean isGenerateIndexFile() {
return generateIndexFile;
}
public void setGenerateIndexFile(boolean generateIndexFile) {
this.generateIndexFile = generateIndexFile;
}
public boolean isLive() {
return isLive;
}
@@ -248,7 +235,6 @@ public class M3U8Entity extends DbEntity implements Parcelable {
dest.writeInt(this.peerNum);
dest.writeByte(this.isLive ? (byte) 1 : (byte) 0);
dest.writeString(this.cacheDir);
dest.writeByte(this.generateIndexFile ? (byte) 1 : (byte) 0);
dest.writeString(this.keyPath);
dest.writeString(this.keyUrl);
dest.writeString(this.method);
@@ -261,7 +247,6 @@ public class M3U8Entity extends DbEntity implements Parcelable {
this.peerNum = in.readInt();
this.isLive = in.readByte() != 0;
this.cacheDir = in.readString();
this.generateIndexFile = in.readByte() != 0;
this.keyPath = in.readString();
this.keyUrl = in.readString();
this.method = in.readString();

View File

@@ -80,9 +80,8 @@ public abstract class AbsGroupUtil implements IUtil, Runnable {
/**
* 初始化组合任务状态
*/
protected void initState() {
mState = new GroupRunState(getWrapper().getKey(), mListener,
mGTWrapper.getSubTaskWrapper().size(), mSubQueue);
private void initState() {
mState = new GroupRunState(getWrapper().getKey(), mListener, mSubQueue);
for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) {
if (wrapper.getEntity().getState() == IEntity.STATE_COMPLETE) {
mState.updateCompleteNum();

View File

@@ -112,30 +112,4 @@ public abstract class AbsSubDLoadUtil implements IUtil {
mSchedulers.obtainMessage(ISchedulers.STOP, this).sendToTarget();
}
}
//@Override public void start() {
// if (mWrapper.getRequestType() == ITaskWrapper.D_HTTP) {
// if (needGetInfo) {
// new Thread(new HttpFileInfoThread(mWrapper, new OnFileInfoCallback() {
//
// @Override public void onComplete(String url, CompleteInfo info) {
// mDLoader = new Downloader(mListener, mWrapper);
// mDLoader.start();
// }
//
// @Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
// mSchedulers.obtainMessage(ISchedulers.FAIL, SubDLoadUtil.this).sendToTarget();
// }
// })).start();
// } else {
// mDLoader = new Downloader(mListener, mWrapper);
// mDLoader.start();
// }
// } else if (mWrapper.getRequestType() == ITaskWrapper.D_FTP) {
// mDLoader = new Downloader(mListener, mWrapper);
// mDLoader.start();
// } else {
// ALog.w(TAG, String.format("不识别的类型requestType%s", mWrapper.getRequestType()));
// }
//}
}

View File

@@ -72,14 +72,16 @@ public class GroupRunState {
private String mGroupHash;
GroupRunState(String groupHash, IDGroupListener listener, int subSize,
SimpleSubQueue queue) {
GroupRunState(String groupHash, IDGroupListener listener, SimpleSubQueue queue) {
this.listener = listener;
this.queue = queue;
mSubSize = subSize;
mGroupHash = groupHash;
}
public void setSubSize(int subSize){
mSubSize = subSize;
}
/**
* 组合任务是否正在自行
*

View File

@@ -142,7 +142,6 @@ public class NormalLoader extends AbsLoader {
String.format(IRecordHandler.SUB_PATH, mTempFile.getPath(), record.threadId))
: mTempFile;
config.isBlock = mRecord.isBlock;
config.isOpenDynamicFile = mRecord.isOpenDynamicFile;
config.startThreadNum = startNum;
config.taskWrapper = mTaskWrapper;
config.record = record;
@@ -200,8 +199,7 @@ public class NormalLoader extends AbsLoader {
ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress));
getEntity().setCurrentProgress(currentProgress);
}
mStateHandler.obtainMessage(IThreadState.STATE_UPDATE_PROGRESS, currentProgress)
.sendToTarget();
mStateManager.updateProgress(currentProgress);
startThreadTask();
}

View File

@@ -19,9 +19,9 @@ import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import com.arialyy.aria.core.TaskRecord;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.inf.IThreadState;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
@@ -59,6 +59,13 @@ public class ThreadStateManager implements IThreadState {
mListener = listener;
}
/**
* 不要使用handle更新启动线程的进度因为有延迟
*/
void updateProgress(long curProgress) {
mProgress = curProgress;
}
@Override public boolean handleMessage(Message msg) {
switch (msg.what) {
case STATE_STOP:

View File

@@ -48,7 +48,7 @@ public class ThreadTaskManager {
}
private ThreadTaskManager() {
mExePool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
mExePool = Executors.newCachedThreadPool();
}
/**

View File

@@ -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.processor;
import com.arialyy.aria.core.inf.IEventHandler;
/**
* M3U8 密钥下载地址处理器,可用于解密被加密的密钥的下载地址
*/
public interface IKeyUrlConverter extends IEventHandler {
/**
* 将被加密的密钥下载地址转换为可使用的http下载地址
*
* @param keyUrl 加密的url地址
* @return 可正常访问的http地址
*/
String convert(String keyUrl);
}

View File

@@ -22,7 +22,7 @@ import com.arialyy.aria.util.CommonUtil;
import java.io.File;
/**
* 文件任务适配器
* 文件任务适配器
*
* @Author lyy
* @Date 2019-09-19

View File

@@ -34,10 +34,6 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter {
* 速度限制工具
*/
protected BandwidthLimiter mSpeedBandUtil;
/**
* 当前线程的下去区间的进度
*/
private long mRangeProgress;
private ThreadRecord mThreadRecord;
private IThreadTaskObserver mObserver;
private AbsTaskWrapper mWrapper;
@@ -48,7 +44,6 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter {
mThreadRecord = config.record;
mWrapper = config.taskWrapper;
mThreadConfig = config;
mRangeProgress = mThreadRecord.startLocation;
if (getTaskConfig().getMaxSpeed() > 0) {
mSpeedBandUtil = new BandwidthLimiter(getTaskConfig().getMaxSpeed(), config.startThreadNum);
}
@@ -68,7 +63,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter {
* 当前线程的下去区间的进度
*/
protected long getRangeProgress() {
return mRangeProgress;
return mObserver.getThreadProgress();
}
protected ThreadRecord getThreadRecord() {
@@ -97,7 +92,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter {
return mThreadConfig;
}
@Override public void setThreadStateObserver(IThreadTaskObserver observer) {
@Override public void attach(IThreadTaskObserver observer) {
mObserver = observer;
}

View File

@@ -36,7 +36,7 @@ public interface IThreadTaskAdapter {
void setMaxSpeed(int speed);
/**
* 设置线程任务状态观察者
* 注册观察者
*/
void setThreadStateObserver(IThreadTaskObserver observer);
void attach(IThreadTaskObserver observer);
}

View File

@@ -53,4 +53,9 @@ public interface IThreadTaskObserver {
* @param len 新增的长度
*/
void updateProgress(long len);
/**
* 获取线程当前进度
*/
long getThreadProgress();
}

View File

@@ -33,6 +33,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.NetUtils;
@@ -49,7 +50,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
* 线程重试次数
*/
private final int RETRY_NUM = 2;
private final String TAG = "AbsThreadTask";
private final String TAG = CommonUtil.getClassName(getClass());
private IEntity mEntity;
protected AbsTaskWrapper mTaskWrapper;
private int mFailTimes = 0;
@@ -94,7 +95,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
*/
public void setAdapter(IThreadTaskAdapter adapter) {
mAdapter = adapter;
mAdapter.setThreadStateObserver(this);
mAdapter.attach(this);
}
/**
@@ -317,6 +318,11 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
}
}
@Override
public long getThreadProgress() {
return mRangeProgress;
}
/**
* 取消任务
*/
@@ -335,7 +341,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
*/
protected void fail(final long subCurrentLocation, BaseException ex, boolean needRetry) {
if (ex != null) {
ALog.e(TAG, ALog.getExceptionString(ex));
ex.printStackTrace();
}
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) {
writeConfig(false, 0);
@@ -367,8 +373,8 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
|| isNotNetRetry) && !isBreak()) {
ALog.w(TAG, String.format("ts切片【%s】正在重试", getFileName()));
mFailTimes++;
mConfig.tempFile.delete();
FileUtil.createFile(mConfig.tempFile.getPath());
FileUtil.deleteFile(mConfig.tempFile);
FileUtil.createFile(mConfig.tempFile);
ThreadTaskManager.getInstance().retryThread(this);
} else {
sendFailMsg(null);
@@ -462,8 +468,6 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
if (mRecord != null) {
mRecord.isComplete = isComplete;
if (mConfig.isBlock) {
mRecord.startLocation = record;
} else if (mConfig.isOpenDynamicFile) {
mRecord.startLocation = mConfig.tempFile.length();
} else {
if (0 < record && record < mRecord.endLocation) {

View File

@@ -29,6 +29,11 @@ public class UTaskWrapper extends AbsTaskWrapper<UploadEntity> {
*/
private String tempUrl;
/**
* 如果文件路径被其它任务占用设置为true时删除其它任务
*/
private boolean forceUpload = false;
public UTaskWrapper(UploadEntity entity) {
super(entity);
}
@@ -48,6 +53,14 @@ public class UTaskWrapper extends AbsTaskWrapper<UploadEntity> {
return getEntity().getKey();
}
public boolean isForceUpload() {
return forceUpload;
}
public void setForceUpload(boolean forceUpload) {
this.forceUpload = forceUpload;
}
@Override public UploadConfig getConfig() {
return Configuration.getInstance().uploadCfg;
}

View File

@@ -17,6 +17,7 @@ package com.arialyy.aria.core.upload;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import com.arialyy.aria.core.common.AbsNormalEntity;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.orm.annotation.Primary;
@@ -58,7 +59,10 @@ public class UploadEntity extends AbsNormalEntity implements Parcelable {
}
@Override public int getTaskType() {
return getUrl().startsWith("ftp") ? ITaskWrapper.D_FTP : ITaskWrapper.D_HTTP;
if (TextUtils.isEmpty(getUrl())){
return ITaskWrapper.ERROR;
}
return getUrl().startsWith("ftp") ? ITaskWrapper.U_FTP : ITaskWrapper.U_HTTP;
}
public UploadEntity() {

View File

@@ -24,6 +24,7 @@ import com.arialyy.aria.core.event.ErrorEvent;
import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.inf.ITaskOption;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.ComponentUtil;
/**

View File

@@ -34,7 +34,7 @@ class DBConfig {
static boolean DEBUG = false;
static Map<String, Class> mapping = new LinkedHashMap<>();
static String DB_NAME;
static int VERSION = 55;
static int VERSION = 56;
/**
* 是否将数据库保存在Sd卡{@code true} 是

View File

@@ -76,9 +76,6 @@ final class SqlHelper extends SQLiteOpenHelper {
// 需要使用如下语句:
db.execSQL("PRAGMA foreign_keys=ON;");
}
if (DBConfig.DEBUG) {
db.enableWriteAheadLogging();
}
}
@Override public void onCreate(SQLiteDatabase db) {
@@ -139,6 +136,7 @@ final class SqlHelper extends SQLiteOpenHelper {
SQLiteDatabase.CREATE_IF_NECESSARY);
}
}
db.enableWriteAheadLogging();
return db;
}

View File

@@ -16,6 +16,7 @@
package com.arialyy.aria.orm;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.orm.annotation.Default;
import com.arialyy.aria.orm.annotation.Foreign;
import com.arialyy.aria.orm.annotation.Ignore;
@@ -178,7 +179,8 @@ final class SqlUtil {
int modifiers = field.getModifiers();
String fieldName = field.getName();
return (ignore != null && ignore.value()) || fieldName.equals("rowID") || fieldName.equals(
"shadow$_klass_") || fieldName.equals("shadow$_monitor_") || field.isSynthetic() || Modifier
AriaConfig.IGNORE_CLASS_KLASS) || fieldName.equals(AriaConfig.IGNORE_CLASS_MONITOR)
|| field.isSynthetic() || Modifier
.isStatic(modifiers) || Modifier.isFinal(modifiers);
}

View File

@@ -17,6 +17,10 @@
package com.arialyy.aria.util;
import android.text.TextUtils;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.orm.DbEntity;
import java.lang.reflect.Modifier;
import java.util.List;
@@ -27,6 +31,48 @@ import java.util.List;
public class CheckUtil {
private static final String TAG = "CheckUtil";
/**
* 检查和处理路径冲突
*
* @param isForceDownload true如果路径冲突将删除其它任务的记录的
* @param filePath 文件保存路径
* @return false 任务不再执行true 任务继续执行
*/
public static boolean checkDownloadPathConflicts(boolean isForceDownload, String filePath) {
if (DbEntity.checkDataExist(DownloadEntity.class, "downloadPath=?", filePath)) {
if (!isForceDownload) {
ALog.e(TAG, String.format("下载失败,保存路径【%s】已经被其它任务占用请设置其它保存路径", filePath));
return false;
} else {
ALog.w(TAG, String.format("保存路径【%s】已经被其它任务占用当前任务将覆盖该路径的文件", filePath));
RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_DOWNLOAD);
return true;
}
}
return true;
}
/**
* 检查和处理路径冲突
*
* @param isForceUpload true如果路径冲突将删除其它任务的记录的
* @param filePath 文件保存路径
* @return false 任务不再执行true 任务继续执行
*/
public static boolean checkUploadPathConflicts(boolean isForceUpload, String filePath) {
if (DbEntity.checkDataExist(UploadEntity.class, "filePath=?", filePath)) {
if (!isForceUpload) {
ALog.e(TAG, String.format("上传失败,文件路径【%s】已经被其它任务占用请设置其它保存路径", filePath));
return false;
} else {
ALog.w(TAG, String.format("文件路径【%s】已经被其它任务占用当前任务将覆盖该路径的文件", filePath));
RecordUtil.delTaskRecord(filePath, IRecordHandler.TYPE_UPLOAD);
return true;
}
}
return true;
}
/**
* 检查成员类是否是静态和public
*/

View File

@@ -71,15 +71,15 @@ public class CommonUtil {
ALog.e(TAG, "sql语句表达式不能为null");
return false;
}
if (expression.length == 1) {
ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression)));
return false;
}
//if (expression.length == 1) {
// ALog.e(TAG, String.format("表达式需要写入参数,参数信息:%s", Arrays.toString(expression)));
// return false;
//}
String where = expression[0];
if (!where.contains("?")) {
ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression)));
return false;
}
//if (!where.contains("?")) {
// ALog.e(TAG, String.format("请在where语句的'='后编写?,参数信息:%s", Arrays.toString(expression)));
// return false;
//}
Pattern pattern = Pattern.compile("\\?");
Matcher matcher = pattern.matcher(where);
int count = 0;
@@ -626,6 +626,16 @@ public class CommonUtil {
Collections.addAll(fields, personClazz.getDeclaredFields());
}
Collections.addAll(fields, clazz.getDeclaredFields());
List<Field> ignore = new ArrayList<>();
for (Field field : fields) {
if (field.getName().equals(AriaConfig.IGNORE_CLASS_KLASS) || field.getName()
.equals(AriaConfig.IGNORE_CLASS_MONITOR)) {
ignore.add(field);
}
}
if (!ignore.isEmpty()){
fields.removeAll(ignore);
}
return fields;
}

View File

@@ -50,17 +50,7 @@ public class ErrorHelp {
CommonUtil.getAppPath(AriaConfig.getInstance().getAPP()),
getData("yyyy-MM-dd_HH_mm_ss"));
File log = new File(path);
if (!log.getParentFile().exists()) {
log.getParentFile().mkdirs();
}
if (!log.exists()) {
try {
log.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
FileUtil.createFile(path);
return path;
}
@@ -79,7 +69,7 @@ public class ErrorHelp {
try {
File file = new File(getLogPath());
if (!file.exists()) {
FileUtil.createFile(file.getPath());
FileUtil.createFile(file);
}
writer = new PrintWriter(new FileWriter(file.getPath(), true));
writer.append(stringBuffer);

View File

@@ -86,22 +86,23 @@ public class FileUtil {
ALog.e(TAG, "文件路径不能为null");
return false;
}
File file = new File(path);
return createFile(new File(path));
}
/**
* 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件
*
* @return {@code true} 创建成功、{@code false} 创建失败
*/
public static boolean createFile(File file) {
if (file.getParentFile() == null || !file.getParentFile().exists()) {
ALog.d(TAG, "目标文件所在路径不存在,准备创建……");
if (!createDir(file.getParent())) {
ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + path + "");
}
}
// 创建目标文件
if (file.exists()) {
final File to = new File(file.getAbsolutePath() + System.currentTimeMillis());
if (file.renameTo(to)) {
to.delete();
} else {
file.delete();
ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + file.getPath() + "");
}
}
// 文件存在,删除文件
deleteFile(file);
try {
if (file.createNewFile()) {
//ALog.d(TAG, "创建文件成功:" + file.getAbsolutePath());
@@ -327,7 +328,7 @@ public class FileUtil {
ALog.d(TAG, String.format("block = %s", block));
File subFile = new File(subPath);
if (!subFile.exists()) {
subFile.createNewFile();
createFile(subFile);
}
FileOutputStream fos = new FileOutputStream(subFile);
FileChannel sfoc = fos.getChannel();

View File

@@ -128,7 +128,7 @@ public class RecordUtil {
filePath = ((DownloadEntity) entity).getDownloadPath();
} else if (entity instanceof UploadEntity) {
type = IRecordHandler.TYPE_UPLOAD;
filePath = ((UploadEntity) entity).getFilePath();
filePath = entity.getFilePath();
} else {
ALog.w(TAG, "删除记录失败,未知类型");
return;
@@ -295,7 +295,7 @@ public class RecordUtil {
}
/**
* 删除ts文件
* 删除ts文件,和索引文件(如果有的话)
*/
private static void removeTsCache(File targetFile, long bandWidth) {
@@ -321,6 +321,12 @@ public class RecordUtil {
cDir.delete();
}
}
File indexFile = new File(String.format("%s.index", targetFile.getPath()));
if (indexFile.exists()) {
indexFile.delete();
}
}
/**

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">PublicComponent</string>
</resources>

View File

@@ -23,7 +23,7 @@ Aria有以下特点
[怎样使用Aria?](#使用)
如果你觉得Aria对你有帮助的star和issues将是对我最大支持.`^_^`
如果你觉得Aria对你有帮助的star和issues将是对我最大支持当然也非常欢迎你能PR[PR方法](https://www.zhihu.com/question/21682976/answer/79489643)`^_^`
## 示例
* 多任务下载
@@ -44,20 +44,20 @@ Aria有以下特点
## 引入库
[![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE)
[![Core](https://img.shields.io/badge/Core-3.7.2-blue)](https://github.com/AriaLyy/Aria)
[![Compiler](https://img.shields.io/badge/Compiler-3.7.2-blue)](https://github.com/AriaLyy/Aria)
[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.2-orange)](https://github.com/AriaLyy/Aria)
[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.2-orange)](https://github.com/AriaLyy/Aria)
[![Core](https://img.shields.io/badge/Core-3.7.9-blue)](https://github.com/AriaLyy/Aria)
[![Compiler](https://img.shields.io/badge/Compiler-3.7.9-blue)](https://github.com/AriaLyy/Aria)
[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.9-orange)](https://github.com/AriaLyy/Aria)
[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.9-orange)](https://github.com/AriaLyy/Aria)
```java
implementation 'com.arialyy.aria:core:3.7.2'
annotationProcessor 'com.arialyy.aria:compiler:3.7.2'
implementation 'com.arialyy.aria:ftpComponent:3.7.2' # 如果需要使用ftp请增加该组件
implementation 'com.arialyy.aria:m3u8Component:3.7.2' # 如果需要使用m3u8下载功能请增加该组件
implementation 'com.arialyy.aria:core:3.7.9'
annotationProcessor 'com.arialyy.aria:compiler:3.7.9'
implementation 'com.arialyy.aria:ftpComponent:3.7.9' # 如果需要使用ftp请增加该组件
implementation 'com.arialyy.aria:m3u8Component:3.7.9' # 如果需要使用m3u8下载功能请增加该组件
```
如果出现android support依赖错误请将 `compile 'com.arialyy.aria:aria-core:<last-version>'`替换为
如果出现android support依赖错误请将 `compile 'com.arialyy.aria:core:<last-version>'`替换为
```
api('com.arialyy.aria:aria-core:<last-version>'){
api('com.arialyy.aria:core:<last-version>'){
exclude group: 'androidx.appcompat.app'
}
```
@@ -65,7 +65,7 @@ api('com.arialyy.aria:aria-core:<last-version>'){
__注意3.5.4以下版本升级时,需要更新[配置文件](https://aria.laoyuyu.me/aria_doc/start/config.html)__
__注意3.7 以上版本已经适配了AndroidX__
__注意3.7 以上版本已经适配了AndroidX如果是使用support库的可使用[老版本](https://github.com/AriaLyy/Aria/tree/v3.6.6)__
***
## 使用
@@ -137,24 +137,9 @@ protected void onCreate(Bundle savedInstanceState) {
### 版本日志
+ v_3.7
- fix bug https://github.com/AriaLyy/Aria/issues/450
- fix bug https://github.com/AriaLyy/Aria/issues/466
- fix bug https://github.com/AriaLyy/Aria/issues/454
- fix bug https://github.com/AriaLyy/Aria/issues/467
- fix bug https://github.com/AriaLyy/Aria/issues/459
- fix bug https://github.com/AriaLyy/Aria/issues/487
- fix bug https://github.com/AriaLyy/Aria/issues/483
- fix bug https://github.com/AriaLyy/Aria/issues/482
- fix bug https://github.com/AriaLyy/Aria/issues/473
- 移除隐藏api的反射 https://github.com/AriaLyy/Aria/issues/456
- 新增ftp免证书登陆功能h ttps://github.com/AriaLyy/Aria/issues/455
- 适配androidX
- 修复组合任务恢复下载会出现进度显示为0的问题
- m3u8点播下载新增创建ts索引功能
- 修复多任务的m3u8点播下载时一个任务调用`jumpIndex`其它m3u8任务也会自动调用`jumpIndex`的问题
- 添加权限检查
+ v_3.7.9 (2019/11/28)
- fix bug https://github.com/AriaLyy/Aria/issues/537
[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
## 混淆配置
@@ -176,6 +161,11 @@ protected void onCreate(Bundle savedInstanceState) {
有任何问题,可以在[issues](https://github.com/AriaLyy/Aria/issues)给我留言反馈。</br>
在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。</br>
## 打赏
如果觉得框架写的不错并且帮助到了你,可以请我喝杯热茶。`^_^`</br>
<img src="https://raw.githubusercontent.com/AriaLyy/Aria/master/img/ali_pay.png" width=336 height=336/>
<img src="https://raw.githubusercontent.com/AriaLyy/Aria/master/img/wx_pay.png" width=336 height=336/>
***
License

1
SFtpComponent/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,33 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.appcompat:appcompat:${rootProject.ext.XAppcompatVersion}"
implementation "com.jcraft:jsch:0.1.55"
implementation "com.jcraft:jzlib:1.1.3"
implementation project(path: ':FtpComponent')
implementation project(path: ':PublicComponent')
}

View File

21
SFtpComponent/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arialyy.aria.sftp" />

View File

@@ -0,0 +1,42 @@
/*
* 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.sftp;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import java.util.Vector;
/**
* sftp文件信息适配器
*/
public abstract class BaseInfoThreadAdapter<WRAPPER extends AbsTaskWrapper> {
private WRAPPER mWrapper;
public BaseInfoThreadAdapter(WRAPPER taskWrapper) {
mWrapper = taskWrapper;
}
public WRAPPER getWrapper() {
return mWrapper;
}
/**
* 处理文件
*
* @return true 处理文件成功false 处理文件失败,结束任务
*/
protected abstract boolean handlerFile(Vector vector);
}

View File

@@ -0,0 +1,100 @@
/*
* 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.sftp;
import com.arialyy.aria.core.AriaConfig;
import com.arialyy.aria.core.FtpUrlEntity;
import com.arialyy.aria.core.common.AbsNormalEntity;
import com.arialyy.aria.core.inf.OnFileInfoCallback;
import com.arialyy.aria.core.upload.UploadEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.ftp.FtpTaskOption;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.SftpException;
import java.util.Vector;
/**
* https://cloud.tencent.com/developer/article/1354612
*
* @author lyy
*/
public class SFtpInfoThread<ENTITY extends AbsNormalEntity, TASK_WRAPPER extends AbsTaskWrapper<ENTITY>>
implements Runnable {
private final String TAG = CommonUtil.getClassName(getClass());
protected ENTITY mEntity;
protected TASK_WRAPPER mTaskWrapper;
protected FtpTaskOption mTaskOption;
private int mConnectTimeOut;
protected OnFileInfoCallback mCallback;
protected long mSize = 0;
protected String charSet = "UTF-8";
private boolean isUpload = false;
private SFtpUtil mSFtpUtil;
private BaseInfoThreadAdapter mAdapter;
public SFtpInfoThread(SFtpUtil ftpUtil, TASK_WRAPPER taskWrapper,
OnFileInfoCallback callback) {
mSFtpUtil = ftpUtil;
mTaskWrapper = taskWrapper;
mEntity = taskWrapper.getEntity();
mTaskOption = (FtpTaskOption) taskWrapper.getTaskOption();
mConnectTimeOut = AriaConfig.getInstance().getDConfig().getConnectTimeOut();
mCallback = callback;
if (mEntity instanceof UploadEntity) {
isUpload = true;
}
}
public void setAdapter(BaseInfoThreadAdapter adapter) {
mAdapter = adapter;
}
@Override public void run() {
if (mAdapter == null) {
ALog.e(TAG, "adapter为空");
return;
}
try {
ChannelSftp channelSftp =
(ChannelSftp) mSFtpUtil.getSession().openChannel(SFtpUtil.CMD_TYPE_SFTP);
Vector files = channelSftp.ls(getUrlEntity().remotePath);
if (files.isEmpty()) {
ALog.e(TAG, String.format("路径【%s】没有文件", getUrlEntity().remotePath));
mCallback.onFail(mEntity, null, false);
return;
}
if (!mAdapter.handlerFile(files)) {
ALog.e(TAG, "文件处理失败");
mCallback.onFail(mEntity, null, false);
return;
}
} catch (JSchException ex) {
ex.printStackTrace();
} catch (SftpException e) {
e.printStackTrace();
}
}
private FtpUrlEntity getUrlEntity() {
return mTaskOption.getUrlEntity();
}
}

View File

@@ -0,0 +1,200 @@
/*
* 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.sftp;
import android.text.TextUtils;
import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
/**
* sftp工具类
*
* @author lyy
*/
public class SFtpUtil {
private final String TAG = CommonUtil.getClassName(getClass());
/**
* 用于执行命令
*/
public static final String CMD_TYPE_EXEC = "exec";
/**
* 用于处理文件
*/
public static final String CMD_TYPE_SFTP = "sftp";
private String ip, userName, password;
private int port;
private Session session;
private boolean isLogin = false;
private SFtpUtil() {
createClient();
}
/**
* 创建客户端
*/
private void createClient() {
JSch jSch = new JSch();
try {
if (TextUtils.isEmpty(userName)) {
session = jSch.getSession(userName, ip, port);
} else {
session = jSch.getSession(ip);
}
if (!TextUtils.isEmpty(password)) {
session.setPassword(password);
}
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);// 为Session对象设置properties
session.setTimeout(3000);// 设置超时
login();
isLogin = true;
} catch (JSchException e) {
e.printStackTrace();
}
}
/**
* 执行登录
*/
public Session login() {
try {
session.connect(); // 通过Session建立连接
} catch (JSchException e) {
e.printStackTrace();
}
return session;
}
/**
* 登出
*/
public void logout() {
if (session != null) {
session.disconnect();
}
isLogin = false;
}
public Session getSession() {
return session;
}
/**
* 执行命令
*
* @param cmd sftp命令
*/
public void execCommand(String cmd) {
if (TextUtils.isEmpty(cmd)) {
ALog.e(TAG, "命令为空");
return;
}
if (!isLogin) {
ALog.e(TAG, "没有登录");
return;
}
ChannelExec channel = null;
try {
channel = (ChannelExec) session.openChannel(CMD_TYPE_EXEC);
channel.setCommand(cmd);
channel.connect();
String rst = getResult(channel.getInputStream());
ALog.i(TAG, String.format("result: %s", rst));
} catch (IOException e) {
e.printStackTrace();
} catch (JSchException e) {
e.printStackTrace();
} finally {
if (channel != null) {
channel.disconnect();
}
}
}
/**
* 执行命令后,获取服务器端返回的数据
*
* @return 服务器端返回的数据
*/
private String getResult(InputStream in) throws IOException {
if (in == null){
ALog.e(TAG, "输入流为空");
return null;
}
StringBuilder sb = new StringBuilder();
BufferedReader isr = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = isr.readLine()) != null) {
sb.append(line);
}
in.close();
isr.close();
return sb.toString();
}
public static class Builder {
private String ip, userName, password;
private int port = 22;
public Builder setIp(String ip) {
this.ip = ip;
return this;
}
public Builder setUserName(String userName) {
this.userName = userName;
return this;
}
public Builder setPassword(String password) {
this.password = password;
return this;
}
public Builder setPort(int port) {
this.port = port;
return this;
}
public SFtpUtil build() {
SFtpUtil login = new SFtpUtil();
login.ip = ip;
login.userName = userName;
login.password = password;
login.port = port;
if (TextUtils.isEmpty(ip)) {
throw new IllegalArgumentException("ip不能为空");
}
if (port < 0 || port > 65534) {
throw new IllegalArgumentException("端口错误");
}
return login;
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.sftp.download;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.sftp.BaseInfoThreadAdapter;
import java.util.Vector;
final class DSFtpInfoThreadAdapter extends BaseInfoThreadAdapter<DTaskWrapper> {
DSFtpInfoThreadAdapter(DTaskWrapper taskWrapper) {
super(taskWrapper);
}
@Override protected boolean handlerFile(Vector vector) {
return false;
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.sftp.download;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.task.IThreadTask;
import com.arialyy.aria.core.task.ThreadTask;
import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.ftp.download.FtpDLoaderAdapter;
/**
* sftp下载适配器
*
* @author lyy
*/
final class SFtpDLoaderAdapter extends FtpDLoaderAdapter {
SFtpDLoaderAdapter(ITaskWrapper wrapper) {
super(wrapper);
}
@Override public IThreadTask createThreadTask(SubThreadConfig config) {
ThreadTask threadTask = new ThreadTask(config);
SFtpDThreadTaskAdapter adapter = new SFtpDThreadTaskAdapter(config);
threadTask.setAdapter(adapter);
return threadTask;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.sftp.download;
import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.common.CompleteInfo;
import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.OnFileInfoCallback;
import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.loader.AbsLoader;
import com.arialyy.aria.core.loader.AbsNormalLoaderUtil;
import com.arialyy.aria.core.loader.NormalLoader;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.ftp.FtpTaskOption;
import com.arialyy.aria.sftp.SFtpInfoThread;
import com.arialyy.aria.sftp.SFtpUtil;
/**
* sftp下载工具
*
* @author lyy
*/
public class SFtpDLoaderUtil extends AbsNormalLoaderUtil {
private SFtpUtil mSftpUtil;
protected SFtpDLoaderUtil(AbsTaskWrapper wrapper, IEventListener listener) {
super(wrapper, listener);
wrapper.generateTaskOption(FtpTaskOption.class);
FtpTaskOption option = (FtpTaskOption) wrapper.getTaskOption();
mSftpUtil = new SFtpUtil.Builder()
.setIp(option.getUrlEntity().hostName)
.setPort(Integer.parseInt(option.getUrlEntity().port))
.setUserName(option.getUrlEntity().user)
.setPassword(option.getUrlEntity().password)
.build();
}
@Override protected AbsLoader createLoader() {
NormalLoader loader = new NormalLoader(getListener(), getTaskWrapper());
loader.setAdapter(new SFtpDLoaderAdapter(getTaskWrapper()));
return loader;
}
@Override protected Runnable createInfoThread() {
DSFtpInfoThreadAdapter adapter = new DSFtpInfoThreadAdapter((DTaskWrapper) getTaskWrapper());
SFtpInfoThread infoThread = new SFtpInfoThread<>(mSftpUtil, (DTaskWrapper) getTaskWrapper(),
new OnFileInfoCallback() {
@Override public void onComplete(String key, CompleteInfo info) {
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
}
});
infoThread.setAdapter(adapter);
return infoThread;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.sftp.download;
import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.task.AbsThreadTaskAdapter;
/**
* sftp 线程任务适配器
*
* @author lyy
*/
public class SFtpDThreadTaskAdapter extends AbsThreadTaskAdapter {
SFtpDThreadTaskAdapter(SubThreadConfig config) {
super(config);
}
@Override protected void handlerThreadTask() {
}
}

View File

@@ -72,6 +72,8 @@ dependencies {
implementation project(':M3U8Component')
implementation project(':FtpComponent')
implementation project(path: ':AriaAnnotations')
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
}
repositories {
mavenCentral()

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:sharedUserId="com.arialyy"
package="com.arialyy.simple">
<uses-permission android:name="android.permission.INTERNET"/>

View File

@@ -32,7 +32,7 @@
3、只对新的多线程下载任务有效
4、只对多线程的任务有效
-->
<useBlock value="false"/>
<useBlock value="true"/>
<!--设置下载线程数下载线程数不能小于1
注意:

View File

@@ -34,7 +34,7 @@ public class BaseApplication extends Application {
super.onCreate();
INSTANCE = this;
AbsFrame.init(this);
Aria.init(this);
//Aria.init(this);
if (BuildConfig.DEBUG) {
//StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
// .detectAll()

View File

@@ -71,6 +71,29 @@ public class DownloadModule extends BaseModule {
return list;
}
/**
* 创建m3u8下载地址
*/
public List<FileListEntity> createM3u8TestList(){
String[] names = new String[]{"m3u8test1.ts", "m3u8test2.ts"};
String[] urls = new String[]{
"http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8",
"http://qn.shytong.cn/8f4011f2a31bd347da42b54fe37a7ba8-transcode.m3u8"
};
List<FileListEntity> list = new ArrayList<>();
int i = 0;
for (String name : names) {
FileListEntity entity = new FileListEntity();
entity.name = name;
entity.key = urls[i];
entity.type = 2;
entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + name;
list.add(entity);
i++;
}
return list;
}
private String[] getStringArray(int array) {
return getContext().getResources().getStringArray(array);
}
@@ -91,7 +114,7 @@ public class DownloadModule extends BaseModule {
FileListEntity entity = new FileListEntity();
entity.urls = getStringArray(urls);
entity.names = getStringArray(names);
entity.isGroup = true;
entity.type = 1;
entity.name = alias;
entity.key = CommonUtil.getMd5Code(Arrays.asList(entity.urls));
entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + alias;

View File

@@ -34,11 +34,12 @@ public class HttpDownloadModule extends BaseViewModule {
private final String defUrl =
"http://hzdown.muzhiwan.com/2017/05/08/nl.noio.kingdom_59104935e56f0.apk";
//"https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg";
//"http://9.9.9.205:5000/download/Cyberduck-6.9.4.30164.zip";
//"http://202.98.201.103:7000/vrs/TPK/ZTC440402001Z.tpk";
private final String defFilePath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
+ "/ZTC440402001Z.tpk";
+ "/tttt.apk";
private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>();
private DownloadEntity singDownloadInfo;
@@ -47,9 +48,12 @@ public class HttpDownloadModule extends BaseViewModule {
* 单任务下载的信息
*/
LiveData<DownloadEntity> getHttpDownloadInfo(Context context) {
String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl);
//String url = AppUtil.getConfigValue(context, HTTP_URL_KEY, defUrl);
//String url =
// "http://sdkdown.muzhiwan.com/openfile/2019/05/21/com.netease.tom.mzw_5ce3ef8754d05.apk";
String url = "http://image.totwoo.com/totwoo-TOTWOO-v3.5.6.apk";
//String url = "https://imtt.dd.qq.com/16891/apk/70BFFDB05AB8686F2A4CF3E07588A377.apk?fsname=com.tencent.tmgp.speedmobile_1.16.0.33877_1160033877.apk&csr=1bbd";
//String url = "https://ss1.baidu.com/-4o3dSag_xI4khGko9WTAnF6hhy/image/h%3D300/sign=a9e671b9a551f3dedcb2bf64a4eff0ec/4610b912c8fcc3cef70d70409845d688d53f20f7.jpg";
String filePath = AppUtil.getConfigValue(context, HTTP_PATH_KEY, defFilePath);
singDownloadInfo = Aria.download(context).getFirstDownloadEntity(url);

View File

@@ -215,6 +215,7 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
@Download.onTaskResume
void taskResume(DownloadTask task) {
ALog.d(TAG, "resume");
if (task.getKey().equals(mUrl)) {
getBinding().setStateStr(getString(R.string.stop));
}
@@ -275,8 +276,9 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
if (Aria.download(this).load(mTaskId).isRunning()) {
Aria.download(this).load(mTaskId).stop();
} else {
mUrl = "http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk";
Aria.download(this).load(mTaskId)
//.updateUrl("http://sdkdown.muzhiwan.com/openfile/2019/07/11/com.netease.syfz.mzw_5d26f8d9cee27.apk")
.updateUrl(mUrl)
.resume();
}
break;
@@ -289,8 +291,8 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
private void startD() {
HttpOption option = new HttpOption();
option.addHeader("1", "@")
.setFileLenAdapter(new FileLenAdapter())
.useServerFileName(true);
.useServerFileName(true)
.setFileLenAdapter(new FileLenAdapter());
mTaskId = Aria.download(SingleTaskActivity.this)
.load(mUrl)
.setFilePath(mFilePath, true)

View File

@@ -95,13 +95,13 @@ public class DownloadGroupActivity extends BaseActivity<ActivityDownloadGroupBin
mTaskId = Aria.download(this)
.loadGroup(mUrls)
.setDirPath(
Environment.getExternalStorageDirectory().getPath() + "/Download/group_test_2")
.setGroupAlias("任务组测试")
Environment.getExternalStorageDirectory().getPath() + "/Download/group_imgs")
.setGroupAlias("图片测试")
//.setSubFileName(getModule(GroupModule.class).getSubName2())
.setSubFileName(getModule(GroupModule.class).getSubName())
//.setSubFileName(getModule(GroupModule.class).getSubName())
.unknownSize()
.option(getHttpOption())
.setFileSize(114981416)
//.setFileSize(114981416)
//.updateUrls(temp)
.create();
getBinding().setStateStr(getString(R.string.stop));

Some files were not shown because too many files have changed in this diff Show More