built-in EH compatible with extension (#613)

* switch off integrated H to show E-H source & extension

* More languages for E-H/ExH, also more popular with rate 5

* Don't hide Multi source when toggle Integrated H

* Avoid crash trying to load ehentaiBrowseDisplayMode

* change source ID to matching general extension

* migration old source ID to new source ID

* Allow importing of EHentai extension backups

* limit library update & metadata source to only Multi lang

* update app version

* fix spotless

* also migrate saved search/feed & pinned EH/EXH source ID when restore from backup or update app
This commit is contained in:
Cuong-Tran 2025-01-26 02:20:06 +07:00 committed by GitHub
parent 747cd48abc
commit 2e744ed5e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 386 additions and 116 deletions

View file

@ -30,8 +30,8 @@ android {
defaultConfig {
applicationId = "app.komikku"
versionCode = 71
versionName = "1.12.2"
versionCode = 72
versionName = "1.13.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")

View file

@ -25,7 +25,7 @@ class SourcePreferences(
fun incognitoExtensions() = preferenceStore.getStringSet("incognito_extensions", emptySet())
fun pinnedSources() = preferenceStore.getStringSet("pinned_catalogues", emptySet())
fun pinnedSources() = preferenceStore.getStringSet(pinnedSourcesPrefKey, emptySet())
fun lastUsedSource() = preferenceStore.getLong(
Preference.appStateKey("last_catalogue_source"),
@ -109,3 +109,5 @@ class SourcePreferences(
fun relatedMangas() = preferenceStore.getBoolean("related_mangas", true)
// KMK <--
}
const val pinnedSourcesPrefKey = "pinned_catalogues"

View file

@ -31,8 +31,8 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import exh.source.isEhBasedSource
import exh.util.SourceTagsUtil
import androidx.compose.material3.SuggestionChipDefaults as SuggestionChipDefaultsM3
@ -64,7 +64,7 @@ value class SearchMetadataChips(
} else {
SourceTagsUtil.getWrappedTag(source.id, fullTag = it.name)
} ?: it.name,
border = if (source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) {
border = if (source.isEhBasedSource()) {
when (it.type) {
EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 2
EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 1

View file

@ -72,9 +72,6 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.debug.SettingsDebugScreen
import exh.log.EHLogLevel
import exh.pref.DelegateSourcePreferences
import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import exh.util.toAnnotatedString
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
@ -740,16 +737,17 @@ object SettingsAdvancedScreen : SearchableSettings {
title = stringResource(SYMR.strings.toggle_hentai_features),
subtitle = stringResource(SYMR.strings.toggle_hentai_features_summary),
onValueChanged = {
if (it) {
BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID
BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
} else {
BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID
}
true
},
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.enableSourceBlacklist(),
title = stringResource(SYMR.strings.enable_source_blacklist),
subtitle = stringResource(
SYMR.strings.enable_source_blacklist_summary,
stringResource(MR.strings.app_name),
),
),
Preference.PreferenceItem.SwitchPreference(
pref = delegateSourcePreferences.delegateSources(),
title = stringResource(SYMR.strings.toggle_delegated_sources),
@ -770,14 +768,6 @@ object SettingsAdvancedScreen : SearchableSettings {
})"
}.toMap().toImmutableMap(),
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.enableSourceBlacklist(),
title = stringResource(SYMR.strings.enable_source_blacklist),
subtitle = stringResource(
SYMR.strings.enable_source_blacklist_summary,
stringResource(MR.strings.app_name),
),
),
kotlin.run {
var enableEncryptDatabase by rememberSaveable { mutableStateOf(false) }

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers
import eu.kanade.tachiyomi.data.backup.models.BackupFeed
import exh.EXHMigrations
import exh.util.nullIfBlank
import tachiyomi.data.DatabaseHandler
import uy.kohesive.injekt.Injekt
@ -20,7 +21,11 @@ class FeedRestorer(
saved_searchQueries.selectAll()
}
backupFeeds.filter { backupFeed ->
backupFeeds.map {
// KMK -->
EXHMigrations.migrateBackupFeed(it)
// KMK <--
}.filter { backupFeed ->
// Filter out source's global Popular/Latest feed already existed
backupFeed.savedSearch == null &&
currentFeeds.none { currentFeed ->

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers
import android.content.Context
import eu.kanade.domain.source.service.pinnedSourcesPrefKey
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.models.BackupPreference
import eu.kanade.tachiyomi.data.backup.models.BackupSourcePreferences
@ -12,6 +13,7 @@ import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue
import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.source.sourcePreferences
import exh.EXHMigrations
import tachiyomi.core.common.preference.AndroidPreferenceStore
import tachiyomi.core.common.preference.PreferenceStore
import uy.kohesive.injekt.Injekt
@ -70,7 +72,15 @@ class PreferenceRestorer(
}
is StringSetPreferenceValue -> {
if (prefs[key] is Set<*>?) {
preferenceStore.getStringSet(key).set(value.value)
preferenceStore.getStringSet(key).set(
// KMK -->
if (key == pinnedSourcesPrefKey) {
EXHMigrations.migratePinnedSources(value.value)
} else {
// KMK <--
value.value
},
)
}
}
}

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.data.backup.restore.restorers
import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch
import exh.EXHMigrations
import exh.util.nullIfBlank
import tachiyomi.data.DatabaseHandler
import uy.kohesive.injekt.Injekt
@ -22,7 +23,11 @@ class SavedSearchRestorer(
// KMK <--
}
backupSavedSearches.filter { backupSavedSearch ->
backupSavedSearches.map {
// KMK -->
EXHMigrations.migrateBackupSavedSearch(it)
// KMK <--
}.filter { backupSavedSearch ->
currentSavedSearches.none { currentSavedSearch ->
currentSavedSearch.source == backupSavedSearch.source &&
currentSavedSearch.name == backupSavedSearch.name &&

View file

@ -17,8 +17,8 @@ import eu.kanade.tachiyomi.extension.util.ExtensionLoader
import eu.kanade.tachiyomi.util.system.toast
import exh.log.xLogD
import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import exh.source.EHENTAI_EXT_SOURCES
import exh.source.EXHENTAI_EXT_SOURCES
import exh.source.MERGED_SOURCE_ID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.stateIn
import logcat.LogPriority
import tachiyomi.core.common.util.lang.withUIContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.UnsortedPreferences
import tachiyomi.domain.source.model.StubSource
import tachiyomi.i18n.MR
import uy.kohesive.injekt.Injekt
@ -116,8 +117,10 @@ class ExtensionManager(
// SY -->
return when (sourceId) {
EH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source)
EXH_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source)
// KMK -->
in EHENTAI_EXT_SOURCES -> ContextCompat.getDrawable(context, R.mipmap.ic_ehentai_source)
in EXHENTAI_EXT_SOURCES -> ContextCompat.getDrawable(context, R.mipmap.ic_exhentai_source)
// KMK <--
MERGED_SOURCE_ID -> ContextCompat.getDrawable(context, R.mipmap.ic_merged_source)
else -> null
}
@ -166,8 +169,17 @@ class ExtensionManager(
}
}
private fun Extension.isBlacklisted(blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get()): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
private fun Extension.isBlacklisted(
blacklistEnabled: Boolean = preferences.enableSourceBlacklist().get(),
// KMK -->
isHentaiEnabled: Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get(),
// KMK <--
): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS &&
blacklistEnabled &&
// KMK -->
isHentaiEnabled
// KMK <--
}
// EXH <--

View file

@ -23,6 +23,9 @@ import tachiyomi.core.common.preference.Preference
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.UnsortedPreferences
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.time.Instant
import kotlin.time.Duration.Companion.days
@ -179,8 +182,15 @@ internal class ExtensionApi {
// SY -->
private fun Extension.isBlacklisted(
blacklistEnabled: Boolean = sourcePreferences.enableSourceBlacklist().get(),
// KMK -->
isHentaiEnabled: Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get(),
// KMK <--
): Boolean {
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS && blacklistEnabled
return pkgName in BlacklistedSources.BLACKLISTED_EXTENSIONS &&
blacklistEnabled &&
// KMK -->
isHentaiEnabled
// KMK <--
}
// SY <--
}

View file

@ -15,9 +15,9 @@ import eu.kanade.tachiyomi.source.online.english.Tsumino
import exh.log.xLogD
import exh.source.BlacklistedSources
import exh.source.DelegatedHttpSource
import exh.source.EH_SOURCE_ID
import exh.source.EHENTAI_EXT_SOURCES
import exh.source.EIGHTMUSES_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import exh.source.EXHENTAI_EXT_SOURCES
import exh.source.EnhancedHttpSource
import exh.source.HBROWSE_SOURCE_ID
import exh.source.MERGED_SOURCE_ID
@ -79,8 +79,13 @@ class AndroidSourceManager(
.combine(preferences.enableExhentai().changes()) { extensions, enableExhentai ->
extensions to enableExhentai
}
// KMK -->
.combine(
preferences.isHentaiEnabled().changes(),
) { (a, b), c -> Triple(a, b, c) }
// KMK <--
// SY <--
.collectLatest { (extensions, enableExhentai) ->
.collectLatest { (extensions, enableExhentai/* KMK --> */, isHentaiEnabled/* KMK <-- */) ->
val mutableMap = ConcurrentHashMap<Long, Source>(
mapOf(
LocalSource.ID to LocalSource(
@ -93,16 +98,24 @@ class AndroidSourceManager(
),
),
).apply {
// SY -->
put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context))
if (enableExhentai) {
put(EXH_SOURCE_ID, EHentai(EXH_SOURCE_ID, true, context))
// KMK -->
if (isHentaiEnabled) {
EHENTAI_EXT_SOURCES.forEach { (id, lang) ->
put(id, EHentai(id, false, context, lang))
}
if (enableExhentai) {
EXHENTAI_EXT_SOURCES.forEach { (id, lang) ->
put(id, EHentai(id, true, context, lang))
}
}
// SY -->
put(MERGED_SOURCE_ID, MergedSource())
// SY <--
}
put(MERGED_SOURCE_ID, MergedSource())
// SY <--
// KMK <--
}
extensions.forEach { extension ->
extension.sources.mapNotNull { it.toInternalSource() }.forEach {
extension.sources.mapNotNull { it.toInternalSource(/* KMK --> */isHentaiEnabled/* KMK <-- */) }.forEach {
mutableMap[it.id] = it
registerStubSource(StubSource.from(it))
}
@ -123,7 +136,11 @@ class AndroidSourceManager(
}
}
private fun Source.toInternalSource(): Source? {
private fun Source.toInternalSource(
// KMK -->
isHentaiEnabled: Boolean,
// KMK <--
): Source? {
// EXH -->
val sourceQName = this::class.qualifiedName
val factories = DELEGATED_SOURCES.entries
@ -158,7 +175,12 @@ class AndroidSourceManager(
this
}
return if (id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) {
return if (
// KMK -->
isHentaiEnabled &&
// KMK <--
id in BlacklistedSources.BLACKLISTED_EXT_SOURCES
) {
xLogD(
"Removing blacklisted source: (id: %s, name: %s, lang: %s)!",
id,

View file

@ -97,7 +97,13 @@ class EHentai(
override val id: Long,
val exh: Boolean,
val context: Context,
// KMK -->
override val lang: String = "all",
// KMK <--
) : HttpSource(),
// KMK -->
EhBasedSource,
// KMK <--
MetadataSource<EHentaiSearchMetadata, Document>,
UrlImportableSource,
NamespaceSource,
@ -114,9 +120,19 @@ class EHentai(
override val baseUrl: String
get() = "https://$domain"
override val lang = "all"
override val supportsLatest = true
// KMK -->
private val ehLang = languageMapping[lang]
// true if lang is a "natural human language"
private fun isLangNatural(): Boolean = lang !in listOf("none", "other", "all")
private fun languageTag(): String {
return "language:$ehLang"
}
// KMK <--
private val preferences: UnsortedPreferences by injectLazy()
private val updateHelper: EHentaiUpdateHelper by injectLazy()
@ -273,7 +289,6 @@ class EHentai(
private fun getDateTag(element: Element?): Long? {
val text = element?.text()?.nullIfBlank()
return if (text != null) {
println(text)
val date = ZonedDateTime.parse(text, MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC))
date?.toInstant()?.toEpochMilli()
} else {
@ -467,9 +482,18 @@ class EHentai(
return if (it.text() == ">") it.attr("href") else null
}
override fun popularMangaRequest(page: Int): Request {
return exGet("$baseUrl/popular")
}
override fun popularMangaRequest(page: Int) =
// KMK -->
if (isLangNatural()) {
exGet("$baseUrl/?f_search=${languageTag()}&f_srdd=5&f_sr=on", page)
} else {
if (page > 1) {
exGet("$baseUrl/?f_srdd=5&f_sr=on", page - 1)
} else {
// KMK <--
exGet("$baseUrl/popular")
}
}
private fun <T : MangasPage> Observable<T>.checkValid(): Observable<MangasPage> = map {
it.checkValid()
@ -565,7 +589,14 @@ class EHentai(
)
}
override fun latestUpdatesRequest(page: Int) = exGet(baseUrl, page)
override fun latestUpdatesRequest(page: Int) =
// KMK -->
if (isLangNatural()) {
exGet("$baseUrl/?f_search=${languageTag()}", page)
} else {
// KMK <--
exGet(baseUrl, page)
}
override fun popularMangaParse(response: Response) = genericMangaParse(response)
override fun searchMangaParse(response: Response) = genericMangaParse(response)
@ -911,7 +942,7 @@ class EHentai(
// Session-less extended display mode (for users without ExHentai)
cookies["sl"] = "dm_2"
// Ignore all content warnings
// Ignore all content warnings ("Offensive For Everyone")
cookies["nw"] = "1"
return cookies
@ -1382,5 +1413,27 @@ class EHentai(
fun buildCookies(cookies: Map<String, String>) = cookies.entries.joinToString(separator = "; ") {
"${URLEncoder.encode(it.key, "UTF-8")}=${URLEncoder.encode(it.value, "UTF-8")}"
}
// KMK -->
val languageMapping = mapOf(
"ja" to "japanese",
"en" to "english",
"zh" to "chinese",
"nl" to "dutch",
"fr" to "french",
"de" to "german",
"hu" to "hungarian",
"it" to "italian",
"ko" to "korean",
"pl" to "polish",
"pt-BR" to "portuguese",
"ru" to "russian",
"es" to "spanish",
"th" to "thai",
"vi" to "vietnamese",
"none" to "n/a",
"other" to "other",
)
// KMK <--
}
}

View file

@ -21,7 +21,6 @@ import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.source.NHENTAI_SOURCE_ID
import exh.util.trimOrNull
import exh.util.urlImportFetchSearchManga
import exh.util.urlImportFetchSearchMangaSuspend
@ -239,8 +238,6 @@ class NHentai(delegate: HttpSource, val context: Context) :
}
companion object {
const val otherId = NHENTAI_SOURCE_ID
private val jsonParser = Json {
ignoreUnknownKeys = true
}

View file

@ -998,31 +998,21 @@ class LibraryScreenModel(
unfiltered.asFlow().cancellable().filter { item ->
val mangaId = item.libraryManga.manga.id
val sourceId = item.libraryManga.manga.source
if (isMetadataSource(sourceId)) {
if (mangaWithMetaIds.binarySearch(mangaId) < 0) {
// No meta? Filter using title
filterManga(
queries = parsedQuery,
libraryManga = item.libraryManga,
tracks = tracks[mangaId],
source = sources[sourceId],
loggedInTrackServices = loggedInTrackServices,
)
} else {
val tags = getSearchTags.await(mangaId)
val titles = getSearchTitles.await(mangaId)
filterManga(
queries = parsedQuery,
libraryManga = item.libraryManga,
tracks = tracks[mangaId],
source = sources[sourceId],
checkGenre = false,
searchTags = tags,
searchTitles = titles,
loggedInTrackServices = loggedInTrackServices,
)
}
if (isMetadataSource(sourceId) && mangaWithMetaIds.binarySearch(mangaId) >= 0) {
val tags = getSearchTags.await(mangaId)
val titles = getSearchTitles.await(mangaId)
filterManga(
queries = parsedQuery,
libraryManga = item.libraryManga,
tracks = tracks[mangaId],
source = sources[sourceId],
checkGenre = false,
searchTags = tags,
searchTitles = titles,
loggedInTrackServices = loggedInTrackServices,
)
} else {
// No meta? Filter using title
filterManga(
queries = parsedQuery,
libraryManga = item.libraryManga,

View file

@ -94,9 +94,6 @@ import eu.kanade.tachiyomi.util.view.setComposeContent
import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateWorker
import exh.log.DebugModeOverlay
import exh.source.BlacklistedSources
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collectLatest
@ -400,13 +397,6 @@ class MainActivity : BaseActivity() {
chapterCache.clear()
}
}
// SY -->
if (!unsortedPreferences.isHentaiEnabled().get()) {
BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID
BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
}
// SY -->
}
// KMK -->

View file

@ -1,7 +1,11 @@
package exh
import exh.source.BlacklistedSources
import eu.kanade.tachiyomi.data.backup.models.BackupFeed
import eu.kanade.tachiyomi.data.backup.models.BackupSavedSearch
import exh.source.EH_OLD_ID
import exh.source.EH_SOURCE_ID
import exh.source.EXH_OLD_ID
import exh.source.EXH_SOURCE_ID
import exh.source.HBROWSE_OLD_ID
import exh.source.HBROWSE_SOURCE_ID
import exh.source.NHENTAI_OLD_ID
@ -42,16 +46,101 @@ object EXHMigrations {
)
}
// KMK -->
// Allow importing of EHentai extension backups
if (newManga.source in BlacklistedSources.EHENTAI_EXT_SOURCES) {
if (newManga.source == EH_OLD_ID) {
newManga = newManga.copy(
source = EH_SOURCE_ID,
)
}
if (newManga.source == EXH_OLD_ID) {
newManga = newManga.copy(
source = EXH_SOURCE_ID,
)
}
// KMK <--
return newManga
}
// KMK -->
/**
* Migrate old source ID of delegated sources in old backup
*/
fun migrateBackupSavedSearch(savedSearch: BackupSavedSearch): BackupSavedSearch {
return when (savedSearch.source) {
NHENTAI_OLD_ID -> savedSearch.copy(
source = NHENTAI_SOURCE_ID,
)
TSUMINO_OLD_ID -> savedSearch.copy(
source = TSUMINO_SOURCE_ID,
)
HBROWSE_OLD_ID -> savedSearch.copy(
source = HBROWSE_SOURCE_ID,
)
EH_OLD_ID -> savedSearch.copy(
source = EH_SOURCE_ID,
)
EXH_OLD_ID -> savedSearch.copy(
source = EXH_SOURCE_ID,
)
else -> savedSearch
}
}
/**
* Migrate old source ID of delegated sources in old backup
*/
fun migrateBackupFeed(feed: BackupFeed): BackupFeed {
return when (feed.source) {
NHENTAI_OLD_ID -> feed.copy(
source = NHENTAI_SOURCE_ID,
)
TSUMINO_OLD_ID -> feed.copy(
source = TSUMINO_SOURCE_ID,
)
HBROWSE_OLD_ID -> feed.copy(
source = HBROWSE_SOURCE_ID,
)
EH_OLD_ID -> feed.copy(
source = EH_SOURCE_ID,
)
EXH_OLD_ID -> feed.copy(
source = EXH_SOURCE_ID,
)
else -> feed
}
}
/**
* Migrate old source ID of delegated sources in old backup
*/
fun migratePinnedSources(pinnedSources: Set<String>): Set<String> {
var pinned = pinnedSources
if (NHENTAI_OLD_ID.toString() in pinned) {
pinned = pinned.minus(NHENTAI_OLD_ID.toString())
.plus(NHENTAI_SOURCE_ID.toString())
}
if (TSUMINO_OLD_ID.toString() in pinned) {
pinned = pinned.minus(TSUMINO_OLD_ID.toString())
.plus(TSUMINO_SOURCE_ID.toString())
}
if (HBROWSE_OLD_ID.toString() in pinned) {
pinned = pinned.minus(HBROWSE_OLD_ID.toString())
.plus(HBROWSE_SOURCE_ID.toString())
}
if (EH_OLD_ID.toString() in pinned) {
pinned = pinned.minus(EH_OLD_ID.toString())
.plus(EH_SOURCE_ID.toString())
}
if (EXH_OLD_ID.toString() in pinned) {
pinned = pinned.minus(EXH_OLD_ID.toString())
.plus(EXH_SOURCE_ID.toString())
}
return pinned
}
// KMK -->
private fun getUrlWithoutDomain(orig: String): String {
return try {
val uri = URI(orig)

View file

@ -1,27 +1,7 @@
package exh.source
object BlacklistedSources {
val EHENTAI_EXT_SOURCES = longArrayOf(
8100626124886895451,
57122881048805941,
4678440076103929247,
1876021963378735852,
3955189842350477641,
4348288691341764259,
773611868725221145,
5759417018342755550,
825187715438990384,
6116711405602166104,
7151438547982231541,
2171445159732592630,
3032959619549451093,
5980349886941016589,
6073266008352078708,
5499077866612745456,
6140480779421365791,
)
val BLACKLISTED_EXT_SOURCES = EHENTAI_EXT_SOURCES
val BLACKLISTED_EXT_SOURCES = EHENTAI_EXT_SOURCES.keys
val BLACKLISTED_EXTENSIONS = arrayOf(
"eu.kanade.tachiyomi.extension.all.ehentai",

View file

@ -1,7 +1,9 @@
package mihon.core.migration
import eu.kanade.domain.source.service.SourcePreferences
import kotlinx.coroutines.runBlocking
import tachiyomi.core.common.preference.PreferenceStore
import tachiyomi.core.common.preference.getAndSet
import tachiyomi.data.DatabaseHandler
object MigrateUtils {
@ -9,7 +11,23 @@ object MigrateUtils {
val handler = migrationContext.get<DatabaseHandler>() ?: return
runBlocking {
handler.await { ehQueries.migrateSource(newId, oldId) }
// KMK -->
// Migrate saved searches & feeds
handler.await { ehQueries.migrateSourceSavedSearch(newId, oldId) }
handler.await { ehQueries.migrateSourceFeed(newId, oldId) }
}
// Also update pin
val preferences = migrationContext.get<SourcePreferences>() ?: return
val isPinned = oldId.toString() in preferences.pinnedSources().get()
if (isPinned) {
preferences.pinnedSources().getAndSet { pinned ->
pinned.minus(oldId.toString())
.plus(newId.toString())
}
}
// KMK <--
}
@Suppress("UNCHECKED_CAST")

View file

@ -0,0 +1,21 @@
package mihon.core.migration.migrations
import exh.source.EH_OLD_ID
import exh.source.EH_SOURCE_ID
import exh.source.EXH_OLD_ID
import exh.source.EXH_SOURCE_ID
import mihon.core.migration.MigrateUtils
import mihon.core.migration.Migration
import mihon.core.migration.MigrationContext
import tachiyomi.core.common.util.lang.withIOContext
class EHentaiMigration : Migration {
override val version: Float = 72f
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
MigrateUtils.updateSourceId(migrationContext, EH_SOURCE_ID, EH_OLD_ID)
MigrateUtils.updateSourceId(migrationContext, EXH_SOURCE_ID, EXH_OLD_ID)
return@withIOContext true
}
}

View file

@ -49,5 +49,6 @@ val migrations: List<Migration>
OfficialExtensionRepositoryMigration(),
IntegratedHentaiMigration(),
SetupAppUpdateMigration(),
EHentaiMigration(),
// KMK <--
)

View file

@ -6,6 +6,18 @@ UPDATE mangas
SET source = :newId
WHERE source = :oldId;
-- KMK -->
migrateSourceSavedSearch:
UPDATE saved_search
SET source = :newId
WHERE source = :oldId;
migrateSourceFeed:
UPDATE feed_saved_search
SET source = :newId
WHERE source = :oldId;
-- KMK <--
getChaptersByMangaIds:
SELECT * FROM chapters WHERE manga_id IN :mangaIds;

View file

@ -1,6 +1,7 @@
package exh.source
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.all.EhBasedSource
import tachiyomi.domain.manga.model.Manga
// Used to speed up isLewdSource
@ -18,13 +19,21 @@ var LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf(
// This method MUST be fast!
fun isMetadataSource(source: Long) = source in 6900..6999 ||
// KMK -->
source == EH_SOURCE_ID ||
source == EXH_SOURCE_ID ||
// KMK <--
metadataDelegatedSourceIds.binarySearch(source) >= 0
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID
// KMK -->
fun Source.isEhBasedSource() = this is EhBasedSource && id in EHENTAI_EXT_SOURCES || id in EXHENTAI_EXT_SOURCES
// KMK <--
fun Source.isMdBasedSource() = id in mangaDexSourceIds
fun Manga.isEhBasedManga() = source == EH_SOURCE_ID || source == EXH_SOURCE_ID
// KMK -->
fun Manga.isEhBasedManga() = source in EHENTAI_EXT_SOURCES || source in EXHENTAI_EXT_SOURCES
// KMK <--
fun Source.getMainSource(): Source = if (this is EnhancedHttpSource) {
this.source()

View file

@ -0,0 +1,3 @@
package eu.kanade.tachiyomi.source.online.all
interface EhBasedSource

View file

@ -2,8 +2,12 @@ package exh.source
// Lewd source IDs
const val LEWD_SOURCE_SERIES = 6900L
const val EH_SOURCE_ID = LEWD_SOURCE_SERIES + 1
const val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2
// KMK -->
const val EH_SOURCE_ID = 1713178126840476467 // LEWD_SOURCE_SERIES + 1
const val EXH_SOURCE_ID = 6225928719850211219 // LEWD_SOURCE_SERIES + 2
// KMK <--
const val NHENTAI_SOURCE_ID = 7309872737163460316L
const val PURURIN_SOURCE_ID = 2221515250486218861L
const val TSUMINO_SOURCE_ID = 6707338697138388238L
@ -11,11 +15,58 @@ const val EIGHTMUSES_SOURCE_ID = 1802675169972965535L
const val HBROWSE_SOURCE_ID = 1401584337232758222L
const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69
// KMK -->
const val EH_OLD_ID = 6901L
const val EXH_OLD_ID = 6902L
// KMK <--
const val NHENTAI_OLD_ID = 6907L
const val TSUMINO_OLD_ID = 6909L
const val HBROWSE_OLD_ID = 6912L
// KMK -->
val EHENTAI_EXT_SOURCES = mapOf(
8100626124886895451 to "ja", // E-Hentai (Ja)
57122881048805941 to "en", // E-Hentai (En)
4678440076103929247 to "zh", // E-Hentai (Zh)
1876021963378735852 to "nl", // E-Hentai (Nl)
3955189842350477641 to "fr", // E-Hentai (Fr)
4348288691341764259 to "de", // E-Hentai (De)
773611868725221145 to "hu", // E-Hentai (Hu)
5759417018342755550 to "it", // E-Hentai (It)
825187715438990384 to "ko", // E-Hentai (Ko)
6116711405602166104 to "pl", // E-Hentai (Pl)
7151438547982231541 to "pt-BR", // E-Hentai (PtBr)
2171445159732592630 to "ru", // E-Hentai (Ru)
3032959619549451093 to "es", // E-Hentai (Es)
5980349886941016589 to "th", // E-Hentai (Th)
6073266008352078708 to "vi", // E-Hentai (Vi)
5499077866612745456 to "none", // E-Hentai (None)
6140480779421365791 to "other", // E-Hentai (Other)
EH_SOURCE_ID to "all", // E-Hentai (Multi)
)
val EXHENTAI_EXT_SOURCES = mapOf(
4069364610143267166 to "ja", // E-Hentai (Ja)
6024400692103629868 to "en", // E-Hentai (En)
1394807835077780591 to "zh", // E-Hentai (Zh)
3403092744483051659 to "nl", // E-Hentai (Nl)
2063958147920418330 to "fr", // E-Hentai (Fr)
4277540540471304114 to "de", // E-Hentai (De)
5580677059017993793 to "hu", // E-Hentai (Hu)
6348816521182710144 to "it", // E-Hentai (It)
933801322201782118 to "ko", // E-Hentai (Ko)
250678340923599076 to "pl", // E-Hentai (Pl)
7151438547982231541 to "pt-BR", // E-Hentai (PtBr)
2298110591802103872 to "ru", // E-Hentai (Ru)
1471697890032830855 to "es", // E-Hentai (Es)
5297549186919793998 to "th", // E-Hentai (Th)
1904260310237764859 to "vi", // E-Hentai (Vi)
2351087900641384311 to "none", // E-Hentai (None)
609703168489499116 to "other", // E-Hentai (Other)
EXH_SOURCE_ID to "all", // E-Hentai (Multi)
)
val COMICK_IDS = setOf(
982606170401027267, // all
2971557565147974499, // en