Delegate hitomi, it is now the first fully delegated factory source. To continue using hitomi please download the extension. This comes with a lot of fixes for future delegated factory sources

This commit is contained in:
Jobobby04 2020-08-10 23:29:10 -04:00
parent f8efe5d189
commit 9a3fdc23e6
11 changed files with 91 additions and 386 deletions

View File

@ -42,7 +42,7 @@ android {
minSdkVersion AndroidConfig.minSdk
targetSdkVersion AndroidConfig.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 4
versionCode 5
versionName "1.1.1"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""

View File

@ -20,7 +20,6 @@ import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PERV_EDEN_EN_SOURCE_ID
@ -85,7 +84,6 @@ class ExtensionManager(
PERV_EDEN_EN_SOURCE_ID -> context.getDrawable(R.mipmap.ic_perveden_source)
PERV_EDEN_IT_SOURCE_ID -> context.getDrawable(R.mipmap.ic_perveden_source)
NHENTAI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_nhentai_source)
HITOMI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_hitomi_source)
MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source)
else -> null
}

View File

@ -104,7 +104,7 @@ open class SourceManager(private val context: Context) {
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 }
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.sourceId }
currentDelegatedSources.plusAssign(map)
enhancedSource
} else source
@ -139,7 +139,6 @@ open class SourceManager(private val context: 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)
return exSrcs
}
// SY <--
@ -210,10 +209,17 @@ open class SourceManager(private val context: Context) {
1802675169972965535,
"eu.kanade.tachiyomi.extension.all.eromuse.EroMuse",
EightMuses::class
),
DelegatedSource(
"Hitomi",
fillInSourceId,
"eu.kanade.tachiyomi.extension.all.hitomi.Hitomi",
Hitomi::class,
true
)
).associateBy { it.originalSourceQualifiedClassName }
var currentDelegatedSources = mutableMapOf<String, DelegatedSource>()
var currentDelegatedSources = mutableMapOf<Long, DelegatedSource>()
data class DelegatedSource(
val sourceName: String,

View File

@ -3,100 +3,49 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context
import android.net.Uri
import android.os.Build
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
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.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.asJsoup
import exh.HITOMI_SOURCE_ID
import exh.hitomi.HitomiNozomi
import exh.metadata.metadata.HitomiSearchMetadata
import exh.metadata.metadata.HitomiSearchMetadata.Companion.BASE_URL
import exh.metadata.metadata.HitomiSearchMetadata.Companion.LTN_BASE_URL
import exh.metadata.metadata.HitomiSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.HitomiDescriptionAdapter
import exh.util.urlImportFetchSearchManga
import java.text.SimpleDateFormat
import java.util.Locale
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.vepta.vdm.ByteCursor
import rx.Observable
import rx.Single
import rx.schedulers.Schedulers
import uy.kohesive.injekt.injectLazy
/**
* Man, I hate this source :(
*/
class Hitomi(val context: Context) : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImportableSource {
private val prefs: PreferencesHelper by injectLazy()
override val id = HITOMI_SOURCE_ID
/**
* Whether the source has support for latest updates.
*/
override val supportsLatest = true
/**
* Name of the source.
*/
override val name = "hitomi.la"
/**
* The class of the metadata used by this source
*/
class Hitomi(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate),
LewdSource<HitomiSearchMetadata, Document>,
UrlImportableSource {
override val metaClass = HitomiSearchMetadata::class
override val lang = if (delegate.lang == "other") "all" else delegate.lang
override val id: Long
get() = if (delegate.lang == "other") otherId else delegate.id
private var cachedTagIndexVersion: Long? = null
private var tagIndexVersionCacheTime: Long = 0
private fun tagIndexVersion(): Single<Long> {
val sCachedTagIndexVersion = cachedTagIndexVersion
return if (sCachedTagIndexVersion == null ||
tagIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()
) {
HitomiNozomi.getIndexVersion(client, "tagindex").subscribeOn(Schedulers.io()).doOnNext {
cachedTagIndexVersion = it
tagIndexVersionCacheTime = System.currentTimeMillis()
}.toSingle()
} else {
Single.just(sCachedTagIndexVersion)
// Support direct URL importing
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
urlImportFetchSearchManga(context, query) {
super.fetchSearchManga(page, query, filters)
}
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.flatMap {
parseToManga(manga, it.asJsoup()).andThen(Observable.just(manga))
}
}
private var cachedGalleryIndexVersion: Long? = null
private var galleryIndexVersionCacheTime: Long = 0
private fun galleryIndexVersion(): Single<Long> {
val sCachedGalleryIndexVersion = cachedGalleryIndexVersion
return if (sCachedGalleryIndexVersion == null ||
galleryIndexVersionCacheTime + INDEX_VERSION_CACHE_TIME_MS < System.currentTimeMillis()
) {
HitomiNozomi.getIndexVersion(client, "galleriesindex").subscribeOn(Schedulers.io()).doOnNext {
cachedGalleryIndexVersion = it
galleryIndexVersionCacheTime = System.currentTimeMillis()
}.toSingle()
} else {
Single.just(sCachedGalleryIndexVersion)
}
}
/**
* Parse the supplied input into the supplied metadata object
*/
override fun parseIntoMetadata(metadata: HitomiSearchMetadata, input: Document) {
with(metadata) {
url = input.location()
@ -109,41 +58,49 @@ class Hitomi(val context: Context) : HttpSource(), LewdSource<HitomiSearchMetada
title = galleryElement.selectFirst("h1").text()
artists = galleryElement.select("h2 a").map { it.text() }
tags += artists.map { RaisedTag("artist", it, TAG_TYPE_VIRTUAL) }
tags += artists.map { RaisedTag("artist", it, RaisedSearchMetadata.TAG_TYPE_VIRTUAL) }
input.select(".gallery-info tr").forEach {
val content = it.child(1)
when (it.child(0).text().toLowerCase()) {
"group" -> {
group = content.text()
tags += RaisedTag("group", group!!, TAG_TYPE_VIRTUAL)
tags += RaisedTag("group", group!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
}
"type" -> {
type = content.text()
tags += RaisedTag("type", type!!, TAG_TYPE_VIRTUAL)
tags += RaisedTag("type", type!!, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
}
"series" -> {
series = content.select("a").map { it.text() }
tags += series.map {
RaisedTag("series", it, TAG_TYPE_VIRTUAL)
RaisedTag("series", it, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
}
}
"language" -> {
language = content.selectFirst("a")?.attr("href")?.split('-')?.get(1)
language?.let {
tags += RaisedTag("language", it, TAG_TYPE_VIRTUAL)
tags += RaisedTag("language", it, RaisedSearchMetadata.TAG_TYPE_VIRTUAL)
}
}
"characters" -> {
characters = content.select("a").map { it.text() }
tags += characters.map { RaisedTag("character", it, TAG_TYPE_DEFAULT) }
tags += characters.map {
RaisedTag(
"character", it,
HitomiSearchMetadata.TAG_TYPE_DEFAULT
)
}
}
"tags" -> {
tags += content.select("a").map {
val ns = if (it.attr("href").startsWith("/tag/male")) "male"
else if (it.attr("href").startsWith("/tag/female")) "female"
else "misc"
RaisedTag(ns, it.text().dropLast(if (ns == "misc") 0 else 2), TAG_TYPE_DEFAULT)
RaisedTag(
ns, it.text().dropLast(if (ns == "misc") 0 else 2),
HitomiSearchMetadata.TAG_TYPE_DEFAULT
)
}
}
}
@ -153,273 +110,6 @@ class Hitomi(val context: Context) : HttpSource(), LewdSource<HitomiSearchMetada
}
}
override val lang = "all"
/**
* Base url of the website without the trailing slash, like: http://mysite.com
*/
override val baseUrl = BASE_URL
/**
* Returns the request for the popular manga given the page.
*
* @param page the page number to retrieve.
*/
override fun popularMangaRequest(page: Int) = HitomiNozomi.rangedGet(
"$LTN_BASE_URL/popular-all.nozomi",
100L * (page - 1),
99L + 100 * (page - 1)
)
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun popularMangaParse(response: Response) = throw UnsupportedOperationException()
/**
* Returns the request for the search manga given the page.
*
* @param page the page number to retrieve.
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException()
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return urlImportFetchSearchManga(context, query) {
val splitQuery = query.split(" ")
val positive = splitQuery.filter { !it.startsWith('-') }.toMutableList()
val negative = (splitQuery - positive).map { it.removePrefix("-") }
// TODO Cache the results coming out of HitomiNozomi
val hn = Single.zip(tagIndexVersion(), galleryIndexVersion()) { tv, gv -> tv to gv }
.map { HitomiNozomi(client, it.first, it.second) }
var base = if (positive.isEmpty()) {
hn.flatMap { n -> n.getGalleryIdsFromNozomi(null, "index", "all").map { n to it.toSet() } }
} else {
val q = positive.removeAt(0)
hn.flatMap { n -> n.getGalleryIdsForQuery(q).map { n to it.toSet() } }
}
base = positive.fold(base) { acc, q ->
acc.flatMap { (nozomi, mangas) ->
nozomi.getGalleryIdsForQuery(q).map {
nozomi to mangas.intersect(it)
}
}
}
base = negative.fold(base) { acc, q ->
acc.flatMap { (nozomi, mangas) ->
nozomi.getGalleryIdsForQuery(q).map {
nozomi to (mangas - it)
}
}
}
base.flatMap { (_, ids) ->
val chunks = ids.chunked(PAGE_SIZE)
nozomiIdsToMangas(chunks[page - 1]).map { mangas ->
MangasPage(mangas, page < chunks.size)
}
}.toObservable()
}
}
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException()
/**
* Returns the request for latest manga given the page.
*
* @param page the page number to retrieve.
*/
override fun latestUpdatesRequest(page: Int) = HitomiNozomi.rangedGet(
"$LTN_BASE_URL/index-all.nozomi",
100L * (page - 1),
99L + 100 * (page - 1)
)
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return client.newCall(popularMangaRequest(page))
.asObservableSuccess()
.flatMap { responseToMangas(it) }
}
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return client.newCall(latestUpdatesRequest(page))
.asObservableSuccess()
.flatMap { responseToMangas(it) }
}
fun responseToMangas(response: Response): Observable<MangasPage> {
val range = response.header("Content-Range")!!
val total = range.substringAfter('/').toLong()
val end = range.substringBefore('/').substringAfter('-').toLong()
val body = response.body!!
return parseNozomiPage(body.bytes())
.map {
MangasPage(it, end < total - 1)
}
}
private fun parseNozomiPage(array: ByteArray): Observable<List<SManga>> {
val cursor = ByteCursor(array)
val ids = (1..array.size / 4).map {
cursor.nextInt()
}
return nozomiIdsToMangas(ids).toObservable()
}
private fun nozomiIdsToMangas(ids: List<Int>): Single<List<SManga>> {
return Single.zip(
ids.map {
client.newCall(GET("$LTN_BASE_URL/galleryblock/$it.html"))
.asObservableSuccess()
.subscribeOn(Schedulers.io()) // Perform all these requests in parallel
.map { parseGalleryBlock(it) }
.toSingle()
}
) { it.map { m -> m as SManga } }
}
private fun parseGalleryBlock(response: Response): SManga {
val doc = response.asJsoup()
return SManga.create().apply {
val titleElement = doc.selectFirst("h1")
title = titleElement.text()
thumbnail_url = "https:" + if (prefs.eh_hl_useHighQualityThumbs().get()) {
doc.selectFirst("img").attr("srcset").substringBefore(' ')
} else {
doc.selectFirst("img").attr("src")
}
url = titleElement.child(0).attr("href")
// TODO Parse tags and stuff
}
}
/**
* Returns an observable with the updated details for a manga. Normally it's not needed to
* override this method.
*
* @param manga the manga to be updated.
*/
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.flatMap {
parseToManga(manga, it.asJsoup()).andThen(
Observable.just(
manga.apply {
initialized = true
}
)
)
}
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return Observable.just(
listOf(
SChapter.create().apply {
url = manga.url
name = "Chapter"
chapter_number = 0.0f
}
)
)
}
override fun pageListRequest(chapter: SChapter): Request {
return GET("$LTN_BASE_URL/galleries/${HitomiSearchMetadata.hlIdFromUrl(chapter.url)}.js")
}
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException()
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
/**
* Parses the response from the site and returns a list of pages.
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response): List<Page> {
val str = response.body!!.string()
val json = JsonParser.parseString(str.removePrefix("var galleryinfo = "))
return json["files"].array.mapIndexed { index, jsonElement ->
val hash = jsonElement["hash"].string
val ext = if (jsonElement["haswebp"].string == "0" || !prefs.hitomiAlwaysWebp().get()) jsonElement["name"].string.split('.').last() else "webp"
val path = if (jsonElement["haswebp"].string == "0" || !prefs.hitomiAlwaysWebp().get()) "images" else "webp"
val hashPath1 = hash.takeLast(1)
val hashPath2 = hash.takeLast(3).take(2)
Page(
index,
"",
"https://${subdomainFromGalleryId(hashPath2)}a.hitomi.la/$path/$hashPath1/$hashPath2/$hash.$ext"
)
}
}
// https://ltn.hitomi.la/common.js
private fun subdomainFromGalleryId(pathSegment: String): Char {
var numberOfFrontends = 3
val b = 16
var g = Integer.parseInt(pathSegment, b)
if (g < 0x30) {
numberOfFrontends = 2
}
if (g < 0x09) {
g = 1
}
return (97 + g.rem(numberOfFrontends)).toChar()
}
/**
* Parses the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
override fun imageRequest(page: Page): Request {
val request = super.imageRequest(page)
val hlId = request.url.pathSegments.let {
it[it.lastIndex - 1]
}
return request.newBuilder()
.header("Referer", "$BASE_URL/reader/$hlId.html")
.build()
}
override val matchingHosts = listOf(
"hitomi.la"
)
@ -439,9 +129,7 @@ class Hitomi(val context: Context) : HttpSource(), LewdSource<HitomiSearchMetada
}
companion object {
private val INDEX_VERSION_CACHE_TIME_MS = 1000 * 60 * 10
private val PAGE_SIZE = 25
const val otherId = 2703068117101782422L
private val DATE_FORMAT by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
SimpleDateFormat("yyyy-MM-dd HH:mm:ssX", Locale.US)

View File

@ -44,7 +44,6 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import exh.EH_SOURCE_ID
import exh.EXHMigrations
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PERV_EDEN_EN_SOURCE_ID
import exh.PERV_EDEN_IT_SOURCE_ID
@ -221,9 +220,6 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
if (NHENTAI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += NHENTAI_SOURCE_ID
}
if (HITOMI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += HITOMI_SOURCE_ID
}
}
// SY -->

View File

@ -38,7 +38,6 @@ import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PERV_EDEN_EN_SOURCE_ID
import exh.PERV_EDEN_IT_SOURCE_ID
@ -189,9 +188,6 @@ class SettingsAdvancedController : SettingsController() {
if (NHENTAI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += NHENTAI_SOURCE_ID
}
if (HITOMI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += HITOMI_SOURCE_ID
}
} else {
if (EH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
@ -208,9 +204,6 @@ class SettingsAdvancedController : SettingsController() {
if (NHENTAI_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= NHENTAI_SOURCE_ID
}
if (HITOMI_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= HITOMI_SOURCE_ID
}
}
true
}

View File

@ -2,6 +2,7 @@ package exh
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.HentaiCafe
@ -22,7 +23,7 @@ const val NHENTAI_SOURCE_ID = LEWD_SOURCE_SERIES + 7
val HENTAI_CAFE_SOURCE_ID = delegatedSourceId<HentaiCafe>()
val PURURIN_SOURCE_ID = delegatedSourceId<Pururin>()
val TSUMINO_SOURCE_ID = delegatedSourceId<Tsumino>()
const val HITOMI_SOURCE_ID = LEWD_SOURCE_SERIES + 10
const val HITOMI_OLD_SOURCE_ID = LEWD_SOURCE_SERIES + 10
val EIGHTMUSES_SOURCE_ID = delegatedSourceId<EightMuses>()
val HBROWSE_SOURCE_ID = delegatedSourceId<HBrowse>()
const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69
@ -32,18 +33,11 @@ private val DELEGATED_LEWD_SOURCES = listOf(
Pururin::class,
Tsumino::class,
HBrowse::class,
EightMuses::class
EightMuses::class,
Hitomi::class
)
val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf(
EH_SOURCE_ID,
EXH_SOURCE_ID,
NHENTAI_SOURCE_ID,
HENTAI_CAFE_SOURCE_ID,
TSUMINO_SOURCE_ID,
HITOMI_SOURCE_ID,
PURURIN_SOURCE_ID
)
private val hitomiClass = listOf(Hitomi::class)
private inline fun <reified T> delegatedSourceId(): Long? {
return SourceManager.DELEGATED_SOURCES.entries.find {
@ -53,13 +47,27 @@ private inline fun <reified T> delegatedSourceId(): Long? {
// Used to speed up isLewdSource
val lewdDelegatedSourceIds = SourceManager.currentDelegatedSources.filter {
!it.value.factory && it.value.newSourceClass in DELEGATED_LEWD_SOURCES
it.value.newSourceClass in DELEGATED_LEWD_SOURCES
}.map { it.value.sourceId }.sorted()
val hitomiSourceIds = SourceManager.currentDelegatedSources.filter {
it.value.newSourceClass in hitomiClass
}.map { it.value.sourceId }.sorted()
// This method MUST be fast!
fun isLewdSource(source: Long) = source in 6900..6999 ||
lewdDelegatedSourceIds.binarySearch(source) >= 0
val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf(
EH_SOURCE_ID,
EXH_SOURCE_ID,
NHENTAI_SOURCE_ID,
HENTAI_CAFE_SOURCE_ID,
TSUMINO_SOURCE_ID,
PURURIN_SOURCE_ID,
*hitomiSourceIds.toTypedArray()
)
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID
fun Source.isNamespaceSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID || id == NHENTAI_SOURCE_ID || id == HITOMI_SOURCE_ID || id == PURURIN_SOURCE_ID || id == TSUMINO_SOURCE_ID || id == EIGHTMUSES_SOURCE_ID || id == HBROWSE_SOURCE_ID
fun Source.isNamespaceSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID || id == NHENTAI_SOURCE_ID || id in hitomiSourceIds || id == PURURIN_SOURCE_ID || id == TSUMINO_SOURCE_ID || id == EIGHTMUSES_SOURCE_ID || id == HBROWSE_SOURCE_ID

View File

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.source.online.all.Hitomi
import exh.source.BlacklistedSources
import java.io.File
import java.net.URI
@ -85,6 +86,23 @@ object EXHMigrations {
.executeAsBlocking()
}
}
if (oldVersion < 5) {
db.inTransaction {
// Migrate Tsumino source IDs
db.lowLevel().executeSQL(
RawQuery.builder()
.query(
"""
UPDATE ${MangaTable.TABLE}
SET ${MangaTable.COL_SOURCE} = ${Hitomi.otherId}
WHERE ${MangaTable.COL_SOURCE} = 6910
""".trimIndent()
)
.affectsTables(MangaTable.TABLE)
.build()
)
}
}
// if (oldVersion < 1) { }
// do stuff here when releasing changed crap

View File

@ -41,7 +41,7 @@ object DebugFunctions {
val sourceManager: SourceManager by injectLazy()
fun forceUpgradeMigration() {
prefs.eh_lastVersionCode().set(0)
prefs.eh_lastVersionCode().set(1)
EXHMigrations.upgrade(prefs)
}
@ -68,7 +68,7 @@ object DebugFunctions {
}
private val throttleManager = EHentaiThrottleManager()
fun getDelegatedSourceList(): String = currentDelegatedSources.map { it.value.sourceName }.joinToString(separator = "\n")
fun getDelegatedSourceList(): String = currentDelegatedSources.map { it.value.sourceName + " : " + it.value.sourceId + " : " + it.value.factory }.joinToString(separator = "\n")
fun resetEHGalleriesForUpdater() {
throttleManager.resetThrottle()

View File

@ -239,9 +239,7 @@ abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
override fun getFilterList() = delegate.getFilterList()
private fun ensureDelegateCompatible() {
if (versionId != delegate.versionId ||
lang != delegate.lang
) {
if ((versionId != delegate.versionId || lang != delegate.lang) && id != delegate.id) {
throw IncompatibleDelegateException("Delegate source is not compatible (versionId: $versionId <=> ${delegate.versionId}, lang: $lang <=> ${delegate.lang})!")
}
}

View File

@ -3,20 +3,20 @@ package exh.util
import eu.kanade.tachiyomi.data.database.models.Manga
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
import exh.TSUMINO_SOURCE_ID
import exh.hitomiSourceIds
import exh.metadata.metadata.base.RaisedTag
import java.util.Locale
class SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? {
return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId == NHENTAI_SOURCE_ID || sourceId == HITOMI_SOURCE_ID) {
return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId == NHENTAI_SOURCE_ID || sourceId in hitomiSourceIds) {
val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) RaisedTag(namespace, tag, TAG_TYPE_DEFAULT) else null
if (parsed?.namespace != null) {
when (sourceId) {
HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.namespace, parsed.name.substringBefore('|').trim())
in hitomiSourceIds -> wrapTagHitomi(parsed.namespace, parsed.name.substringBefore('|').trim())
NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.namespace, parsed.name.substringBefore('|').trim())
PURURIN_SOURCE_ID -> parsed.name.substringBefore('|').trim()
TSUMINO_SOURCE_ID -> parsed.name.substringBefore('|').trim()