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