From 0b739766fa33af9c89c2f363f4cd87cb41616b56 Mon Sep 17 00:00:00 2001 From: fengyuecanzhu <1021300691@qq.com> Date: Thu, 30 Jun 2022 21:17:16 +0800 Subject: [PATCH] fix lanzou api --- .../xyz/fycz/myreader/webapi/LanZouApi.kt | 29 ++-- dynamic/build.gradle | 103 ++++++++++++- .../main/java/xyz/fycz/dynamic/AppLoadImpl.kt | 5 +- .../java/xyz/fycz/dynamic/fix/App246Fix4.kt | 60 ++++++++ .../xyz/fycz/dynamic/fix/AppSubSourceFix.kt | 62 ++++++++ .../xyz/fycz/dynamic/utils/LanZouUtils.kt | 138 ++++++++++++++++++ 6 files changed, 377 insertions(+), 20 deletions(-) create mode 100644 dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix4.kt create mode 100644 dynamic/src/main/java/xyz/fycz/dynamic/fix/AppSubSourceFix.kt create mode 100644 dynamic/src/main/java/xyz/fycz/dynamic/utils/LanZouUtils.kt diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/LanZouApi.kt b/app/src/main/java/xyz/fycz/myreader/webapi/LanZouApi.kt index b4cb5cc..e3481fd 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/LanZouApi.kt +++ b/app/src/main/java/xyz/fycz/myreader/webapi/LanZouApi.kt @@ -158,12 +158,13 @@ object LanZouApi { * @param url * @param password */ - private fun getFileUrl(url: String, password: String = ""): Observable { + fun getFileUrl(url: String, password: String = ""): Observable { return Observable.create { val html = OkHttpUtils.getHtml(url) val url2 = if (password.isEmpty()) { val url1 = getUrl1(html) - val key = getKey(OkHttpUtils.getHtml(url1)) + val data = StringUtils.getSubString(OkHttpUtils.getHtml(url1), "},", "},") + val key = getKeyValueByKey(data, "sign") + "&" + getKeyValueByKey(data, "websignkey") getUrl2(key, url1) } else { getUrl2(StringHelper.getSubString(html, "sign=", "&"), url, password) @@ -177,27 +178,25 @@ object LanZouApi { } } - private fun getUrl1(html: String): String { + fun getUrl1(html: String): String { val doc = Jsoup.parse(html) return URLCONST.LAN_ZOU_URL + doc.getElementsByTag("iframe").attr("src") } - private fun getKey(html: String): String { - var lanzousKeyStart = "var pposturl = '" - val keyName = StringHelper.getSubString(html, "'sign':", ",") - lanzousKeyStart = if (keyName.endsWith("'")) { - "'sign':'" + fun getKeyValueByKey(html: String, key: String): String { + val keyName = StringHelper.getSubString(html, "'$key':", ",") + return if (keyName.endsWith("'")) { + key + "=" + keyName.replace("'", "") } else { - "var $keyName = '" + val lanzousKeyStart = "var $keyName = '" + key + "=" + StringHelper.getSubString(html, lanzousKeyStart, "'") } - return StringHelper.getSubString(html, lanzousKeyStart, "'") } - - private fun getUrl2(key: String, referer: String, password: String = ""): String { + fun getUrl2(key: String, referer: String, password: String = ""): String { val mediaType = "application/x-www-form-urlencoded".toMediaTypeOrNull() val body = if (password.isEmpty()) { - "action=downprocess&sign=$key&ves=1" + "action=downprocess&signs=?ctdf&websign=&ves=1&$key" } else { "action=downprocess&sign=$key&p=$password" } @@ -213,7 +212,7 @@ object LanZouApi { return getUrl2(html) } - private fun getUrl2(o: String): String { + fun getUrl2(o: String): String { /*val info = o.split(",").toTypedArray() val zt = info[0].substring(info[0].indexOf(":") + 1) if (!"1".endsWith(zt)) { @@ -239,7 +238,7 @@ object LanZouApi { * * @param path */ - private fun getRedirectUrl(path: String): String { + fun getRedirectUrl(path: String): String { val conn = URL(path) .openConnection() as HttpURLConnection conn.instanceFollowRedirects = false diff --git a/dynamic/build.gradle b/dynamic/build.gradle index c475cb4..0e68753 100644 --- a/dynamic/build.gradle +++ b/dynamic/build.gradle @@ -48,14 +48,111 @@ android { } dependencies { - compileOnly("androidx.core:core-ktx:$kotlin_version") testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - compileOnly("me.fycz.maple:maple:1.9") - compileOnly 'org.greenrobot:greendao:3.3.0' + // androidx + compileOnly "androidx.core:core-ktx:$kotlin_version" + compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + compileOnly 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' compileOnly 'androidx.appcompat:appcompat:1.3.1' + compileOnly 'androidx.constraintlayout:constraintlayout:2.1.3' + + //anko + def anko_version = '0.10.8' + compileOnly "org.jetbrains.anko:anko-sdk27:$anko_version" + compileOnly "org.jetbrains.anko:anko-sdk27-listeners:$anko_version" + + //Glide + compileOnly 'com.github.bumptech.glide:glide:4.13.1' + + compileOnly 'com.squareup.okhttp3:okhttp:4.9.3' + compileOnly 'com.google.code.gson:gson:2.9.0' + + compileOnly 'com.journeyapps:zxing-android-embedded:3.5.0' + + compileOnly 'org.greenrobot:greendao:3.3.0' + compileOnly 'com.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1' + + //JSoup + compileOnly 'org.jsoup:jsoup:1.14.3' + compileOnly 'cn.wanghaomiao:JsoupXpath:2.5.1' + compileOnly 'com.jayway.jsonpath:json-path:2.7.0' + + //SmartRefreshLayout + compileOnly 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.2' + compileOnly 'com.scwang.smartrefresh:SmartRefreshHeader:1.1.2' + + + compileOnly 'com.google.android.material:material:1.4.0' + + //Scroller + compileOnly 'com.futuremind.recyclerfastscroll:fastscroll:0.2.5' + + //Toasty + compileOnly 'com.github.GrenderG:Toasty:1.5.0' + + //字符串比较 + compileOnly 'net.ricecode:string-similarity:1.0.0' + + compileOnly 'com.jayway.jsonpath:json-path:2.7.0' + //RxAndroid + compileOnly 'io.reactivex.rxjava2:rxjava:2.2.19' + compileOnly 'io.reactivex.rxjava2:rxandroid:2.1.1' + + //ImmersionBar + compileOnly 'com.gyf.immersionbar:immersionbar:3.0.0' + + //简繁转换 + compileOnly 'com.luhuiguo:chinese-utils:1.0' + + //颜色选择 + compileOnly 'com.jaredrummler:colorpicker:1.1.0' + + //二维码 + compileOnly 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7' + + //编码识别 + compileOnly 'com.github.albfernandez:juniversalchardet:2.4.0' + + // 标签 https://github.com/hongyangAndroid/FlowLayout + compileOnly 'com.hyman:flowlayout-lib:1.1.2' + + compileOnly 'com.liulishuo.filedownloader:library:1.7.7' + + //SwipeBackLayout + compileOnly 'me.imid.swipebacklayout.lib:library:1.1.0' + + //JS + //noinspection GradleDependency + compileOnly 'com.github.gedoor:rhino-android:1.6' + + //XXPermissions + compileOnly 'com.github.getActivity:XXPermissions:11.2' + + //epub + compileOnly('com.positiondev.epublib:epublib-core:3.1') { + exclude group: 'org.slf4j' + exclude group: 'xmlpull' + } + + compileOnly 'com.nshmura:recyclertablayout:1.5.0' + + // https://mvnrepository.com/artifact/net.lingala.zip4j/zip4j + compileOnly group: 'net.lingala.zip4j', name: 'zip4j', version: '2.9.1' + + + //协程 + def coroutines_version = '1.5.1' + compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + compileOnly("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version") + + //apache + compileOnly('org.apache.commons:commons-text:1.9') + + //https://github.com/fengyuecanzhu/Maple + compileOnly("me.fycz.maple:maple:1.9") compileOnly project(":app") compileOnly project(":DialogX") } \ No newline at end of file diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt b/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt index 3fe42d5..afc6cbe 100644 --- a/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt +++ b/dynamic/src/main/java/xyz/fycz/dynamic/AppLoadImpl.kt @@ -48,9 +48,10 @@ class AppLoadImpl : IAppLoader { App244Fix::class.java, App244Fix2::class.java, App246Fix::class.java, - App246Fix2::class.java, //AppSubSourceFix::class.java, + App246Fix2::class.java, App246Fix3::class.java, + App246Fix4::class.java, ) override fun onLoad(appParam: AppParam) { @@ -70,7 +71,7 @@ class AppLoadImpl : IAppLoader { } if (sb.isNotEmpty()) { if (sb.endsWith("\n")) sb.substring(0, sb.length - 1) - val key = "fix2022-06-28" + val key = "fix2022-06-30" val hasRead = spu.getBoolean(key, false) if (!hasRead) { announce("插件更新", "更新内容:\n$sb") diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix4.kt b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix4.kt new file mode 100644 index 0000000..f22f3ee --- /dev/null +++ b/dynamic/src/main/java/xyz/fycz/dynamic/fix/App246Fix4.kt @@ -0,0 +1,60 @@ +/* + * This file is part of FYReader. + * FYReader is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FYReader is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FYReader. If not, see . + * + * Copyright (C) 2020 - 2022 fengyuecanzhu + */ + +package xyz.fycz.dynamic.fix + +import me.fycz.maple.MapleBridge +import me.fycz.maple.MapleUtils +import me.fycz.maple.MethodReplacement +import xyz.fycz.dynamic.utils.LanZouUtils +import xyz.fycz.myreader.webapi.LanZouApi + +/** + * @author fengyue + * @date 2022/6/30 20:40 + */ +@AppFix([243, 244, 245, 246], ["修复书源订阅失败的问题"], "2022-06-30") +class App246Fix4: AppFixHandle { + override fun onFix(key: String): BooleanArray { + val result = try { + fxLanZouApi() + true + } catch (e: Exception) { + MapleUtils.log(e) + false + } + fixResult(key, "lanZouApi", result) + return booleanArrayOf(result) + } + + private fun fxLanZouApi() { + MapleUtils.findAndHookMethod( + LanZouApi::class.java, + "getFileUrl", + String::class.java, + String::class.java, + object : MethodReplacement(){ + override fun replaceHookedMethod(param: MapleBridge.MethodHookParam): Any { + return LanZouUtils.getFileUrl(param.args[0] as String, param.args[1] as String) + } + } + ) + } + + +} \ No newline at end of file diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/fix/AppSubSourceFix.kt b/dynamic/src/main/java/xyz/fycz/dynamic/fix/AppSubSourceFix.kt new file mode 100644 index 0000000..5a6734d --- /dev/null +++ b/dynamic/src/main/java/xyz/fycz/dynamic/fix/AppSubSourceFix.kt @@ -0,0 +1,62 @@ +/* + * This file is part of FYReader. + * FYReader is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FYReader is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FYReader. If not, see . + * + * Copyright (C) 2020 - 2022 fengyuecanzhu + */ + +package xyz.fycz.dynamic.fix + +import me.fycz.maple.MapleBridge +import me.fycz.maple.MapleUtils +import me.fycz.maple.MethodHook +import xyz.fycz.myreader.common.URLCONST +import xyz.fycz.myreader.webapi.LanZouApi + +/** + * @author fengyue + * @date 2022/6/21 18:30 + */ +@AppFix([], ["更新订阅书源链接,仅支持v2.4.3版本及以上版本"], "2022-06-21") +class AppSubSourceFix : AppFixHandle{ + override fun onFix(key: String): BooleanArray { + val result = try { + fxSubSource() + true + } catch (e: Exception) { + MapleUtils.log(e) + false + } + fixResult(key, "subSource", result) + return booleanArrayOf(result) + } + + private fun fxSubSource() { + MapleUtils.findAndHookMethod( + LanZouApi::class.java, + "getFoldFiles", + String::class.java, + Int::class.java, + String::class.java, + object : MethodHook(){ + override fun beforeHookedMethod(param: MapleBridge.MethodHookParam) { + if (param.args[0] == URLCONST.SUB_SOURCE_URL){ + param.args[0] = "https://fycz.lanzoum.com/b00pucrch" + param.args[2] = "b0ox" + } + } + } + ) + } +} \ No newline at end of file diff --git a/dynamic/src/main/java/xyz/fycz/dynamic/utils/LanZouUtils.kt b/dynamic/src/main/java/xyz/fycz/dynamic/utils/LanZouUtils.kt new file mode 100644 index 0000000..df9a0c0 --- /dev/null +++ b/dynamic/src/main/java/xyz/fycz/dynamic/utils/LanZouUtils.kt @@ -0,0 +1,138 @@ +/* + * This file is part of FYReader. + * FYReader is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FYReader is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FYReader. If not, see . + * + * Copyright (C) 2020 - 2022 fengyuecanzhu + */ + +package xyz.fycz.dynamic.utils + +import io.reactivex.Observable +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody +import org.jsoup.Jsoup +import xyz.fycz.myreader.common.URLCONST +import xyz.fycz.myreader.entity.lanzou.LanZouParseBean +import xyz.fycz.myreader.util.help.StringHelper +import xyz.fycz.myreader.util.utils.GSON +import xyz.fycz.myreader.util.utils.OkHttpUtils +import xyz.fycz.myreader.util.utils.StringUtils +import xyz.fycz.myreader.util.utils.fromJsonObject +import java.net.HttpURLConnection +import java.net.URL + +/** + * @author fengyue + * @date 2022/1/22 18:50 + */ +object LanZouUtils { + + /** + * 通过api获取蓝奏云可下载直链 + * + * @param url + * @param password + */ + fun getFileUrl(url: String, password: String = ""): Observable { + return Observable.create { + val html = OkHttpUtils.getHtml(url) + val url2 = if (password.isEmpty()) { + val url1 = getUrl1(html) + val data = StringUtils.getSubString(OkHttpUtils.getHtml(url1), "},", "},") + val key = getKeyValueByKey(data, "sign") + "&" + getKeyValueByKey(data, "websignkey") + getUrl2(key, url1) + } else { + getUrl2(StringHelper.getSubString(html, "sign=", "&"), url, password) + } + if (url2.contains("file")) { + it.onNext(getRedirectUrl(url2)) + } else { + it.onError(Throwable(url2)) + } + it.onComplete() + } + } + + fun getUrl1(html: String): String { + val doc = Jsoup.parse(html) + return URLCONST.LAN_ZOU_URL + doc.getElementsByTag("iframe").attr("src") + } + + fun getKeyValueByKey(html: String, key: String): String { + val keyName = StringHelper.getSubString(html, "'$key':", ",") + return if (keyName.endsWith("'")) { + key + "=" + keyName.replace("'", "") + } else { + val lanzousKeyStart = "var $keyName = '" + key + "=" + StringHelper.getSubString(html, lanzousKeyStart, "'") + } + } + + fun getUrl2(key: String, referer: String, password: String = ""): String { + val mediaType = "application/x-www-form-urlencoded".toMediaTypeOrNull() + val body = if (password.isEmpty()) { + "action=downprocess&signs=?ctdf&websign=&ves=1&$key" + } else { + "action=downprocess&sign=$key&p=$password" + } + val requestBody = body.toRequestBody(mediaType) + + val headers = HashMap() + headers["Referer"] = referer + + val html = OkHttpUtils.getHtml( + URLCONST.LAN_ZOU_URL + "/ajaxm.php", requestBody, + "UTF-8", headers + ) + return getUrl2(html) + } + + private fun getUrl2(o: String): String { + val lanZouBean = GSON.fromJsonObject(o) + lanZouBean?.run { + return if (zt == 1) { + "$dom/file/$url" + } else { + "解析失败\n信息:$inf" + } + } + return "" + } + + /** + * 获取重定向地址 + * + * @param path + */ + fun getRedirectUrl(path: String): String { + val conn = URL(path) + .openConnection() as HttpURLConnection + conn.instanceFollowRedirects = false + conn.connectTimeout = 5000 + conn.setRequestProperty( + "User-Agent", + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" + ) + conn.setRequestProperty("Accept-Language", "zh-cn") + conn.setRequestProperty("Connection", "Keep-Alive") + conn.setRequestProperty( + "Accept", + "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, */*" + ) + conn.connect() + val redirectUrl = conn.getHeaderField("Location") + conn.disconnect() + return redirectUrl + } +} \ No newline at end of file