package eu.kanade.tachiyomi.source import android.content.Context import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.asImmediateFlow import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.Hitomi import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.all.NHentai import eu.kanade.tachiyomi.source.online.all.PervEden import eu.kanade.tachiyomi.source.online.english.EightMuses import eu.kanade.tachiyomi.source.online.english.HBrowse import eu.kanade.tachiyomi.source.online.english.HentaiCafe import eu.kanade.tachiyomi.source.online.english.Pururin import eu.kanade.tachiyomi.source.online.english.Tsumino import exh.EH_SOURCE_ID import exh.EXH_SOURCE_ID import exh.PERV_EDEN_EN_SOURCE_ID import exh.PERV_EDEN_IT_SOURCE_ID import exh.metadata.metadata.PervEdenLang import exh.source.BlacklistedSources import exh.source.DelegatedHttpSource import exh.source.EnhancedHttpSource import kotlin.reflect.KClass import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import rx.Observable import uy.kohesive.injekt.injectLazy open class SourceManager(private val context: Context) { private val sourcesMap = mutableMapOf() private val stubSourcesMap = mutableMapOf() // SY --> private val prefs: PreferencesHelper by injectLazy() private val scope = CoroutineScope(Job() + Dispatchers.Main) // SY <-- init { createInternalSources().forEach { registerSource(it) } // SY --> // Recreate sources when they change prefs.enableExhentai().asImmediateFlow { createEHSources().forEach { registerSource(it) } }.launchIn(scope) registerSource(MergedSource()) // SY <-- } open fun get(sourceKey: Long): Source? { return sourcesMap[sourceKey] } fun getOrStub(sourceKey: Long): Source { return sourcesMap[sourceKey] ?: stubSourcesMap.getOrPut(sourceKey) { StubSource(sourceKey) } } fun getOnlineSources() = sourcesMap.values.filterIsInstance() fun getVisibleOnlineSources() = sourcesMap.values.filterIsInstance().filter { it.id !in BlacklistedSources.HIDDEN_SOURCES } fun getCatalogueSources() = sourcesMap.values.filterIsInstance() // SY --> fun getVisibleCatalogueSources() = sourcesMap.values.filterIsInstance().filter { it.id !in BlacklistedSources.HIDDEN_SOURCES } fun getDelegatedCatalogueSources() = sourcesMap.values.filterIsInstance().mapNotNull { enhancedHttpSource -> enhancedHttpSource.enhancedSource as? DelegatedHttpSource } // SY <-- internal fun registerSource(source: Source, overwrite: Boolean = false) { // EXH --> val sourceQName = source::class.qualifiedName val factories = DELEGATED_SOURCES.entries.filter { it.value.factory }.map { it.value.originalSourceQualifiedClassName } val delegate = if (sourceQName != null) { val matched = factories.find { sourceQName.startsWith(it) } if (matched != null) { DELEGATED_SOURCES[matched] } else DELEGATED_SOURCES[sourceQName] } else null val newSource = if (source is HttpSource && delegate != null) { XLog.d("[EXH] Delegating source: %s -> %s!", sourceQName, delegate.newSourceClass.qualifiedName) val enhancedSource = EnhancedHttpSource( source, delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(source, context) ) val map = listOf(DelegatedSource(enhancedSource.originalSource.name, enhancedSource.originalSource.id, enhancedSource.originalSource::class.qualifiedName ?: delegate.originalSourceQualifiedClassName, (enhancedSource.enhancedSource as DelegatedHttpSource)::class, delegate.factory)).associateBy { it.originalSourceQualifiedClassName } currentDelegatedSources.plusAssign(map) enhancedSource } else source if (source.id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) { XLog.d("[EXH] Removing blacklisted source: (id: %s, name: %s, lang: %s)!", source.id, source.name, (source as? CatalogueSource)?.lang) return } // EXH <-- if (overwrite || !sourcesMap.containsKey(source.id)) { sourcesMap[source.id] = newSource } } internal fun unregisterSource(source: Source) { sourcesMap.remove(source.id) } private fun createInternalSources(): List = listOf( LocalSource(context) ) // SY --> private fun createEHSources(): List { val exSrcs = mutableListOf( EHentai(EH_SOURCE_ID, false, context) ) if (prefs.enableExhentai().get()) { exSrcs += EHentai(EXH_SOURCE_ID, true, context) } exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, PervEdenLang.en, context) exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, PervEdenLang.it, context) exSrcs += NHentai(context) exSrcs += Hitomi(context) exSrcs += EightMuses(context) return exSrcs } // SY <-- private inner class StubSource(override val id: Long) : Source { override val name: String get() = id.toString() override fun fetchMangaDetails(manga: SManga): Observable { return Observable.error(getSourceNotInstalledException()) } override fun fetchChapterList(manga: SManga): Observable> { return Observable.error(getSourceNotInstalledException()) } override fun fetchPageList(chapter: SChapter): Observable> { return Observable.error(getSourceNotInstalledException()) } override fun toString(): String { return name } private fun getSourceNotInstalledException(): Exception { return Exception(context.getString(R.string.source_not_installed, id.toString())) } } // SY --> companion object { private const val fillInSourceId = 9999L val DELEGATED_SOURCES = listOf( DelegatedSource( "Hentai Cafe", 260868874183818481, "eu.kanade.tachiyomi.extension.all.foolslide.HentaiCafe", HentaiCafe::class ), DelegatedSource( "Pururin", 2221515250486218861, "eu.kanade.tachiyomi.extension.en.pururin.Pururin", Pururin::class ), DelegatedSource( "Tsumino", 6707338697138388238, "eu.kanade.tachiyomi.extension.en.tsumino.Tsumino", Tsumino::class )/*, DelegatedSource( "MangaDex", fillInSourceId, "eu.kanade.tachiyomi.extension.all.mangadex", MangaDex::class, true )*/, DelegatedSource( "HBrowse", 1401584337232758222, "eu.kanade.tachiyomi.extension.en.hbrowse.HBrowse", HBrowse::class ) ).associateBy { it.originalSourceQualifiedClassName } var currentDelegatedSources = mutableMapOf() data class DelegatedSource( val sourceName: String, val sourceId: Long, val originalSourceQualifiedClassName: String, val newSourceClass: KClass, val factory: Boolean = false ) } // SY <-- }