Merge pull request #399 from Celeter/master

web更新
pull/401/head^2
kunfei 4 years ago committed by GitHub
commit 49accb3593
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      app/src/main/assets/updateLog.md
  2. 2
      app/src/main/assets/web/new/css/detail.ef664f74.css
  3. 4
      app/src/main/assets/web/new/index.html
  4. 0
      app/src/main/assets/web/new/js/about.b239b102.js
  5. 2
      app/src/main/assets/web/new/js/app.f319fc97.js
  6. 1
      app/src/main/assets/web/new/js/detail.06210052.js
  7. 1
      app/src/main/assets/web/new/js/detail.11777eca.js
  8. 22
      app/src/main/assets/web/new/precache-manifest.64ecbb9cbd290bcd22476b715a2e6c05.js
  9. 2
      app/src/main/assets/web/new/service-worker.js
  10. 88
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  11. 18
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  12. 65
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  13. 4
      app/src/main/java/io/legado/app/model/rss/RssParser.kt
  14. 27
      app/src/main/java/io/legado/app/utils/StringUtils.kt

@ -3,6 +3,18 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
- 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/09/29**
* 增加了几个方法用于处理文件
```
//文件下载,content为十六进制字符串,url用于生成文件名,返回文件路径
downloadFile(content: String, url: String): String
//文件解压,zipPath为压缩文件路径,返回解压路径
unzipFile(zipPath: String): String
//文件夹内所有文件读取
getTxtInFolder(unzipPath: String): String
```
* 增加type字段,返回16进制字符串,栗:`https://www.baidu.com,{"type":"zip"}`
**2020/09/24**
* 修复规则解析bug

@ -1,3 +1,3 @@
<!DOCTYPE html><html lang=en style="padding: 0;height:100%"><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="favicon.ico" /><![endif]--><title>Legado Bookshelf</title><link href=css/about.dbe575e1.css rel=prefetch><link href=css/detail.9ba76c69.css rel=prefetch><link href=js/about.59a63964.js rel=prefetch><link href=js/about~detail.1caf6ef5.js rel=prefetch><link href=js/detail.11777eca.js rel=prefetch><link href=css/app.e4c919b7.css rel=preload as=style><link href=css/chunk-vendors.ad4ff18f.css rel=preload as=style><link href=js/app.d7843716.js rel=preload as=script><link href=js/chunk-vendors.8dd9045a.js rel=preload as=script><link href=css/chunk-vendors.ad4ff18f.css rel=stylesheet><link href=css/app.e4c919b7.css rel=stylesheet><link rel=icon type=image/png sizes=32x32 href=img/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=img/icons/favicon-16x16.png><link rel=manifest href=manifest.json><meta name=theme-color content=#4DBA87><meta name=apple-mobile-web-app-capable content=no><meta name=apple-mobile-web-app-status-bar-style content=default><meta name=apple-mobile-web-app-title content=yd-web-tool><link rel=apple-touch-icon href=img/icons/apple-touch-icon-152x152.png><link rel=mask-icon href=img/icons/safari-pinned-tab.svg color=#4DBA87><meta name=msapplication-TileImage content=img/icons/msapplication-icon-144x144.png><meta name=msapplication-TileColor content=#000000></head><style>body::-webkit-scrollbar {
<!DOCTYPE html><html lang=en style="padding: 0;height:100%"><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="favicon.ico" /><![endif]--><title>Legado Bookshelf</title><link href=css/about.dbe575e1.css rel=prefetch><link href=css/detail.ef664f74.css rel=prefetch><link href=js/about.b239b102.js rel=prefetch><link href=js/about~detail.1caf6ef5.js rel=prefetch><link href=js/detail.06210052.js rel=prefetch><link href=css/app.e4c919b7.css rel=preload as=style><link href=css/chunk-vendors.ad4ff18f.css rel=preload as=style><link href=js/app.f319fc97.js rel=preload as=script><link href=js/chunk-vendors.8dd9045a.js rel=preload as=script><link href=css/chunk-vendors.ad4ff18f.css rel=stylesheet><link href=css/app.e4c919b7.css rel=stylesheet><link rel=icon type=image/png sizes=32x32 href=img/icons/favicon-32x32.png><link rel=icon type=image/png sizes=16x16 href=img/icons/favicon-16x16.png><link rel=manifest href=manifest.json><meta name=theme-color content=#4DBA87><meta name=apple-mobile-web-app-capable content=no><meta name=apple-mobile-web-app-status-bar-style content=default><meta name=apple-mobile-web-app-title content=yd-web-tool><link rel=apple-touch-icon href=img/icons/apple-touch-icon-152x152.png><link rel=mask-icon href=img/icons/safari-pinned-tab.svg color=#4DBA87><meta name=msapplication-TileImage content=img/icons/msapplication-icon-144x144.png><meta name=msapplication-TileColor content=#000000></head><style>body::-webkit-scrollbar {
display: none;
}</style><body style="margin: 0;height:100%"><noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.8dd9045a.js></script><script src=js/app.d7843716.js></script></body></html>
}</style><body style="margin: 0;height:100%"><noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script src=js/chunk-vendors.8dd9045a.js></script><script src=js/app.f319fc97.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,10 +1,10 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "a77097c019b699bc81ee",
"revision": "1f198e66ea99b77b82a1",
"url": "css/about.dbe575e1.css"
},
{
"revision": "4d729c4b428d537ebd8d",
"revision": "e82f63018b8756547377",
"url": "css/app.e4c919b7.css"
},
{
@ -12,8 +12,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "css/chunk-vendors.ad4ff18f.css"
},
{
"revision": "8f2124417070a994ebbd",
"url": "css/detail.9ba76c69.css"
"revision": "f2d5bf5a45e7429367f8",
"url": "css/detail.ef664f74.css"
},
{
"revision": "535877f50039c0cb49a6196a5b7517cd",
@ -40,28 +40,28 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "img/noCover.b5c48bc1.jpeg"
},
{
"revision": "ad9f43586bb9220e0df71ce8fad92d8b",
"revision": "48e76635babe1b6abdaface8af25dd98",
"url": "index.html"
},
{
"revision": "a77097c019b699bc81ee",
"url": "js/about.59a63964.js"
"revision": "1f198e66ea99b77b82a1",
"url": "js/about.b239b102.js"
},
{
"revision": "2c81bd893f3a92f018d8",
"url": "js/about~detail.1caf6ef5.js"
},
{
"revision": "4d729c4b428d537ebd8d",
"url": "js/app.d7843716.js"
"revision": "e82f63018b8756547377",
"url": "js/app.f319fc97.js"
},
{
"revision": "3e91096748e0f4d6bb89",
"url": "js/chunk-vendors.8dd9045a.js"
},
{
"revision": "8f2124417070a994ebbd",
"url": "js/detail.11777eca.js"
"revision": "f2d5bf5a45e7429367f8",
"url": "js/detail.06210052.js"
},
{
"revision": "b46d04eb43bc31ca0f9f95121646440d",

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"precache-manifest.78eb8adcb8f052b2a72d462abe0dc498.js"
"precache-manifest.64ecbb9cbd290bcd22476b715a2e6c05.js"
);
workbox.core.setCacheNameDetails({prefix: "yd-web-tool"});

@ -3,12 +3,14 @@ package io.legado.app.help
import android.util.Base64
import androidx.annotation.Keep
import io.legado.app.constant.AppConst.dateFormat
import io.legado.app.help.http.SSLHelper
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.htmlFormat
import io.legado.app.utils.msg
import io.legado.app.utils.*
import io.legado.app.utils.EncodingDetect
import org.jsoup.Connection
import org.jsoup.Jsoup
import java.net.URLEncoder
import java.nio.charset.Charset
import java.util.*
@Keep
@ -40,6 +42,84 @@ interface JsExtensions {
}
}
/**
* js实现文件下载
*/
fun downloadFile(content: String, url: String): String {
val zipPath = FileUtils.getPath(
FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
"${MD5Utils.md5Encode16(url)}.zip"
)
FileUtils.deleteFile(zipPath)
val zipFile = FileUtils.createFileIfNotExist(zipPath)
StringUtils.hexStringToByte(content).let {
if (it != null) {
zipFile.writeBytes(it)
}
}
return zipPath
}
/**
* js实现压缩文件解压
*/
fun unzipFile(zipPath: String): String {
val unzipPath = FileUtils.getPath(
FileUtils.createFolderIfNotExist(FileUtils.getCachePath()),
FileUtils.getNameExcludeExtension(zipPath)
)
FileUtils.deleteFile(unzipPath)
val zipFile = FileUtils.createFileIfNotExist(zipPath)
val unzipFolder = FileUtils.createFolderIfNotExist(unzipPath)
ZipUtils.unzipFile(zipFile, unzipFolder)
FileUtils.deleteFile(zipPath)
return unzipPath
}
/**
* js实现文件夹内所有文件读取
*/
fun getTxtInFolder(unzipPath: String): String {
val unzipFolder = FileUtils.createFolderIfNotExist(unzipPath)
val contents = StringBuilder()
unzipFolder.listFiles().let {
if (it != null) {
for (f in it) {
val charsetName = EncodingDetect.getEncode(f)
contents.append(String(f.readBytes(), Charset.forName(charsetName)))
.append("\n")
}
contents.deleteCharAt(contents.length - 1)
}
}
FileUtils.deleteFile(unzipPath)
return contents.toString()
}
/**
* js实现重定向拦截,不能删
*/
fun get(urlStr: String, headers: Map<String, String>): Connection.Response {
return Jsoup.connect(urlStr)
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory)
.ignoreContentType(true)
.followRedirects(false)
.headers(headers)
.method(Connection.Method.GET)
.execute()
}
fun post(urlStr: String, body: String, headers: Map<String, String>): Connection.Response {
return Jsoup.connect(urlStr)
.sslSocketFactory(SSLHelper.unsafeSSLSocketFactory)
.ignoreContentType(true)
.followRedirects(false)
.requestBody(body)
.headers(headers)
.method(Connection.Method.POST)
.execute()
}
/**
* js实现解码,不能删
*/

@ -48,7 +48,11 @@ object HttpHelper {
return null
}
fun getBytes(url: String, queryMap: Map<String, String>, headers: Map<String, String>): ByteArray? {
fun getBytes(
url: String,
queryMap: Map<String, String>,
headers: Map<String, String>
): ByteArray? {
NetworkUtils.getBaseUrl(url)?.let { baseUrl ->
return getByteRetrofit(baseUrl)
.create(HttpGetApi::class.java)
@ -78,16 +82,16 @@ object HttpHelper {
return null
}
inline fun <reified T> getApiService(baseUrl: String, encode: String? = null): T {
return getRetrofit(baseUrl, encode).create(T::class.java)
}
inline fun <reified T> getApiServiceWithProxy(
inline fun <reified T> getApiService(
baseUrl: String,
encode: String? = null,
proxy: String? = null
): T {
return getRetrofitWithProxy(baseUrl, encode, proxy).create(T::class.java)
return if (proxy.isNullOrEmpty()) {
getRetrofit(baseUrl, encode).create(T::class.java)
} else {
getRetrofitWithProxy(baseUrl, encode, proxy).create(T::class.java)
}
}
inline fun <reified T> getBytesApiService(baseUrl: String): T {

@ -60,6 +60,7 @@ class AnalyzeUrl(
private var method = RequestMethod.GET
private val splitUrlRegex = Regex(",\\s*(?=\\{)")
private var proxy: String? = null
private var type: String? = null
init {
baseUrl?.let {
@ -191,6 +192,7 @@ class AnalyzeUrl(
val option = GSON.fromJsonObject<UrlOption>(urlArray[1])
option?.let { _ ->
option.method?.let { if (it.equals("POST", true)) method = RequestMethod.POST }
option.type?.let { type = it }
option.headers?.let { headers ->
if (headers is Map<*, *>) {
headers.forEach { entry ->
@ -304,19 +306,19 @@ class AnalyzeUrl(
method == RequestMethod.POST -> {
if (fieldMap.isNotEmpty()) {
HttpHelper
.getApiService<HttpPostApi>(baseUrl, charset)
.getApiService<HttpPostApi>(baseUrl, charset, proxy)
.postMap(url, fieldMap, headerMap)
} else {
HttpHelper
.getApiService<HttpPostApi>(baseUrl, charset)
.getApiService<HttpPostApi>(baseUrl, charset, proxy)
.postBody(url, requestBody!!, headerMap)
}
}
fieldMap.isEmpty() -> HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset)
.getApiService<HttpGetApi>(baseUrl, charset, proxy)
.get(url, headerMap)
else -> HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset)
.getApiService<HttpGetApi>(baseUrl, charset, proxy)
.getMap(url, fieldMap, headerMap)
}
}
@ -326,6 +328,9 @@ class AnalyzeUrl(
jsStr: String? = null,
sourceRegex: String? = null,
): Res {
if (type != null) {
return Res(url, StringUtils.byteToHexString(getResponseBytes(tag)))
}
if (useWebView) {
val params = AjaxWebView.AjaxParams(url)
params.headerMap = headerMap
@ -343,51 +348,24 @@ class AnalyzeUrl(
val res = when {
method == RequestMethod.POST -> {
if (fieldMap.isNotEmpty()) {
if (proxy == null) {
HttpHelper
.getApiService<HttpPostApi>(baseUrl, charset)
.postMapAsync(url, fieldMap, headerMap)
} else {
HttpHelper
.getApiServiceWithProxy<HttpPostApi>(baseUrl, charset, proxy)
.postMapAsync(url, fieldMap, headerMap)
}
} else {
if (proxy == null) {
HttpHelper
.getApiService<HttpPostApi>(baseUrl, charset)
.postBodyAsync(url, requestBody!!, headerMap)
} else {
HttpHelper
.getApiServiceWithProxy<HttpPostApi>(baseUrl, charset, proxy)
.postBodyAsync(url, requestBody!!, headerMap)
}
}
}
fieldMap.isEmpty() -> {
if (proxy == null) {
HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset)
.getAsync(url, headerMap)
.getApiService<HttpPostApi>(baseUrl, charset, proxy)
.postMapAsync(url, fieldMap, headerMap)
} else {
HttpHelper
.getApiServiceWithProxy<HttpGetApi>(baseUrl, charset, proxy)
.getAsync(url, headerMap)
.getApiService<HttpPostApi>(baseUrl, charset, proxy)
.postBodyAsync(url, requestBody!!, headerMap)
}
}
fieldMap.isEmpty() -> {
HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset, proxy)
.getAsync(url, headerMap)
}
else -> {
if (proxy == null) {
HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset)
.getMapAsync(url, fieldMap, headerMap)
} else {
HttpHelper
.getApiServiceWithProxy<HttpGetApi>(baseUrl, charset, proxy)
.getMapAsync(url, fieldMap, headerMap)
}
HttpHelper
.getApiService<HttpGetApi>(baseUrl, charset, proxy)
.getMapAsync(url, fieldMap, headerMap)
}
}
return Res(NetworkUtils.getUrl(res), res.body())
@ -448,6 +426,7 @@ class AnalyzeUrl(
val webView: Any?,
val headers: Any?,
val body: Any?,
val type: String?
)
}

@ -117,11 +117,11 @@ object RssParser {
private fun getImageUrl(input: String): String? {
var url: String? = null
val patternImg = "(<img .*?>)".toPattern()
val patternImg = "(<img [^>]*>)".toPattern()
val matcherImg = patternImg.matcher(input)
if (matcherImg.find()) {
val imgTag = matcherImg.group(1)
val patternLink = "src\\s*=\\s*\"(.+?)\"".toPattern()
val patternLink = "src\\s*=\\s*\"([^\"]+)\"".toPattern()
val matcherLink = patternLink.matcher(imgTag!!)
if (matcherLink.find()) {
url = matcherLink.group(1)!!.trim()

@ -45,6 +45,7 @@ object StringUtils {
//将时间转换成日期
fun dateConvert(time: Long, pattern: String): String {
val date = Date(time)
@SuppressLint("SimpleDateFormat")
val format = SimpleDateFormat(pattern)
return format.format(date)
@ -292,4 +293,30 @@ object StringUtils {
return buf.toString()
}
fun byteToHexString(bytes: ByteArray?): String {
if (bytes == null) return ""
val sb = StringBuilder(bytes.size * 2)
for (b in bytes) {
val hex = 0xff and b.toInt()
if (hex < 16) {
sb.append('0')
}
sb.append(Integer.toHexString(hex))
}
return sb.toString()
}
fun hexStringToByte(hexString: String): ByteArray? {
val hexStr = hexString.replace(" ", "")
val len = hexStr.length
val bytes = ByteArray(len / 2)
var i = 0
while (i < len) {
// 两位一组,表示一个字节,把这样表示的16进制字符串,还原成一个字节
bytes[i / 2] = ((Character.digit(hexString[i], 16) shl 4) +
Character.digit(hexString[i + 1], 16)).toByte()
i += 2
}
return bytes
}
}

Loading…
Cancel
Save