1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-28 14:50:29 +08:00
新增
1. 增强型数据访问选项
2. 漫画下载数量并发限制
修复
1. 同时下载过多漫画时失败 (fix #67)
2. 主页滑动横幅后刷新闪退 (fix #65)
3. v2.3.0 增强型数据访问下登录失败
优化
1. 网络不佳时自动打开增强型数据访问
2. 不再反复读取代理状态
This commit is contained in:
源文雨
2024-04-17 01:23:30 +09:00
parent 05a774d542
commit 82062941b8
7 changed files with 36 additions and 29 deletions

View File

@@ -9,8 +9,8 @@ android {
applicationId 'top.fumiama.copymanga' applicationId 'top.fumiama.copymanga'
minSdkVersion 23 minSdkVersion 23
targetSdkVersion 34 targetSdkVersion 34
versionCode 58 versionCode 59
versionName '2.3.0' versionName '2.3.1'
resourceConfigurations += ['zh', 'zh-rCN'] resourceConfigurations += ['zh', 'zh-rCN']
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -14,7 +14,6 @@ class Shelf(private val token: String, getString: (Int) -> String) {
private val apiUrl: String = getString(R.string.shelfOperateApiUrl).format(hostUrl) private val apiUrl: String = getString(R.string.shelfOperateApiUrl).format(hostUrl)
private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl) private val queryApiUrlTemplate = getString(R.string.bookUserQueryApiUrl)
private val referer: String = getString(R.string.referer).format(DownloadTools.app_ver) private val referer: String = getString(R.string.referer).format(DownloadTools.app_ver)
private val ua: String = getString(R.string.pc_ua).format(DownloadTools.app_ver)
private val addApiUrl get() = "$apiUrl?platform=3".let { CMApi.apiProxy?.wrap(it)?:it } private val addApiUrl get() = "$apiUrl?platform=3".let { CMApi.apiProxy?.wrap(it)?:it }
private val delApiUrl get() = "${apiUrl}s?platform=3".let { CMApi.apiProxy?.wrap(it)?:it } private val delApiUrl get() = "${apiUrl}s?platform=3".let { CMApi.apiProxy?.wrap(it)?:it }
suspend fun add(comicId: String): String = withContext(Dispatchers.IO) { suspend fun add(comicId: String): String = withContext(Dispatchers.IO) {
@@ -29,9 +28,13 @@ class Shelf(private val token: String, getString: (Int) -> String) {
append(token) append(token)
} }
val re = DownloadTools.requestWithBody( val re = DownloadTools.requestWithBody(
addApiUrl, "POST", body.encodeToByteArray(), referer, ua addApiUrl, "POST", body.encodeToByteArray()
)?.decodeToString() ?: return@withContext "空回应" )?.decodeToString() ?: return@withContext "空回应"
return@withContext Gson().fromJson(re, ReturnBase::class.java).message return@withContext try {
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
"$re ${e.message}"
}
} }
suspend fun del(vararg bookIds: Int): String = withContext(Dispatchers.IO) { suspend fun del(vararg bookIds: Int): String = withContext(Dispatchers.IO) {
@@ -48,9 +51,13 @@ class Shelf(private val token: String, getString: (Int) -> String) {
append(token) append(token)
} }
val re = DownloadTools.requestWithBody( val re = DownloadTools.requestWithBody(
delApiUrl, "DELETE", body.encodeToByteArray(), referer, ua delApiUrl, "DELETE", body.encodeToByteArray()
)?.decodeToString() ?: return@withContext "空回应" )?.decodeToString() ?: return@withContext "空回应"
return@withContext Gson().fromJson(re, ReturnBase::class.java).message return@withContext try {
Gson().fromJson(re, ReturnBase::class.java).message
} catch (e: Exception) {
"$re ${e.message}"
}
} }
suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) { suspend fun query(pathWord: String): BookQueryStructure? = withContext(Dispatchers.IO) {
@@ -58,7 +65,7 @@ class Shelf(private val token: String, getString: (Int) -> String) {
Gson().fromJson(DownloadTools.getHttpContent( Gson().fromJson(DownloadTools.getHttpContent(
queryApiUrlTemplate.format(hostUrl, pathWord).let { queryApiUrlTemplate.format(hostUrl, pathWord).let {
CMApi.apiProxy?.wrap(it)?:it CMApi.apiProxy?.wrap(it)?:it
}, referer, ua }, referer
).decodeToString(), BookQueryStructure::class.java) ).decodeToString(), BookQueryStructure::class.java)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()

View File

@@ -78,10 +78,7 @@ open class AutoDownloadHandler(
var cnt = 0 var cnt = 0
while (cnt++ <= 3) { while (cnt++ <= 3) {
try { try {
val data = DownloadTools.getHttpContent( val data = DownloadTools.getHttpContent(CMApi.apiProxy?.wrap(url)?:url)
CMApi.apiProxy?.wrap(url)?:url, null,
DownloadTools.pc_ua
)
if(exit) return@withContext if(exit) return@withContext
val fi = data.inputStream() val fi = data.inputStream()
val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass)) val pass = setGsonItem(Gson().fromJson(fi.reader(), jsonClass))

View File

@@ -18,8 +18,7 @@ class PausableDownloader(private val url: String, private val waitMilliseconds:
try { try {
val data = (DownloadTools.getHttpContent( val data = (DownloadTools.getHttpContent(
(if(isApi) CMApi.apiProxy?.wrap(url) else null)?:url, (if(isApi) CMApi.apiProxy?.wrap(url) else null)?:url,
DownloadTools.referer, DownloadTools.referer
DownloadTools.pc_ua
)) ))
whenFinish?.let { it(data) } whenFinish?.let { it(data) }
return@withContext true return@withContext true

View File

@@ -105,7 +105,7 @@ object DownloadTools {
capsule capsule
} }
suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = null): ByteArray = suspend fun getHttpContent(u: String, refer: String? = null, ua: String? = pc_ua): ByteArray =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
if (Comandy.useComandy) { if (Comandy.useComandy) {
getComandyApiConnection(u, "GET", refer, ua).let { capsule -> getComandyApiConnection(u, "GET", refer, ua).let { capsule ->
@@ -150,7 +150,7 @@ object DownloadTools {
FutureTask(if (Comandy.useComandy) Callable{ FutureTask(if (Comandy.useComandy) Callable{
try { try {
Comandy.instance?.request(Gson().toJson( Comandy.instance?.request(Gson().toJson(
getComandyNormalConnection(u, "GET")) getComandyNormalConnection(u, "GET", pc_ua))
)?.let { result -> )?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)?.let { Gson().fromJson(result, ComandyCapsule::class.java)?.let {
if (it.code != 200) null if (it.code != 200) null
@@ -164,7 +164,7 @@ object DownloadTools {
} else Callable { } else Callable {
var ret: ByteArray? = null var ret: ByteArray? = null
try { try {
val connection = getNormalConnection(u, "GET") val connection = getNormalConnection(u, "GET", pc_ua)
val ci = connection.inputStream val ci = connection.inputStream
if(readSize > 0) { if(readSize > 0) {
ret = ByteArray(readSize) ret = ByteArray(readSize)
@@ -186,22 +186,22 @@ object DownloadTools {
} }
}*/ }*/
fun requestWithBody(url: String, method: String, body: ByteArray, refer: String? = null, ua: String? = null): ByteArray? { fun requestWithBody(url: String, method: String, body: ByteArray, refer: String? = referer, ua: String? = pc_ua, contentType: String? = "application/x-www-form-urlencoded;charset=utf-8"): ByteArray? {
Log.d("MyDT", "$method Http: $url") Log.d("MyDT", "$method Http: $url")
var ret: ByteArray? = null var ret: ByteArray? = null
val task = FutureTask(if(Comandy.useComandy) Callable{ val task = FutureTask(if(Comandy.useComandy) Callable{
try { try {
val capsule = getComandyApiConnection(url, method, refer, ua) val capsule = getComandyApiConnection(url, method, refer, ua)
contentType?.let { capsule.headers["content-type"] = it }
capsule.data = body.decodeToString() capsule.data = body.decodeToString()
Comandy.instance?.request(Gson().toJson(capsule))?.let { result -> Comandy.instance?.request(Gson().toJson(capsule))?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)?.let { Gson().fromJson(result, ComandyCapsule::class.java)?.let {
if (it.code != 200) null it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }?:"empty comandy data".encodeToByteArray()
else it.data?.let { d -> Base64.decode(d, Base64.DEFAULT) }
} }
} }
} catch (ex: Exception) { } catch (ex: Exception) {
ex.printStackTrace() ex.printStackTrace()
null ex.message?.encodeToByteArray()
} }
} }
else Callable { else Callable {

View File

@@ -342,7 +342,7 @@ class HomeHandler(private val that: WeakReference<HomeFragment>) : AutoDownloadH
cv.tic.apply { post { text = name } } cv.tic.apply { post { text = name } }
homeF?.let { homeF?.let {
if(img.startsWith("http")) { if(img.startsWith("http")) {
Log.d("MyHH", "load card image: $img") //Log.d("MyHH", "load card image: $img")
val waitMillis = cardLoadingWaits.getAndIncrement().toLong()*200 val waitMillis = cardLoadingWaits.getAndIncrement().toLong()*200
val g = Glide.with(it).load(GlideUrl(CMApi.imageProxy?.wrap(img)?:img, CMApi.myGlideHeaders)) val g = Glide.with(it).load(GlideUrl(CMApi.imageProxy?.wrap(img)?:img, CMApi.myGlideHeaders))
.addListener(GlideHideLottieViewListener(WeakReference(cv.laic)) { .addListener(GlideHideLottieViewListener(WeakReference(cv.laic)) {

View File

@@ -2,6 +2,7 @@ package top.fumiama.copymanga.user
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Base64 import android.util.Base64
import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -10,6 +11,8 @@ import top.fumiama.copymanga.json.LoginInfoStructure
import top.fumiama.copymanga.tools.api.CMApi import top.fumiama.copymanga.tools.api.CMApi
import top.fumiama.copymanga.tools.http.Comandy import top.fumiama.copymanga.tools.http.Comandy
import top.fumiama.copymanga.tools.http.DownloadTools import top.fumiama.copymanga.tools.http.DownloadTools
import top.fumiama.copymanga.tools.http.DownloadTools.app_ver
import top.fumiama.copymanga.tools.http.DownloadTools.pc_ua
import top.fumiama.dmzj.copymanga.R import top.fumiama.dmzj.copymanga.R
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.Charset import java.nio.charset.Charset
@@ -20,12 +23,13 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
var err = "" var err = ""
if (Comandy.useComandy) getComandyLoginConnection(username, pwd, salt).let { capsule -> if (Comandy.useComandy) getComandyLoginConnection(username, pwd, salt).let { capsule ->
try { try {
Comandy.instance?.request(Gson().toJson(capsule))?.let { result -> val para = Gson().toJson(capsule)
Comandy.instance?.request(para)?.let { result ->
Gson().fromJson(result, ComandyCapsule::class.java)!!.let { Gson().fromJson(result, ComandyCapsule::class.java)!!.let {
if (it.code != 200) { if (it.code != 200) {
val l = LoginInfoStructure() val l = LoginInfoStructure()
l.code = it.code l.code = it.code
l.message = it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() } l.message = it.data?.let { d -> Base64.decode(d, Base64.DEFAULT).decodeToString() }?:"HTTP ${it.code}"
return@withContext l return@withContext l
} }
Base64.decode(it.data, Base64.DEFAULT) Base64.decode(it.data, Base64.DEFAULT)
@@ -37,7 +41,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
} }
}?.let { }?.let {
try { try {
saveInfo(it) return@withContext saveInfo(it)
} catch (e: Exception) { } catch (e: Exception) {
err = e.message.toString() err = e.message.toString()
} }
@@ -46,7 +50,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
inputStream.use { inputStream.use {
it?.readBytes()?.let { data -> it?.readBytes()?.let { data ->
try { try {
saveInfo(data) return@withContext saveInfo(data)
} catch (e: Exception) { } catch (e: Exception) {
err = e.message.toString() err = e.message.toString()
} }
@@ -143,7 +147,7 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
setRequestProperty("accept", "application/json") setRequestProperty("accept", "application/json")
val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0" val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0"
val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString() val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
outputStream.write("username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp&region=$r&webp=1".toByteArray()) outputStream.write("username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=$app_ver&source=copyApp&region=$r&webp=1".toByteArray())
} }
} }
} }
@@ -152,14 +156,14 @@ class Member(private val pref: SharedPreferences, private val getString: (Int) -
getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let { getString(R.string.loginApiUrl).format(CMApi.myHostApiUrl).let {
CMApi.apiProxy?.wrap(it)?:it CMApi.apiProxy?.wrap(it)?:it
}.let { }.let {
DownloadTools.getComandyApiConnection(it, "POST").apply { DownloadTools.getComandyApiConnection(it, "POST", null, pc_ua).apply {
pref.apply { pref.apply {
headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8" headers["content-type"] = "application/x-www-form-urlencoded;charset=utf-8"
headers["platform"] = "3" headers["platform"] = "3"
headers["accept"] = "application/json" headers["accept"] = "application/json"
val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0" val r = if(!getBoolean("settings_cat_net_sw_use_foreign", false)) "1" else "0"
val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString() val pwdEncoded = Base64.encode("$pwd-$salt".toByteArray(), Base64.DEFAULT).decodeToString()
data = "username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=1.4.4&source=copyApp&region=$r&webp=1" data = "username=${URLEncoder.encode(username, Charset.defaultCharset().name())}&password=$pwdEncoded&salt=$salt&platform=3&authorization=Token+&version=$app_ver&source=copyApp&region=$r&webp=1"
} }
} }
} }