Simplify extension store decoding and add gzip compression support (#3453)
This commit is contained in:
@@ -13,6 +13,9 @@ import mihon.data.extension.model.NetworkExtensionStore
|
|||||||
import mihon.data.extension.model.NetworkLegacyExtension
|
import mihon.data.extension.model.NetworkLegacyExtension
|
||||||
import mihon.data.extension.model.NetworkLegacyExtensionRepo
|
import mihon.data.extension.model.NetworkLegacyExtensionRepo
|
||||||
import mihon.domain.extension.model.ExtensionStore
|
import mihon.domain.extension.model.ExtensionStore
|
||||||
|
import okio.BufferedSource
|
||||||
|
import okio.buffer
|
||||||
|
import okio.gzip
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import kotlin.coroutines.cancellation.CancellationException
|
import kotlin.coroutines.cancellation.CancellationException
|
||||||
|
|
||||||
@@ -22,49 +25,35 @@ class ExtensionStoreService(
|
|||||||
private val protoBuf: ProtoBuf,
|
private val protoBuf: ProtoBuf,
|
||||||
) {
|
) {
|
||||||
suspend fun fetch(indexUrl: String): Result<ExtensionStore> {
|
suspend fun fetch(indexUrl: String): Result<ExtensionStore> {
|
||||||
return fetch(indexUrl, forceV2 = false)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun fetch(indexUrl: String, forceV2: Boolean): Result<ExtensionStore> {
|
|
||||||
var updatedIndexUrl: String = indexUrl
|
var updatedIndexUrl: String = indexUrl
|
||||||
return try {
|
return try {
|
||||||
val store = network.client.newCall(GET(indexUrl)).awaitSuccess().body.source().use { source ->
|
val response = network.client.newCall(GET(updatedIndexUrl)).awaitSuccess()
|
||||||
try {
|
val store = response.body.source().decompressIfGzipped().use { source ->
|
||||||
protoBuf.decodeFromByteArray<NetworkExtensionStore>(source.peek().readByteArray())
|
val networkStore = when (source.peek().readByte()) {
|
||||||
} catch (e: IllegalArgumentException) {
|
// "[..."
|
||||||
logcat(LogPriority.ERROR, e) {
|
0x5B.toByte() -> run {
|
||||||
"Failed to add extension store '$updatedIndexUrl'"
|
if (!indexUrl.endsWith("/index.min.json")) {
|
||||||
}
|
throw IllegalArgumentException("Provided legacy store url is not valid")
|
||||||
try {
|
|
||||||
json.decodeFromBufferedSource<NetworkExtensionStore>(source.peek())
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
if (forceV2) throw e
|
|
||||||
logcat(LogPriority.ERROR, e) {
|
|
||||||
"Failed to add extension store '$updatedIndexUrl'"
|
|
||||||
}
|
}
|
||||||
val legacyIndex = try {
|
updatedIndexUrl = indexUrl.replace("/index.min.json", "/repo.json")
|
||||||
json.decodeFromBufferedSource<NetworkLegacyExtensionRepo>(source.peek())
|
network.client.newCall(GET(updatedIndexUrl)).awaitSuccess().body.source().use {
|
||||||
} catch (e: IllegalArgumentException) {
|
json.decodeFromBufferedSource<NetworkLegacyExtensionRepo>(it)
|
||||||
if (!indexUrl.endsWith("/index.min.json")) {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
logcat(LogPriority.ERROR, e) {
|
|
||||||
"Failed to add extension store '$updatedIndexUrl'"
|
|
||||||
}
|
|
||||||
updatedIndexUrl = indexUrl.replace("/index.min.json", "/repo.json")
|
|
||||||
network.client.newCall(GET(updatedIndexUrl)).awaitSuccess().body.source().use {
|
|
||||||
json.decodeFromBufferedSource<NetworkLegacyExtensionRepo>(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legacyIndex.indexV2 != null) {
|
|
||||||
return fetch(legacyIndex.indexV2, forceV2 = true)
|
|
||||||
} else {
|
|
||||||
legacyIndex
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// "{..."
|
||||||
|
0x7B.toByte() -> try {
|
||||||
|
json.decodeFromBufferedSource<NetworkLegacyExtensionRepo>(source.peek())
|
||||||
|
} catch (_: IllegalArgumentException) {
|
||||||
|
json.decodeFromBufferedSource<NetworkExtensionStore>(source)
|
||||||
|
}
|
||||||
|
else -> protoBuf.decodeFromByteArray<NetworkExtensionStore>(source.readByteArray())
|
||||||
}
|
}
|
||||||
.toExtensionStore(updatedIndexUrl)
|
|
||||||
|
if (networkStore is NetworkLegacyExtensionRepo && networkStore.indexV2 != null) {
|
||||||
|
return fetch(networkStore.indexV2)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkStore.toExtensionStore(updatedIndexUrl)
|
||||||
}
|
}
|
||||||
Result.success(store)
|
Result.success(store)
|
||||||
} catch (e: CancellationException) {
|
} catch (e: CancellationException) {
|
||||||
@@ -81,14 +70,13 @@ class ExtensionStoreService(
|
|||||||
return try {
|
return try {
|
||||||
val extensions = if (!store.isLegacy) {
|
val extensions = if (!store.isLegacy) {
|
||||||
val response = network.client.newCall(GET(store.indexUrl)).awaitSuccess()
|
val response = network.client.newCall(GET(store.indexUrl)).awaitSuccess()
|
||||||
response.body.source().use { source ->
|
response.body.source().decompressIfGzipped().use { source ->
|
||||||
try {
|
when (source.peek().readByte()) {
|
||||||
protoBuf.decodeFromByteArray<NetworkExtensionStore>(source.peek().readByteArray())
|
// "{..."
|
||||||
.toAvailableExtensions(store)
|
0x7B.toByte() -> json.decodeFromBufferedSource<NetworkExtensionStore>(source)
|
||||||
} catch (_: IllegalArgumentException) {
|
else -> protoBuf.decodeFromByteArray<NetworkExtensionStore>(source.readByteArray())
|
||||||
json.decodeFromBufferedSource<NetworkExtensionStore>(source.peek())
|
|
||||||
.toAvailableExtensions(store)
|
|
||||||
}
|
}
|
||||||
|
.toAvailableExtensions(store)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val storeBaseUrl = store.indexUrl.removeSuffix("/repo.json")
|
val storeBaseUrl = store.indexUrl.removeSuffix("/repo.json")
|
||||||
@@ -105,4 +93,16 @@ class ExtensionStoreService(
|
|||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun BufferedSource.decompressIfGzipped(): BufferedSource {
|
||||||
|
val isGzip = peek().use { peeked ->
|
||||||
|
try {
|
||||||
|
peeked.readShort().toInt() == 0x1f8b
|
||||||
|
} catch (_: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (isGzip) gzip().buffer() else this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user