Get file name according to roc 6266

v4
laoyuyu 2 years ago
parent ef754f04cc
commit 1415de11c6
  1. 4
      Http/src/main/java/com/arialyy/aria/http/HttpUtil.kt
  2. 3
      Http/src/main/java/com/arialyy/aria/http/download/HttpBlockThreadInterceptor.kt
  3. 7
      Http/src/main/java/com/arialyy/aria/http/download/HttpDBlockInterceptor.kt
  4. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDCheckInterceptor.kt
  5. 52
      Http/src/main/java/com/arialyy/aria/http/download/HttpDHeaderInterceptor.kt
  6. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDOptionAdapter.kt
  7. 22
      Http/src/main/java/com/arialyy/aria/http/download/HttpDStartController.kt
  8. 2
      Http/src/main/java/com/arialyy/aria/http/download/HttpDStopController.kt
  9. 25
      Http/src/main/java/com/arialyy/aria/http/download/HttpDTaskAdapter.kt
  10. 130
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGCheckInterceptor.kt
  11. 23
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGStartController.kt
  12. 93
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGSubTaskInterceptor.kt
  13. 5
      HttpGroup/src/main/java/com/arialyy/dua/group/HttpDGroupAdapter.kt
  14. 2
      PublicComponent/src/main/java/com/arialyy/aria/core/inf/ITaskAdapter.java
  15. 8
      PublicComponent/src/main/java/com/arialyy/aria/core/task/DownloadGroupTask.java
  16. 39
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskCachePool.kt
  17. 3
      PublicComponent/src/main/java/com/arialyy/aria/core/task/TaskResp.kt
  18. 8
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DEntityDao.kt
  19. 8
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/DGEntityDao.kt
  20. 2
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/MEntityDao.kt
  21. 2
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/RecordDao.kt
  22. 6
      PublicComponent/src/main/java/com/arialyy/aria/orm/dao/UEntityDao.kt
  23. 15
      PublicComponent/src/main/java/com/arialyy/aria/orm/entity/DEntity.kt
  24. 37
      PublicComponent/src/main/java/com/arialyy/aria/util/DuaUtil.kt
  25. 5
      PublicComponent/src/main/java/com/arialyy/aria/util/Ext.kt
  26. 22
      PublicComponent/src/main/java/com/arialyy/aria/util/FileUtils.kt

@ -95,6 +95,10 @@ object HttpUtil {
Timber.e("invalid uri, ${option.savePathUri}") Timber.e("invalid uri, ${option.savePathUri}")
return false return false
} }
if (!FileUtils.uriIsDir(option.savePathUri!!)){
Timber.e("invalid uri, that path not a dir")
return false
}
return true return true
} }
} }

@ -17,7 +17,6 @@ package com.arialyy.aria.http.download
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.IBlockManager import com.arialyy.aria.core.inf.IBlockManager
import com.arialyy.aria.core.task.BlockManager
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.IThreadTask import com.arialyy.aria.core.task.IThreadTask
import com.arialyy.aria.core.task.TaskChain import com.arialyy.aria.core.task.TaskChain
@ -39,7 +38,7 @@ class HttpBlockThreadInterceptor : ITaskInterceptor {
blockManager = chain.blockManager as IBlockManager blockManager = chain.blockManager as IBlockManager
val unfinishedBlockList = blockManager.unfinishedBlockList val unfinishedBlockList = blockManager.unfinishedBlockList
if (unfinishedBlockList.isEmpty()) { if (unfinishedBlockList.isEmpty()) {
return TaskResp(TaskResp.CODE_BLOCK_QUEUE_NULL) return TaskResp(TaskResp.CODE_INTERRUPT)
} }
createThreadTask(unfinishedBlockList, chain) createThreadTask(unfinishedBlockList, chain)
return TaskResp(TaskResp.CODE_SUCCESS) return TaskResp(TaskResp.CODE_SUCCESS)

@ -20,7 +20,6 @@ import com.arialyy.aria.core.inf.IBlockManager
import com.arialyy.aria.core.inf.ITaskManager import com.arialyy.aria.core.inf.ITaskManager
import com.arialyy.aria.core.task.ITask import com.arialyy.aria.core.task.ITask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskCachePool
import com.arialyy.aria.core.task.TaskChain import com.arialyy.aria.core.task.TaskChain
import com.arialyy.aria.core.task.TaskResp import com.arialyy.aria.core.task.TaskResp
import com.arialyy.aria.http.HttpTaskOption import com.arialyy.aria.http.HttpTaskOption
@ -51,13 +50,13 @@ internal class HttpDBlockInterceptor : ITaskInterceptor {
option = task.getTaskOption(HttpTaskOption::class.java) option = task.getTaskOption(HttpTaskOption::class.java)
if (task.taskState.fileSize < 0) { if (task.taskState.fileSize < 0) {
Timber.e("file size < 0") Timber.e("file size < 0")
return TaskResp(TaskResp.CODE_GET_FILE_INFO_FAIL) return TaskResp(TaskResp.CODE_INTERRUPT)
} }
val savePath = FileUri.getPathByUri(task.getTaskOption(HttpTaskOption::class.java).savePathUri) val savePath = FileUri.getPathByUri(task.getTaskOption(HttpTaskOption::class.java).savePathUri)
if (savePath.isNullOrEmpty()) { if (savePath.isNullOrEmpty()) {
Timber.e("saveUri is null") Timber.e("saveUri is null")
return TaskResp(TaskResp.CODE_SAVE_URI_NULL) return TaskResp(TaskResp.CODE_INTERRUPT)
} }
// if task not support resume, don't save record // if task not support resume, don't save record
@ -95,7 +94,7 @@ internal class HttpDBlockInterceptor : ITaskInterceptor {
*/ */
private suspend fun checkRecord(): Int { private suspend fun checkRecord(): Int {
val recordDao = DuaContext.getServiceManager().getDbService().getDuaDb().getRecordDao() val recordDao = DuaContext.getServiceManager().getDbService().getDuaDb().getRecordDao()
val recordWrapper = recordDao.getTaskRecordByKey(task.filePath) val recordWrapper = recordDao.queryTaskRecordByKey(task.filePath)
if (recordWrapper == null) { if (recordWrapper == null) {
Timber.i("record not found, create record") Timber.i("record not found, create record")

@ -36,7 +36,7 @@ class HttpDCheckInterceptor : ITaskInterceptor {
private suspend fun findDEntityBySavePath(option: HttpTaskOption): DEntity { private suspend fun findDEntityBySavePath(option: HttpTaskOption): DEntity {
val savePath = option.savePathUri val savePath = option.savePathUri
val dao = DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao() val dao = DuaContext.getServiceManager().getDbService().getDuaDb().getDEntityDao()
val de = dao.getDEntityBySavePath(savePath.toString()) val de = dao.queryDEntityBySavePath(savePath.toString())
if (de != null) { if (de != null) {
return de return de
} }

@ -29,6 +29,8 @@ import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.http.HttpUtil import com.arialyy.aria.http.HttpUtil
import com.arialyy.aria.http.request.IRequest import com.arialyy.aria.http.request.IRequest
import com.arialyy.aria.orm.entity.BlockRecord import com.arialyy.aria.orm.entity.BlockRecord
import com.arialyy.aria.orm.entity.DEntity
import com.arialyy.aria.util.DuaUtil
import com.arialyy.aria.util.FileUtils import com.arialyy.aria.util.FileUtils
import timber.log.Timber import timber.log.Timber
import java.io.BufferedReader import java.io.BufferedReader
@ -45,6 +47,7 @@ import java.util.UUID
internal class HttpDHeaderInterceptor : ITaskInterceptor { internal class HttpDHeaderInterceptor : ITaskInterceptor {
private lateinit var task: ITask private lateinit var task: ITask
private lateinit var taskOption: HttpTaskOption private lateinit var taskOption: HttpTaskOption
private var disposition: String? = null
companion object { companion object {
private val CODE_30X = listOf( private val CODE_30X = listOf(
@ -75,6 +78,12 @@ internal class HttpDHeaderInterceptor : ITaskInterceptor {
getOptionAdapter().isSupportResume = fileSize != 0L getOptionAdapter().isSupportResume = fileSize != 0L
getOptionAdapter().isSupportBlock = getOptionAdapter().isSupportBlock =
getOptionAdapter().isSupportResume && fileSize > BlockRecord.BLOCK_SIZE getOptionAdapter().isSupportResume && fileSize > BlockRecord.BLOCK_SIZE
val fileName =
getFileName(if (taskOption.redirectUrl.isNullOrEmpty()) taskOption.sourUrl!! else taskOption.redirectUrl!!)
(task.taskState.entity as DEntity?)?.let {
it.fileName = fileName
it.update()
}
return chain.proceed(task) return chain.proceed(task)
} }
} catch (e: IOException) { } catch (e: IOException) {
@ -83,7 +92,7 @@ internal class HttpDHeaderInterceptor : ITaskInterceptor {
chain.getTask().getTaskOption(HttpTaskOption::class.java).sourUrl chain.getTask().getTaskOption(HttpTaskOption::class.java).sourUrl
}" }"
) )
return TaskResp(TaskResp.CODE_GET_FILE_INFO_FAIL) return TaskResp(TaskResp.CODE_INTERRUPT)
} }
Timber.e("can't get fileSize") Timber.e("can't get fileSize")
return TaskResp(TaskResp.CODE_INTERRUPT) return TaskResp(TaskResp.CODE_INTERRUPT)
@ -102,10 +111,51 @@ internal class HttpDHeaderInterceptor : ITaskInterceptor {
return handleConnect(conn) return handleConnect(conn)
} }
/**
* get file name
* if already set file name, use it.
* if no file name set, will try to get the file name
*/
private fun getFileName(url: String): String {
if (getOptionAdapter().fileName?.isNotEmpty() == true) {
return getOptionAdapter().fileName!!
}
// step1, get file name from header
if (!disposition.isNullOrEmpty()) {
val tempName = getFileNameFromContentDisposition(disposition!!)
if (!tempName.isNullOrEmpty()) {
Timber.d("get file name from disposition")
return tempName
}
}
// step2. get file name from url
val fileName = FileUtils.getFileNameFromUrl(url)
if (!fileName.isNullOrEmpty()) {
Timber.d("get file name from url")
return fileName
}
// step3. get file name from md5
val md5Name = DuaUtil.getMD5Hash(url)
Timber.d("use the md5 code of the url to get the file name, $md5Name")
return md5Name
}
/**
* [RFC6266](https://httpwg.org/specs/rfc6266.html#rfc.section.4)
*/
private fun getFileNameFromContentDisposition(contentDisposition: String): String? {
val regex = Regex("filename=\"(.+?)\"")
val matchResult = regex.find(contentDisposition)
return matchResult?.groupValues?.getOrNull(1)
}
@Throws(IOException::class) @Throws(IOException::class)
private fun handleConnect(conn: HttpURLConnection): Long { private fun handleConnect(conn: HttpURLConnection): Long {
val code = conn.responseCode val code = conn.responseCode
disposition = conn.getHeaderField("Content-Disposition")
when { when {
code == HttpURLConnection.HTTP_PARTIAL -> return getFileSizeFromHeader(conn.headerFields) code == HttpURLConnection.HTTP_PARTIAL -> return getFileSizeFromHeader(conn.headerFields)
code == HttpURLConnection.HTTP_OK -> { code == HttpURLConnection.HTTP_OK -> {

@ -39,4 +39,6 @@ class HttpDOptionAdapter : IHttpTaskOptionAdapter {
*/ */
var isSupportBlock = true var isSupportBlock = true
var fileName: String? = null
} }

@ -37,11 +37,11 @@ import java.net.HttpURLConnection
* @Date 12:38 PM 2023/1/22 * @Date 12:38 PM 2023/1/22
**/ **/
class HttpDStartController(target: Any, val url: String) : HttpBaseStartController(target) { class HttpDStartController(target: Any, val url: String) : HttpBaseStartController(target) {
private val taskOptionSupport = HttpDOptionAdapter() private val taskOptionAdapter = HttpDOptionAdapter()
init { init {
httpTaskOption.sourUrl = url httpTaskOption.sourUrl = url
httpTaskOption.taskOptionAdapter = taskOptionSupport httpTaskOption.taskOptionAdapter = taskOptionAdapter
} }
override fun setTaskInterceptor(taskInterceptor: ITaskInterceptor): HttpDStartController { override fun setTaskInterceptor(taskInterceptor: ITaskInterceptor): HttpDStartController {
@ -60,7 +60,7 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseStartControll
* Maybe the server has special rules, you need set [IHttpFileLenAdapter] to get the file length from [HttpURLConnection.getHeaderFields] * Maybe the server has special rules, you need set [IHttpFileLenAdapter] to get the file length from [HttpURLConnection.getHeaderFields]
*/ */
fun setHttpFileLenAdapter(adapter: IHttpFileLenAdapter): HttpDStartController { fun setHttpFileLenAdapter(adapter: IHttpFileLenAdapter): HttpDStartController {
taskOptionSupport.fileSizeAdapter = adapter taskOptionAdapter.fileSizeAdapter = adapter
return this return this
} }
@ -73,7 +73,19 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseStartControll
} }
/** /**
* set file save path, eg: /mnt/sdcard/Downloads/test.zip * set file name
*/
fun setFileName(fileName: String): HttpDStartController {
taskOptionAdapter.fileName = fileName
return this
}
/**
* set file save path, if you don't set the [setFileName],
* it will automatically try to get the file name, and if it fails,
* the md5 code of the url will be used
* eg: /mnt/sdcard/Downloads/
*
*/ */
fun setSavePath(savePath: Uri): HttpDStartController { fun setSavePath(savePath: Uri): HttpDStartController {
httpTaskOption.savePathUri = savePath httpTaskOption.savePathUri = savePath
@ -84,7 +96,7 @@ class HttpDStartController(target: Any, val url: String) : HttpBaseStartControll
if (HttpUtil.checkHttpDParams(httpTaskOption)) { if (HttpUtil.checkHttpDParams(httpTaskOption)) {
throw IllegalArgumentException("invalid params") throw IllegalArgumentException("invalid params")
} }
val tempTask = TaskCachePool.getTaskByUrl(url) val tempTask = TaskCachePool.getTaskByKey(url)
if (tempTask != null) { if (tempTask != null) {
return tempTask as DownloadTask return tempTask as DownloadTask
} }

@ -56,7 +56,7 @@ class HttpDStopController(val taskId: Int) {
Timber.e("task not found, taskId: $taskId") Timber.e("task not found, taskId: $taskId")
return return
} }
val util = TaskCachePool.getTaskAdapter(Uri.parse(task.filePath)) val util = task.adapter
if (util == null) { if (util == null) {
Timber.e("resume fail, please restart task, taskId: $taskId") Timber.e("resume fail, please restart task, taskId: $taskId")
return return

@ -99,29 +99,4 @@ internal class HttpDTaskAdapter : AbsTaskAdapter() {
Looper.loop() Looper.loop()
} }
} }
override fun resume() {
getTask().getTaskOption(HttpTaskOption::class.java).taskInterceptor.let {
if (it.isNotEmpty()) {
addInterceptors(it)
}
}
DuaContext.duaScope.launch(Dispatchers.IO) {
Looper.prepare()
blockManager?.setLooper()
addCoreInterceptor(TimerInterceptor())
addCoreInterceptor(HttpDBlockInterceptor())
addCoreInterceptor(HttpBlockThreadInterceptor())
val resp = interceptor()
if (resp == null || resp.code != TaskResp.CODE_SUCCESS) {
getTask().getTaskOption(HttpTaskOption::class.java).eventListener.onFail(
false,
AriaException("start task fail, task interrupt, code: ${resp?.code ?: TaskResp.CODE_INTERRUPT}")
)
blockManager?.stop()
return@launch
}
Looper.loop()
}
}
} }

@ -15,11 +15,20 @@
*/ */
package com.arialyy.dua.group package com.arialyy.dua.group
import android.net.Uri
import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.task.ITask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskChain import com.arialyy.aria.core.task.TaskChain
import com.arialyy.aria.core.task.TaskResp import com.arialyy.aria.core.task.TaskResp
import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.orm.entity.DEntity
import com.arialyy.aria.orm.entity.DGEntity
import com.arialyy.aria.util.CheckUtil import com.arialyy.aria.util.CheckUtil
import com.arialyy.aria.util.FileUri
import com.arialyy.aria.util.FileUtils
import timber.log.Timber import timber.log.Timber
import java.io.File
/** /**
* 1. Check if the save path is valid * 1. Check if the save path is valid
@ -30,18 +39,117 @@ import timber.log.Timber
**/ **/
internal class HttpDGCheckInterceptor : ITaskInterceptor { internal class HttpDGCheckInterceptor : ITaskInterceptor {
private lateinit var task: ITask
private lateinit var optionAdapter: HttpDGOptionAdapter
private lateinit var taskOption: HttpTaskOption
private val dgDao by lazy {
DuaContext.getServiceManager().getDbService().getDuaDb().getDGEntityDao()
}
override suspend fun interceptor(chain: TaskChain): TaskResp { override suspend fun interceptor(chain: TaskChain): TaskResp {
// if (optionAdapter.subUrl.isEmpty()){ task = chain.getTask()
// Timber.e("sub-task list is empty") taskOption = task.getTaskOption(HttpTaskOption::class.java)
// return -1 optionAdapter = taskOption.getOptionAdapter(HttpDGOptionAdapter::class.java)
// }
// if (checkParams(taskOption.savePathUri)) {
// optionAdapter.subUrl.forEach { return TaskResp(TaskResp.CODE_INTERRUPT)
// if (!CheckUtil.checkUrl(it)){ }
// Timber.e("invalid url: $it")
// return -1 if (checkRecord()) {
// } return TaskResp(TaskResp.CODE_INTERRUPT)
// } }
return chain.proceed(task)
}
/**
* @return false interrupt task
*/
private suspend fun checkRecord(): Boolean {
val savePath = taskOption.savePathUri
val dgE = dgDao.queryDGEntityByPath(savePath.toString())
if (dgE == null) {
createNewEntity()
return true
}
val dir = File(FileUri.getPathByUri(savePath)!!)
if (dir.exists()) {
val subUrl = mutableListOf<String>()
dgE.subList.forEach { subUrl.add(it.sourceUrl) }
val diffUrl = subUrl.subtract(dgE.subList.toSet())
if (diffUrl.isNotEmpty()) {
Timber.e("invalid savePath, the path existed: $savePath, \ndiffUrl: $diffUrl")
return false
}
}
return true
}
/**
* save new record
*/
private suspend fun createNewEntity(): Boolean {
Timber.d("create new task")
// create sub task record
val dgEntity = DGEntity(
savePath = taskOption.savePathUri!!,
urls = optionAdapter.subUrlList.toList(),
subNameList = optionAdapter.subNameList
)
val subTask = mutableListOf<DEntity>()
val dir = File(FileUri.getPathByUri(taskOption.savePathUri)!!)
optionAdapter.subUrlList.forEachIndexed { index, it ->
val subName = if (optionAdapter.subNameList.isNotEmpty()) {
optionAdapter.subNameList[index]
} else {
FileUtils.getFileNameFromUrl(it) ?: "subTask_${index}"
}
val subFile = File(
dir.path,
subName
)
subTask.add(
DEntity(
sourceUrl = it,
savePath = Uri.parse(subFile.toString()),
fileName = subName
)
)
}
dgEntity.subList.addAll(subTask)
dgDao.insert(dgEntity)
return true
}
/**
* check option
* @return false interrupt task
*/
private fun checkParams(savePath: Uri?): Boolean {
if (savePath == null) {
Timber.d("save path is null")
return false
}
if (optionAdapter.subUrlList.isEmpty()) {
Timber.e("sub list is null")
return false
}
optionAdapter.subUrlList.forEach {
if (!CheckUtil.checkUrl(it)) {
Timber.e("invalid url: $it")
return false
}
}
if (optionAdapter.subNameList.isNotEmpty() && optionAdapter.subNameList.size != optionAdapter.subUrlList.size) {
Timber.e("subNameList.size must be consistent subUrlList.size")
return false
}
return true
} }
} }

@ -16,16 +16,15 @@
package com.arialyy.dua.group package com.arialyy.dua.group
import android.net.Uri import android.net.Uri
import com.arialyy.aria.core.command.StartCmd
import com.arialyy.aria.core.task.DownloadGroupTask import com.arialyy.aria.core.task.DownloadGroupTask
import com.arialyy.aria.core.task.ITaskInterceptor import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskCachePool import com.arialyy.aria.core.task.TaskCachePool
import com.arialyy.aria.http.HttpBaseStartController import com.arialyy.aria.http.HttpBaseStartController
import com.arialyy.aria.http.HttpOption import com.arialyy.aria.http.HttpOption
import com.arialyy.aria.http.HttpUtil import com.arialyy.aria.http.HttpUtil
import com.arialyy.aria.util.FileUri
import com.arialyy.aria.util.FileUtils import com.arialyy.aria.util.FileUtils
import timber.log.Timber import timber.log.Timber
import java.io.File
/** /**
* @Author laoyuyu * @Author laoyuyu
@ -69,10 +68,14 @@ class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartContr
return this return this
} }
private fun createTask(): DownloadGroupTask { private fun getTask(createNewTask: Boolean = true): DownloadGroupTask {
if (HttpUtil.checkHttpDParams(httpTaskOption)) { if (HttpUtil.checkHttpDParams(httpTaskOption)) {
throw IllegalArgumentException("invalid params") throw IllegalArgumentException("invalid params")
} }
val temp = TaskCachePool.getTaskByKey(savePath.toString())
if (temp != null) {
return temp as DownloadGroupTask
}
val task = DownloadGroupTask(httpTaskOption) val task = DownloadGroupTask(httpTaskOption)
task.adapter = HttpDGroupAdapter() task.adapter = HttpDGroupAdapter()
TaskCachePool.putTask(task) TaskCachePool.putTask(task)
@ -88,15 +91,9 @@ class HttpDGStartController(target: Any, val savePath: Uri) : HttpBaseStartContr
Timber.e("invalid savePath: $savePath") Timber.e("invalid savePath: $savePath")
return -1 return -1
} }
val dir = File(FileUri.getPathByUri(savePath)!!)
if (dir.exists()) { val task = getTask()
Timber.e("invalid savePath, the path existed: $savePath") val resp = StartCmd(task).executeCmd()
return -1 return if (resp.isInterrupt()) -1 else task.taskId
}
if (optionAdapter.subNameList.isNotEmpty() && optionAdapter.subNameList.size != optionAdapter.subUrlList.size) {
Timber.e("subNameList.size must be consistent subUrlList.size")
return -1
}
return createTask().taskId
} }
} }

@ -1,93 +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.dua.group
import android.net.Uri
import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.task.ITask
import com.arialyy.aria.core.task.ITaskInterceptor
import com.arialyy.aria.core.task.TaskChain
import com.arialyy.aria.core.task.TaskResp
import com.arialyy.aria.http.HttpTaskOption
import com.arialyy.aria.orm.entity.DEntity
import com.arialyy.aria.orm.entity.DGEntity
import com.arialyy.aria.util.FileUri
import timber.log.Timber
import java.io.File
/**
* @Author laoyuyu
* @Description
* @Date 7:27 PM 2023/3/8
**/
internal class HttpDGSubTaskInterceptor : ITaskInterceptor {
private val dgDao by lazy {
DuaContext.getServiceManager().getDbService().getDuaDb().getDGEntityDao()
}
private lateinit var task: ITask
private lateinit var option: HttpTaskOption
private lateinit var dgOption: HttpDGOptionAdapter
override suspend fun interceptor(chain: TaskChain): TaskResp {
task = chain.getTask()
option = task.getTaskOption(HttpTaskOption::class.java)
dgOption = option.getOptionAdapter(HttpDGOptionAdapter::class.java)
if (!checkRecord()) {
return TaskResp(TaskResp.CODE_INTERRUPT)
}
}
/**
* check dg task record, if dfEntiry exist, return false
* otherwise, save new record
*/
private suspend fun checkRecord(): Boolean {
val entity = dgDao.getDGEntityByPath(option.savePathUri.toString())
if (entity != null) {
Timber.e("task already exist, filePath: ${option.savePathUri}")
return false
}
// create sub task record
val dgEntity = DGEntity(
savePath = option.savePathUri!!,
urls = dgOption.subUrlList.toList(),
subNameList = dgOption.subNameList
)
val subTask = mutableListOf<DEntity>()
val dir = File(FileUri.getPathByUri(option.savePathUri)!!)
dgOption.subUrlList.forEachIndexed { index, it ->
val subFile = File(
dir.path,
if (dgOption.subNameList.isNotEmpty()) dgOption.subNameList[index] else "dgTask${index}"
)
subTask.add(
DEntity(
sourceUrl = it,
savePath = Uri.parse(subFile.toString()),
)
)
}
dgEntity.subList.addAll(subTask)
dgDao.insert(dgEntity)
return true
}
}

@ -76,9 +76,10 @@ internal class HttpDGroupAdapter : AbsTaskAdapter() {
DuaContext.duaScope.launch(Dispatchers.IO) { DuaContext.duaScope.launch(Dispatchers.IO) {
Looper.prepare() Looper.prepare()
taskManager.setLooper() taskManager.setLooper()
addCoreInterceptor(HttpDCheckInterceptor()) addCoreInterceptor(HttpDGCheckInterceptor())
addCoreInterceptor(TimerInterceptor()) addCoreInterceptor(TimerInterceptor())
addCoreInterceptor(HttpBlockThreadInterceptor()) addCoreInterceptor()
val resp = interceptor() val resp = interceptor()
if (resp == null || resp.code != TaskResp.CODE_SUCCESS) { if (resp == null || resp.code != TaskResp.CODE_SUCCESS) {
getTask().getTaskOption(HttpTaskOption::class.java).eventListener.onFail( getTask().getTaskOption(HttpTaskOption::class.java).eventListener.onFail(

@ -49,6 +49,4 @@ public interface ITaskAdapter {
* 开始 * 开始
*/ */
void start(); void start();
void resume();
} }

@ -33,6 +33,14 @@ public class DownloadGroupTask extends AbsTask {
return DOWNLOAD_GROUP; return DOWNLOAD_GROUP;
} }
/**
* @return Always return null
*/
@Deprecated
@Override public String getUrl() {
return "";
}
@Override public String getFilePath() { @Override public String getFilePath() {
return Objects.requireNonNull(getTaskOption(TaskOption.class).getSavePathUri()).toString(); return Objects.requireNonNull(getTaskOption(TaskOption.class).getSavePathUri()).toString();
} }

@ -18,7 +18,6 @@ package com.arialyy.aria.core.task
import android.net.Uri import android.net.Uri
import com.arialyy.aria.core.inf.BaseEntity import com.arialyy.aria.core.inf.BaseEntity
import com.arialyy.aria.core.inf.IEntity import com.arialyy.aria.core.inf.IEntity
import com.arialyy.aria.core.inf.ITaskAdapter
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
@ -32,29 +31,38 @@ object TaskCachePool {
* key: taskId * key: taskId
*/ */
private val entityMap = ConcurrentHashMap<Int, BaseEntity>() private val entityMap = ConcurrentHashMap<Int, BaseEntity>()
private val taskAdapterMap = ConcurrentHashMap<Uri, ITaskAdapter>()
/** /**
* task cache map; key: url * task cache map; key: if task is [DownloadGroupTask], it's filePath else it's service url
*/ */
private val taskMap = ConcurrentHashMap<String, ITask>() private val taskMap = ConcurrentHashMap<String, ITask>()
fun removeTask(url: String) { /**
taskMap.remove(url) * @param key if task is [DownloadGroupTask], it's filePath else it's service url
*/
fun removeTask(key: String) {
taskMap.remove(key)
} }
/** /**
* if task is completed, stopped, canceled, return null * if task is completed, stopped, canceled, return null
* @param key if task is [DownloadGroupTask], it's filePath else it's service url
*/ */
fun getTaskByUrl(url: String) = taskMap[url] fun getTaskByKey(key: String) = taskMap[key]
fun getTaskById(taskId: Int): ITask? { fun getTaskById(taskId: Int): ITask? {
return taskMap.values.find { it.taskId == taskId } return taskMap.values.find { it.taskId == taskId }
} }
fun putTask(task: ITask) { fun putTask(task: ITask) {
taskMap[task.url] = task when (task) {
taskAdapterMap[Uri.parse(task.filePath)] = task.adapter is DownloadTask -> {
taskMap[task.url] = task
}
is DownloadGroupTask -> {
taskMap[task.filePath] = task
}
}
} }
/** /**
@ -69,21 +77,6 @@ object TaskCachePool {
return null return null
} }
// /**
// * @param filePath task unique identifier, like: savePath, sourceUrl
// */
// fun putTaskUtil(filePath: Uri, taskUtil: ITaskAdapter) {
// taskAdapterMap[filePath] = taskUtil
// taskUtil.stop()
// }
/**
* @param filePath unique identifier, like: savePath, sourceUrl
*/
fun getTaskAdapter(filePath: Uri): ITaskAdapter? {
return taskAdapterMap[filePath]
}
fun putEntity(taskId: Int, entity: BaseEntity) { fun putEntity(taskId: Int, entity: BaseEntity) {
if (taskId <= 0) { if (taskId <= 0) {
Timber.e("invalid taskId: $taskId") Timber.e("invalid taskId: $taskId")

@ -10,9 +10,6 @@ class TaskResp(val code: Int = CODE_DEF) {
const val CODE_SUCCESS = 1 const val CODE_SUCCESS = 1
const val CODE_INTERRUPT = 999 const val CODE_INTERRUPT = 999
const val CODE_DEF = 0 const val CODE_DEF = 0
const val CODE_SAVE_URI_NULL = 3
const val CODE_GET_FILE_INFO_FAIL = 2
const val CODE_BLOCK_QUEUE_NULL = 4
} }
var fileSize: Long = 0 var fileSize: Long = 0

@ -33,16 +33,16 @@ interface DEntityDao {
@Transaction @Transaction
@Query("SELECT * FROM DEntity") @Query("SELECT * FROM DEntity")
suspend fun getDEntityList(): List<DEntity> suspend fun queryDEntityList(): List<DEntity>
@Query("SELECT * FROM DEntity WHERE :savePath=savePath") @Query("SELECT * FROM DEntity WHERE :savePath=savePath")
suspend fun getDEntityBySavePath(savePath: String): DEntity? suspend fun queryDEntityBySavePath(savePath: String): DEntity?
@Query("SELECT * FROM DEntity WHERE :dId=dId") @Query("SELECT * FROM DEntity WHERE :dId=dId")
suspend fun getDEntityById(did: String): DEntity? suspend fun queryDEntityById(did: String): DEntity?
@Query("SELECT * FROM DEntity WHERE :sourceUrl=sourceUrl") @Query("SELECT * FROM DEntity WHERE :sourceUrl=sourceUrl")
suspend fun getDEntityBySource(sourceUrl: String): DEntity? suspend fun queryDEntityBySource(sourceUrl: String): DEntity?
@Insert @Insert
suspend fun insert(dEntity: DEntity) suspend fun insert(dEntity: DEntity)

@ -35,19 +35,19 @@ interface DGEntityDao {
@Transaction @Transaction
@Query("SELECT * FROM DGEntity") @Query("SELECT * FROM DGEntity")
suspend fun getDGEntityList(): List<DGSubRelation> suspend fun queryDGEntityList(): List<DGSubRelation>
@Transaction @Transaction
@Query("SELECT * FROM DGEntity WHERE :path=savePath") @Query("SELECT * FROM DGEntity WHERE :path=savePath")
suspend fun getDGEntityByPath(path: String): DGSubRelation? suspend fun queryDGEntityByPath(path: String): DGSubRelation?
@Transaction @Transaction
@Query("SELECT * FROM DGEntity WHERE :gid=gid") @Query("SELECT * FROM DGEntity WHERE :gid=gid")
suspend fun getDGEntityByGid(gid: Int): DGSubRelation suspend fun queryDGEntityByGid(gid: Int): DGSubRelation
@Transaction @Transaction
@Query("SELECT * FROM DGEntity WHERE :gid=gid") @Query("SELECT * FROM DGEntity WHERE :gid=gid")
suspend fun getDGList(): List<DGSubRelation> suspend fun queryDGList(): List<DGSubRelation>
@Insert @Insert
suspend fun insertSubList(subList: List<DEntity>) suspend fun insertSubList(subList: List<DEntity>)

@ -32,7 +32,7 @@ import com.arialyy.aria.orm.entity.MKeyInfo
interface MEntityDao { interface MEntityDao {
@Query("SELECT * FROM MEntity WHERE :savePath=savePath") @Query("SELECT * FROM MEntity WHERE :savePath=savePath")
suspend fun getMEntityByPath(savePath: String): MEntity suspend fun queryMEntityByPath(savePath: String): MEntity
@Update @Update
suspend fun update(entity: MEntity) suspend fun update(entity: MEntity)

@ -35,7 +35,7 @@ interface RecordDao {
@Transaction @Transaction
@Query("SELECT * FROM TaskRecord WHERE :key=taskKey") @Query("SELECT * FROM TaskRecord WHERE :key=taskKey")
suspend fun getTaskRecordByKey(key: String): RecordRelation? suspend fun queryTaskRecordByKey(key: String): RecordRelation?
@Insert @Insert
@Deprecated( @Deprecated(

@ -33,13 +33,13 @@ interface UEntityDao {
@Transaction @Transaction
@Query("SELECT * FROM DEntity") @Query("SELECT * FROM DEntity")
suspend fun getUEntityList(): List<UEntity> suspend fun queryUEntityList(): List<UEntity>
@Query("SELECT * FROM DEntity WHERE :uId=uId") @Query("SELECT * FROM DEntity WHERE :uId=uId")
suspend fun getUEntityById(uId: String): UEntity suspend fun queryUEntityById(uId: String): UEntity
@Query("SELECT * FROM UEntity WHERE :filePath=filePath") @Query("SELECT * FROM UEntity WHERE :filePath=filePath")
suspend fun getUEntityBySource(filePath: String): UEntity suspend fun queryUEntityBySource(filePath: String): UEntity
@Insert @Insert
suspend fun insert(uEntity: UEntity) suspend fun insert(uEntity: UEntity)

@ -22,9 +22,11 @@ import androidx.room.PrimaryKey
import androidx.room.TypeConverters import androidx.room.TypeConverters
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import com.arialyy.aria.core.inf.BaseEntity import com.arialyy.aria.core.inf.BaseEntity
import com.arialyy.aria.util.FileUri
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import java.io.File
/** /**
* Download Entity * Download Entity
@ -44,11 +46,22 @@ data class DEntity(
/** /**
* file save path, it's uri * file save path, it's uri
* eg: /mnt/Sdcard/Download/
*/ */
val savePath: Uri, private val savePath: Uri,
var fileName: String = "",
val isSub: Boolean = false val isSub: Boolean = false
) : BaseEntity() { ) : BaseEntity() {
private var dirFile: File? = null
fun getFilePath(): String {
if (dirFile == null) {
dirFile = File(FileUri.getPathByUri(savePath)!!)
}
return File(dirFile, fileName).path
}
override fun update() { override fun update() {
updateTime = System.currentTimeMillis() updateTime = System.currentTimeMillis()

@ -0,0 +1,37 @@
/*
* 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 java.security.MessageDigest
/**
* @Author laoyuyu
* @Description
* @Date 21:38 2023/3/11
**/
object DuaUtil {
fun getMD5Hash(str: String): String {
val md = MessageDigest.getInstance("MD5")
val messageDigest = md.digest(str.toByteArray())
val no = messageDigest.toBigInteger() // 使用 Kotlin 扩展函数将字节数组转换为 BigInteger
var hashText = no.toString(16)
while (hashText.length < 32) {
hashText = "0$hashText"
}
return hashText
}
}

@ -20,9 +20,14 @@ import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import com.arialyy.aria.core.DuaContext import com.arialyy.aria.core.DuaContext
import timber.log.Timber import timber.log.Timber
import java.math.BigInteger
internal const val AUTHORITY = ".andoFileProvider" internal const val AUTHORITY = ".andoFileProvider"
fun ByteArray.toBigInteger(): BigInteger {
return BigInteger(1, this)
}
/** /**
* @return 传入的Uri是否已具备访问权限 (Whether the incoming Uri has access permission) * @return 传入的Uri是否已具备访问权限 (Whether the incoming Uri has access permission)
*/ */

@ -34,6 +34,28 @@ object FileUtils {
RegexOption.IGNORE_CASE RegexOption.IGNORE_CASE
) )
/**
* Check if uri is a folder
* @return true it's folder
*/
fun uriIsDir(uri: Uri): Boolean {
val path = FileUri.getPathByUri(uri)
if (path.isNullOrBlank()) {
throw IllegalArgumentException("invalid uri: ${uri}")
}
return File(path).isDirectory
}
/**
* get file name from url
*/
fun getFileNameFromUrl(url: String?): String? {
if (url == null) return null
val fn = getFileNameFromPath(url) ?: return null
if (!fn.contains(".")) return null
return fn
}
/** /**
* 创建文件 当文件不存在的时候就创建一个文件 如果文件存在先删除原文件然后重新创建一个新文件 * 创建文件 当文件不存在的时候就创建一个文件 如果文件存在先删除原文件然后重新创建一个新文件
* *

Loading…
Cancel
Save