Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: decode decrypted cover #2314

Merged
merged 3 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app/src/main/assets/help/ruleHelp.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,5 +155,8 @@ let options = {
> 可直接填写链接或者JavaScript,如果执行结果是字符串链接将会自动打开浏览器

* 图片解密
> 适用于图片需要二次解密的情况,直接填写JavaScript,返回解密后的bytes
> 部分变量说明:java(仅支持[js扩展类](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/help/JsExtensions.kt)),result为待解密图片的bytes,src为图片链接
> 适用于图片需要二次解密的情况,直接填写JavaScript,返回解密后的`ByteArray`
> 部分变量说明:java(仅支持[js扩展类](https://github.com/gedoor/legado/blob/master/app/src/main/java/io/legado/app/help/JsExtensions.kt)),result为待解密图片的`ByteArray`,src为图片链接

* 封面解密
> 同图片解密 其中result为待解密封面的`inputStream`
5 changes: 3 additions & 2 deletions app/src/main/java/io/legado/app/data/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ val appDb by lazy {
}

@Database(
version = 53,
version = 54,
exportSchema = true,
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
Expand All @@ -37,7 +37,8 @@ val appDb by lazy {
AutoMigration(from = 49, to = 50),
AutoMigration(from = 50, to = 51),
AutoMigration(from = 51, to = 52),
AutoMigration(from = 52, to = 53)
AutoMigration(from = 52, to = 53),
AutoMigration(from = 53, to = 54),
]
)
abstract class AppDatabase : RoomDatabase() {
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/io/legado/app/data/entities/BookSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ data class BookSource(
override var loginUi: String? = null,
// 登录检测js
var loginCheckJs: String? = null,
// 封面解密js
var coverDecodeJs: String? = null,
// 注释
var bookSourceComment: String? = null,
// 自定义变量说明
Expand Down Expand Up @@ -206,6 +208,7 @@ data class BookSource(
&& equal(loginUrl, source.loginUrl)
&& equal(loginUi, source.loginUi)
&& equal(loginCheckJs, source.loginCheckJs)
&& equal(coverDecodeJs, source.coverDecodeJs)
&& equal(exploreUrl, source.exploreUrl)
&& equal(searchUrl, source.searchUrl)
&& getSearchRule() == source.getSearchRule()
Expand Down
10 changes: 9 additions & 1 deletion app/src/main/java/io/legado/app/data/entities/RssSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ data class RssSource(
override var loginUi: String? = null,
//登录检测js
var loginCheckJs: String? = null,
//封面解密js
var coverDecodeJs: String? = null,
var sortUrl: String? = null,
var singleUrl: Boolean = false,
/*列表规则*/
Expand All @@ -58,6 +60,9 @@ data class RssSource(
var enableJs: Boolean = true,
var loadWithBaseUrl: Boolean = true,
/*其它规则*/
// 最后更新时间,用于排序
@ColumnInfo(defaultValue = "0")
var lastUpdateTime: Long = 0,
var customOrder: Int = 0
) : Parcelable, BaseSource {

Expand Down Expand Up @@ -90,6 +95,7 @@ data class RssSource(
&& equal(loginUrl, source.loginUrl)
&& equal(loginUi, source.loginUi)
&& equal(loginCheckJs, source.loginCheckJs)
&& equal(coverDecodeJs, source.coverDecodeJs)
&& equal(sortUrl, source.sortUrl)
&& singleUrl == source.singleUrl
&& articleStyle == source.articleStyle
Expand Down Expand Up @@ -174,7 +180,9 @@ data class RssSource(
enableJs = doc.readBool("$.enableJs") ?: true,
loadWithBaseUrl = doc.readBool("$.loadWithBaseUrl") ?: true,
enabledCookieJar = doc.readBool("$.enabledCookieJar") ?: false,
customOrder = doc.readInt("$.customOrder") ?: 0
customOrder = doc.readInt("$.customOrder") ?: 0,
lastUpdateTime = doc.readLong("$.lastUpdateTime") ?: 0L,
coverDecodeJs = doc.readString("$.coverDecodeJs")
)
}
}
Expand Down
34 changes: 11 additions & 23 deletions app/src/main/java/io/legado/app/help/BookHelp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -136,31 +136,19 @@ object BookHelp {
downloadImages.add(src)
val analyzeUrl = AnalyzeUrl(src, source = bookSource)
try {
var bytes = analyzeUrl.getByteArrayAwait()
val bytes = analyzeUrl.getByteArrayAwait()
//某些图片被加密,需要进一步解密
bookSource?.getContentRule()?.imageDecode?.let { imageDecode ->
if (imageDecode.isBlank()) {
return@let
}
kotlin.runCatching {
bookSource.evalJS(imageDecode) {
put("book", book)
put("result", bytes)
put("src", src)
} as ByteArray
}.onSuccess {
bytes = it
}.onFailure {
AppLog.putDebug("${src}解密bytes错误", it)
}
ImageUtils.decode(
src, bytes, isCover = false, bookSource, book
)?.let {
FileUtils.createFileIfNotExist(
downloadDir,
cacheFolderName,
book.getFolderName(),
cacheImageFolderName,
"${MD5Utils.md5Encode16(src)}.${getImageSuffix(src)}"
).writeBytes(it)
}
FileUtils.createFileIfNotExist(
downloadDir,
cacheFolderName,
book.getFolderName(),
cacheImageFolderName,
"${MD5Utils.md5Encode16(src)}.${getImageSuffix(src)}"
).writeBytes(bytes)
} catch (e: Exception) {
AppLog.putDebug("${src}下载错误", e)
} finally {
Expand Down
20 changes: 16 additions & 4 deletions app/src/main/java/io/legado/app/help/glide/OkHttpStreamFetcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,28 @@ import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.util.ContentLengthInputStream
import com.bumptech.glide.util.Preconditions
import io.legado.app.data.appDb
import io.legado.app.data.entities.BaseSource
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.http.addHeaders
import io.legado.app.help.http.okHttpClient
import io.legado.app.utils.isWifiConnect
import io.legado.app.utils.ImageUtils
import okhttp3.Call
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody
import splitties.init.appCtx
import java.io.IOException
import java.io.InputStream
import java.io.ByteArrayInputStream


class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Options) :
DataFetcher<InputStream>, okhttp3.Callback {
private var stream: InputStream? = null
private var responseBody: ResponseBody? = null
private var callback: DataFetcher.DataCallback<in InputStream>? = null
private var source: BaseSource? = null

@Volatile
private var call: Call? = null
Expand All @@ -40,7 +44,7 @@ class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Option
val requestBuilder: Request.Builder = Request.Builder().url(url.toStringUrl())
val headerMap = HashMap<String, String>()
options.get(OkHttpModelLoader.sourceOriginOption)?.let { sourceUrl ->
val source = appDb.bookSourceDao.getBookSource(sourceUrl)
source = appDb.bookSourceDao.getBookSource(sourceUrl)
?: appDb.rssSourceDao.getByKey(sourceUrl)
source?.getHeaderMap(true)?.let {
headerMap.putAll(it)
Expand Down Expand Up @@ -81,9 +85,17 @@ class OkHttpStreamFetcher(private val url: GlideUrl, private val options: Option
override fun onResponse(call: Call, response: Response) {
responseBody = response.body
if (response.isSuccessful) {
val contentLength: Long = Preconditions.checkNotNull(responseBody).contentLength()
stream = ContentLengthInputStream.obtain(responseBody!!.byteStream(), contentLength)
callback?.onDataReady(stream)
val decodeResult = ImageUtils.decode(
url.toStringUrl(), responseBody!!.byteStream(),
isCover = true, source
)
if (decodeResult == null) {
callback?.onLoadFailed(NoStackTraceException("封面二次解密失败"))
} else {
val contentLength: Long = if (decodeResult is ByteArrayInputStream) decodeResult.available().toLong() else Preconditions.checkNotNull(responseBody).contentLength()
stream = ContentLengthInputStream.obtain(decodeResult, contentLength)
callback?.onDataReady(stream)
}
} else {
callback?.onLoadFailed(HttpException(response.message, response.code))
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/io/legado/app/help/source/SourceAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ object SourceAnalyzer {
loginUrl = jsonItem.readString("loginUrl")
loginUi = jsonItem.readString("loginUi")
loginCheckJs = jsonItem.readString("loginCheckJs")
coverDecodeJs = jsonItem.readString("coverDecodeJs")
bookSourceComment = jsonItem.readString("bookSourceComment") ?: ""
bookUrlPattern = jsonItem.readString("ruleBookUrlPattern")
customOrder = jsonItem.readInt("serialNumber") ?: 0
Expand Down Expand Up @@ -165,6 +166,7 @@ object SourceAnalyzer {
sourceAny.loginUi?.toString()
}
source.loginCheckJs = sourceAny.loginCheckJs
source.coverDecodeJs = sourceAny.coverDecodeJs
source.bookSourceComment = sourceAny.bookSourceComment
source.variableComment = sourceAny.variableComment
source.lastUpdateTime = sourceAny.lastUpdateTime
Expand Down Expand Up @@ -236,6 +238,7 @@ object SourceAnalyzer {
var loginUrl: Any? = null, // 登录规则
var loginUi: Any? = null, // 登录UI
var loginCheckJs: String? = null, // 登录检测js
var coverDecodeJs: String? = null, // 封面解密js
var bookSourceComment: String? = "", // 书源注释
var variableComment: String? = null, // 变量说明
var lastUpdateTime: Long = 0, // 最后更新时间,用于排序
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ class BookSourceEditActivity :
add(EditEntity("loginUrl", source?.loginUrl, R.string.login_url))
add(EditEntity("loginUi", source?.loginUi, R.string.login_ui))
add(EditEntity("loginCheckJs", source?.loginCheckJs, R.string.login_check_js))
add(EditEntity("coverDecodeJs", source?.coverDecodeJs, R.string.cover_decode_js))
add(EditEntity("bookUrlPattern", source?.bookUrlPattern, R.string.book_url_pattern))
add(EditEntity("header", source?.header, R.string.source_http_header))
add(EditEntity("variableComment", source?.variableComment, R.string.variable_comment))
Expand Down Expand Up @@ -345,6 +346,7 @@ class BookSourceEditActivity :
"loginUrl" -> source.loginUrl = it.value
"loginUi" -> source.loginUi = it.value
"loginCheckJs" -> source.loginCheckJs = it.value
"coverDecodeJs" -> source.coverDecodeJs = it.value
"bookUrlPattern" -> source.bookUrlPattern = it.value
"header" -> source.header = it.value
"bookSourceComment" -> source.bookSourceComment = it.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class RssSourceEditActivity :
add(EditEntity("loginUrl", source?.loginUrl, R.string.login_url))
add(EditEntity("loginUi", source?.loginUi, R.string.login_ui))
add(EditEntity("loginCheckJs", source?.loginCheckJs, R.string.login_check_js))
add(EditEntity("coverDecodeJs", source?.coverDecodeJs, R.string.cover_decode_js))
add(EditEntity("header", source?.header, R.string.source_http_header))
add(EditEntity("variableComment", source?.variableComment, R.string.variable_comment))
add(EditEntity("concurrentRate", source?.concurrentRate, R.string.concurrent_rate))
Expand Down Expand Up @@ -203,6 +204,7 @@ class RssSourceEditActivity :
"loginUrl" -> source.loginUrl = it.value
"loginUi" -> source.loginUi = it.value
"loginCheckJs" -> source.loginCheckJs = it.value
"coverDecodeJs" -> source.coverDecodeJs = it.value
"header" -> source.header = it.value
"variableComment" -> source.variableComment = it.value
"concurrentRate" -> source.concurrentRate = it.value
Expand Down
69 changes: 69 additions & 0 deletions app/src/main/java/io/legado/app/utils/ImageUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.legado.app.utils

import io.legado.app.constant.AppLog
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BaseSource
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource
import java.io.InputStream
import java.io.ByteArrayInputStream

/**
* 加密图片解密工具
*/
object ImageUtils {

/**
* @param isCover 根据这个执行书源中不同的解密规则
* @return 解密失败返回Null 解密规则为空不处理
*/
fun decode(
src: String, bytes: ByteArray, isCover: Boolean,
source: BaseSource?, book: Book? = null
): ByteArray? {
val ruleJs = getRuleJs(source, isCover)
if (ruleJs.isNullOrBlank()) return bytes
//解密库hutool.crypto ByteArray|InputStream -> ByteArray
return kotlin.runCatching {
source?.evalJS(ruleJs) {
put("book", book)
put("result", bytes)
put("src", src)
} as ByteArray
}.onFailure {
AppLog.putDebug("${src}解密错误", it)
}.getOrNull()
}

fun decode(
src: String, inputStream: InputStream, isCover: Boolean,
source: BaseSource?, book: Book? = null
): InputStream? {
val ruleJs = getRuleJs(source, isCover)
if (ruleJs.isNullOrBlank()) return inputStream
//解密库hutool.crypto ByteArray|InputStream -> ByteArray
return kotlin.runCatching {
val bytes = source?.evalJS(ruleJs) {
put("book", book)
put("result", inputStream)
put("src", src)
} as ByteArray
ByteArrayInputStream(bytes)
}.onFailure {
AppLog.putDebug("${src}解密错误", it)
}.getOrNull()
}


private fun getRuleJs(
source: BaseSource?, isCover: Boolean
): String? {
return when (source) {
is BookSource ->
if (isCover) source?.coverDecodeJs else source?.getContentRule()?.imageDecode
is RssSource -> source?.coverDecodeJs
else -> null
}
}

}
3 changes: 2 additions & 1 deletion app/src/main/res/values-es-rES/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@
<string name="anti_alias">Anti-Aliasing</string>
<string name="pref_anti_alias_summary">Anti-Aliasing when draw picture</string>
<string name="upload_url">上传URL</string>
<string name="download_url_rule">下载URL规则</string>
<string name="download_url_rule">downloadUrlRule(downloadUrls)</string>
<string name="sort_by_respondTime">Ordenar por tiempo de respuesta</string>
<string name="export_success">导出成功</string>
<string name="path">路径</string>
Expand Down Expand Up @@ -1032,4 +1032,5 @@
<string name="ignore_audio_focus_title">忽略音频焦点</string>
<string name="ignore_audio_focus_summary">允许与其他应用同时播放音频</string>
<string name="refresh_sort">刷新分类</string>
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
</resources>
3 changes: 2 additions & 1 deletion app/src/main/res/values-ja-rJP/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@
<string name="anti_alias">Anti-Aliasing</string>
<string name="pref_anti_alias_summary">Anti-Aliasing when draw picture</string>
<string name="upload_url">上传URL</string>
<string name="download_url_rule">下载URL规则</string>
<string name="download_url_rule">downloadUrlRule(downloadUrls)</string>
<string name="sort_by_respondTime">Sort by respond time</string>
<string name="export_success">导出成功</string>
<string name="path">路径</string>
Expand Down Expand Up @@ -1035,4 +1035,5 @@
<string name="ignore_audio_focus_title">忽略音频焦点</string>
<string name="ignore_audio_focus_summary">允许与其他应用同时播放音频</string>
<string name="refresh_sort">刷新分类</string>
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
</resources>
3 changes: 2 additions & 1 deletion app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,7 @@
<string name="anti_alias">Anti-Aliasing</string>
<string name="pref_anti_alias_summary">Anti-Aliasing when draw picture</string>
<string name="upload_url">上传URL</string>
<string name="download_url_rule">下载URL规则</string>
<string name="download_url_rule">downloadUrlRule(downloadUrls)</string>
<string name="sort_by_respondTime">Classificar por tempo de resposta</string>
<string name="export_success">导出成功</string>
<string name="path">路径</string>
Expand Down Expand Up @@ -1035,4 +1035,5 @@
<string name="ignore_audio_focus_title">忽略音频焦点</string>
<string name="ignore_audio_focus_summary">允许与其他应用同时播放音频</string>
<string name="refresh_sort">刷新分类</string>
<string name="cover_decode_js">Decode Cover Js(coverDecodeJs)</string>
</resources>
3 changes: 2 additions & 1 deletion app/src/main/res/values-zh-rHK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@
<string name="anti_alias">抗鋸齒</string>
<string name="pref_anti_alias_summary">繪製圖片時抗鋸齒</string>
<string name="upload_url">上傳URL</string>
<string name="download_url_rule">下載URL規則</string>
<string name="download_url_rule">下载URL规则(downloadUrls)</string>
<string name="sort_by_respondTime">響應時間排序</string>
<string name="export_success">導出成功</string>
<string name="path">路徑</string>
Expand Down Expand Up @@ -1032,4 +1032,5 @@
<string name="ignore_audio_focus_title">忽略音频焦点</string>
<string name="ignore_audio_focus_summary">允许与其他应用同时播放音频</string>
<string name="refresh_sort">刷新分类</string>
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
</resources>
3 changes: 2 additions & 1 deletion app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -874,7 +874,7 @@
<string name="anti_alias">抗鋸齒</string>
<string name="pref_anti_alias_summary">繪製圖片時抗鋸齒</string>
<string name="upload_url">上傳URL</string>
<string name="download_url_rule">下載URL規則</string>
<string name="download_url_rule">下载URL规则(downloadUrls)</string>
<string name="sort_by_respondTime">反應時間排序</string>
<string name="export_success">匯出成功</string>
<string name="path">路徑</string>
Expand Down Expand Up @@ -1034,4 +1034,5 @@
<string name="ignore_audio_focus_title">忽略音频焦点</string>
<string name="ignore_audio_focus_summary">允许与其他应用同时播放音频</string>
<string name="refresh_sort">刷新分类</string>
<string name="cover_decode_js">封面解密(coverDecodeJs)</string>
</resources>
Loading