Delegate NHentai, to continue using NHentai download the extension, SY requires NHentai version 1.2.28

This commit is contained in:
Jobobby04 2020-08-12 00:19:51 -04:00
parent 5a1bc6e25b
commit 3fa5322133
23 changed files with 71 additions and 385 deletions

View File

@ -187,8 +187,6 @@ object PreferenceKeys {
const val eh_lock_manually = "eh_lock_manually" const val eh_lock_manually = "eh_lock_manually"
const val eh_nh_useHighQualityThumbs = "eh_nh_hq_thumbs"
const val eh_showSyncIntro = "eh_show_sync_intro" const val eh_showSyncIntro = "eh_show_sync_intro"
const val eh_readOnlySync = "eh_sync_read_only" const val eh_readOnlySync = "eh_sync_read_only"

View File

@ -312,8 +312,6 @@ class PreferencesHelper(val context: Context) {
fun eh_sessionCookie() = flowPrefs.getString(Keys.eh_sessionCookie, "") fun eh_sessionCookie() = flowPrefs.getString(Keys.eh_sessionCookie, "")
fun eh_hathPerksCookies() = flowPrefs.getString(Keys.eh_hathPerksCookie, "") fun eh_hathPerksCookies() = flowPrefs.getString(Keys.eh_hathPerksCookie, "")
fun eh_nh_useHighQualityThumbs() = flowPrefs.getBoolean(Keys.eh_nh_useHighQualityThumbs, false)
fun eh_showSyncIntro() = flowPrefs.getBoolean(Keys.eh_showSyncIntro, true) fun eh_showSyncIntro() = flowPrefs.getBoolean(Keys.eh_showSyncIntro, true)
fun eh_readOnlySync() = flowPrefs.getBoolean(Keys.eh_readOnlySync, false) fun eh_readOnlySync() = flowPrefs.getBoolean(Keys.eh_readOnlySync, false)

View File

@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
import kotlinx.coroutines.async import kotlinx.coroutines.async
import rx.Observable import rx.Observable
@ -79,7 +78,6 @@ class ExtensionManager(
return when (source.id) { return when (source.id) {
EH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source) EH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source)
EXH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source) EXH_SOURCE_ID -> context.getDrawable(R.mipmap.ic_ehentai_source)
NHENTAI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_nhentai_source)
MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source) MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source)
else -> null else -> null
} }

View File

@ -140,7 +140,6 @@ open class SourceManager(private val context: Context) {
if (prefs.enableExhentai().get()) { if (prefs.enableExhentai().get()) {
exSrcs += EHentai(EXH_SOURCE_ID, true, context) exSrcs += EHentai(EXH_SOURCE_ID, true, context)
} }
exSrcs += NHentai(context)
return exSrcs return exSrcs
} }
// SY <-- // SY <--
@ -230,6 +229,13 @@ open class SourceManager(private val context: Context) {
PERV_EDEN_IT_SOURCE_ID, PERV_EDEN_IT_SOURCE_ID,
"eu.kanade.tachiyomi.extension.it.perveden.Perveden", "eu.kanade.tachiyomi.extension.it.perveden.Perveden",
PervEden::class PervEden::class
),
DelegatedSource(
"NHentai",
fillInSourceId,
"eu.kanade.tachiyomi.extension.all.nhentai.NHentai",
NHentai::class,
true
) )
).associateBy { it.originalSourceQualifiedClassName } ).associateBy { it.originalSourceQualifiedClassName }

View File

@ -8,116 +8,38 @@ import com.github.salomonbrys.kotson.nullLong
import com.github.salomonbrys.kotson.nullObj import com.github.salomonbrys.kotson.nullObj
import com.github.salomonbrys.kotson.nullString import com.github.salomonbrys.kotson.nullString
import com.google.gson.JsonParser import com.google.gson.JsonParser
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage 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.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.asJsoup
import exh.NHENTAI_SOURCE_ID
import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.NHentaiSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.NHentaiDescriptionAdapter import exh.ui.metadata.adapters.NHentaiDescriptionAdapter
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
/** open class NHentai(delegate: HttpSource, val context: Context) :
* NHentai source DelegatedHttpSource(delegate),
*/ LewdSource<NHentaiSearchMetadata, Response>,
UrlImportableSource {
class NHentai(val context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata, Response>, UrlImportableSource {
override val metaClass = NHentaiSearchMetadata::class override val metaClass = NHentaiSearchMetadata::class
override val lang = if (delegate.lang == "other") "all" else delegate.lang
override fun fetchPopularManga(page: Int): Observable<MangasPage> { override val id: Long
// TODO There is currently no way to get the most popular mangas get() = if (delegate.lang == "other") otherId else delegate.id
// TODO Instead, we delegate this to the latest updates thing to avoid confusing users with an empty screen
return fetchLatestUpdates(page)
}
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException()
override fun popularMangaParse(response: Response) = throw UnsupportedOperationException()
// Support direct URL importing // Support direct URL importing
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
val trimmedIdQuery = query.trim().removePrefix("id:") urlImportFetchSearchManga(context, query) {
val newQuery = if (trimmedIdQuery.toIntOrNull() ?: -1 >= 0) { super.fetchSearchManga(page, query, filters)
"$baseUrl/g/$trimmedIdQuery/"
} else query
return urlImportFetchSearchManga(context, newQuery) {
searchMangaRequestObservable(page, query, filters).flatMap {
client.newCall(it).asObservableSuccess()
}.map { response ->
searchMangaParse(response)
}
}
}
private fun searchMangaRequestObservable(page: Int, query: String, filters: FilterList): Observable<Request> {
val filterList = if (filters.isEmpty()) getFilterList() else filters
val advQuery = combineQuery(filterList)
val favoriteFilter = filterList.findInstance<FavoriteFilter>()
val isOkayToSort = filterList.findInstance<UploadedFilter>()?.state?.isBlank() ?: true
val url: HttpUrl.Builder
if (favoriteFilter?.state == true) {
url = "$baseUrl/favorites".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("q", "$query $advQuery")
.addQueryParameter("page", page.toString())
} else {
url = "$baseUrl/search".toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("q", "$query $advQuery")
.addQueryParameter("page", page.toString())
if (isOkayToSort) {
filterList.findInstance<SortFilter>()?.let { f ->
url.addQueryParameter("sort", f.toUriPart())
}
}
} }
return client.newCall(nhGet(url.toString()))
.asObservableSuccess()
.map { nhGet(url.toString(), page) }
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException()
override fun searchMangaParse(response: Response) = parseResultPage(response)
override fun latestUpdatesRequest(page: Int): Request {
val uri = Uri.parse(baseUrl).buildUpon()
uri.appendQueryParameter("page", page.toString())
return nhGet(uri.toString(), page)
}
override fun latestUpdatesParse(response: Response) = parseResultPage(response)
override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException()
/**
* 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> { override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga)) return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess() .asObservableSuccess()
@ -132,37 +54,10 @@ class NHentai(val context: Context) : HttpSource(), LewdSource<NHentaiSearchMeta
} }
} }
override fun mangaDetailsRequest(manga: SManga) = nhGet(baseUrl + manga.url)
private fun parseResultPage(response: Response): MangasPage {
val doc = response.asJsoup()
// TODO Parse lang + tags
val mangas = doc.select(".gallery > a").map {
SManga.create().apply {
url = it.attr("href")
title = it.selectFirst(".caption").text()
// last() is a hack to ignore the lazy-loader placeholder image on the front page
thumbnail_url = it.select("img").last().attr("src")
// In some pages, the thumbnail url does not include the protocol
if (!thumbnail_url!!.startsWith("https:")) thumbnail_url = "https:$thumbnail_url"
}
}
val hasNextPage = if (!response.request.url.queryParameterNames.contains(REVERSE_PARAM)) {
doc.selectFirst(".next") != null
} else {
response.request.url.queryParameter(REVERSE_PARAM)!!.toBoolean()
}
return MangasPage(mangas, hasNextPage)
}
override fun parseIntoMetadata(metadata: NHentaiSearchMetadata, input: Response) { override fun parseIntoMetadata(metadata: NHentaiSearchMetadata, input: Response) {
val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace(UNICODE_ESCAPE_REGEX) { it.groupValues[1].toInt(radix = 16).toChar().toString() } val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace(
UNICODE_ESCAPE_REGEX
) { it.groupValues[1].toInt(radix = 16).toChar().toString() }
val obj = JsonParser.parseString(json).asJsonObject val obj = JsonParser.parseString(json).asJsonObject
with(metadata) { with(metadata) {
@ -199,166 +94,12 @@ class NHentai(val context: Context) : HttpSource(), LewdSource<NHentaiSearchMeta
tags.clear() tags.clear()
}?.forEach { }?.forEach {
if (it.first != null && it.second != null) { if (it.first != null && it.second != null) {
tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == "category") TAG_TYPE_VIRTUAL else TAG_TYPE_DEFAULT)) tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == "category") RaisedSearchMetadata.TAG_TYPE_VIRTUAL else NHentaiSearchMetadata.TAG_TYPE_DEFAULT))
} }
} }
} }
} }
private fun getOrLoadMetadata(mangaId: Long?, nhId: Long) = getOrLoadMetadata(mangaId) {
client.newCall(nhGet(baseUrl + NHentaiSearchMetadata.nhIdToPath(nhId)))
.asObservableSuccess()
.toSingle()
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = Observable.just(
listOf(
SChapter.create().apply {
url = manga.url
name = "Chapter"
chapter_number = 1f
getOrLoadMetadata(mangaId, NHentaiSearchMetadata.nhUrlToId(manga.url)).toBlocking().value().uploadDate?.let { date_upload = it * 1000 }
}
)
)
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = getOrLoadMetadata(chapter.mangaId, NHentaiSearchMetadata.nhUrlToId(chapter.url)).map { metadata ->
if (metadata.mediaId == null) {
emptyList()
} else {
metadata.pageImageTypes.mapIndexed { index, s ->
val imageUrl = imageUrlFromType(metadata.mediaId!!, index + 1, s)
Page(index, imageUrl!!, imageUrl)
}
}
}.toObservable()
override fun fetchImageUrl(page: Page) = Observable.just(page.imageUrl!!)!!
private fun imageUrlFromType(mediaId: String, page: Int, t: String) = NHentaiSearchMetadata.typeToExtension(t)?.let {
"https://i.nhentai.net/galleries/$mediaId/$page.$it"
}
override fun chapterListParse(response: Response): List<SChapter> {
throw NotImplementedError("Unused method called!")
}
override fun pageListParse(response: Response): List<Page> {
throw NotImplementedError("Unused method called!")
}
override fun imageUrlParse(response: Response): String {
throw NotImplementedError("Unused method called!")
}
private fun combineQuery(filters: FilterList): String {
val stringBuilder = StringBuilder()
val advSearch = filters.filterIsInstance<AdvSearchEntryFilter>().flatMap { filter ->
val splitState = filter.state.split(",").map(String::trim).filterNot(String::isBlank)
splitState.map {
AdvSearchEntry(filter.name, it.removePrefix("-"), it.startsWith("-"))
}
}
advSearch.forEach { entry ->
if (entry.exclude) stringBuilder.append("-")
stringBuilder.append("${entry.name}:")
stringBuilder.append(entry.text)
stringBuilder.append(" ")
}
val langFilter = filters.filterIsInstance<FilterLang>().firstOrNull()
if (langFilter != null) {
val language = SOURCE_LANG_LIST.first { it.first == langFilter.values[langFilter.state] }.second
if (!language.isBlank()) {
stringBuilder.append("language:$language")
}
}
return stringBuilder.toString()
}
data class AdvSearchEntry(val name: String, val text: String, val exclude: Boolean)
override fun getFilterList(): FilterList = FilterList(
Filter.Header("Separate tags with commas (,)"),
Filter.Header("Prepend with dash (-) to exclude"),
TagFilter(),
CategoryFilter(),
GroupFilter(),
ArtistFilter(),
ParodyFilter(),
CharactersFilter(),
Filter.Header("Uploaded valid units are h, d, w, m, y."),
Filter.Header("example: (>20d)"),
UploadedFilter(),
Filter.Separator(),
SortFilter(),
Filter.Header("Sort is ignored if favorites only"),
FavoriteFilter(),
FilterLang()
)
class TagFilter : AdvSearchEntryFilter("Tags")
class CategoryFilter : AdvSearchEntryFilter("Categories")
class GroupFilter : AdvSearchEntryFilter("Groups")
class ArtistFilter : AdvSearchEntryFilter("Artists")
class ParodyFilter : AdvSearchEntryFilter("Parodies")
class CharactersFilter : AdvSearchEntryFilter("Characters")
class UploadedFilter : AdvSearchEntryFilter("Uploaded")
open class AdvSearchEntryFilter(name: String) : Filter.Text(name)
private class FavoriteFilter : Filter.CheckBox("Show favorites only", false)
// language filtering
private class FilterLang : Filter.Select<String>("Language", SOURCE_LANG_LIST.map { it.first }.toTypedArray())
private class SortFilter : UriPartFilter(
"Sort By",
arrayOf(
Pair("Popular: All Time", "popular"),
Pair("Popular: Week", "popular-week"),
Pair("Popular: Today", "popular-today"),
Pair("Recent", "date")
)
)
private open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
private val appName by lazy {
context.getString(R.string.app_name)
}
private fun nhGet(url: String, tag: Any? = null) = GET(url)
.newBuilder()
.header(
"User-Agent",
"Mozilla/5.0 (X11; Linux x86_64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/56.0.2924.87 " +
"Safari/537.36 " +
"$appName/${BuildConfig.VERSION_CODE}"
)
.tag(tag).build()
override val id = NHENTAI_SOURCE_ID
override val lang = "all"
override val name = "nhentai"
override val baseUrl = NHentaiSearchMetadata.BASE_URL
override val supportsLatest = true
// === URL IMPORT STUFF
override val matchingHosts = listOf( override val matchingHosts = listOf(
"nhentai.net" "nhentai.net"
) )
@ -376,15 +117,9 @@ class NHentai(val context: Context) : HttpSource(), LewdSource<NHentaiSearchMeta
} }
companion object { companion object {
const val otherId = 7309872737163460316L
private val GALLERY_JSON_REGEX = Regex(".parse\\(\"(.*)\"\\);") private val GALLERY_JSON_REGEX = Regex(".parse\\(\"(.*)\"\\);")
private val UNICODE_ESCAPE_REGEX = Regex("\\\\u([0-9a-fA-F]{4})") private val UNICODE_ESCAPE_REGEX = Regex("\\\\u([0-9a-fA-F]{4})")
private const val REVERSE_PARAM = "TEH_REVERSE"
private val SOURCE_LANG_LIST = listOf(
Pair("All", ""),
Pair("English", "english"),
Pair("Japanese", "japanese"),
Pair("Chinese", "chinese")
)
} }
} }

View File

@ -45,9 +45,11 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.NHENTAI_SOURCE_ID import exh.PERV_EDEN_EN_SOURCE_ID
import exh.PERV_EDEN_IT_SOURCE_ID
import exh.favorites.FavoritesIntroDialog import exh.favorites.FavoritesIntroDialog
import exh.favorites.FavoritesSyncStatus import exh.favorites.FavoritesSyncStatus
import exh.nHentaiSourceIds
import exh.ui.LoaderManager import exh.ui.LoaderManager
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlinx.android.synthetic.main.main_activity.tabs import kotlinx.android.synthetic.main.main_activity.tabs
@ -521,7 +523,7 @@ class LibraryController(
binding.actionToolbar.findItem(R.id.action_download_unread)?.isVisible = selectedMangas.any { it.source != LocalSource.ID } binding.actionToolbar.findItem(R.id.action_download_unread)?.isVisible = selectedMangas.any { it.source != LocalSource.ID }
// SY --> // SY -->
binding.actionToolbar.findItem(R.id.action_clean)?.isVisible = selectedMangas.any { it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID || it.source == NHENTAI_SOURCE_ID } binding.actionToolbar.findItem(R.id.action_clean)?.isVisible = selectedMangas.any { it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID || it.source in nHentaiSourceIds || it.source == PERV_EDEN_EN_SOURCE_ID || it.source == PERV_EDEN_IT_SOURCE_ID }
// SY <-- // SY <--
} }
return false return false
@ -640,7 +642,7 @@ class LibraryController(
// SY --> // SY -->
private fun cleanTitles() { private fun cleanTitles() {
val mangas = selectedMangas.filter { it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID || it.source == NHENTAI_SOURCE_ID }.toList() val mangas = selectedMangas.filter { it.source == EH_SOURCE_ID || it.source == EXH_SOURCE_ID || it.source in nHentaiSourceIds }.toList()
presenter.cleanTitles(mangas) presenter.cleanTitles(mangas)
destroyActionModeIfNeeded() destroyActionModeIfNeeded()
} }

View File

@ -44,7 +44,6 @@ import eu.kanade.tachiyomi.util.lang.launchUI
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXHMigrations import exh.EXHMigrations
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.eh.EHentaiUpdateWorker import exh.eh.EHentaiUpdateWorker
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
import exh.uconfig.WarnConfigureDialogController import exh.uconfig.WarnConfigureDialogController
@ -209,9 +208,6 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
if (EXH_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { if (EXH_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
} }
if (NHENTAI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += NHENTAI_SOURCE_ID
}
} }
// SY --> // SY -->

View File

@ -38,7 +38,6 @@ import eu.kanade.tachiyomi.util.system.powerManager
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.debug.SettingsDebugController import exh.debug.SettingsDebugController
import exh.log.EHLogLevel import exh.log.EHLogLevel
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
@ -177,9 +176,6 @@ class SettingsAdvancedController : SettingsController() {
if (EXH_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { if (EXH_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
} }
if (NHENTAI_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += NHENTAI_SOURCE_ID
}
} else { } else {
if (EH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) { if (EH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
@ -187,9 +183,6 @@ class SettingsAdvancedController : SettingsController() {
if (EXH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) { if (EXH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID
} }
if (NHENTAI_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= NHENTAI_SOURCE_ID
}
} }
true true
} }

View File

@ -73,12 +73,6 @@ class SettingsMainController : SettingsController() {
titleRes = R.string.pref_category_eh titleRes = R.string.pref_category_eh
onClick { navigateTo(SettingsEhController()) } onClick { navigateTo(SettingsEhController()) }
} }
preference {
iconRes = R.drawable.eh_ic_nhlogo_color
iconTint = tintColor
titleRes = R.string.pref_category_nh
onClick { navigateTo(SettingsNhController()) }
}
} }
// SY <-- // SY <--
preference { preference {

View File

@ -1,26 +0,0 @@
package eu.kanade.tachiyomi.ui.setting
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
import eu.kanade.tachiyomi.util.preference.defaultValue
import eu.kanade.tachiyomi.util.preference.summaryRes
import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes
/**
* nhentai Settings fragment
*/
class SettingsNhController : SettingsController() {
override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
titleRes = R.string.pref_category_nh
switchPreference {
titleRes = R.string.high_quality_thumbnails
summaryRes = R.string.high_quality_thumbnails_summary
key = PreferenceKeys.eh_nh_useHighQualityThumbs
defaultValue = false
}
}
}

View File

@ -3,6 +3,7 @@ package exh
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.Hitomi import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.all.PervEden import eu.kanade.tachiyomi.source.online.all.PervEden
import eu.kanade.tachiyomi.source.online.english.EightMuses import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse import eu.kanade.tachiyomi.source.online.english.HBrowse
@ -20,7 +21,6 @@ const val EH_SOURCE_ID = LEWD_SOURCE_SERIES + 1
const val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2 const val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2
const val PERV_EDEN_EN_SOURCE_ID = 4673633799850248749 const val PERV_EDEN_EN_SOURCE_ID = 4673633799850248749
const val PERV_EDEN_IT_SOURCE_ID = 1433898225963724122 const val PERV_EDEN_IT_SOURCE_ID = 1433898225963724122
const val NHENTAI_SOURCE_ID = LEWD_SOURCE_SERIES + 7
const val HENTAI_CAFE_SOURCE_ID = 260868874183818481 const val HENTAI_CAFE_SOURCE_ID = 260868874183818481
const val PURURIN_SOURCE_ID = 2221515250486218861 const val PURURIN_SOURCE_ID = 2221515250486218861
const val TSUMINO_SOURCE_ID = 6707338697138388238 const val TSUMINO_SOURCE_ID = 6707338697138388238
@ -35,10 +35,12 @@ private val DELEGATED_LEWD_SOURCES = listOf(
HBrowse::class, HBrowse::class,
EightMuses::class, EightMuses::class,
Hitomi::class, Hitomi::class,
PervEden::class PervEden::class,
NHentai::class
) )
private val hitomiClass = listOf(Hitomi::class) private val hitomiClass = listOf(Hitomi::class)
private val nHentaiClass = listOf(NHentai::class)
// Used to speed up isLewdSource // Used to speed up isLewdSource
val lewdDelegatedSourceIds = SourceManager.currentDelegatedSources.filter { val lewdDelegatedSourceIds = SourceManager.currentDelegatedSources.filter {
@ -49,6 +51,10 @@ val hitomiSourceIds = SourceManager.currentDelegatedSources.filter {
it.value.newSourceClass in hitomiClass it.value.newSourceClass in hitomiClass
}.map { it.value.sourceId }.sorted() }.map { it.value.sourceId }.sorted()
val nHentaiSourceIds = SourceManager.currentDelegatedSources.filter {
it.value.newSourceClass in nHentaiClass
}.map { it.value.sourceId }.sorted()
// This method MUST be fast! // This method MUST be fast!
fun isLewdSource(source: Long) = source in 6900..6999 || fun isLewdSource(source: Long) = source in 6900..6999 ||
lewdDelegatedSourceIds.binarySearch(source) >= 0 lewdDelegatedSourceIds.binarySearch(source) >= 0
@ -56,13 +62,13 @@ fun isLewdSource(source: Long) = source in 6900..6999 ||
val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf( val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf(
EH_SOURCE_ID, EH_SOURCE_ID,
EXH_SOURCE_ID, EXH_SOURCE_ID,
NHENTAI_SOURCE_ID,
HENTAI_CAFE_SOURCE_ID, HENTAI_CAFE_SOURCE_ID,
TSUMINO_SOURCE_ID, TSUMINO_SOURCE_ID,
PURURIN_SOURCE_ID, PURURIN_SOURCE_ID,
*hitomiSourceIds.toTypedArray() *hitomiSourceIds.toTypedArray(),
*nHentaiSourceIds.toTypedArray()
) )
fun Source.isEhBasedSource() = id == EH_SOURCE_ID || id == EXH_SOURCE_ID 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 in hitomiSourceIds || 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 in nHentaiSourceIds || id in hitomiSourceIds || id == PURURIN_SOURCE_ID || id == TSUMINO_SOURCE_ID || id == EIGHTMUSES_SOURCE_ID || id == HBROWSE_SOURCE_ID

View File

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.UpdaterJob import eu.kanade.tachiyomi.data.updater.UpdaterJob
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
import eu.kanade.tachiyomi.source.online.all.Hitomi import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.all.NHentai
import exh.source.BlacklistedSources import exh.source.BlacklistedSources
import java.io.File import java.io.File
import java.net.URI import java.net.URI
@ -128,10 +129,22 @@ object EXHMigrations {
.affectsTables(MangaTable.TABLE) .affectsTables(MangaTable.TABLE)
.build() .build()
) )
db.lowLevel().executeSQL(
RawQuery.builder()
.query(
"""
UPDATE ${MangaTable.TABLE}
SET ${MangaTable.COL_SOURCE} = ${NHentai.otherId}
WHERE ${MangaTable.COL_SOURCE} = 6907
""".trimIndent()
)
.affectsTables(MangaTable.TABLE)
.build()
)
} }
} }
// if (oldVersion < 1) { } // if (oldVersion < 1) { } (1 is current release version)
// do stuff here when releasing changed crap // do stuff here when releasing changed crap
// TODO BE CAREFUL TO NOT FUCK UP MergedSources IF CHANGING URLs // TODO BE CAREFUL TO NOT FUCK UP MergedSources IF CHANGING URLs
@ -155,6 +168,13 @@ object EXHMigrations {
manga.source = PERV_EDEN_IT_SOURCE_ID manga.source = PERV_EDEN_IT_SOURCE_ID
} }
if (manga.source == 6907L) {
// Migrate the old source to the delegated one
manga.source = NHentai.otherId
// Migrate nhentai URLs
manga.url = getUrlWithoutDomain(manga.url)
}
// Migrate HentaiCafe source IDs // Migrate HentaiCafe source IDs
if (manga.source == 6908L) { if (manga.source == 6908L) {
manga.source = HENTAI_CAFE_SOURCE_ID manga.source = HENTAI_CAFE_SOURCE_ID
@ -173,16 +193,6 @@ object EXHMigrations {
manga.source = HBROWSE_SOURCE_ID manga.source = HBROWSE_SOURCE_ID
} }
// Migrate nhentai URLs
if (manga.source == NHENTAI_SOURCE_ID) {
manga.url = getUrlWithoutDomain(manga.url)
}
// Allow importing of nhentai extension backups
if (manga.source in BlacklistedSources.NHENTAI_EXT_SOURCES) {
manga.source = NHENTAI_SOURCE_ID
}
// Allow importing of EHentai extension backups // Allow importing of EHentai extension backups
if (manga.source in BlacklistedSources.EHENTAI_EXT_SOURCES) { if (manga.source in BlacklistedSources.EHENTAI_EXT_SOURCES) {
manga.source = EH_SOURCE_ID manga.source = EH_SOURCE_ID

View File

@ -2,13 +2,11 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX import exh.metadata.ONGOING_SUFFIX
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import java.util.Date import java.util.Date
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
class NHentaiSearchMetadata : RaisedSearchMetadata() { class NHentaiSearchMetadata : RaisedSearchMetadata() {
@ -41,13 +39,8 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
nhId?.let { manga.url = nhIdToPath(it) } nhId?.let { manga.url = nhIdToPath(it) }
if (mediaId != null) { if (mediaId != null) {
val hqThumbs = Injekt.get<PreferencesHelper>().eh_nh_useHighQualityThumbs().get() typeToExtension(coverImageType)?.let {
typeToExtension(if (hqThumbs) coverImageType else thumbnailImageType)?.let { manga.thumbnail_url = "https://t.nhentai.net/galleries/$mediaId/cover.$it"
manga.thumbnail_url = "https://t.nhentai.net/galleries/$mediaId/${if (hqThumbs) {
"cover"
} else {
"thumb"
}}.$it"
} }
} }

View File

@ -3,11 +3,6 @@ package exh.source
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
object BlacklistedSources { object BlacklistedSources {
val NHENTAI_EXT_SOURCES = listOf(
3122156392225024195,
4726175775739752699,
2203215402871965477
)
val EHENTAI_EXT_SOURCES = listOf( val EHENTAI_EXT_SOURCES = listOf(
8100626124886895451, 8100626124886895451,
57122881048805941, 57122881048805941,
@ -28,12 +23,10 @@ object BlacklistedSources {
6140480779421365791 6140480779421365791
) )
val BLACKLISTED_EXT_SOURCES = NHENTAI_EXT_SOURCES + val BLACKLISTED_EXT_SOURCES = EHENTAI_EXT_SOURCES
EHENTAI_EXT_SOURCES
val BLACKLISTED_EXTENSIONS = listOf( val BLACKLISTED_EXTENSIONS = listOf(
"eu.kanade.tachiyomi.extension.all.ehentai", "eu.kanade.tachiyomi.extension.all.ehentai"
"eu.kanade.tachiyomi.extension.all.nhentai"
) )
var HIDDEN_SOURCES = listOf( var HIDDEN_SOURCES = listOf(

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.NHENTAI_SOURCE_ID import exh.nHentaiSourceIds
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -12,7 +12,7 @@ fun Manga.isLewd(): Boolean {
val sourceName = Injekt.get<SourceManager>().get(source)?.name val sourceName = Injekt.get<SourceManager>().get(source)?.name
val currentTags = getGenres() ?: emptyList() val currentTags = getGenres() ?: emptyList()
if (source == EH_SOURCE_ID || source == EXH_SOURCE_ID || source == NHENTAI_SOURCE_ID) { if (source == EH_SOURCE_ID || source == EXH_SOURCE_ID || source in nHentaiSourceIds) {
return !currentTags.any { tag -> isNonHentaiTag(tag) } return !currentTags.any { tag -> isNonHentaiTag(tag) }
} }

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -24,7 +24,6 @@
<!-- Subsections --> <!-- Subsections -->
<string name="pref_category_all_sources">Todas as fontes</string> <string name="pref_category_all_sources">Todas as fontes</string>
<string name="pref_category_eh">E-Hentai</string> <string name="pref_category_eh">E-Hentai</string>
<string name="pref_category_nh">nhentai</string>
<string name="pref_category_fork">Configurações do fork</string> <string name="pref_category_fork">Configurações do fork</string>
<!-- EH Settings --> <!-- EH Settings -->
@ -136,10 +135,6 @@
<!-- Library settings --> <!-- Library settings -->
<string name="pref_skip_pre_migration_summary">Use as últimas fontes e preferências da pré-migração salvas para migrar em massa</string> <string name="pref_skip_pre_migration_summary">Use as últimas fontes e preferências da pré-migração salvas para migrar em massa</string>
<!-- Other Settings -->
<string name="high_quality_thumbnails">Usar miniaturas de alta qualidade</string>
<string name="high_quality_thumbnails_summary">Pode carregar os resultados da pesquisa mais lentamente</string>
<!-- Reader Settings --> <!-- Reader Settings -->
<string name="download_threads">Downloads simultâneos</string> <string name="download_threads">Downloads simultâneos</string>
<string name="download_threads_summary">Valores maiores podem acelerar significativamente o download, mas podem também causar banimentos. O valor recomendado é 2 ou 3. Valor atual: %s</string> <string name="download_threads_summary">Valores maiores podem acelerar significativamente o download, mas podem também causar banimentos. O valor recomendado é 2 ou 3. Valor atual: %s</string>

View File

@ -29,7 +29,6 @@
<!-- Subsections --> <!-- Subsections -->
<string name="pref_category_all_sources">All Sources</string> <string name="pref_category_all_sources">All Sources</string>
<string name="pref_category_eh">E-Hentai</string> <string name="pref_category_eh">E-Hentai</string>
<string name="pref_category_nh">nhentai</string>
<string name="pref_category_fork">Fork Settings</string> <string name="pref_category_fork">Fork Settings</string>
<!-- EH Settings --> <!-- EH Settings -->
@ -151,10 +150,6 @@
<!-- Library settings --> <!-- Library settings -->
<string name="pref_skip_pre_migration_summary">Use last saved pre-migration preferences and sources to mass migrate</string> <string name="pref_skip_pre_migration_summary">Use last saved pre-migration preferences and sources to mass migrate</string>
<!-- Other Settings -->
<string name="high_quality_thumbnails">Use high-quality thumbnails</string>
<string name="high_quality_thumbnails_summary">May slow down search results</string>
<!-- Reader Settings --> <!-- Reader Settings -->
<string name="download_threads">Download threads</string> <string name="download_threads">Download threads</string>
<string name="download_threads_summary">Higher values can speed up image downloading significantly, but can also trigger bans. Recommended value is 2 or 3. Current value is: %s</string> <string name="download_threads_summary">Higher values can speed up image downloading significantly, but can also trigger bans. Recommended value is 2 or 3. Current value is: %s</string>