Use index.json from extensions repo to list sources instead of the id

Manga details shows the source name, just with (source not installed), backups show the actual source missing

Also fixed the error for "Source not found" error in manga info
This commit is contained in:
Jays2Kings 2021-09-14 21:59:14 -04:00
parent 495d2316ed
commit 7a8de6c0e7
11 changed files with 89 additions and 25 deletions

View file

@ -29,7 +29,7 @@ class FullBackupRestoreValidator : AbstractBackupRestoreValidator() {
val sources = backup.backupSources.map { it.sourceId to it.name }.toMap()
val missingSources = sources
.filter { sourceManager.get(it.key) == null }
.values
.map { sourceManager.getOrStub(it.key).name }
.sorted()
val trackers = backup.backupManga

View file

@ -34,7 +34,7 @@ class LegacyBackupRestoreValidator : AbstractBackupRestoreValidator() {
val sources = getSourceMapping(json)
val missingSources = sources
.filter { sourceManager.get(it.key) == null }
.values
.map { sourceManager.getOrStub(it.key).name }
.sorted()
val trackers = mangas

View file

@ -112,8 +112,11 @@ class ExtensionManager(
availableExtensionsRelay.call(value)
updatedInstalledExtensionsStatuses(value)
listener?.extensionsUpdated()
setupAvailableSourcesMap()
}
private var availableSources = hashMapOf<String, Extension.AvailableSource>()
/**
* Relay used to notify the untrusted extensions.
*/
@ -200,6 +203,15 @@ class ExtensionManager(
}
}
private fun setupAvailableSourcesMap() {
availableSources = hashMapOf()
availableExtensions.map { it.sources.orEmpty() }.flatten().forEach {
availableSources[it.id] = it
}
}
fun getStubSource(id: Long) = availableSources[id.toString()]
/**
* Finds the available extensions in the [api] and updates [availableExtensions].
*/

View file

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.system.withIOContext
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import uy.kohesive.injekt.injectLazy
@ -30,9 +31,9 @@ internal class ExtensionGithubApi {
}
}
suspend fun checkForUpdates(context: Context): List<Extension.Available> {
suspend fun checkForUpdates(context: Context, prefetchedExtensions: List<Extension.Available>? = null): List<Extension.Available> {
return withIOContext {
val extensions = findExtensions()
val extensions = prefetchedExtensions ?: findExtensions()
val installedExtensions = ExtensionLoader.loadExtensions(context)
.filterIsInstance<LoadResult.Success>()
@ -68,9 +69,16 @@ internal class ExtensionGithubApi {
val versionCode = element.jsonObject["code"]!!.jsonPrimitive.int
val lang = element.jsonObject["lang"]!!.jsonPrimitive.content
val nsfw = element.jsonObject["nsfw"]!!.jsonPrimitive.int == 1
val sources = element.jsonObject["sources"]?.jsonArray?.map f@{
val sName = it.jsonObject["name"]?.jsonPrimitive?.content ?: return@f null
val sId = it.jsonObject["id"]?.jsonPrimitive?.content ?: return@f null
val sLang = it.jsonObject["lang"]?.jsonPrimitive?.content ?: ""
val sBaseUrl = it.jsonObject["baseUrl"]?.jsonPrimitive?.content ?: ""
Extension.AvailableSource(sName, sId, sLang, sBaseUrl)
}?.filterNotNull()
val icon = "${REPO_URL_PREFIX}icon/${apkName.replace(".apk", ".png")}"
Extension.Available(name, pkgName, versionName, versionCode, lang, nsfw, apkName, icon)
Extension.Available(name, pkgName, versionName, versionCode, lang, nsfw, apkName, icon, sources)
}
}

View file

@ -33,9 +33,17 @@ sealed class Extension {
override val lang: String,
override val isNsfw: Boolean,
val apkName: String,
val iconUrl: String
val iconUrl: String,
val sources: List<AvailableSource>? = null
) : Extension()
data class AvailableSource(
val name: String,
val id: String,
val lang: String,
val baseUrl: String
)
data class Untrusted(
override val name: String,
override val pkgName: String,

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.source
import android.content.Context
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.english.KireiCake
import eu.kanade.tachiyomi.source.online.english.MangaPlus
import rx.Observable
import uy.kohesive.injekt.injectLazy
open class SourceManager(private val context: Context) {
@ -19,6 +21,8 @@ open class SourceManager(private val context: Context) {
private val stubSourcesMap = mutableMapOf<Long, StubSource>()
protected val extensionManager: ExtensionManager by injectLazy()
private val delegatedSources = listOf(
DelegatedSource(
"reader.kireicake.com",
@ -83,10 +87,10 @@ open class SourceManager(private val context: Context) {
LocalSource(context)
)
private inner class StubSource(override val id: Long) : Source {
inner class StubSource(override val id: Long) : Source {
override val name: String
get() = id.toString()
get() = extensionManager.getStubSource(id)?.name ?: id.toString()
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return Observable.error(getSourceNotInstalledException())
@ -108,7 +112,7 @@ open class SourceManager(private val context: Context) {
return SourceNotFoundException(
context.getString(
R.string.source_not_installed_,
id.toString()
extensionManager.getStubSource(id)?.name ?: id.toString()
),
id
)
@ -117,6 +121,13 @@ open class SourceManager(private val context: Context) {
override fun hashCode(): Int {
return id.hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as StubSource
return id == other.id
}
}
private data class DelegatedSource(

View file

@ -58,6 +58,7 @@ import eu.kanade.tachiyomi.data.updater.UpdateChecker
import eu.kanade.tachiyomi.data.updater.UpdateResult
import eu.kanade.tachiyomi.data.updater.UpdaterNotifier
import eu.kanade.tachiyomi.databinding.MainActivityBinding
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.extension.api.ExtensionGithubApi
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.MaterialMenuSheet
@ -91,7 +92,6 @@ import eu.kanade.tachiyomi.util.view.updatePadding
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.EndAnimatorListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -119,6 +119,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
private var animationSet: AnimatorSet? = null
private val downloadManager: DownloadManager by injectLazy()
private val mangaShortcutManager: MangaShortcutManager by injectLazy()
private val extensionManager: ExtensionManager by injectLazy()
private val hideBottomNav
get() = router.backstackSize > 1 && router.backstack[1].controller !is DialogController
@ -384,6 +385,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
}
}
getExtensionUpdates(true)
preferences.extensionUpdatesCount()
.asImmediateFlowIn(lifecycleScope) {
@ -402,7 +404,6 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
else -> Gravity.TOP
}
}
setExtensionsBadge()
setFloatingToolbar(canShowFloatingToolbar(router.backstack.lastOrNull()?.controller), changeBG = false)
}
@ -509,7 +510,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
override fun onResume() {
super.onResume()
getAppUpdates()
getExtensionUpdates()
getExtensionUpdates(false)
DownloadService.callListeners()
showDLQueueTutorial()
}
@ -577,11 +578,17 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
}
private fun getExtensionUpdates() {
if (Date().time >= preferences.lastExtCheck().getOrDefault() + TimeUnit.HOURS.toMillis(6)) {
GlobalScope.launch(Dispatchers.IO) {
fun getExtensionUpdates(force: Boolean) {
if ((force && extensionManager.availableExtensions.isEmpty()) ||
Date().time >= preferences.lastExtCheck().getOrDefault() + TimeUnit.HOURS.toMillis(6)
) {
lifecycleScope.launch(Dispatchers.IO) {
try {
val pendingUpdates = ExtensionGithubApi().checkForUpdates(this@MainActivity)
extensionManager.findAvailableExtensionsAsync()
val pendingUpdates = ExtensionGithubApi().checkForUpdates(
this@MainActivity,
extensionManager.availableExtensions.takeIf { it.isNotEmpty() }
)
preferences.extensionUpdatesCount().set(pendingUpdates.size)
preferences.lastExtCheck().set(Date().time)
} catch (e: java.lang.Exception) {

View file

@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.UnattendedTrackService
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceNotFoundException
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSChapter
import eu.kanade.tachiyomi.source.model.toSManga
@ -438,7 +439,9 @@ class MangaDetailsPresenter(
private fun trimException(e: java.lang.Exception): String {
return (
if (e.message?.contains(": ") == true) e.message?.split(": ")?.drop(1)
if (e !is SourceNotFoundException &&
e.message?.contains(": ") == true
) e.message?.split(": ")?.drop(1)
?.joinToString(": ")
else e.message
) ?: preferences.context.getString(R.string.unknown_error)

View file

@ -16,6 +16,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.toDrawable
import androidx.core.text.buildSpannedString
import androidx.core.text.scale
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
@ -30,6 +32,7 @@ import eu.kanade.tachiyomi.data.image.coil.loadManga
import eu.kanade.tachiyomi.databinding.ChapterHeaderItemBinding
import eu.kanade.tachiyomi.databinding.MangaHeaderItemBinding
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.util.system.getResourceColor
@ -361,15 +364,25 @@ class MangaHeaderHolder(
}
)
)
binding.mangaSource.text = run {
val preferences = presenter.preferences
val enabledLanguages = preferences.enabledLanguages().get()
with(binding.mangaSource) {
val enabledLanguages = presenter.preferences.enabledLanguages().get()
.filterNot { it == "all" }
if (enabledLanguages.size > 1 && presenter.extension?.lang == "all") {
presenter.source.toString()
} else {
return@run presenter.source.name
text = buildSpannedString {
append(
if (enabledLanguages.size > 1 && presenter.extension?.lang == "all") {
presenter.source.toString()
} else {
presenter.source.name
}
)
if (presenter.source is SourceManager.StubSource &&
presenter.source.name != presenter.source.id.toString()
) {
scale(0.9f) {
append(" (${context.getString(R.string.source_not_installed)})")
}
}
}
}

View file

@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.data.backup.full.models.BackupFull
import eu.kanade.tachiyomi.data.backup.legacy.LegacyBackupRestoreValidator
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.system.getFilePicker
import eu.kanade.tachiyomi.util.system.MiuiUtil
import eu.kanade.tachiyomi.util.system.toast
@ -78,6 +79,7 @@ class SettingsBackupController : SettingsController() {
}
if (!BackupRestoreService.isRunning(context)) {
(activity as? MainActivity)?.getExtensionUpdates(true)
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "application/*"

View file

@ -140,7 +140,7 @@
app:layout_constrainedWidth="true"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="1"
android:maxLines="2"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/title"