Parcourir la source

Merge remote-tracking branch 'origin/master'

kunfei il y a 3 ans
Parent
commit
7164bd6a52

+ 3 - 2
app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt

@@ -22,6 +22,7 @@ import io.legado.app.help.AppWebDav
 import io.legado.app.help.book.*
 import io.legado.app.help.book.*
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.help.coroutine.Coroutine
 import io.legado.app.lib.webdav.ObjectNotFoundException
 import io.legado.app.lib.webdav.ObjectNotFoundException
+import io.legado.app.model.analyzeRule.AnalyzeUrl
 import io.legado.app.model.BookCover
 import io.legado.app.model.BookCover
 import io.legado.app.model.ReadBook
 import io.legado.app.model.ReadBook
 import io.legado.app.model.localBook.LocalBook
 import io.legado.app.model.localBook.LocalBook
@@ -242,7 +243,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
             webFiles.clear()
             webFiles.clear()
             val fileName = "${book.name} 作者:${book.author}"
             val fileName = "${book.name} 作者:${book.author}"
             book.downloadUrls!!.map {
             book.downloadUrls!!.map {
-                val mFileName = UrlUtil.getFileName(it) ?: fileName
+                val mFileName = UrlUtil.getFileName(AnalyzeUrl(it, source = bookSource)) ?: fileName
                 WebFile(it, mFileName)
                 WebFile(it, mFileName)
             }
             }
         }.onError {
         }.onError {
@@ -456,7 +457,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
         }
         }
 
 
         // 后缀
         // 后缀
-        val suffix: String = name.substringAfterLast(".")
+        val suffix: String = UrlUtil.getSuffix(name)
 
 
         // txt epub umd pdf等文件
         // txt epub umd pdf等文件
         val isSupported: Boolean = AppPattern.bookFileRegex.matches(name)
         val isSupported: Boolean = AppPattern.bookFileRegex.matches(name)

+ 44 - 11
app/src/main/java/io/legado/app/utils/UrlUtil.kt

@@ -1,5 +1,7 @@
 package io.legado.app.utils
 package io.legado.app.utils
 
 
+import io.legado.app.model.analyzeRule.AnalyzeUrl
+import io.legado.app.constant.AppLog
 import java.net.HttpURLConnection
 import java.net.HttpURLConnection
 import java.net.URL
 import java.net.URL
 import java.net.URLDecoder
 import java.net.URLDecoder
@@ -29,19 +31,33 @@ object UrlUtil {
             .replace("|", "%7C")
             .replace("|", "%7C")
     }
     }
 
 
+
+    /* 阅读定义的url,{urlOption} */
+    fun getFileName(analyzeUrl: AnalyzeUrl): String? {
+        return getFileName(analyzeUrl.url, analyzeUrl.headerMap)
+    }
+
     /**
     /**
-     * 根据网络url获取文件名
+     * 根据网络url获取文件信息 文件名
      */
      */
-    fun getFileName(fileUrl: String): String? {
+    fun getFileName(fileUrl: String, headerMap: Map<String, String>? = null): String? {
+        // 如果获取到后缀可直接截取链接
+        if (getSuffix(fileUrl, "") != "") return fileUrl.substringAfterLast("/")
         return kotlin.runCatching {
         return kotlin.runCatching {
-            var fileName: String
+            var fileName: String = ""
             val url = URL(fileUrl)
             val url = URL(fileUrl)
             val conn: HttpURLConnection = url.openConnection() as HttpURLConnection
             val conn: HttpURLConnection = url.openConnection() as HttpURLConnection
-            // head方式
+            // HEAD方式获取返回头信息
             conn.requestMethod = "HEAD"
             conn.requestMethod = "HEAD"
+            // 下载链接可能还需要书源header才能成功访问
+            headerMap?.forEach { key, value ->
+                conn.setRequestProperty(key, value)
+            }
             conn.connect()
             conn.connect()
 
 
-            // 方法一
+            // val contentLength = conn.getContentLengthLong()
+            // Content-Disposition
+            // 解析文件名 filename= filename*=
             val raw: String? = conn.getHeaderField("Content-Disposition")
             val raw: String? = conn.getHeaderField("Content-Disposition")
             if (raw != null && raw.indexOf("=") > 0) {
             if (raw != null && raw.indexOf("=") > 0) {
                 fileName = raw.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
                 fileName = raw.split("=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
@@ -50,22 +66,39 @@ object UrlUtil {
                         fileName.toByteArray(StandardCharsets.ISO_8859_1),
                         fileName.toByteArray(StandardCharsets.ISO_8859_1),
                         StandardCharsets.UTF_8 //?
                         StandardCharsets.UTF_8 //?
                     )
                     )
-            } else {
-                // 方法二 截取
+            } else if (conn.getHeaderField("Location") != null) {
+                // Location跳转到实际的下载链接
                 var newUrl: String = conn.url.path ?: return null
                 var newUrl: String = conn.url.path ?: return null
                 newUrl = URLDecoder.decode(newUrl, "UTF-8")
                 newUrl = URLDecoder.decode(newUrl, "UTF-8")
-                fileName = newUrl.substringAfterLast("/")
+                if (getSuffix(newUrl, "") != "") {
+                    fileName = newUrl.substringAfterLast("/")
+                }
+            } else {
+                // 其余情况 返回响应头
+                val headers = conn.getHeaderFields()
+                val headersString = buildString {
+                    headers.forEach { key, value ->
+                        value.forEach {
+                            append(key)
+                            append(": ")
+                            append(it)
+                            append("\n")
+                        }
+                    }
+                }
+                AppLog.putDebug("Cannot obtain URL file name:\n$headersString")
             }
             }
             fileName
             fileName
         }.getOrNull()
         }.getOrNull()
     }
     }
 
 
-    fun getSuffix(url: String, default: String): String {
-        val suffix = url.substringAfterLast(".").substringBeforeLast(",")
+    /* 获取合法的文件后缀 */
+    fun getSuffix(str: String, default: String? = null): String {
+        val suffix = str.substringAfterLast(".").substringBeforeLast(",")
         //检查截取的后缀字符是否合法 [a-zA-Z0-9]
         //检查截取的后缀字符是否合法 [a-zA-Z0-9]
         val fileSuffixRegex = Regex("^[a-z\\d]+$", RegexOption.IGNORE_CASE)
         val fileSuffixRegex = Regex("^[a-z\\d]+$", RegexOption.IGNORE_CASE)
         return if (suffix.length > 5 || !suffix.matches(fileSuffixRegex)) {
         return if (suffix.length > 5 || !suffix.matches(fileSuffixRegex)) {
-            default
+            default ?: throw IllegalArgumentException("Cannot find illegal suffix:\n target: $str\nsuffix: $suffix")
         } else {
         } else {
             suffix
             suffix
         }
         }