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