laoyuyu 5 years ago
parent 012f94511b
commit 5ad485890c
  1. 26
      .github/ISSUE_TEMPLATE/Custom.md
  2. 25
      Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java
  3. 8
      Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java
  4. 5
      DEV_LOG.md
  5. 4
      FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java
  6. 15
      FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDLoaderAdapter.java
  7. 1
      FtpComponent/src/main/java/com/arialyy/aria/ftp/download/FtpDirDLoaderUtil.java
  8. 5
      HttpComponent/src/main/java/com/arialyy/aria/http/download/DGroupLoaderUtil.java
  9. 19
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/BaseM3U8Loader.java
  10. 106
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8InfoThread.java
  11. 9
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java
  12. 10
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java
  13. 8
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java
  14. 111
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java
  15. 13
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java
  16. 29
      M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java
  17. 15
      PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java
  18. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java
  19. 3
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java
  20. 9
      PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java
  21. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java
  22. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java
  23. 4
      PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java
  24. 7
      PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java
  25. 24
      PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java
  26. 8
      PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java
  27. 29
      README.md
  28. 58
      SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java
  29. 87
      SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java
  30. 40
      SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java
  31. 71
      SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java
  32. 35
      SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java
  33. 25
      app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java
  34. 1
      app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java
  35. 3
      app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java
  36. 10
      app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8LiveDLoadActivity.java
  37. 18
      app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java
  38. 6
      app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java
  39. 47
      app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java
  40. 40
      app/src/main/java/com/arialyy/simple/core/download/mutil/FileListAdapter.java
  41. 8
      app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java
  42. 2
      app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java
  43. 1
      app/src/main/java/com/arialyy/simple/core/download/mutil/MultiTaskActivity.java
  44. 4
      build.gradle

@ -4,14 +4,32 @@ about: 请详细描述你遇到的问题
--- ---
## Aria版本 <!-- 请详细描述以下信息,即使是一个简单的bug,没有充足的信息,作者却需要花费大量的时间去查找可能的原因!! -->
## 预期行为
## 什么问题
## 当前行为
## 如何复现此问题
## 可能的解决方案
<!-- 不是强制性的,针对该错误的修复/原因的建议-->
## 控制台日志
## 重现步骤
<!-- 请提供明确的步骤 -->
1.
2.
3.
4.
## 错误日志
## 版本
* 框架版本
* 系统版本

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

@ -18,7 +18,6 @@ package com.arialyy.aria.core.download.m3u8;
import com.arialyy.aria.core.common.BaseOption; import com.arialyy.aria.core.common.BaseOption;
import com.arialyy.aria.core.processor.IBandWidthUrlConverter; import com.arialyy.aria.core.processor.IBandWidthUrlConverter;
import com.arialyy.aria.core.processor.ITsMergeHandler; 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.CheckUtil;
import com.arialyy.aria.util.ComponentUtil; import com.arialyy.aria.util.ComponentUtil;
@ -27,8 +26,8 @@ import com.arialyy.aria.util.ComponentUtil;
*/ */
public class M3U8Option<OP extends M3U8Option> extends BaseOption { public class M3U8Option<OP extends M3U8Option> extends BaseOption {
private boolean generateIndexFileTemp = false; private boolean generateIndexFile = false;
private boolean mergeFile = false; private boolean mergeFile = true;
private int bandWidth; private int bandWidth;
private ITsMergeHandler mergeHandler; private ITsMergeHandler mergeHandler;
private IBandWidthUrlConverter bandWidthUrlConverter; private IBandWidthUrlConverter bandWidthUrlConverter;
@ -41,9 +40,10 @@ public class M3U8Option<OP extends M3U8Option> extends BaseOption {
/** /**
* 生成m3u8索引文件 * 生成m3u8索引文件
* 注意创建索引文件{@link #merge(boolean)}方法设置与否都不再合并文件 * 注意创建索引文件{@link #merge(boolean)}方法设置与否都不再合并文件
* 如果是直播文件下载创建索引文件的操作将导致只能同时下载一个切片
*/ */
public OP generateIndexFile() { public OP generateIndexFile() {
this.generateIndexFileTemp = true; this.generateIndexFile = true;
return (OP) this; return (OP) this;
} }

@ -1,4 +1,9 @@
## 开发日志 ## 开发日志
+ 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) + v_3.7.4 (2019/11/2)
- 修复一个class被莫名改变的问题 - 修复一个class被莫名改变的问题
- 修复非分块模式下导致的一个下载失败问题 - 修复非分块模式下导致的一个下载失败问题

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

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

@ -91,7 +91,6 @@ public class FtpDirDLoaderUtil extends AbsGroupUtil {
} finally { } finally {
LOCK.unlock(); LOCK.unlock();
} }
initState();
for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) { for (DTaskWrapper wrapper : getWrapper().getSubTaskWrapper()) {
if (needCloneInfo) { if (needCloneInfo) {
cloneInfo(wrapper); cloneInfo(wrapper);

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

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

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

@ -24,6 +24,7 @@ import com.arialyy.aria.core.download.M3U8Entity;
import com.arialyy.aria.core.inf.IRecordHandler; import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.RecordUtil; import com.arialyy.aria.util.RecordUtil;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -54,18 +55,22 @@ public class M3U8RecordAdapter extends AbsRecordHandlerAdapter {
String cacheDir = mOption.getCacheDir(); String cacheDir = mOption.getCacheDir();
long currentProgress = 0; long currentProgress = 0;
int completeNum = 0; int completeNum = 0;
File targetFile = new File(mTaskRecord.filePath);
if (!targetFile.exists()){
FileUtil.createFile(targetFile);
}
M3U8Entity m3U8Entity = ((DownloadEntity) getEntity()).getM3U8Entity(); M3U8Entity m3U8Entity = ((DownloadEntity) getEntity()).getM3U8Entity();
// 重新下载所有切片 // 重新下载所有切片
boolean reDownload = 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())); String.format(M3U8InfoThread.M3U8_INDEX_FORMAT, getEntity().getFilePath())).exists()));
for (ThreadRecord record : mTaskRecord.threadRecords) { for (ThreadRecord record : mTaskRecord.threadRecords) {
File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId)); File temp = new File(BaseM3U8Loader.getTsFilePath(cacheDir, record.threadId));
if (!record.isComplete || reDownload) { if (!record.isComplete || reDownload) {
if (temp.exists()) { if (temp.exists()) {
temp.delete(); FileUtil.deleteFile(temp);
} }
record.startLocation = 0; record.startLocation = 0;
//ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId)); //ALog.d(TAG, String.format("分片【%s】未完成,将重新下载该分片", record.threadId));

@ -101,14 +101,14 @@ public class M3U8TaskOption implements ITaskOption {
/** /**
* 生成索引占位字段 * 生成索引占位字段
*/ */
private boolean generateIndexFileTemp = false; private boolean generateIndexFile = false;
public boolean isGenerateIndexFileTemp() { public boolean isGenerateIndexFile() {
return generateIndexFileTemp; return generateIndexFile;
} }
public void setGenerateIndexFileTemp(boolean generateIndexFileTemp) { public void setGenerateIndexFile(boolean generateIndexFile) {
this.generateIndexFileTemp = generateIndexFileTemp; this.generateIndexFile = generateIndexFile;
} }
public int getJumpIndex() { public int getJumpIndex() {

@ -89,6 +89,14 @@ public class M3U8ThreadTaskAdapter extends AbsThreadTaskAdapter {
} }
} }
int code = conn.getResponseCode();
if (code != HttpURLConnection.HTTP_OK) {
fail(new TaskException(TAG,
String.format("连接错误,http错误码:%s,url:%s", code, getThreadConfig().url)),
false);
return;
}
is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn)); is = new BufferedInputStream(ConnectionHelp.convertInputStream(conn));
if (mHttpTaskOption.isChunked()) { if (mHttpTaskOption.isChunked()) {
readChunked(is); readChunked(is);

@ -24,17 +24,22 @@ import com.arialyy.aria.core.common.SubThreadConfig;
import com.arialyy.aria.core.download.DTaskWrapper; import com.arialyy.aria.core.download.DTaskWrapper;
import com.arialyy.aria.core.inf.IThreadState; import com.arialyy.aria.core.inf.IThreadState;
import com.arialyy.aria.core.listener.IEventListener; 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.manager.ThreadTaskManager;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.core.task.ThreadTask; import com.arialyy.aria.core.task.ThreadTask;
import com.arialyy.aria.m3u8.BaseM3U8Loader; import com.arialyy.aria.m3u8.BaseM3U8Loader;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.m3u8.IdGenerator; import com.arialyy.aria.m3u8.IdGenerator;
import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8Listener;
import com.arialyy.aria.m3u8.M3U8TaskOption;
import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.FileUtil;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ArrayBlockingQueue; 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.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import static com.arialyy.aria.m3u8.M3U8InfoThread.M3U8_INDEX_FORMAT;
/** /**
* M3U8点播文件下载器 * 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 Handler mStateHandler;
private ArrayBlockingQueue<Long> mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); private ArrayBlockingQueue<Long> mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM);
private ReentrantLock LOCK = new ReentrantLock(); private ReentrantLock LOCK = new ReentrantLock();
private Condition mCondition = LOCK.newCondition(); 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) { M3U8LiveLoader(M3U8Listener listener, DTaskWrapper wrapper) {
super(listener, wrapper); super(listener, wrapper);
if (((M3U8TaskOption) wrapper.getM3u8Option()).isGenerateIndexFile()) {
ALog.i(TAG, "直播文件下载,创建索引文件的操作将导致只能同时下载一个切片");
EXEC_MAX_NUM = 1;
}
} }
@Override protected IThreadState createStateManager(Looper looper) { @Override protected IThreadState createStateManager(Looper looper) {
@ -66,8 +79,8 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return manager; return manager;
} }
void offerPeer(String peerUrl) { void offerPeer(ExtInfo extInfo) {
mPeerQueue.offer(peerUrl); mPeerQueue.offer(extInfo);
} }
@Override protected void handleTask() { @Override protected void handleTask() {
@ -80,13 +93,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
try { try {
LOCK.lock(); LOCK.lock();
while (mFlagQueue.size() < EXEC_MAX_NUM) { while (mFlagQueue.size() < EXEC_MAX_NUM) {
String url = mPeerQueue.poll(); ExtInfo extInfo = mPeerQueue.poll();
if (url == null) { if (extInfo == null) {
break; break;
} }
ThreadTask task = createThreadTask(cacheDir, index, url); mCurExtInfo = extInfo;
ThreadTask task = createThreadTask(cacheDir, index, extInfo.url);
getTaskList().put(index, task); getTaskList().put(index, task);
mFlagQueue.offer(startThreadTask(task)); mFlagQueue.offer(startThreadTask(task, task.getConfig().peerIndex));
index++; index++;
} }
if (mFlagQueue.size() > 0) { if (mFlagQueue.size() > 0) {
@ -106,11 +120,15 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return mTempFile.length(); return mTempFile.length();
} }
private void notifyLock() { private void notifyLock(boolean success, int peerId) {
try { try {
LOCK.lock(); LOCK.lock();
long id = mFlagQueue.take(); 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(); mCondition.signalAll();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); 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标志 * @return 线程唯一id标志
*/ */
private long startThreadTask(ThreadTask task) { private long startThreadTask(ThreadTask task, int indexId) {
ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task); ThreadTaskManager.getInstance().startThread(mTaskWrapper.getKey(), task);
((M3U8Listener) mListener).onPeerStart(mTaskWrapper.getKey(),
task.getConfig().tempFile.getPath(),
indexId);
return IdGenerator.getInstance().nextId(); return IdGenerator.getInstance().nextId();
} }
@ -139,6 +171,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
record.tsUrl = tsUrl; record.tsUrl = tsUrl;
record.threadType = TaskRecord.TYPE_M3U8_LIVE; record.threadType = TaskRecord.TYPE_M3U8_LIVE;
record.threadId = indexId; record.threadId = indexId;
mRecord.threadRecords.add(record);
SubThreadConfig config = new SubThreadConfig(); SubThreadConfig config = new SubThreadConfig();
config.url = tsUrl; config.url = tsUrl;
@ -148,6 +181,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
config.taskWrapper = mTaskWrapper; config.taskWrapper = mTaskWrapper;
config.record = record; config.record = record;
config.stateHandler = mStateHandler; config.stateHandler = mStateHandler;
config.peerIndex = indexId;
if (!config.tempFile.exists()) { if (!config.tempFile.exists()) {
FileUtil.createFile(config.tempFile); FileUtil.createFile(config.tempFile);
@ -163,10 +197,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
* *
* @return {@code true} 合并成功{@code false}合并失败 * @return {@code true} 合并成功{@code false}合并失败
*/ */
public boolean mergeFile() { boolean mergeFile() {
if (getEntity().getM3U8Entity().isGenerateIndexFile()) {
return generateIndexFile();
}
ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler();
String cacheDir = getCacheDir(); String cacheDir = getCacheDir();
List<String> partPath = new ArrayList<>(); List<String> partPath = new ArrayList<>();
@ -213,7 +244,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
/** /**
* 任务状态回调 * 任务状态回调
*/ */
private IEventListener mListener; private M3U8Listener mListener;
private long mProgress; //当前总进度 private long mProgress; //当前总进度
private Looper mLooper; private Looper mLooper;
@ -222,7 +253,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
*/ */
LiveStateManager(Looper looper, IEventListener listener) { LiveStateManager(Looper looper, IEventListener listener) {
mLooper = looper; mLooper = looper;
mListener = listener; mListener = (M3U8Listener) listener;
} }
/** /**
@ -234,6 +265,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
} }
@Override public boolean handleMessage(Message msg) { @Override public boolean handleMessage(Message msg) {
int peerIndex = msg.getData().getInt(ISchedulers.DATA_M3U8_PEER_INDEX);
switch (msg.what) { switch (msg.what) {
case STATE_STOP: case STATE_STOP:
if (isBreak()) { if (isBreak()) {
@ -248,15 +280,46 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
} }
break; break;
case STATE_COMPLETE: 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; break;
case STATE_RUNNING: case STATE_RUNNING:
mProgress += (long) msg.obj; mProgress += (long) msg.obj;
break; break;
case STATE_FAIL:
notifyLock(false, peerIndex);
mListener.onPeerFail(mTaskWrapper.getKey(),
msg.getData().getString(ISchedulers.DATA_M3U8_PEER_PATH), peerIndex);
break;
} }
return false; 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() { @Override public boolean isFail() {
return false; return false;
} }
@ -269,4 +332,14 @@ public class M3U8LiveLoader extends BaseM3U8Loader {
return mProgress; return mProgress;
} }
} }
static class ExtInfo {
String url;
String extInf;
ExtInfo(String url, String extInf) {
this.url = url;
this.extInf = extInf;
}
}
} }

@ -27,6 +27,7 @@ import com.arialyy.aria.core.processor.ILiveTsUrlConverter;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.M3U8Exception; import com.arialyy.aria.exception.M3U8Exception;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.http.HttpTaskOption; import com.arialyy.aria.http.HttpTaskOption;
import com.arialyy.aria.m3u8.M3U8InfoThread; import com.arialyy.aria.m3u8.M3U8InfoThread;
import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8Listener;
@ -87,7 +88,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
} }
}); });
infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() { infoThread.setOnGetPeerCallback(new M3U8InfoThread.OnGetLivePeerCallback() {
@Override public void onGetPeer(String url) { @Override public void onGetPeer(String url, String extInf) {
if (mPeerUrls.contains(url)) { if (mPeerUrls.contains(url)) {
return; return;
} }
@ -104,7 +105,7 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
fail(new M3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false); fail(new M3U8Exception(TAG, String.format("ts地址错误,url:%s", url)), false);
return; return;
} }
getLoader().offerPeer(url); getLoader().offerPeer(new M3U8LiveLoader.ExtInfo(url, extInf));
} }
}); });
return infoThread; return infoThread;
@ -129,7 +130,13 @@ public class M3U8LiveUtil extends AbsNormalLoaderUtil {
if (mInfoThread != null) { if (mInfoThread != null) {
mInfoThread.setStop(true); mInfoThread.setStop(true);
closeTimer(); 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()) { if (getLoader().mergeFile()) {
getListener().onComplete(); getListener().onComplete();
} else { } else {

@ -31,14 +31,16 @@ import com.arialyy.aria.core.inf.IThreadState;
import com.arialyy.aria.core.listener.IEventListener; import com.arialyy.aria.core.listener.IEventListener;
import com.arialyy.aria.core.listener.ISchedulers; import com.arialyy.aria.core.listener.ISchedulers;
import com.arialyy.aria.core.manager.ThreadTaskManager; 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.core.task.ThreadTask;
import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.exception.TaskException;
import com.arialyy.aria.m3u8.BaseM3U8Loader; import com.arialyy.aria.m3u8.BaseM3U8Loader;
import com.arialyy.aria.core.processor.ITsMergeHandler;
import com.arialyy.aria.m3u8.M3U8Listener; import com.arialyy.aria.m3u8.M3U8Listener;
import com.arialyy.aria.m3u8.M3U8TaskOption; import com.arialyy.aria.m3u8.M3U8TaskOption;
import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter; import com.arialyy.aria.m3u8.M3U8ThreadTaskAdapter;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.FileUtil;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -219,6 +221,12 @@ public class M3U8VodLoader extends BaseM3U8Loader {
} }
} }
mManager.updateStateCount(); mManager.updateStateCount();
if (mCompleteNum <= 0){
mListener.onStart(0);
}else {
int percent = mCompleteNum * 100 / mRecord.threadRecords.size();
mListener.onResume(percent);
}
} }
/** /**
@ -446,7 +454,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
* M3U8线程状态管理 * M3U8线程状态管理
*/ */
private class VodStateManager implements IThreadState { private class VodStateManager implements IThreadState {
private final String TAG = "M3U8ThreadStateManager"; private final String TAG = CommonUtil.getClassName(VodStateManager.class);
/** /**
* 任务状态回调 * 任务状态回调
@ -456,7 +464,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
private int cancelNum = 0; // 已经取消的线程的数 private int cancelNum = 0; // 已经取消的线程的数
private int stopNum = 0; // 已经停止的线程数 private int stopNum = 0; // 已经停止的线程数
private int failNum = 0; // 失败的线程数 private int failNum = 0; // 失败的线程数
private long progress; //当前总进度 private long progress; //当前总进度,百分比进度
private TaskRecord taskRecord; // 任务记录 private TaskRecord taskRecord; // 任务记录
private Looper looper; private Looper looper;
@ -559,7 +567,14 @@ public class M3U8VodLoader extends BaseM3U8Loader {
"startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s", "startThreadNum = %s, stopNum = %s, cancelNum = %s, failNum = %s, completeNum = %s, flagQueueSize = %s",
startThreadNum, stopNum, cancelNum, failNum, mCompleteNum, mFlagQueue.size())); startThreadNum, stopNum, cancelNum, failNum, mCompleteNum, mFlagQueue.size()));
ALog.d(TAG, String.format("vod任务【%s】完成", mTempFile.getName())); 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()) { if (mergeFile()) {
listener.onComplete(); listener.onComplete();
} else { } else {
@ -572,7 +587,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
} }
break; break;
case STATE_RUNNING: case STATE_RUNNING:
progress += (long) msg.obj; //progress += (long) msg.obj;
break; break;
} }
return true; return true;
@ -596,6 +611,7 @@ public class M3U8VodLoader extends BaseM3U8Loader {
int percent = completeNum * 100 / taskRecord.threadRecords.size(); int percent = completeNum * 100 / taskRecord.threadRecords.size();
getEntity().setPercent(percent); getEntity().setPercent(percent);
getEntity().update(); getEntity().update();
progress = percent;
} }
@Override public boolean isFail() { @Override public boolean isFail() {
@ -625,9 +641,6 @@ public class M3U8VodLoader extends BaseM3U8Loader {
* @return {@code true} 合并成功{@code false}合并失败 * @return {@code true} 合并成功{@code false}合并失败
*/ */
private boolean mergeFile() { private boolean mergeFile() {
if (getEntity().getM3U8Entity().isGenerateIndexFile()) {
return generateIndexFile();
}
ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler(); ITsMergeHandler mergeHandler = mM3U8Option.getMergeHandler();
String cacheDir = getCacheDir(); String cacheDir = getCacheDir();
List<String> partPath = new ArrayList<>(); List<String> partPath = new ArrayList<>();

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

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

@ -200,8 +200,7 @@ public class NormalLoader extends AbsLoader {
ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress)); ALog.d(TAG, String.format("进度修正,当前进度:%s", currentProgress));
getEntity().setCurrentProgress(currentProgress); getEntity().setCurrentProgress(currentProgress);
} }
mStateHandler.obtainMessage(IThreadState.STATE_UPDATE_PROGRESS, currentProgress) mStateManager.updateProgress(currentProgress);
.sendToTarget();
startThreadTask(); startThreadTask();
} }

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

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

@ -97,7 +97,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter {
return mThreadConfig; return mThreadConfig;
} }
@Override public void setThreadStateObserver(IThreadTaskObserver observer) { @Override public void attach(IThreadTaskObserver observer) {
mObserver = observer; mObserver = observer;
} }

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

@ -33,6 +33,7 @@ import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.exception.BaseException; import com.arialyy.aria.exception.BaseException;
import com.arialyy.aria.util.ALog; import com.arialyy.aria.util.ALog;
import com.arialyy.aria.util.BufferedRandomAccessFile; import com.arialyy.aria.util.BufferedRandomAccessFile;
import com.arialyy.aria.util.CommonUtil;
import com.arialyy.aria.util.ErrorHelp; import com.arialyy.aria.util.ErrorHelp;
import com.arialyy.aria.util.FileUtil; import com.arialyy.aria.util.FileUtil;
import com.arialyy.aria.util.NetUtils; import com.arialyy.aria.util.NetUtils;
@ -49,7 +50,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
* 线程重试次数 * 线程重试次数
*/ */
private final int RETRY_NUM = 2; private final int RETRY_NUM = 2;
private final String TAG = "AbsThreadTask"; private final String TAG = CommonUtil.getClassName(getClass());
private IEntity mEntity; private IEntity mEntity;
protected AbsTaskWrapper mTaskWrapper; protected AbsTaskWrapper mTaskWrapper;
private int mFailTimes = 0; private int mFailTimes = 0;
@ -94,7 +95,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
*/ */
public void setAdapter(IThreadTaskAdapter adapter) { public void setAdapter(IThreadTaskAdapter adapter) {
mAdapter = adapter; mAdapter = adapter;
mAdapter.setThreadStateObserver(this); mAdapter.attach(this);
} }
/** /**
@ -335,7 +336,7 @@ public class ThreadTask implements IThreadTask, IThreadTaskObserver {
*/ */
protected void fail(final long subCurrentLocation, BaseException ex, boolean needRetry) { protected void fail(final long subCurrentLocation, BaseException ex, boolean needRetry) {
if (ex != null) { if (ex != null) {
ALog.e(TAG, ALog.getExceptionString(ex)); ex.printStackTrace();
} }
if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) { if (mTaskWrapper.getRequestType() == ITaskWrapper.M3U8_VOD) {
writeConfig(false, 0); writeConfig(false, 0);

@ -17,6 +17,9 @@
package com.arialyy.aria.util; package com.arialyy.aria.util;
import android.text.TextUtils; import android.text.TextUtils;
import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.inf.IRecordHandler;
import com.arialyy.aria.orm.DbEntity;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List; import java.util.List;
@ -27,6 +30,27 @@ import java.util.List;
public class CheckUtil { public class CheckUtil {
private static final String TAG = "CheckUtil"; private static final String TAG = "CheckUtil";
/**
* 检查和处理路径冲突
*
* @param isForceDownload true如果路径冲突将删除其它任务的记录的
* @param filePath 文件保存路径
* @return false 任务不再执行true 任务继续执行
*/
public static boolean checkAndHandlePathConflicts(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;
}
/** /**
* 检查成员类是否是静态和public * 检查成员类是否是静态和public
*/ */

@ -295,7 +295,7 @@ public class RecordUtil {
} }
/** /**
* 删除ts文件 * 删除ts文件和索引文件如果有的话
*/ */
private static void removeTsCache(File targetFile, long bandWidth) { private static void removeTsCache(File targetFile, long bandWidth) {
@ -321,6 +321,12 @@ public class RecordUtil {
cDir.delete(); cDir.delete();
} }
} }
File indexFile = new File(String.format("%s.index", targetFile.getPath()));
if (indexFile.exists()) {
indexFile.delete();
}
} }
/** /**

@ -23,7 +23,7 @@ Aria有以下特点:
[怎样使用Aria?](#使用) [怎样使用Aria?](#使用)
如果你觉得Aria对你有帮助,您的star和issues将是对我最大支持.`^_^` 如果你觉得Aria对你有帮助,你的star和issues将是对我最大支持,当然,也非常欢迎你能PR,[PR方法](https://www.zhihu.com/question/21682976/answer/79489643)`^_^`
## 示例 ## 示例
* 多任务下载 * 多任务下载
@ -44,16 +44,16 @@ Aria有以下特点:
## 引入库 ## 引入库
[![license](http://img.shields.io/badge/license-Apache2.0-brightgreen.svg?style=flat)](https://github.com/AriaLyy/Aria/blob/master/LICENSE) [![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.4-blue)](https://github.com/AriaLyy/Aria) [![Core](https://img.shields.io/badge/Core-3.7.5-blue)](https://github.com/AriaLyy/Aria)
[![Compiler](https://img.shields.io/badge/Compiler-3.7.4-blue)](https://github.com/AriaLyy/Aria) [![Compiler](https://img.shields.io/badge/Compiler-3.7.5-blue)](https://github.com/AriaLyy/Aria)
[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.4-orange)](https://github.com/AriaLyy/Aria) [![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.5-orange)](https://github.com/AriaLyy/Aria)
[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.4-orange)](https://github.com/AriaLyy/Aria) [![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.5-orange)](https://github.com/AriaLyy/Aria)
```java ```java
implementation 'com.arialyy.aria:core:3.7.4' implementation 'com.arialyy.aria:core:3.7.5'
annotationProcessor 'com.arialyy.aria:compiler:3.7.4' annotationProcessor 'com.arialyy.aria:compiler:3.7.5'
implementation 'com.arialyy.aria:ftpComponent:3.7.4' # 如果需要使用ftp,请增加该组件 implementation 'com.arialyy.aria:ftpComponent:3.7.5' # 如果需要使用ftp,请增加该组件
implementation 'com.arialyy.aria:m3u8Component:3.7.4' # 如果需要使用m3u8下载功能,请增加该组件 implementation 'com.arialyy.aria:m3u8Component:3.7.5' # 如果需要使用m3u8下载功能,请增加该组件
``` ```
如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:<last-version>'`替换为 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:<last-version>'`替换为
``` ```
@ -137,10 +137,11 @@ protected void onCreate(Bundle savedInstanceState) {
### 版本日志 ### 版本日志
+ v_3.7.4 (2019/11/2) + v_3.7.5 (2019/11/10)
- 修复一个class被莫名改变的问题 - 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/493 - fix bug https://github.com/AriaLyy/Aria/issues/503
- 修复m3u8创建索引不成功的问题
[更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md) [更多版本记录](https://github.com/AriaLyy/Aria/blob/master/DEV_LOG.md)
@ -164,7 +165,7 @@ protected void onCreate(Bundle savedInstanceState) {
在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。</br> 在提交问题前,希望你已经查看过[wiki](https://aria.laoyuyu.me/aria_doc/)或搜索过[issues](https://github.com/AriaLyy/Aria/issues)。</br>
## 打赏 ## 打赏
如果觉得框架写的不错并且帮助到了你,可以请我喝杯热</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/ali_pay.png" width=336 height=336/>
<img src="https://raw.githubusercontent.com/AriaLyy/Aria/master/img/wx_pay.png" width=336 height=336/> <img src="https://raw.githubusercontent.com/AriaLyy/Aria/master/img/wx_pay.png" width=336 height=336/>

@ -0,0 +1,58 @@
/*
* 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.inf.OnFileInfoCallback;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSchException;
/**
* 获取sftp文件信息
*
* @author lyy
*/
public class AbsSFtpFileInfoThread<WP extends AbsTaskWrapper> implements Runnable {
private WP mWrapper;
private SFtpUtil mUtil;
private OnFileInfoCallback mCallback;
public AbsSFtpFileInfoThread(SFtpUtil util, WP wrapper, OnFileInfoCallback callback) {
mWrapper = wrapper;
mUtil = util;
mCallback = callback;
}
@Override public void run() {
startFlow();
}
private void startFlow() {
}
private ChannelSftp createChannel() {
ChannelSftp sftp = null;
try {
sftp = (ChannelSftp) mUtil.getSession().openChannel(SFtpUtil.CMD_TYPE_SFTP);
sftp.connect();
} catch (JSchException e) {
e.printStackTrace();
}
return sftp;
}
}

@ -16,24 +16,39 @@
package com.arialyy.aria.sftp; package com.arialyy.aria.sftp;
import android.text.TextUtils; 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.JSch;
import com.jcraft.jsch.JSchException; import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session; 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; import java.util.Properties;
/** /**
* sftp登录 * sftp工具类
* *
* @author lyy * @author lyy
*/ */
public class SFtpLogin { 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 String ip, userName, password;
private int port; private int port;
private Session session; private Session session;
private boolean isLogin = false; private boolean isLogin = false;
private SFtpLogin() { private SFtpUtil() {
createClient(); createClient();
} }
@ -55,6 +70,7 @@ public class SFtpLogin {
config.put("StrictHostKeyChecking", "no"); config.put("StrictHostKeyChecking", "no");
session.setConfig(config);// 为Session对象设置properties session.setConfig(config);// 为Session对象设置properties
session.setTimeout(3000);// 设置超时 session.setTimeout(3000);// 设置超时
login();
isLogin = true; isLogin = true;
} catch (JSchException e) { } catch (JSchException e) {
e.printStackTrace(); e.printStackTrace();
@ -83,6 +99,65 @@ public class SFtpLogin {
isLogin = false; 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 { public static class Builder {
private String ip, userName, password; private String ip, userName, password;
private int port = 22; private int port = 22;
@ -107,8 +182,8 @@ public class SFtpLogin {
return this; return this;
} }
public SFtpLogin build() { public SFtpUtil build() {
SFtpLogin login = new SFtpLogin(); SFtpUtil login = new SFtpUtil();
login.ip = ip; login.ip = ip;
login.userName = userName; login.userName = userName;
login.password = password; login.password = password;

@ -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;
}
}

@ -0,0 +1,71 @@
/*
* 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.AbsSFtpFileInfoThread;
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() {
return new AbsSFtpFileInfoThread<>(mSftpUtil, (DTaskWrapper) getTaskWrapper(), new OnFileInfoCallback() {
@Override public void onComplete(String key, CompleteInfo info) {
}
@Override public void onFail(AbsEntity entity, BaseException e, boolean needRetry) {
mSftpUtil.logout();
}
});
}
}

@ -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() {
}
}

@ -71,6 +71,29 @@ public class DownloadModule extends BaseModule {
return list; 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) { private String[] getStringArray(int array) {
return getContext().getResources().getStringArray(array); return getContext().getResources().getStringArray(array);
} }
@ -91,7 +114,7 @@ public class DownloadModule extends BaseModule {
FileListEntity entity = new FileListEntity(); FileListEntity entity = new FileListEntity();
entity.urls = getStringArray(urls); entity.urls = getStringArray(urls);
entity.names = getStringArray(names); entity.names = getStringArray(names);
entity.isGroup = true; entity.type = 1;
entity.name = alias; entity.name = alias;
entity.key = CommonUtil.getMd5Code(Arrays.asList(entity.urls)); entity.key = CommonUtil.getMd5Code(Arrays.asList(entity.urls));
entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + alias; entity.downloadPath = Environment.getExternalStorageDirectory() + "/Download/" + alias;

@ -215,6 +215,7 @@ public class SingleTaskActivity extends BaseActivity<ActivitySingleBinding> {
@Download.onTaskResume @Download.onTaskResume
void taskResume(DownloadTask task) { void taskResume(DownloadTask task) {
ALog.d(TAG, "resume");
if (task.getKey().equals(mUrl)) { if (task.getKey().equals(mUrl)) {
getBinding().setStateStr(getString(R.string.stop)); getBinding().setStateStr(getString(R.string.stop));
} }

@ -24,12 +24,14 @@ import com.arialyy.aria.core.common.FtpOption;
import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.task.DownloadGroupTask; import com.arialyy.aria.core.task.DownloadGroupTask;
import com.arialyy.aria.util.ALog;
import com.arialyy.frame.util.show.L; import com.arialyy.frame.util.show.L;
import com.arialyy.frame.util.show.T; import com.arialyy.frame.util.show.T;
import com.arialyy.simple.R; import com.arialyy.simple.R;
import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.base.BaseActivity;
import com.arialyy.simple.databinding.ActivityDownloadGroupBinding; import com.arialyy.simple.databinding.ActivityDownloadGroupBinding;
import com.arialyy.simple.widget.SubStateLinearLayout; import com.arialyy.simple.widget.SubStateLinearLayout;
import java.security.AlgorithmConstraints;
/** /**
* Created by lyy on 2017/7/6. * Created by lyy on 2017/7/6.
@ -128,6 +130,7 @@ public class FTPDirDownloadActivity extends BaseActivity<ActivityDownloadGroupBi
} }
@DownloadGroup.onTaskResume() void taskResume(DownloadGroupTask task) { @DownloadGroup.onTaskResume() void taskResume(DownloadGroupTask task) {
ALog.d(TAG, "ftp dir resume");
} }
@DownloadGroup.onTaskStop() void taskStop(DownloadGroupTask task) { @DownloadGroup.onTaskStop() void taskStop(DownloadGroupTask task) {

@ -213,17 +213,10 @@ public class M3U8LiveDLoadActivity extends BaseActivity<ActivityM3u8LiveBinding>
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
case R.id.start: case R.id.start:
startD();
if (!AppUtil.chekEntityValid(mEntity)) {
startD();
break;
}
if (Aria.download(this).load(mEntity.getId()).isRunning()) { if (Aria.download(this).load(mEntity.getId()).isRunning()) {
Aria.download(this).load(mEntity.getId()).stop(); Aria.download(this).load(mEntity.getId()).stop();
} else { } else {
Aria.download(this).load(mEntity.getId()) startD();
.m3u8LiveOption(getLiveoption())
.resume();
} }
break; break;
case R.id.cancel: case R.id.cancel:
@ -245,6 +238,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity<ActivityM3u8LiveBinding>
private M3U8LiveOption getLiveoption() { private M3U8LiveOption getLiveoption() {
M3U8LiveOption option = new M3U8LiveOption(); M3U8LiveOption option = new M3U8LiveOption();
option option
//.generateIndexFile()
.setLiveTsUrlConvert(new LiveTsUrlConverter()); .setLiveTsUrlConvert(new LiveTsUrlConverter());
//.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl));
return option; return option;

@ -90,10 +90,10 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
getBinding().setFilePath(entity.getFilePath()); getBinding().setFilePath(entity.getFilePath());
mUrl = entity.getUrl(); mUrl = entity.getUrl();
mFilePath = entity.getFilePath(); mFilePath = entity.getFilePath();
mVideoFragment = new VideoPlayerFragment(0, entity); //mVideoFragment = new VideoPlayerFragment(0, entity);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); //FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.video_content, mVideoFragment); //ft.add(R.id.video_content, mVideoFragment);
ft.commit(); //ft.commit();
} }
}); });
getBinding().setViewModel(this); getBinding().setViewModel(this);
@ -176,7 +176,7 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
@M3U8.onPeerComplete @M3U8.onPeerComplete
void onPeerComplete(String m3u8Url, String peerPath, int peerIndex) { void onPeerComplete(String m3u8Url, String peerPath, int peerIndex) {
//ALog.d(TAG, "peer complete, path: " + peerPath + ", index: " + peerIndex); //ALog.d(TAG, "peer complete, path: " + peerPath + ", index: " + peerIndex);
mVideoFragment.addPlayer(peerIndex, peerPath); //mVideoFragment.addPlayer(peerIndex, peerPath);
} }
@M3U8.onPeerFail @M3U8.onPeerFail
@ -217,6 +217,7 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
@Download.onTaskResume @Download.onTaskResume
void taskResume(DownloadTask task) { void taskResume(DownloadTask task) {
ALog.d(TAG, "m3u8 vod resume");
if (task.getKey().equals(mUrl)) { if (task.getKey().equals(mUrl)) {
getBinding().setStateStr(getString(R.string.stop)); getBinding().setStateStr(getString(R.string.stop));
} }
@ -303,9 +304,10 @@ public class M3U8VodDLoadActivity extends BaseActivity<ActivityM3u8VodBinding> {
private M3U8VodOption getM3U8Option() { private M3U8VodOption getM3U8Option() {
M3U8VodOption option = new M3U8VodOption(); M3U8VodOption option = new M3U8VodOption();
option option
.generateIndexFile() //.generateIndexFile()
.setVodTsUrlConvert(new VodTsUrlConverter()) //.merge(true)
.setMergeHandler(new TsMergeHandler()); .setVodTsUrlConvert(new VodTsUrlConverter());
//.setMergeHandler(new TsMergeHandler());
//.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl));
return option; return option;
} }

@ -35,10 +35,12 @@ public class M3U8VodModule extends BaseViewModule {
//private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8"; //private final String defUrl = "https://www.gaoya123.cn/2019/1557993797897.m3u8";
// 多码率地址: // 多码率地址:
//private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"; //private final String defUrl = "http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8";
private final String defUrl = "http://cn1.fa1244.cn/hls/20190516/6d271eaa73b2e4cb51d13831b0c1ab4c/1557976262/index.m3u8"; //private final String defUrl = "http://cn1.fa1244.cn/hls/20190516/6d271eaa73b2e4cb51d13831b0c1ab4c/1557976262/index.m3u8";
private final String defUrl = "http://qn.shytong.cn/b83137769ff6b555/11b0c9970f9a3fa0.mp4.m3u8";
private final String filePath = private final String filePath =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
+ "/道士下山.ts"; //+ "/道士下山.ts";
+ "/bb1.ts";
private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>(); private MutableLiveData<DownloadEntity> liveData = new MutableLiveData<>();
private DownloadEntity singDownloadInfo; private DownloadEntity singDownloadInfo;

@ -28,7 +28,7 @@ import com.arialyy.aria.core.common.AbsEntity;
import com.arialyy.aria.core.download.DownloadEntity; import com.arialyy.aria.core.download.DownloadEntity;
import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.download.DownloadGroupEntity;
import com.arialyy.aria.core.inf.IEntity; import com.arialyy.aria.core.inf.IEntity;
import com.arialyy.aria.core.wrapper.AbsTaskWrapper; import com.arialyy.aria.core.wrapper.ITaskWrapper;
import com.arialyy.aria.util.CommonUtil; import com.arialyy.aria.util.CommonUtil;
import com.arialyy.simple.R; import com.arialyy.simple.R;
import com.arialyy.simple.base.adapter.AbsHolder; import com.arialyy.simple.base.adapter.AbsHolder;
@ -152,12 +152,19 @@ public class DownloadAdapter extends AbsRVAdapter<AbsEntity, DownloadAdapter.Sim
* 只更新速度 * 只更新速度
*/ */
private void updateSpeed(SimpleHolder holder, final AbsEntity entity) { private void updateSpeed(SimpleHolder holder, final AbsEntity entity) {
holder.speed.setText(entity.getConvertSpeed());
if (entity.getTaskType() == ITaskWrapper.M3U8_VOD
|| entity.getTaskType() == ITaskWrapper.M3U8_LIVE) {
holder.progress.setProgress(entity.getPercent());
holder.fileSize.setVisibility(View.GONE);
} else {
long size = entity.getFileSize(); long size = entity.getFileSize();
long progress = entity.getCurrentProgress(); long progress = entity.getCurrentProgress();
int current = size == 0 ? 0 : (int) (progress * 100 / size); int current = size == 0 ? 0 : (int) (progress * 100 / size);
holder.speed.setText(entity.getConvertSpeed());
holder.fileSize.setText(covertCurrentSize(progress) + "/" + CommonUtil.formatFileSize(size)); holder.fileSize.setText(covertCurrentSize(progress) + "/" + CommonUtil.formatFileSize(size));
holder.progress.setProgress(current); holder.progress.setProgress(current);
}
//if (holder instanceof GroupHolder){ //if (holder instanceof GroupHolder){
// handleSubChild((GroupHolder) holder, entity); // handleSubChild((GroupHolder) holder, entity);
//} //}
@ -278,18 +285,19 @@ public class DownloadAdapter extends AbsRVAdapter<AbsEntity, DownloadAdapter.Sim
return; return;
} }
switch (entity.getTaskType()) { switch (entity.getTaskType()) {
case AbsTaskWrapper.D_FTP: case ITaskWrapper.D_FTP:
Aria.download(getContext()) Aria.download(getContext())
.loadFtp(entity.getId()) .loadFtp(entity.getId())
//.login("lao", "123456") //.login("lao", "123456")
.cancel(true); .cancel(true);
break; break;
case AbsTaskWrapper.D_FTP_DIR: case ITaskWrapper.D_FTP_DIR:
break; break;
case AbsTaskWrapper.D_HTTP: case ITaskWrapper.D_HTTP:
case ITaskWrapper.M3U8_VOD:
Aria.download(getContext()).load(entity.getId()).cancel(true); Aria.download(getContext()).load(entity.getId()).cancel(true);
break; break;
case AbsTaskWrapper.DG_HTTP: case ITaskWrapper.DG_HTTP:
Aria.download(getContext()).load(entity.getId()).cancel(true); Aria.download(getContext()).load(entity.getId()).cancel(true);
break; break;
} }
@ -297,17 +305,18 @@ public class DownloadAdapter extends AbsRVAdapter<AbsEntity, DownloadAdapter.Sim
private void resume(AbsEntity entity) { private void resume(AbsEntity entity) {
switch (entity.getTaskType()) { switch (entity.getTaskType()) {
case AbsTaskWrapper.D_FTP: case ITaskWrapper.D_FTP:
//Aria.download(getContext()).loadFtp((DownloadEntity) entity).login("lao", "123456").create(); //Aria.download(getContext()).loadFtp((DownloadEntity) entity).login("lao", "123456").create();
Aria.download(getContext()).loadFtp(entity.getId()).resume(); Aria.download(getContext()).loadFtp(entity.getId()).resume();
break; break;
case AbsTaskWrapper.D_FTP_DIR: case ITaskWrapper.D_FTP_DIR:
Aria.download(getContext()).loadFtpDir(entity.getId()).resume(); Aria.download(getContext()).loadFtpDir(entity.getId()).resume();
break; break;
case AbsTaskWrapper.D_HTTP: case ITaskWrapper.D_HTTP:
case ITaskWrapper.M3U8_VOD:
Aria.download(getContext()).load(entity.getId()).resume(); Aria.download(getContext()).load(entity.getId()).resume();
break; break;
case AbsTaskWrapper.DG_HTTP: case ITaskWrapper.DG_HTTP:
Aria.download(getContext()).loadGroup(entity.getId()).resume(); Aria.download(getContext()).loadGroup(entity.getId()).resume();
break; break;
} }
@ -315,16 +324,17 @@ public class DownloadAdapter extends AbsRVAdapter<AbsEntity, DownloadAdapter.Sim
private void start(AbsEntity entity) { private void start(AbsEntity entity) {
switch (entity.getTaskType()) { switch (entity.getTaskType()) {
case AbsTaskWrapper.D_FTP: case ITaskWrapper.D_FTP:
//Aria.download(getContext()).loadFtp((DownloadEntity) entity).login("lao", "123456").create(); //Aria.download(getContext()).loadFtp((DownloadEntity) entity).login("lao", "123456").create();
Aria.download(getContext()).loadFtp(entity.getKey()).create(); Aria.download(getContext()).loadFtp(entity.getKey()).create();
break; break;
case AbsTaskWrapper.D_FTP_DIR: case ITaskWrapper.D_FTP_DIR:
break; break;
case AbsTaskWrapper.D_HTTP: case ITaskWrapper.D_HTTP:
case ITaskWrapper.M3U8_VOD:
Aria.download(getContext()).load(entity.getKey()).create(); Aria.download(getContext()).load(entity.getKey()).create();
break; break;
case AbsTaskWrapper.DG_HTTP: case ITaskWrapper.DG_HTTP:
Aria.download(getContext()).loadGroup(((DownloadGroupEntity) entity).getUrls()).create(); Aria.download(getContext()).loadGroup(((DownloadGroupEntity) entity).getUrls()).create();
break; break;
} }
@ -336,15 +346,16 @@ public class DownloadAdapter extends AbsRVAdapter<AbsEntity, DownloadAdapter.Sim
} }
switch (entity.getTaskType()) { switch (entity.getTaskType()) {
case AbsTaskWrapper.D_FTP: case ITaskWrapper.D_FTP:
Aria.download(getContext()).loadFtp(entity.getId()).stop(); Aria.download(getContext()).loadFtp(entity.getId()).stop();
break; break;
case AbsTaskWrapper.D_FTP_DIR: case ITaskWrapper.D_FTP_DIR:
break; break;
case AbsTaskWrapper.D_HTTP: case ITaskWrapper.D_HTTP:
case ITaskWrapper.M3U8_VOD:
Aria.download(getContext()).load(entity.getId()).stop(); Aria.download(getContext()).load(entity.getId()).stop();
break; break;
case AbsTaskWrapper.DG_HTTP: case ITaskWrapper.DG_HTTP:
Aria.download(getContext()).loadGroup(entity.getId()).stop(); Aria.download(getContext()).loadGroup(entity.getId()).stop();
break; break;
} }

@ -22,9 +22,13 @@ import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.arialyy.aria.core.Aria; import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.download.m3u8.M3U8VodOption;
import com.arialyy.aria.core.processor.IVodTsUrlConverter;
import com.arialyy.simple.R; import com.arialyy.simple.R;
import com.arialyy.simple.base.adapter.AbsHolder; import com.arialyy.simple.base.adapter.AbsHolder;
import com.arialyy.simple.base.adapter.AbsRVAdapter; import com.arialyy.simple.base.adapter.AbsRVAdapter;
import com.arialyy.simple.core.download.m3u8.M3U8VodDLoadActivity;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -60,14 +64,23 @@ final class FileListAdapter extends AbsRVAdapter<FileListEntity, FileListAdapter
protected void bindData(FileListHolder holder, int position, final FileListEntity item) { protected void bindData(FileListHolder holder, int position, final FileListEntity item) {
holder.name.setText("文件名:" + item.name); holder.name.setText("文件名:" + item.name);
holder.url.setText("下载地址:" + item.key); holder.url.setText("下载地址:" + item.key);
holder.url.setVisibility(item.isGroup ? View.GONE : View.VISIBLE); if (item.type == 1) {
holder.url.setVisibility(View.GONE);
}
holder.path.setText("保存路径:" + item.downloadPath); holder.path.setText("保存路径:" + item.downloadPath);
if (mBtStates.get(item.key)) { if (mBtStates.get(item.key)) {
holder.bt.setEnabled(true); holder.bt.setEnabled(true);
holder.bt.setOnClickListener(new View.OnClickListener() { holder.bt.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) { @Override public void onClick(View v) {
Toast.makeText(getContext(), "开始下载:" + item.name, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), "开始下载:" + item.name, Toast.LENGTH_SHORT).show();
if (item.isGroup) { switch (item.type) {
case 0:
Aria.download(getContext())
.load(item.key)
.setFilePath(item.downloadPath)
.create();
break;
case 1:
Aria.download(getContext()) Aria.download(getContext())
.loadGroup(Arrays.asList(item.urls)) .loadGroup(Arrays.asList(item.urls))
.setSubFileName(Arrays.asList(item.names)) .setSubFileName(Arrays.asList(item.names))
@ -75,12 +88,20 @@ final class FileListAdapter extends AbsRVAdapter<FileListEntity, FileListAdapter
.setGroupAlias(item.name) .setGroupAlias(item.name)
.unknownSize() .unknownSize()
.create(); .create();
} else { break;
case 2:
M3U8VodOption option = new M3U8VodOption();
option.setVodTsUrlConvert(new IVodTs());
option.generateIndexFile();
Aria.download(getContext()) Aria.download(getContext())
.load(item.key) .load(item.key)
.setFilePath(item.downloadPath) .setFilePath(item.downloadPath, true)
.m3u8VodOption(option)
.create(); .create();
break;
} }
} }
}); });
} else { } else {
@ -125,4 +146,15 @@ final class FileListAdapter extends AbsRVAdapter<FileListEntity, FileListAdapter
bt = findViewById(R.id.bt); bt = findViewById(R.id.bt);
} }
} }
static class IVodTs implements IVodTsUrlConverter{
@Override public List<String> convert(String m3u8Url, List<String> tsUrls) {
List<String> temp = new ArrayList<>();
for (String tsUrl : tsUrls){
temp.add("http://qn.shytong.cn" + tsUrl);
}
return temp;
}
}
} }

@ -22,7 +22,13 @@ package com.arialyy.simple.core.download.mutil;
public class FileListEntity { public class FileListEntity {
public String name, key, downloadPath; public String name, key, downloadPath;
public boolean isGroup = false;
/**
* 0普通任务
* 1组合任务
* 2m3u8任务
*/
public int type = 0;
public String[] urls, names; public String[] urls, names;
} }

@ -113,7 +113,7 @@ public class MultiDownloadActivity extends BaseActivity<ActivityMultiDownloadBin
mAdapter.updateState(task.getEntity()); mAdapter.updateState(task.getEntity());
} }
@Download.onTaskRunning() void taskRunning(DownloadTask task) { @Download.onTaskRunning void taskRunning(DownloadTask task) {
mAdapter.setProgress(task.getEntity()); mAdapter.setProgress(task.getEntity());
} }

@ -57,6 +57,7 @@ public class MultiTaskActivity extends BaseActivity<ActivityMultiBinding> {
setTitle("多任务下载"); setTitle("多任务下载");
mData.addAll(getModule(DownloadModule.class).createGroupTestList()); mData.addAll(getModule(DownloadModule.class).createGroupTestList());
mData.addAll(getModule(DownloadModule.class).createMultiTestList()); mData.addAll(getModule(DownloadModule.class).createMultiTestList());
mData.addAll(getModule(DownloadModule.class).createM3u8TestList());
mAdapter = new FileListAdapter(this, mData); mAdapter = new FileListAdapter(this, mData);
mList = getBinding().list; mList = getBinding().list;
mBar = findViewById(R.id.toolbar); mBar = findViewById(R.id.toolbar);

@ -44,8 +44,8 @@ task clean(type: Delete) {
} }
ext { ext {
versionCode = 374 versionCode = 375
versionName = '3.7.4' versionName = '3.7.5'
userOrg = 'arialyy' userOrg = 'arialyy'
groupId = 'com.arialyy.aria' groupId = 'com.arialyy.aria'
publishVersion = versionName publishVersion = versionName

Loading…
Cancel
Save