Merge remote-tracking branch 'origin/master'

This commit is contained in:
gedoor
2022-01-14 14:44:49 +08:00
9 changed files with 223 additions and 48 deletions
@@ -109,6 +109,8 @@ data class BookChapter(
}
fun getAbsoluteURL(): String {
//二级目录解析的卷链接为空 返回目录页的链接
if (url.startsWith(title) && isVolume) return baseUrl
val urlMatcher = AnalyzeUrl.paramPattern.matcher(url)
val urlBefore = if (urlMatcher.find()) url.substring(0, urlMatcher.start()) else url
val urlAbsoluteBefore = NetworkUtils.getAbsoluteURL(baseUrl, urlBefore)
@@ -3,6 +3,9 @@ package io.legado.app.help
import android.net.Uri
import android.util.Base64
import androidx.annotation.Keep
import cn.hutool.crypto.digest.DigestUtil
import cn.hutool.crypto.symmetric.AES
import cn.hutool.crypto.symmetric.DESede
import io.legado.app.BuildConfig
import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.dateFormat
@@ -132,23 +135,23 @@ interface JsExtensions {
return cacheFile(urlStr, 0)
}
/**
/**
* 缓存以文本方式保存的文件 如.js .txt等
* @param urlStr 网络文件的链接
* @param saveTime 缓存时间,单位:秒
* @return 返回缓存后的文件内容
* @param urlStr 网络文件的链接
* @param saveTime 缓存时间,单位:秒
* @return 返回缓存后的文件内容
*/
fun cacheFile(urlStr: String, saveTime: Int = 0): String? {
val key = md5Encode16(urlStr)
val cache = CacheManager.getFile(key)
if (cache.isNullOrBlank()) {
log("首次下载 $urlStr")
val value = ajax(urlStr) ?: return null
CacheManager.putFile(key, value, saveTime)
return value
}
return cache
}
val key = md5Encode16(urlStr)
val cache = CacheManager.getFile(key)
if (cache.isNullOrBlank()) {
log("首次下载 $urlStr")
val value = ajax(urlStr) ?: return null
CacheManager.putFile(key, value, saveTime)
return value
}
return cache
}
/**
*js实现读取cookie
@@ -688,4 +691,172 @@ interface JsExtensions {
return AppConst.androidId
}
/**
* AES解密,算法参数经过Base64加密
*
* @param data 加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 解密后的字符串
*/
fun aesDecodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return AES(
mode,
padding,
Base64.decode(key, Base64.NO_WRAP),
Base64.decode(iv, Base64.NO_WRAP)
).decryptStr(data)
}
/**
* 3DES解密
*
* @param data 加密的字符串
* @param key 密钥
* @param mode 模式
* @param padding 补码方式
* @param iv 加盐
* @return 解密后的字符串
*/
fun tripleDESDecodeStr(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return DESede(mode, padding, key.toByteArray(), iv.toByteArray()).decryptStr(data)
}
/**
* 3DES解密,算法参数经过Base64加密
*
* @param data 加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 解密后的字符串
*/
fun tripleDESDecodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return DESede(
mode,
padding,
Base64.decode(key, Base64.NO_WRAP),
Base64.decode(iv, Base64.NO_WRAP)
).decryptStr(data)
}
/**
* AES加密并转为Base64,算法参数经过Base64加密
*
* @param data 被加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 加密后的Base64
*/
fun aesEncodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return AES(
mode,
padding,
Base64.decode(key, Base64.NO_WRAP),
Base64.decode(iv, Base64.NO_WRAP)
).encryptBase64(data)
}
/**
* 3DES加密并转为Base64
*
* @param data 被加密的字符串
* @param key 密钥
* @param mode 模式
* @param padding 补码方式
* @param iv 加盐
* @return 加密后的Base64
*/
fun tripleDESEncodeBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return DESede(mode, padding, key.toByteArray(), iv.toByteArray()).encryptBase64(data)
}
/**
* 3DES加密并转为Base64,算法参数经过Base64加密
*
* @param data 被加密的字符串
* @param key Base64后的密钥
* @param mode 模式
* @param padding 补码方式
* @param iv Base64后的加盐
* @return 加密后的Base64
*/
fun tripleDESEncodeArgsBase64Str(
data: String,
key: String,
mode: String,
padding: String,
iv: String
): String? {
return DESede(
mode,
padding,
Base64.decode(key, Base64.NO_WRAP),
Base64.decode(iv, Base64.NO_WRAP)
).encryptBase64(data)
}
/**
* 生成摘要,并转为16进制字符串
*
* @param data 被摘要数据
* @param algorithm 签名算法
* @return 16进制字符串
*/
fun digestHex(
data: String,
algorithm: String,
): String? {
return DigestUtil.digester(algorithm).digestHex(data)
}
/**
* 生成摘要,并转为Base64字符串
*
* @param data 被摘要数据
* @param algorithm 签名算法
* @return Base64字符串
*/
fun digestBase64Str(
data: String,
algorithm: String,
): String? {
return Base64.encodeToString(DigestUtil.digester(algorithm).digest(data), Base64.NO_WRAP)
}
}
@@ -193,8 +193,8 @@ object BookChapterList {
}
if (bookChapter.url.isEmpty()) {
if (bookChapter.isVolume) {
bookChapter.url = bookChapter.title
Debug.log(bookSource.bookSourceUrl, "一级目录${index}未获取到url,使用章节标题替代")
bookChapter.url = bookChapter.title + index
Debug.log(bookSource.bookSourceUrl, "一级目录${index}未获取到url,使用${bookChapter.title}${index}替代")
} else {
bookChapter.url = baseUrl
Debug.log(bookSource.bookSourceUrl, "目录${index}未获取到url,使用baseUrl替代")
@@ -261,10 +261,9 @@ object WebBook {
Debug.log(bookSource.bookSourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
return bookChapter.url
}
if(bookChapter.isVolume && bookChapter.url == bookChapter.title) {
//不返回空值,是为了过书源检测
Debug.log(bookSource.bookSourceUrl, "⇒一级目录正文,使用章节标题:${bookChapter.title}")
return bookChapter.title
if(bookChapter.isVolume && bookChapter.url.startsWith(bookChapter.title)) {
Debug.log(bookSource.bookSourceUrl, "⇒一级目录正文不解析规则")
return bookChapter.tag ?: ""
}
return if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
BookContent.analyzeContent(
@@ -144,7 +144,7 @@ class CheckSourceService : BaseService() {
nextChapterUrl = nextChapterUrl,
needSave = false
)
if (content.isBlank()) {
if ( !toc.first().isVolume && content.isBlank()) {
throw NoStackTraceException("正文内容为空")
}
}.timeout(180000L)
@@ -129,7 +129,7 @@ object ChapterProvider {
matcher.appendTail(sb)
text = sb.toString()
val isTitle = index == 0
val isVolumeTitle = bookChapter.isVolume && isTitle && bookChapter.url == bookChapter.title
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
setTypeText(
@@ -147,7 +147,7 @@ object ChapterProvider {
val text = content.substring(start, matcher.start())
if (text.isNotBlank()) {
val isTitle = index == 0
val isVolumeTitle = bookChapter.isVolume && isTitle && bookChapter.url == bookChapter.title
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
setTypeText(
@@ -169,7 +169,7 @@ object ChapterProvider {
val text = content.substring(start, content.length)
if (text.isNotBlank()) {
val isTitle = index == 0
val isVolumeTitle = bookChapter.isVolume && isTitle && bookChapter.url == bookChapter.title
val isVolumeTitle = bookChapter.isVolume && isTitle && contents.size == 1
val textPaint = if (isTitle) titlePaint else contentPaint
if (!(isTitle && ReadBookConfig.titleMode == 2)) {
setTypeText(
@@ -332,7 +332,7 @@ object ChapterProvider {
}
lineIndex == layout.lineCount - 1 -> {
//最后一行
textLine.text = if(isVolumeTitle) "" else "$words\n"
textLine.text = "$words\n"
isLastLine = true
//标题居中
val startX = if (isTitle && ReadBookConfig.titleMode == 1 || isVolumeTitle)
@@ -349,7 +349,7 @@ object ChapterProvider {
}
else -> {
//中间行
textLine.text = if(isVolumeTitle) "" else words
textLine.text = words
addCharsToLineMiddle(
absStartX,
textLine,
@@ -579,7 +579,7 @@ object ChapterProvider {
tPaint.typeface = titleFont
tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.sp.toFloat()
tPaint.isAntiAlias = true
//to do:卷名的标题排版
//正文
val cPaint = TextPaint()
cPaint.color = ReadBookConfig.textColor
cPaint.letterSpacing = ReadBookConfig.letterSpacing