diff --git a/.github/ISSUE_TEMPLATE/Custom.md b/.github/ISSUE_TEMPLATE/Custom.md index 90ac4ae1..cbe2f5b1 100644 --- a/.github/ISSUE_TEMPLATE/Custom.md +++ b/.github/ISSUE_TEMPLATE/Custom.md @@ -4,14 +4,32 @@ about: 请详细描述你遇到的问题 --- -## Aria版本 + +## 预期行为 -## 什么问题 +## 当前行为 -## 如何复现此问题 +## 可能的解决方案 + -## 控制台日志 + +## 重现步骤 + + +1. +2. +3. +4. + + +## 错误日志 + + +## 版本 +* 框架版本 + +* 系统版本 diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java index 6fffb0d7..c8eb2bbf 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/CheckDEntityUtil.java @@ -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) { @@ -140,15 +133,19 @@ public class CheckDEntityUtil implements ICheckEntityUtil { private boolean checkPathConflicts(String filePath) { //设置文件保存路径,如果新文件路径和旧文件路径不同,则修改路径 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 (!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.checkAndHandlePathConflicts(mWrapper.isForceDownload(), filePath)) { + return false; } File newFile = new File(filePath); diff --git a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java index 8dd23fc8..d9b56734 100644 --- a/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java +++ b/Aria/src/main/java/com/arialyy/aria/core/download/m3u8/M3U8Option.java @@ -18,7 +18,6 @@ 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.ITsMergeHandler; -import com.arialyy.aria.core.processor.IVodTsUrlConverter; import com.arialyy.aria.util.CheckUtil; import com.arialyy.aria.util.ComponentUtil; @@ -27,8 +26,8 @@ import com.arialyy.aria.util.ComponentUtil; */ public class 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; @@ -41,9 +40,10 @@ public class M3U8Option extends BaseOption { /** * 生成m3u8索引文件 * 注意:创建索引文件,{@link #merge(boolean)}方法设置与否都不再合并文件 + * 如果是直播文件下载,创建索引文件的操作将导致只能同时下载一个切片!! */ public OP generateIndexFile() { - this.generateIndexFileTemp = true; + this.generateIndexFile = true; return (OP) this; } diff --git a/DEV_LOG.md b/DEV_LOG.md index 45421b93..f487425f 100644 --- a/DEV_LOG.md +++ b/DEV_LOG.md @@ -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) - 修复一个class被莫名改变的问题 - 修复非分块模式下导致的一个下载失败问题 diff --git a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java index a355a01b..2d7a7aa1 100644 --- a/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java +++ b/FtpComponent/src/main/java/com/arialyy/aria/ftp/FtpDirInfoThread.java @@ -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 mInfos = new ArrayList<>(); public interface OnGetLivePeerCallback { - void onGetPeer(String url); + void onGetPeer(String url, String extInf); } public M3U8InfoThread(DTaskWrapper taskWrapper, OnFileInfoCallback callback) { @@ -122,38 +118,51 @@ final public class M3U8InfoThread implements Runnable { } List 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); - } + ALog.d(TAG, line); if (line.startsWith("#EXT-X-ENDLIST")) { + // 点播文件的下载写入结束标志,直播文件的下载在停止时才写入结束标志 + addIndexInfo(isGenerateIndexFile && !isLive, fos, line); break; - } - ALog.d(TAG, line); - if (line.startsWith("#EXTINF")) { - String info = reader.readLine(); - mInfos.add(info); + } 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 +171,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 +189,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 +208,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"))); } /** diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java index 25de650f..8ba21d6b 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8RecordAdapter.java @@ -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)); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java index 591cbf87..cd1c663d 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8TaskOption.java @@ -101,14 +101,14 @@ public class M3U8TaskOption implements ITaskOption { /** * 生成索引占位字段 */ - private boolean generateIndexFileTemp = false; + private boolean generateIndexFile = false; - public boolean isGenerateIndexFileTemp() { - return generateIndexFileTemp; + public boolean isGenerateIndexFile() { + return generateIndexFile; } - public void setGenerateIndexFileTemp(boolean generateIndexFileTemp) { - this.generateIndexFileTemp = generateIndexFileTemp; + public void setGenerateIndexFile(boolean generateIndexFile) { + this.generateIndexFile = generateIndexFile; } public int getJumpIndex() { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java index dee64820..41d86041 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/M3U8ThreadTaskAdapter.java @@ -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)); if (mHttpTaskOption.isChunked()) { readChunked(is); diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java index 297e5322..f474d7dd 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveLoader.java @@ -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 mFlagQueue = new ArrayBlockingQueue<>(EXEC_MAX_NUM); private ReentrantLock LOCK = new ReentrantLock(); private Condition mCondition = LOCK.newCondition(); - private LinkedBlockingQueue mPeerQueue = new LinkedBlockingQueue<>(); + private LinkedBlockingQueue 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,6 +171,7 @@ 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; @@ -148,6 +181,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { config.taskWrapper = mTaskWrapper; config.record = record; config.stateHandler = mStateHandler; + config.peerIndex = indexId; if (!config.tempFile.exists()) { FileUtil.createFile(config.tempFile); @@ -163,10 +197,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 partPath = new ArrayList<>(); @@ -213,7 +244,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { /** * 任务状态回调 */ - private IEventListener mListener; + private M3U8Listener mListener; private long mProgress; //当前总进度 private Looper mLooper; @@ -222,7 +253,7 @@ public class M3U8LiveLoader extends BaseM3U8Loader { */ LiveStateManager(Looper looper, IEventListener listener) { mLooper = looper; - mListener = listener; + mListener = (M3U8Listener) listener; } /** @@ -234,6 +265,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 +280,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 +332,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; + } + } } diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java index 3620fc03..17a62bcf 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/live/M3U8LiveUtil.java @@ -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 { diff --git a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java index 19211a57..c88fa5e2 100644 --- a/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java +++ b/M3U8Component/src/main/java/com/arialyy/aria/m3u8/vod/M3U8VodLoader.java @@ -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); + } } /** @@ -446,7 +454,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 +464,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { private int cancelNum = 0; // 已经取消的线程的数 private int stopNum = 0; // 已经停止的线程数 private int failNum = 0; // 失败的线程数 - private long progress; //当前总进度 + private long progress; //当前总进度,百分比进度 private TaskRecord taskRecord; // 任务记录 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, 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 { @@ -572,7 +587,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { } break; case STATE_RUNNING: - progress += (long) msg.obj; + //progress += (long) msg.obj; break; } return true; @@ -596,6 +611,7 @@ public class M3U8VodLoader extends BaseM3U8Loader { int percent = completeNum * 100 / taskRecord.threadRecords.size(); getEntity().setPercent(percent); getEntity().update(); + progress = percent; } @Override public boolean isFail() { @@ -625,9 +641,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 partPath = new ArrayList<>(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java index 05a01a41..2a04816f 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/download/M3U8Entity.java @@ -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(); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java index fdd80a2f..0b1999a8 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/group/AbsGroupUtil.java @@ -80,7 +80,7 @@ public abstract class AbsGroupUtil implements IUtil, Runnable { /** * 初始化组合任务状态 */ - protected void initState() { + private void initState() { mState = new GroupRunState(getWrapper().getKey(), mListener, mGTWrapper.getSubTaskWrapper().size(), mSubQueue); for (DTaskWrapper wrapper : mGTWrapper.getSubTaskWrapper()) { diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java index 1fd67739..e778b5f6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/NormalLoader.java @@ -200,8 +200,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(); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java index b7fec6fa..1e6fc16a 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/loader/ThreadStateManager.java @@ -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: diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java index 1515e27a..2865781b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsNormalLoaderAdapter.java @@ -22,7 +22,7 @@ import com.arialyy.aria.util.CommonUtil; import java.io.File; /** - * 但文件任务适配器 + * 单文件任务适配器 * * @Author lyy * @Date 2019-09-19 diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java index f9d9679a..4869426c 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/AbsThreadTaskAdapter.java @@ -97,7 +97,7 @@ public abstract class AbsThreadTaskAdapter implements IThreadTaskAdapter { return mThreadConfig; } - @Override public void setThreadStateObserver(IThreadTaskObserver observer) { + @Override public void attach(IThreadTaskObserver observer) { mObserver = observer; } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java index f03deb65..1e1a1102 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/IThreadTaskAdapter.java @@ -36,7 +36,7 @@ public interface IThreadTaskAdapter { void setMaxSpeed(int speed); /** - * 设置线程任务状态观察者 + * 注册观察者 */ - void setThreadStateObserver(IThreadTaskObserver observer); + void attach(IThreadTaskObserver observer); } diff --git a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java index 23722c8b..23df702b 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/core/task/ThreadTask.java @@ -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); } /** @@ -335,7 +336,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); diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java index 34149011..28915e3e 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/CheckUtil.java @@ -17,6 +17,9 @@ 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.orm.DbEntity; import java.lang.reflect.Modifier; import java.util.List; @@ -27,6 +30,27 @@ import java.util.List; public class 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 */ diff --git a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java index a76cf20d..5ce2fba6 100644 --- a/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java +++ b/PublicComponent/src/main/java/com/arialyy/aria/util/RecordUtil.java @@ -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(); + } } /** diff --git a/README.md b/README.md index f7fe2dae..a1adfe46 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ 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) -[![Core](https://img.shields.io/badge/Core-3.7.4-blue)](https://github.com/AriaLyy/Aria) -[![Compiler](https://img.shields.io/badge/Compiler-3.7.4-blue)](https://github.com/AriaLyy/Aria) -[![FtpComponent](https://img.shields.io/badge/FtpComponent-3.7.4-orange)](https://github.com/AriaLyy/Aria) -[![M3U8Component](https://img.shields.io/badge/M3U8Component-3.7.4-orange)](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.5-blue)](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.5-orange)](https://github.com/AriaLyy/Aria) ```java -implementation 'com.arialyy.aria:core:3.7.4' -annotationProcessor 'com.arialyy.aria:compiler:3.7.4' -implementation 'com.arialyy.aria:ftpComponent:3.7.4' # 如果需要使用ftp,请增加该组件 -implementation 'com.arialyy.aria:m3u8Component:3.7.4' # 如果需要使用m3u8下载功能,请增加该组件 +implementation 'com.arialyy.aria:core:3.7.5' +annotationProcessor 'com.arialyy.aria:compiler:3.7.5' +implementation 'com.arialyy.aria:ftpComponent:3.7.5' # 如果需要使用ftp,请增加该组件 +implementation 'com.arialyy.aria:m3u8Component:3.7.5' # 如果需要使用m3u8下载功能,请增加该组件 ``` 如果出现android support依赖错误,请将 `compile 'com.arialyy.aria:core:'`替换为 ``` @@ -137,10 +137,11 @@ protected void onCreate(Bundle savedInstanceState) { ### 版本日志 - + v_3.7.4 (2019/11/2) - - 修复一个class被莫名改变的问题 - - 修复非分块模式下导致的一个下载失败问题 - - fix bug https://github.com/AriaLyy/Aria/issues/493 ++ 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创建索引不成功的问题 [更多版本记录](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)。
## 打赏 - 如果觉得框架写的不错并且帮助到了你,可以请我喝杯热饮
+ 如果觉得框架写的不错并且帮助到了你,可以请我喝杯热茶。`^_^`
diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java new file mode 100644 index 00000000..858cf640 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/AbsSFtpFileInfoThread.java @@ -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 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; + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java similarity index 59% rename from SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java rename to SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java index 6c8b638c..1fa1c1d8 100644 --- a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpLogin.java +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/SFtpUtil.java @@ -16,24 +16,39 @@ 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登录 + * sftp工具类 * * @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 int port; private Session session; private boolean isLogin = false; - private SFtpLogin() { + private SFtpUtil() { createClient(); } @@ -55,6 +70,7 @@ public class SFtpLogin { config.put("StrictHostKeyChecking", "no"); session.setConfig(config);// 为Session对象设置properties session.setTimeout(3000);// 设置超时 + login(); isLogin = true; } catch (JSchException e) { e.printStackTrace(); @@ -83,6 +99,65 @@ public class SFtpLogin { 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; @@ -107,8 +182,8 @@ public class SFtpLogin { return this; } - public SFtpLogin build() { - SFtpLogin login = new SFtpLogin(); + public SFtpUtil build() { + SFtpUtil login = new SFtpUtil(); login.ip = ip; login.userName = userName; login.password = password; diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java new file mode 100644 index 00000000..ba0e61e8 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderAdapter.java @@ -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; + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java new file mode 100644 index 00000000..5b691cf9 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDLoaderUtil.java @@ -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(); + } + }); + } +} diff --git a/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java new file mode 100644 index 00000000..e4f4caf2 --- /dev/null +++ b/SFtpComponent/src/main/java/com/arialyy/aria/sftp/download/SFtpDThreadTaskAdapter.java @@ -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() { + + } +} diff --git a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java index 046eb007..800ec4e3 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/DownloadModule.java @@ -71,6 +71,29 @@ public class DownloadModule extends BaseModule { return list; } + /** + * 创建m3u8下载地址 + */ + public List 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 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; diff --git a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java index 64c0b12c..48a9f3cb 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/SingleTaskActivity.java @@ -215,6 +215,7 @@ public class SingleTaskActivity extends BaseActivity { @Download.onTaskResume void taskResume(DownloadTask task) { + ALog.d(TAG, "resume"); if (task.getKey().equals(mUrl)) { getBinding().setStateStr(getString(R.string.stop)); } diff --git a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java index 8f8c0c1c..a5fb594f 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/group/FTPDirDownloadActivity.java @@ -24,12 +24,14 @@ import com.arialyy.aria.core.common.FtpOption; import com.arialyy.aria.core.download.DownloadGroupEntity; import com.arialyy.aria.core.inf.IEntity; 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.T; import com.arialyy.simple.R; import com.arialyy.simple.base.BaseActivity; import com.arialyy.simple.databinding.ActivityDownloadGroupBinding; import com.arialyy.simple.widget.SubStateLinearLayout; +import java.security.AlgorithmConstraints; /** * Created by lyy on 2017/7/6. @@ -128,6 +130,7 @@ public class FTPDirDownloadActivity extends BaseActivity public void onClick(View view) { switch (view.getId()) { case R.id.start: - startD(); - if (!AppUtil.chekEntityValid(mEntity)) { - startD(); - break; - } if (Aria.download(this).load(mEntity.getId()).isRunning()) { Aria.download(this).load(mEntity.getId()).stop(); } else { - Aria.download(this).load(mEntity.getId()) - .m3u8LiveOption(getLiveoption()) - .resume(); + startD(); } break; case R.id.cancel: @@ -245,6 +238,7 @@ public class M3U8LiveDLoadActivity extends BaseActivity private M3U8LiveOption getLiveoption() { M3U8LiveOption option = new M3U8LiveOption(); option + //.generateIndexFile() .setLiveTsUrlConvert(new LiveTsUrlConverter()); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java index f04b6041..3f27fcfd 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodDLoadActivity.java @@ -90,10 +90,10 @@ public class M3U8VodDLoadActivity extends BaseActivity { getBinding().setFilePath(entity.getFilePath()); mUrl = entity.getUrl(); mFilePath = entity.getFilePath(); - mVideoFragment = new VideoPlayerFragment(0, entity); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(R.id.video_content, mVideoFragment); - ft.commit(); + //mVideoFragment = new VideoPlayerFragment(0, entity); + //FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + //ft.add(R.id.video_content, mVideoFragment); + //ft.commit(); } }); getBinding().setViewModel(this); @@ -176,7 +176,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @M3U8.onPeerComplete void onPeerComplete(String m3u8Url, String peerPath, int peerIndex) { //ALog.d(TAG, "peer complete, path: " + peerPath + ", index: " + peerIndex); - mVideoFragment.addPlayer(peerIndex, peerPath); + //mVideoFragment.addPlayer(peerIndex, peerPath); } @M3U8.onPeerFail @@ -217,6 +217,7 @@ public class M3U8VodDLoadActivity extends BaseActivity { @Download.onTaskResume void taskResume(DownloadTask task) { + ALog.d(TAG, "m3u8 vod resume"); if (task.getKey().equals(mUrl)) { getBinding().setStateStr(getString(R.string.stop)); } @@ -303,9 +304,10 @@ public class M3U8VodDLoadActivity extends BaseActivity { private M3U8VodOption getM3U8Option() { M3U8VodOption option = new M3U8VodOption(); option - .generateIndexFile() - .setVodTsUrlConvert(new VodTsUrlConverter()) - .setMergeHandler(new TsMergeHandler()); + //.generateIndexFile() + //.merge(true) + .setVodTsUrlConvert(new VodTsUrlConverter()); + //.setMergeHandler(new TsMergeHandler()); //.setBandWidthUrlConverter(new BandWidthUrlConverter(mUrl)); return option; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java index d1fec3c2..0a9c37cf 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java +++ b/app/src/main/java/com/arialyy/simple/core/download/m3u8/M3U8VodModule.java @@ -35,10 +35,12 @@ public class M3U8VodModule extends BaseViewModule { //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://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 = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() - + "/道士下山.ts"; + //+ "/道士下山.ts"; + + "/bb1.ts"; private MutableLiveData liveData = new MutableLiveData<>(); private DownloadEntity singDownloadInfo; diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java index ef05d29a..54b05d65 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/DownloadAdapter.java @@ -28,7 +28,7 @@ import com.arialyy.aria.core.common.AbsEntity; 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.wrapper.AbsTaskWrapper; +import com.arialyy.aria.core.wrapper.ITaskWrapper; import com.arialyy.aria.util.CommonUtil; import com.arialyy.simple.R; import com.arialyy.simple.base.adapter.AbsHolder; @@ -152,12 +152,19 @@ public class DownloadAdapter extends AbsRVAdapter convert(String m3u8Url, List tsUrls) { + List temp = new ArrayList<>(); + for (String tsUrl : tsUrls){ + temp.add("http://qn.shytong.cn" + tsUrl); + } + + return temp; + } + } } diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java index b6d5ca52..b22febd8 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/FileListEntity.java @@ -22,7 +22,13 @@ package com.arialyy.simple.core.download.mutil; public class FileListEntity { public String name, key, downloadPath; - public boolean isGroup = false; + + /** + * 0:普通任务 + * 1:组合任务 + * 2:m3u8任务 + */ + public int type = 0; public String[] urls, names; } diff --git a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java index b740822a..b54bce13 100644 --- a/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java +++ b/app/src/main/java/com/arialyy/simple/core/download/mutil/MultiDownloadActivity.java @@ -113,7 +113,7 @@ public class MultiDownloadActivity extends BaseActivity { setTitle("多任务下载"); mData.addAll(getModule(DownloadModule.class).createGroupTestList()); mData.addAll(getModule(DownloadModule.class).createMultiTestList()); + mData.addAll(getModule(DownloadModule.class).createM3u8TestList()); mAdapter = new FileListAdapter(this, mData); mList = getBinding().list; mBar = findViewById(R.id.toolbar); diff --git a/build.gradle b/build.gradle index 5bd0ce9c..4ba0e57e 100644 --- a/build.gradle +++ b/build.gradle @@ -44,8 +44,8 @@ task clean(type: Delete) { } ext { - versionCode = 374 - versionName = '3.7.4' + versionCode = 375 + versionName = '3.7.5' userOrg = 'arialyy' groupId = 'com.arialyy.aria' publishVersion = versionName