mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 19:34:35 -05:00
refactor github api
This commit is contained in:
@@ -1,52 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.api
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import com.github.salomonbrys.kotson.int
|
|
||||||
import com.github.salomonbrys.kotson.string
|
|
||||||
import com.google.gson.JsonArray
|
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
|
||||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
|
||||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
|
||||||
|
|
||||||
object ExtensionGithubApi {
|
|
||||||
const val BASE_URL = "https://raw.githubusercontent.com"
|
|
||||||
const val REPO_URL_PREFIX = "$BASE_URL/tachiyomiorg/tachiyomi-extensions/repo"
|
|
||||||
|
|
||||||
private fun parseResponse(json: JsonArray): List<Extension.Available> {
|
|
||||||
return json
|
|
||||||
.map { it.asJsonObject }
|
|
||||||
.filter { element ->
|
|
||||||
val versionName = element["version"].string
|
|
||||||
val libVersion = versionName.substringBeforeLast('.').toDouble()
|
|
||||||
libVersion >= ExtensionLoader.LIB_VERSION_MIN && libVersion <= ExtensionLoader.LIB_VERSION_MAX
|
|
||||||
}
|
|
||||||
.map { element ->
|
|
||||||
val name = element["name"].string.substringAfter("Tachiyomi: ")
|
|
||||||
val pkgName = element["pkg"].string
|
|
||||||
val apkName = element["apk"].string
|
|
||||||
val versionName = element["version"].string
|
|
||||||
val versionCode = element["code"].int
|
|
||||||
val lang = element["lang"].string
|
|
||||||
val nsfw = element["nsfw"].int == 1
|
|
||||||
val icon = "$REPO_URL_PREFIX/icon/${apkName.replace(".apk", ".png")}"
|
|
||||||
|
|
||||||
Extension.Available(name, pkgName, versionName, versionCode, lang, nsfw, apkName, icon)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun findExtensions(): List<Extension.Available> {
|
|
||||||
|
|
||||||
val response = ExtensionGithubService.getRepo()
|
|
||||||
return parseResponse(response)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getApkUrl(extension: ExtensionDataClass): String {
|
|
||||||
return "$REPO_URL_PREFIX/apk/${extension.apkName}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.model
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.Source
|
|
||||||
|
|
||||||
sealed class Extension {
|
|
||||||
|
|
||||||
abstract val name: String
|
|
||||||
abstract val pkgName: String
|
|
||||||
abstract val versionName: String
|
|
||||||
abstract val versionCode: Int
|
|
||||||
abstract val lang: String?
|
|
||||||
abstract val isNsfw: Boolean
|
|
||||||
|
|
||||||
data class Installed(
|
|
||||||
override val name: String,
|
|
||||||
override val pkgName: String,
|
|
||||||
override val versionName: String,
|
|
||||||
override val versionCode: Int,
|
|
||||||
override val lang: String,
|
|
||||||
override val isNsfw: Boolean,
|
|
||||||
val sources: List<Source>,
|
|
||||||
val hasUpdate: Boolean = false,
|
|
||||||
val isObsolete: Boolean = false,
|
|
||||||
val isUnofficial: Boolean = false
|
|
||||||
) : Extension()
|
|
||||||
|
|
||||||
data class Available(
|
|
||||||
override val name: String,
|
|
||||||
override val pkgName: String,
|
|
||||||
override val versionName: String,
|
|
||||||
override val versionCode: Int,
|
|
||||||
override val lang: String,
|
|
||||||
override val isNsfw: Boolean,
|
|
||||||
val apkName: String,
|
|
||||||
val iconUrl: String
|
|
||||||
) : Extension()
|
|
||||||
|
|
||||||
data class Untrusted(
|
|
||||||
override val name: String,
|
|
||||||
override val pkgName: String,
|
|
||||||
override val versionName: String,
|
|
||||||
override val versionCode: Int,
|
|
||||||
val signatureHash: String,
|
|
||||||
override val lang: String? = null,
|
|
||||||
override val isNsfw: Boolean = false
|
|
||||||
) : Extension()
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.model
|
|
||||||
|
|
||||||
enum class InstallStep {
|
|
||||||
Pending, Downloading, Installing, Installed, Error;
|
|
||||||
|
|
||||||
fun isCompleted(): Boolean {
|
|
||||||
return this == Installed || this == Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.model
|
|
||||||
|
|
||||||
sealed class LoadResult {
|
|
||||||
|
|
||||||
class Success(val extension: Extension.Installed) : LoadResult()
|
|
||||||
class Untrusted(val extension: Extension.Untrusted) : LoadResult()
|
|
||||||
class Error(val message: String? = null) : LoadResult() {
|
|
||||||
constructor(exception: Throwable) : this(exception.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.extension.util
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
// import android.annotation.SuppressLint
|
|
||||||
// import android.content.Context
|
|
||||||
// import android.content.pm.PackageInfo
|
|
||||||
// import android.content.pm.PackageManager
|
|
||||||
// import dalvik.system.PathClassLoader
|
|
||||||
// import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
|
||||||
// import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
// import eu.kanade.tachiyomi.util.lang.Hash
|
|
||||||
// import kotlinx.coroutines.async
|
|
||||||
// import kotlinx.coroutines.runBlocking
|
|
||||||
// import timber.log.Timber
|
|
||||||
// import uy.kohesive.injekt.injectLazy
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that handles the loading of the extensions installed in the system.
|
|
||||||
*/
|
|
||||||
// @SuppressLint("PackageManagerGetSignatures")
|
|
||||||
internal object ExtensionLoader {
|
|
||||||
|
|
||||||
// private val preferences: PreferencesHelper by injectLazy()
|
|
||||||
// private val allowNsfwSource by lazy {
|
|
||||||
// preferences.allowNsfwSource().get()
|
|
||||||
// }
|
|
||||||
|
|
||||||
private const val EXTENSION_FEATURE = "tachiyomi.extension"
|
|
||||||
private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class"
|
|
||||||
private const val METADATA_NSFW = "tachiyomi.extension.nsfw"
|
|
||||||
const val LIB_VERSION_MIN = 1.2
|
|
||||||
const val LIB_VERSION_MAX = 1.2
|
|
||||||
|
|
||||||
// private const val PACKAGE_FLAGS = PackageManager.GET_CONFIGURATIONS or PackageManager.GET_SIGNATURES
|
|
||||||
|
|
||||||
// inorichi's key
|
|
||||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23"
|
|
||||||
/**
|
|
||||||
* List of the trusted signatures.
|
|
||||||
*/
|
|
||||||
// var trustedSignatures = mutableSetOf<String>() + preferences.trustedSignatures().get() + officialSignature
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a list of all the installed extensions initialized concurrently.
|
|
||||||
*
|
|
||||||
* @param context The application context.
|
|
||||||
*/
|
|
||||||
// fun loadExtensions(context: Context): List<LoadResult> {
|
|
||||||
// val pkgManager = context.packageManager
|
|
||||||
// val installedPkgs = pkgManager.getInstalledPackages(PACKAGE_FLAGS)
|
|
||||||
// val extPkgs = installedPkgs.filter { isPackageAnExtension(it) }
|
|
||||||
//
|
|
||||||
// if (extPkgs.isEmpty()) return emptyList()
|
|
||||||
//
|
|
||||||
// // Load each extension concurrently and wait for completion
|
|
||||||
// return runBlocking {
|
|
||||||
// val deferred = extPkgs.map {
|
|
||||||
// async { loadExtension(context, it.packageName, it) }
|
|
||||||
// }
|
|
||||||
// deferred.map { it.await() }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to load an extension from the given package name. It checks if the extension
|
|
||||||
* contains the required feature flag before trying to load it.
|
|
||||||
*/
|
|
||||||
// fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult {
|
|
||||||
// val pkgInfo = try {
|
|
||||||
// context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
|
|
||||||
// } catch (error: PackageManager.NameNotFoundException) {
|
|
||||||
// // Unlikely, but the package may have been uninstalled at this point
|
|
||||||
// return LoadResult.Error(error)
|
|
||||||
// }
|
|
||||||
// if (!isPackageAnExtension(pkgInfo)) {
|
|
||||||
// return LoadResult.Error("Tried to load a package that wasn't a extension")
|
|
||||||
// }
|
|
||||||
// return loadExtension(context, pkgName, pkgInfo)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads an extension given its package name.
|
|
||||||
*
|
|
||||||
* @param context The application context.
|
|
||||||
* @param pkgName The package name of the extension to load.
|
|
||||||
* @param pkgInfo The package info of the extension.
|
|
||||||
*/
|
|
||||||
// private fun loadExtension(context: Context, pkgName: String, pkgInfo: PackageInfo): LoadResult {
|
|
||||||
// val pkgManager = context.packageManager
|
|
||||||
//
|
|
||||||
// val appInfo = try {
|
|
||||||
// pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
|
|
||||||
// } catch (error: PackageManager.NameNotFoundException) {
|
|
||||||
// // Unlikely, but the package may have been uninstalled at this point
|
|
||||||
// return LoadResult.Error(error)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
|
||||||
// val versionName = pkgInfo.versionName
|
|
||||||
// val versionCode = pkgInfo.versionCode
|
|
||||||
//
|
|
||||||
// if (versionName.isNullOrEmpty()) {
|
|
||||||
// val exception = Exception("Missing versionName for extension $extName")
|
|
||||||
// Timber.w(exception)
|
|
||||||
// return LoadResult.Error(exception)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Validate lib version
|
|
||||||
// val libVersion = versionName.substringBeforeLast('.').toDouble()
|
|
||||||
// if (libVersion < LIB_VERSION_MIN || libVersion > LIB_VERSION_MAX) {
|
|
||||||
// val exception = Exception(
|
|
||||||
// "Lib version is $libVersion, while only versions " +
|
|
||||||
// "$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed"
|
|
||||||
// )
|
|
||||||
// Timber.w(exception)
|
|
||||||
// return LoadResult.Error(exception)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val signatureHash = getSignatureHash(pkgInfo)
|
|
||||||
//
|
|
||||||
// if (signatureHash == null) {
|
|
||||||
// return LoadResult.Error("Package $pkgName isn't signed")
|
|
||||||
// } else if (signatureHash !in trustedSignatures) {
|
|
||||||
// val extension = Extension.Untrusted(extName, pkgName, versionName, versionCode, signatureHash)
|
|
||||||
// Timber.w("Extension $pkgName isn't trusted")
|
|
||||||
// return LoadResult.Untrusted(extension)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1
|
|
||||||
// if (allowNsfwSource == PreferenceValues.NsfwAllowance.BLOCKED && isNsfw) {
|
|
||||||
// return LoadResult.Error("NSFW extension $pkgName not allowed")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val classLoader = PathClassLoader(appInfo.sourceDir, null, context.classLoader)
|
|
||||||
//
|
|
||||||
// val sources = appInfo.metaData.getString(METADATA_SOURCE_CLASS)!!
|
|
||||||
// .split(";")
|
|
||||||
// .map {
|
|
||||||
// val sourceClass = it.trim()
|
|
||||||
// if (sourceClass.startsWith(".")) {
|
|
||||||
// pkgInfo.packageName + sourceClass
|
|
||||||
// } else {
|
|
||||||
// sourceClass
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .flatMap {
|
|
||||||
// try {
|
|
||||||
// when (val obj = Class.forName(it, false, classLoader).newInstance()) {
|
|
||||||
// is Source -> listOf(obj)
|
|
||||||
// is SourceFactory -> {
|
|
||||||
// if (isSourceNsfw(obj)) {
|
|
||||||
// emptyList()
|
|
||||||
// } else {
|
|
||||||
// obj.createSources()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else -> throw Exception("Unknown source class type! ${obj.javaClass}")
|
|
||||||
// }
|
|
||||||
// } catch (e: Throwable) {
|
|
||||||
// Timber.e(e, "Extension load error: $extName.")
|
|
||||||
// return LoadResult.Error(e)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .filter { !isSourceNsfw(it) }
|
|
||||||
//
|
|
||||||
// val langs = sources.filterIsInstance<CatalogueSource>()
|
|
||||||
// .map { it.lang }
|
|
||||||
// .toSet()
|
|
||||||
// val lang = when (langs.size) {
|
|
||||||
// 0 -> ""
|
|
||||||
// 1 -> langs.first()
|
|
||||||
// else -> "all"
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val extension = Extension.Installed(
|
|
||||||
// extName,
|
|
||||||
// pkgName,
|
|
||||||
// versionName,
|
|
||||||
// versionCode,
|
|
||||||
// lang,
|
|
||||||
// isNsfw,
|
|
||||||
// sources,
|
|
||||||
// isUnofficial = signatureHash != officialSignature
|
|
||||||
// )
|
|
||||||
// return LoadResult.Success(extension)
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given package is an extension.
|
|
||||||
*
|
|
||||||
* @param pkgInfo The package info of the application.
|
|
||||||
*/
|
|
||||||
// private fun isPackageAnExtension(pkgInfo: PackageInfo): Boolean {
|
|
||||||
// return pkgInfo.reqFeatures.orEmpty().any { it.name == EXTENSION_FEATURE }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the signature hash of the package or null if it's not signed.
|
|
||||||
*
|
|
||||||
* @param pkgInfo The package info of the application.
|
|
||||||
*/
|
|
||||||
// private fun getSignatureHash(pkgInfo: PackageInfo): String? {
|
|
||||||
// val signatures = pkgInfo.signatures
|
|
||||||
// return if (signatures != null && signatures.isNotEmpty()) {
|
|
||||||
// Hash.sha256(signatures.first().toByteArray())
|
|
||||||
// } else {
|
|
||||||
// null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a Source or SourceFactory is annotated with @Nsfw.
|
|
||||||
*/
|
|
||||||
// private fun isSourceNsfw(clazz: Any): Boolean {
|
|
||||||
// if (allowNsfwSource == PreferenceValues.NsfwAllowance.ALLOWED) {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (clazz !is Source && clazz !is SourceFactory) {
|
|
||||||
// return false
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Annotations are proxied, hence this janky way of checking for them
|
|
||||||
// return clazz.javaClass.annotations
|
|
||||||
// .flatMap { it.javaClass.interfaces.map { it.simpleName } }
|
|
||||||
// .firstOrNull { it == Nsfw::class.java.simpleName } != null
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ package ir.armor.tachidesk.impl
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl
|
import ir.armor.tachidesk.impl.extension.Extension.getExtensionIconUrl
|
||||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||||
import ir.armor.tachidesk.model.database.table.SourceTable
|
import ir.armor.tachidesk.model.database.table.SourceTable
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.impl.extension
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
@@ -8,13 +8,13 @@ package ir.armor.tachidesk.impl
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import ir.armor.tachidesk.impl.ExtensionsList.extensionTableAsDataClass
|
import ir.armor.tachidesk.impl.extension.ExtensionsList.extensionTableAsDataClass
|
||||||
|
import ir.armor.tachidesk.impl.extension.github.ExtensionGithubApi
|
||||||
import ir.armor.tachidesk.impl.util.PackageTools.EXTENSION_FEATURE
|
import ir.armor.tachidesk.impl.util.PackageTools.EXTENSION_FEATURE
|
||||||
import ir.armor.tachidesk.impl.util.PackageTools.LIB_VERSION_MAX
|
import ir.armor.tachidesk.impl.util.PackageTools.LIB_VERSION_MAX
|
||||||
import ir.armor.tachidesk.impl.util.PackageTools.LIB_VERSION_MIN
|
import ir.armor.tachidesk.impl.util.PackageTools.LIB_VERSION_MIN
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ir.armor.tachidesk.impl
|
package ir.armor.tachidesk.impl.extension
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) Contributors to the Suwayomi project
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
@@ -7,9 +7,9 @@ package ir.armor.tachidesk.impl
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
|
import ir.armor.tachidesk.impl.extension.Extension.getExtensionIconUrl
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import ir.armor.tachidesk.impl.extension.github.ExtensionGithubApi
|
||||||
import ir.armor.tachidesk.impl.Extension.getExtensionIconUrl
|
import ir.armor.tachidesk.impl.extension.github.OnlineExtension
|
||||||
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
import ir.armor.tachidesk.model.database.table.ExtensionTable
|
||||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
@@ -25,7 +25,7 @@ object ExtensionsList {
|
|||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
var lastUpdateCheck: Long = 0
|
var lastUpdateCheck: Long = 0
|
||||||
var updateMap = ConcurrentHashMap<String, Extension.Available>()
|
var updateMap = ConcurrentHashMap<String, OnlineExtension>()
|
||||||
|
|
||||||
/** 60,000 milliseconds = 60 seconds */
|
/** 60,000 milliseconds = 60 seconds */
|
||||||
private const val ExtensionUpdateDelayTime = 60 * 1000
|
private const val ExtensionUpdateDelayTime = 60 * 1000
|
||||||
@@ -63,7 +63,7 @@ object ExtensionsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateExtensionDatabase(foundExtensions: List<Extension.Available>) {
|
private fun updateExtensionDatabase(foundExtensions: List<OnlineExtension>) {
|
||||||
transaction {
|
transaction {
|
||||||
foundExtensions.forEach { foundExtension ->
|
foundExtensions.forEach { foundExtension ->
|
||||||
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull()
|
val extensionRecord = ExtensionTable.select { ExtensionTable.pkgName eq foundExtension.pkgName }.firstOrNull()
|
||||||
@@ -1,7 +1,18 @@
|
|||||||
package eu.kanade.tachiyomi.extension.api
|
package ir.armor.tachidesk.impl.extension.github
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import com.github.salomonbrys.kotson.int
|
||||||
|
import com.github.salomonbrys.kotson.string
|
||||||
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonParser
|
import com.google.gson.JsonParser
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
|
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Interceptor.Chain
|
import okhttp3.Interceptor.Chain
|
||||||
@@ -13,10 +24,44 @@ import okio.buffer
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
/**
|
object ExtensionGithubApi {
|
||||||
* Used to get the extension repo listing from GitHub.
|
const val BASE_URL = "https://raw.githubusercontent.com"
|
||||||
*/
|
const val REPO_URL_PREFIX = "$BASE_URL/tachiyomiorg/tachiyomi-extensions/repo"
|
||||||
object ExtensionGithubService {
|
|
||||||
|
private const val LIB_VERSION_MIN = "1.2"
|
||||||
|
private const val LIB_VERSION_MAX = "1.2"
|
||||||
|
|
||||||
|
private fun parseResponse(json: JsonArray): List<OnlineExtension> {
|
||||||
|
return json
|
||||||
|
.map { it.asJsonObject }
|
||||||
|
.filter { element ->
|
||||||
|
val versionName = element["version"].string
|
||||||
|
val libVersion = versionName.substringBeforeLast('.')
|
||||||
|
libVersion == LIB_VERSION_MAX
|
||||||
|
}
|
||||||
|
.map { element ->
|
||||||
|
val name = element["name"].string.substringAfter("Tachiyomi: ")
|
||||||
|
val pkgName = element["pkg"].string
|
||||||
|
val apkName = element["apk"].string
|
||||||
|
val versionName = element["version"].string
|
||||||
|
val versionCode = element["code"].int
|
||||||
|
val lang = element["lang"].string
|
||||||
|
val nsfw = element["nsfw"].int == 1
|
||||||
|
val icon = "$REPO_URL_PREFIX/icon/${apkName.replace(".apk", ".png")}"
|
||||||
|
|
||||||
|
OnlineExtension(name, pkgName, versionName, versionCode, lang, nsfw, apkName, icon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun findExtensions(): List<OnlineExtension> {
|
||||||
|
val response = getRepo()
|
||||||
|
return parseResponse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getApkUrl(extension: ExtensionDataClass): String {
|
||||||
|
return "$REPO_URL_PREFIX/apk/${extension.apkName}"
|
||||||
|
}
|
||||||
|
|
||||||
private val client by lazy {
|
private val client by lazy {
|
||||||
val network: NetworkHelper by injectLazy()
|
val network: NetworkHelper by injectLazy()
|
||||||
network.client.newBuilder()
|
network.client.newBuilder()
|
||||||
@@ -31,9 +76,9 @@ object ExtensionGithubService {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getRepo(): com.google.gson.JsonArray {
|
private fun getRepo(): com.google.gson.JsonArray {
|
||||||
val request = Request.Builder()
|
val request = Request.Builder()
|
||||||
.url("${ExtensionGithubApi.REPO_URL_PREFIX}/index.json.gz")
|
.url("$REPO_URL_PREFIX/index.json.gz")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val response = client.newCall(request).execute().use { response -> response.body!!.string() }
|
val response = client.newCall(request).execute().use { response -> response.body!!.string() }
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package ir.armor.tachidesk.impl.extension.github
|
||||||
|
|
||||||
|
data class OnlineExtension(
|
||||||
|
val name: String,
|
||||||
|
val pkgName: String,
|
||||||
|
val versionName: String,
|
||||||
|
val versionCode: Int,
|
||||||
|
val lang: String,
|
||||||
|
val isNsfw: Boolean,
|
||||||
|
val apkName: String,
|
||||||
|
val iconUrl: String
|
||||||
|
)
|
||||||
@@ -13,11 +13,6 @@ import ir.armor.tachidesk.impl.CategoryManga.removeMangaFromCategory
|
|||||||
import ir.armor.tachidesk.impl.Chapter.getChapter
|
import ir.armor.tachidesk.impl.Chapter.getChapter
|
||||||
import ir.armor.tachidesk.impl.Chapter.getChapterList
|
import ir.armor.tachidesk.impl.Chapter.getChapterList
|
||||||
import ir.armor.tachidesk.impl.Chapter.modifyChapter
|
import ir.armor.tachidesk.impl.Chapter.modifyChapter
|
||||||
import ir.armor.tachidesk.impl.Extension.getExtensionIcon
|
|
||||||
import ir.armor.tachidesk.impl.Extension.installExtension
|
|
||||||
import ir.armor.tachidesk.impl.Extension.uninstallExtension
|
|
||||||
import ir.armor.tachidesk.impl.Extension.updateExtension
|
|
||||||
import ir.armor.tachidesk.impl.ExtensionsList.getExtensionList
|
|
||||||
import ir.armor.tachidesk.impl.Library.addMangaToLibrary
|
import ir.armor.tachidesk.impl.Library.addMangaToLibrary
|
||||||
import ir.armor.tachidesk.impl.Library.getLibraryMangas
|
import ir.armor.tachidesk.impl.Library.getLibraryMangas
|
||||||
import ir.armor.tachidesk.impl.Library.removeMangaFromLibrary
|
import ir.armor.tachidesk.impl.Library.removeMangaFromLibrary
|
||||||
@@ -33,6 +28,11 @@ import ir.armor.tachidesk.impl.Source.getSourceList
|
|||||||
import ir.armor.tachidesk.impl.backup.BackupFlags
|
import ir.armor.tachidesk.impl.backup.BackupFlags
|
||||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
|
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupExport.createLegacyBackup
|
||||||
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
|
import ir.armor.tachidesk.impl.backup.legacy.LegacyBackupImport.restoreLegacyBackup
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.getExtensionIcon
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.installExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.uninstallExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.updateExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.ExtensionsList.getExtensionList
|
||||||
import ir.armor.tachidesk.server.impl_internal.About.getAbout
|
import ir.armor.tachidesk.server.impl_internal.About.getAbout
|
||||||
import ir.armor.tachidesk.server.util.openInBrowser
|
import ir.armor.tachidesk.server.util.openInBrowser
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ package ir.armor.tachidesk
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import ir.armor.tachidesk.impl.Extension.installExtension
|
|
||||||
import ir.armor.tachidesk.impl.Extension.uninstallExtension
|
|
||||||
import ir.armor.tachidesk.impl.Extension.updateExtension
|
|
||||||
import ir.armor.tachidesk.impl.ExtensionsList.getExtensionList
|
|
||||||
import ir.armor.tachidesk.impl.Source.getSourceList
|
import ir.armor.tachidesk.impl.Source.getSourceList
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.installExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.uninstallExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.Extension.updateExtension
|
||||||
|
import ir.armor.tachidesk.impl.extension.ExtensionsList.getExtensionList
|
||||||
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
import ir.armor.tachidesk.impl.util.GetHttpSource.getHttpSource
|
||||||
import ir.armor.tachidesk.impl.util.lang.awaitSingle
|
import ir.armor.tachidesk.impl.util.lang.awaitSingle
|
||||||
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
import ir.armor.tachidesk.model.dataclass.ExtensionDataClass
|
||||||
|
|||||||
Reference in New Issue
Block a user