Allow extension stores to have separate extension list file (#3454)
This commit is contained in:
@@ -4,7 +4,10 @@ import android.annotation.SuppressLint
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonNames
|
||||
import kotlinx.serialization.protobuf.ProtoNumber
|
||||
import mihon.data.extension.model.NetworkExtensionStore.ContentRating
|
||||
import mihon.data.extension.model.NetworkExtensionStore.ExtensionList
|
||||
import mihon.domain.extension.model.ExtensionStore
|
||||
import eu.kanade.tachiyomi.extension.model.Extension as TachiyomiExtension
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
@Serializable
|
||||
@@ -13,7 +16,8 @@ data class NetworkExtensionStore(
|
||||
@ProtoNumber(2) val badgeLabel: String,
|
||||
@ProtoNumber(3) val signingKey: String,
|
||||
@ProtoNumber(4) val contact: Contact,
|
||||
@ProtoNumber(5) val extensions: List<Extension>,
|
||||
@ProtoNumber(101) val extensionList: ExtensionList?,
|
||||
@ProtoNumber(102) val extensionListUrl: String?,
|
||||
) : BaseNetworkExtensionStore {
|
||||
@Serializable
|
||||
data class Contact(
|
||||
@@ -21,6 +25,9 @@ data class NetworkExtensionStore(
|
||||
@ProtoNumber(2) val discord: String?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ExtensionList(@ProtoNumber(1) val extensions: List<Extension>)
|
||||
|
||||
@Serializable
|
||||
data class Extension(
|
||||
@ProtoNumber(1) val name: String,
|
||||
@@ -79,32 +86,33 @@ data class NetworkExtensionStore(
|
||||
discord = contact.discord,
|
||||
),
|
||||
isLegacy = false,
|
||||
extensionListUrl = extensionListUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun toAvailableExtensions(store: ExtensionStore): List<eu.kanade.tachiyomi.extension.model.Extension.Available> {
|
||||
return extensions.map { extension ->
|
||||
val lang = extension.sources.map { it.language }.toSet()
|
||||
eu.kanade.tachiyomi.extension.model.Extension.Available(
|
||||
name = extension.name,
|
||||
pkgName = extension.packageName,
|
||||
apkUrl = extension.resources.apkUrl,
|
||||
iconUrl = extension.resources.iconUrl,
|
||||
libVersion = extension.extensionLib.toDouble(),
|
||||
versionCode = extension.versionCode,
|
||||
versionName = extension.versionName,
|
||||
lang = if (lang.size == 1) lang.first() else "all",
|
||||
isNsfw = extension.sources.maxOfOrNull { it.contentRating } == ContentRating.PORNOGRAPHIC,
|
||||
sources = extension.sources.map { source ->
|
||||
eu.kanade.tachiyomi.extension.model.Extension.Available.Source(
|
||||
id = source.id,
|
||||
name = source.name,
|
||||
lang = source.language,
|
||||
baseUrl = source.homeUrl,
|
||||
)
|
||||
},
|
||||
store = store,
|
||||
)
|
||||
}
|
||||
fun ExtensionList.toAvailableExtensions(store: ExtensionStore): List<TachiyomiExtension.Available> {
|
||||
return extensions.map { extension ->
|
||||
val lang = extension.sources.map { it.language }.toSet()
|
||||
TachiyomiExtension.Available(
|
||||
name = extension.name,
|
||||
pkgName = extension.packageName,
|
||||
apkUrl = extension.resources.apkUrl,
|
||||
iconUrl = extension.resources.iconUrl,
|
||||
libVersion = extension.extensionLib.toDouble(),
|
||||
versionCode = extension.versionCode,
|
||||
versionName = extension.versionName,
|
||||
lang = if (lang.size == 1) lang.first() else "all",
|
||||
isNsfw = extension.sources.maxOfOrNull { it.contentRating } == ContentRating.PORNOGRAPHIC,
|
||||
sources = extension.sources.map { source ->
|
||||
TachiyomiExtension.Available.Source(
|
||||
id = source.id,
|
||||
name = source.name,
|
||||
lang = source.language,
|
||||
baseUrl = source.homeUrl,
|
||||
)
|
||||
},
|
||||
store = store,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ data class NetworkLegacyExtensionRepo(
|
||||
discord = null,
|
||||
),
|
||||
isLegacy = true,
|
||||
extensionListUrl = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class ExtensionStoreRepositoryImpl(
|
||||
contactWebsite = indexUrl,
|
||||
contactDiscord = null,
|
||||
isLegacy = false,
|
||||
extensionListUrl = null,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -67,6 +68,7 @@ class ExtensionStoreRepositoryImpl(
|
||||
contactWebsite = store.contact.website,
|
||||
contactDiscord = store.contact.discord,
|
||||
isLegacy = store.isLegacy,
|
||||
extensionListUrl = store.extensionListUrl,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -117,6 +119,7 @@ class ExtensionStoreRepositoryImpl(
|
||||
contactWebsite: String,
|
||||
contactDiscord: String?,
|
||||
isLegacy: Boolean,
|
||||
extensionListUrl: String?,
|
||||
): ExtensionStore = ExtensionStore(
|
||||
indexUrl = indexUrl,
|
||||
name = name,
|
||||
@@ -127,5 +130,6 @@ class ExtensionStoreRepositoryImpl(
|
||||
discord = contactDiscord,
|
||||
),
|
||||
isLegacy = isLegacy,
|
||||
extensionListUrl = extensionListUrl,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import logcat.LogPriority
|
||||
import mihon.data.extension.model.NetworkExtensionStore
|
||||
import mihon.data.extension.model.NetworkLegacyExtension
|
||||
import mihon.data.extension.model.NetworkLegacyExtensionRepo
|
||||
import mihon.data.extension.model.toAvailableExtensions
|
||||
import mihon.domain.extension.model.ExtensionStore
|
||||
import okio.BufferedSource
|
||||
import okio.buffer
|
||||
@@ -68,7 +69,19 @@ class ExtensionStoreService(
|
||||
|
||||
suspend fun getExtensions(store: ExtensionStore): Result<List<Extension.Available>> {
|
||||
return try {
|
||||
val extensions = if (!store.isLegacy) {
|
||||
val extensions = if (store.extensionListUrl != null) {
|
||||
val response = network.client.newCall(GET(store.extensionListUrl!!)).awaitSuccess()
|
||||
response.body.source().decompressIfGzipped().use { source ->
|
||||
when (source.peek().readByte()) {
|
||||
// "{..."
|
||||
0x7B.toByte() -> json.decodeFromBufferedSource<NetworkExtensionStore.ExtensionList>(source)
|
||||
else -> protoBuf.decodeFromByteArray<NetworkExtensionStore.ExtensionList>(
|
||||
source.readByteArray(),
|
||||
)
|
||||
}
|
||||
.toAvailableExtensions(store)
|
||||
}
|
||||
} else if (!store.isLegacy) {
|
||||
val response = network.client.newCall(GET(store.indexUrl)).awaitSuccess()
|
||||
response.body.source().decompressIfGzipped().use { source ->
|
||||
when (source.peek().readByte()) {
|
||||
@@ -76,6 +89,7 @@ class ExtensionStoreService(
|
||||
0x7B.toByte() -> json.decodeFromBufferedSource<NetworkExtensionStore>(source)
|
||||
else -> protoBuf.decodeFromByteArray<NetworkExtensionStore>(source.readByteArray())
|
||||
}
|
||||
.extensionList!!
|
||||
.toAvailableExtensions(store)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -7,7 +7,8 @@ CREATE TABLE extension_store(
|
||||
signing_key TEXT NOT NULL,
|
||||
contact_website TEXT NOT NULL,
|
||||
contact_discord TEXT,
|
||||
is_legacy INTEGER AS Boolean NOT NULL
|
||||
is_legacy INTEGER AS Boolean NOT NULL,
|
||||
extension_list_url TEXT
|
||||
);
|
||||
|
||||
get:
|
||||
@@ -24,8 +25,8 @@ SELECT COUNT(*)
|
||||
FROM extension_store;
|
||||
|
||||
upsert:
|
||||
INSERT INTO extension_store(index_url, name, badge_label, signing_key, contact_website, contact_discord, is_legacy)
|
||||
VALUES (:indexUrl, :name, :badgeLabel, :signingKey, :contactWebsite, :contactDiscord, :isLegacy)
|
||||
INSERT INTO extension_store(index_url, name, badge_label, signing_key, contact_website, contact_discord, is_legacy, extension_list_url)
|
||||
VALUES (:indexUrl, :name, :badgeLabel, :signingKey, :contactWebsite, :contactDiscord, :isLegacy, :extensionListUrl)
|
||||
ON CONFLICT(index_url)
|
||||
DO UPDATE
|
||||
SET
|
||||
@@ -34,7 +35,8 @@ SET
|
||||
signing_key =: signingKey,
|
||||
contact_website = :contactWebsite,
|
||||
contact_discord = :contactDiscord,
|
||||
is_legacy = :isLegacy
|
||||
is_legacy = :isLegacy,
|
||||
extension_list_url = :extensionListUrl
|
||||
WHERE index_url = :indexUrl;
|
||||
|
||||
delete:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE extension_store ADD COLUMN extension_list_url TEXT;
|
||||
Reference in New Issue
Block a user