Add nhentai source.
This commit is contained in:
parent
0a7812bb2c
commit
c8a8eb0a4d
@ -43,6 +43,7 @@ TachiyomiEH is a fork of the [original Tachiyomi app](https://github.com/inorich
|
|||||||
* E-Hentai
|
* E-Hentai
|
||||||
* ExHentai
|
* ExHentai
|
||||||
* PervEden
|
* PervEden
|
||||||
|
* nhentai
|
||||||
|
|
||||||
TachiyomiEH is fully compatible with Tachiyomi source extensions.
|
TachiyomiEH is fully compatible with Tachiyomi source extensions.
|
||||||
Backups from Tachiyomi are also compatible with TachiyomiEH (and vice versa).
|
Backups from Tachiyomi are also compatible with TachiyomiEH (and vice versa).
|
||||||
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
|
|||||||
import eu.kanade.tachiyomi.source.online.all.EHentaiMetadata
|
import eu.kanade.tachiyomi.source.online.all.EHentaiMetadata
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.source.online.YamlHttpSource
|
import eu.kanade.tachiyomi.source.online.YamlHttpSource
|
||||||
|
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.*
|
import eu.kanade.tachiyomi.source.online.english.*
|
||||||
import eu.kanade.tachiyomi.source.online.german.WieManga
|
import eu.kanade.tachiyomi.source.online.german.WieManga
|
||||||
@ -99,6 +100,7 @@ open class SourceManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, "en")
|
exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, "en")
|
||||||
exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, "it")
|
exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, "it")
|
||||||
|
exSrcs += NHentai(context)
|
||||||
return exSrcs
|
return exSrcs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,10 +164,7 @@ class EHentai(override val id: Long,
|
|||||||
exh = this@EHentai.exh
|
exh = this@EHentai.exh
|
||||||
title = select("#gn").text().nullIfBlank()?.trim()
|
title = select("#gn").text().nullIfBlank()?.trim()
|
||||||
|
|
||||||
altTitles.clear()
|
altTitle = select("#gj").text().nullIfBlank()?.trim()
|
||||||
select("#gj").text().nullIfBlank()?.trim()?.let { newAltTitle ->
|
|
||||||
altTitles.add(newAltTitle)
|
|
||||||
}
|
|
||||||
|
|
||||||
thumbnailUrl = select("#gd1 img").attr("src").nullIfBlank()?.trim()
|
thumbnailUrl = select("#gd1 img").attr("src").nullIfBlank()?.trim()
|
||||||
|
|
||||||
|
@ -0,0 +1,227 @@
|
|||||||
|
package eu.kanade.tachiyomi.source.online.all
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import com.github.salomonbrys.kotson.get
|
||||||
|
import com.github.salomonbrys.kotson.int
|
||||||
|
import com.github.salomonbrys.kotson.long
|
||||||
|
import com.github.salomonbrys.kotson.string
|
||||||
|
import com.google.gson.JsonElement
|
||||||
|
import com.google.gson.JsonNull
|
||||||
|
import com.google.gson.JsonObject
|
||||||
|
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.source.model.*
|
||||||
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import exh.NHENTAI_SOURCE_ID
|
||||||
|
import exh.metadata.MetadataHelper
|
||||||
|
import exh.metadata.copyTo
|
||||||
|
import exh.metadata.models.NHentaiMetadata
|
||||||
|
import exh.metadata.models.Tag
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NHentai source
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NHentai(context: Context) : HttpSource() {
|
||||||
|
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||||
|
//TODO There is currently no way to get the most popular mangas
|
||||||
|
//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): Request {
|
||||||
|
TODO("Currently unavailable!")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response): MangasPage {
|
||||||
|
TODO("Currently unavailable!")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
//Currently we have no filters
|
||||||
|
//TODO Filter builder
|
||||||
|
val uri = Uri.parse("$baseUrl/api/galleries/search").buildUpon()
|
||||||
|
uri.appendQueryParameter("query", query)
|
||||||
|
uri.appendQueryParameter("page", page.toString())
|
||||||
|
return nhGet(uri.toString(), page)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response)
|
||||||
|
= parseResultPage(response)
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request {
|
||||||
|
val uri = Uri.parse("$baseUrl/api/galleries/all").buildUpon()
|
||||||
|
uri.appendQueryParameter("page", page.toString())
|
||||||
|
return nhGet(uri.toString(), page)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response)
|
||||||
|
= parseResultPage(response)
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response)
|
||||||
|
= parseGallery(jsonParser.parse(response.body().string()).asJsonObject)
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga)
|
||||||
|
= urlToDetailsRequest(manga.url)
|
||||||
|
|
||||||
|
fun urlToDetailsRequest(url: String)
|
||||||
|
= nhGet(baseUrl + "/api/gallery/" + url.split("/").last())
|
||||||
|
|
||||||
|
fun parseResultPage(response: Response): MangasPage {
|
||||||
|
val res = jsonParser.parse(response.body().string()).asJsonObject
|
||||||
|
|
||||||
|
val error = res.get("error")
|
||||||
|
if(error == null) {
|
||||||
|
val results = res.getAsJsonArray("result")?.map {
|
||||||
|
parseGallery(it.asJsonObject)
|
||||||
|
}
|
||||||
|
val numPages = res.get("num_pages")?.int
|
||||||
|
if(results != null && numPages != null)
|
||||||
|
return MangasPage(results, numPages < response.request().tag() as Int)
|
||||||
|
} else {
|
||||||
|
Timber.w("An error occurred while performing the search: $error")
|
||||||
|
}
|
||||||
|
return MangasPage(emptyList(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rawParseGallery(obj: JsonObject) = NHentaiMetadata().apply {
|
||||||
|
uploadDate = obj.get("upload_date")?.notNull()?.long
|
||||||
|
|
||||||
|
favoritesCount = obj.get("num_favorites")?.notNull()?.long
|
||||||
|
|
||||||
|
mediaId = obj.get("media_id")?.notNull()?.string
|
||||||
|
|
||||||
|
obj.get("title")?.asJsonObject?.let {
|
||||||
|
japaneseTitle = it.get("japanese")?.notNull()?.string
|
||||||
|
shortTitle = it.get("pretty")?.notNull()?.string
|
||||||
|
englishTitle = it.get("english")?.notNull()?.string
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.get("images")?.asJsonObject?.let {
|
||||||
|
coverImageType = it.get("cover")?.get("t")?.notNull()?.asString
|
||||||
|
it.get("pages")?.asJsonArray?.map {
|
||||||
|
it?.asJsonObject?.get("t")?.notNull()?.asString
|
||||||
|
}?.filterNotNull()?.let {
|
||||||
|
pageImageTypes.clear()
|
||||||
|
pageImageTypes.addAll(it)
|
||||||
|
}
|
||||||
|
thumbnailImageType = it.get("thumbnail")?.get("t")?.notNull()?.asString
|
||||||
|
}
|
||||||
|
|
||||||
|
scanlator = obj.get("scanlator")?.notNull()?.asString
|
||||||
|
|
||||||
|
id = obj.get("id")?.asLong
|
||||||
|
|
||||||
|
obj.get("tags")?.asJsonArray?.map {
|
||||||
|
val asObj = it.asJsonObject
|
||||||
|
Pair(asObj.get("type")?.string, asObj.get("name")?.string)
|
||||||
|
}?.apply {
|
||||||
|
tags.clear()
|
||||||
|
}?.forEach {
|
||||||
|
if(it.first != null && it.second != null)
|
||||||
|
tags.getOrPut(it.first!!, { ArrayList() }).add(Tag(it.second!!, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseGallery(obj: JsonObject) = rawParseGallery(obj).let {
|
||||||
|
metadataHelper.writeGallery(it, id)
|
||||||
|
|
||||||
|
SManga.create().apply {
|
||||||
|
it.copyTo(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lazyLoadMetadata(url: String) =
|
||||||
|
Observable.fromCallable {
|
||||||
|
metadataHelper.fetchNhentaiMetadata(url)
|
||||||
|
?: client.newCall(urlToDetailsRequest(url))
|
||||||
|
.asObservableSuccess()
|
||||||
|
.map {
|
||||||
|
rawParseGallery(jsonParser.parse(it.body().string()).asJsonObject)
|
||||||
|
}.toBlocking().first()
|
||||||
|
}!!
|
||||||
|
|
||||||
|
override fun fetchChapterList(manga: SManga)
|
||||||
|
= lazyLoadMetadata(manga.url).map {
|
||||||
|
listOf(SChapter.create().apply {
|
||||||
|
url = manga.url
|
||||||
|
name = "Chapter"
|
||||||
|
date_upload = it.uploadDate ?: 0
|
||||||
|
chapter_number = 1f
|
||||||
|
})
|
||||||
|
}!!
|
||||||
|
|
||||||
|
override fun fetchPageList(chapter: SChapter)
|
||||||
|
= lazyLoadMetadata(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)
|
||||||
|
}
|
||||||
|
}!!
|
||||||
|
|
||||||
|
override fun fetchImageUrl(page: Page) = Observable.just(page.imageUrl!!)!!
|
||||||
|
|
||||||
|
fun imageUrlFromType(mediaId: String, page: Int, t: String) = NHentaiMetadata.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!")
|
||||||
|
}
|
||||||
|
|
||||||
|
val appName by lazy {
|
||||||
|
context.getString(R.string.app_name)!!
|
||||||
|
}
|
||||||
|
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 = NHentaiMetadata.BASE_URL
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val jsonParser by lazy {
|
||||||
|
JsonParser()
|
||||||
|
}
|
||||||
|
|
||||||
|
val metadataHelper by lazy {
|
||||||
|
MetadataHelper()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JsonElement.notNull() =
|
||||||
|
if(this is JsonNull)
|
||||||
|
null
|
||||||
|
else this
|
||||||
|
}
|
@ -13,6 +13,8 @@ val EXH_METADATA_SOURCE_ID = LEWD_SOURCE_SERIES + 4
|
|||||||
val PERV_EDEN_EN_SOURCE_ID = LEWD_SOURCE_SERIES + 5
|
val PERV_EDEN_EN_SOURCE_ID = LEWD_SOURCE_SERIES + 5
|
||||||
val PERV_EDEN_IT_SOURCE_ID = LEWD_SOURCE_SERIES + 6
|
val PERV_EDEN_IT_SOURCE_ID = LEWD_SOURCE_SERIES + 6
|
||||||
|
|
||||||
|
val NHENTAI_SOURCE_ID = LEWD_SOURCE_SERIES + 7
|
||||||
|
|
||||||
fun isLewdSource(source: Long) = source in 6900..6999
|
fun isLewdSource(source: Long) = source in 6900..6999
|
||||||
|
|
||||||
fun isEhSource(source: Long) = source == EH_SOURCE_ID
|
fun isEhSource(source: Long) = source == EH_SOURCE_ID
|
||||||
@ -23,3 +25,5 @@ fun isExSource(source: Long) = source == EXH_SOURCE_ID
|
|||||||
|
|
||||||
fun isPervEdenSource(source: Long) = source == PERV_EDEN_IT_SOURCE_ID
|
fun isPervEdenSource(source: Long) = source == PERV_EDEN_IT_SOURCE_ID
|
||||||
|| source == PERV_EDEN_EN_SOURCE_ID
|
|| source == PERV_EDEN_EN_SOURCE_ID
|
||||||
|
|
||||||
|
fun isNhentaiSource(source: Long) = source == NHENTAI_SOURCE_ID
|
||||||
|
@ -2,6 +2,7 @@ package exh.metadata
|
|||||||
|
|
||||||
import exh.*
|
import exh.*
|
||||||
import exh.metadata.models.ExGalleryMetadata
|
import exh.metadata.models.ExGalleryMetadata
|
||||||
|
import exh.metadata.models.NHentaiMetadata
|
||||||
import exh.metadata.models.PervEdenGalleryMetadata
|
import exh.metadata.models.PervEdenGalleryMetadata
|
||||||
import exh.metadata.models.SearchableGalleryMetadata
|
import exh.metadata.models.SearchableGalleryMetadata
|
||||||
import io.paperdb.Paper
|
import io.paperdb.Paper
|
||||||
@ -11,6 +12,7 @@ class MetadataHelper {
|
|||||||
fun writeGallery(galleryMetadata: SearchableGalleryMetadata, source: Long)
|
fun writeGallery(galleryMetadata: SearchableGalleryMetadata, source: Long)
|
||||||
= (if(isExSource(source) || isEhSource(source)) exGalleryBook()
|
= (if(isExSource(source) || isEhSource(source)) exGalleryBook()
|
||||||
else if(isPervEdenSource(source)) pervEdenGalleryBook()
|
else if(isPervEdenSource(source)) pervEdenGalleryBook()
|
||||||
|
else if(isNhentaiSource(source)) nhentaiGalleryBook()
|
||||||
else null)?.write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata)!!
|
else null)?.write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata)!!
|
||||||
|
|
||||||
fun fetchEhMetadata(url: String, exh: Boolean): ExGalleryMetadata?
|
fun fetchEhMetadata(url: String, exh: Boolean): ExGalleryMetadata?
|
||||||
@ -31,11 +33,18 @@ class MetadataHelper {
|
|||||||
return pervEdenGalleryBook().read<PervEdenGalleryMetadata>(it.galleryUniqueIdentifier())
|
return pervEdenGalleryBook().read<PervEdenGalleryMetadata>(it.galleryUniqueIdentifier())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchNhentaiMetadata(url: String) = NHentaiMetadata().let {
|
||||||
|
it.url = url
|
||||||
|
nhentaiGalleryBook().read<NHentaiMetadata>(it.galleryUniqueIdentifier())
|
||||||
|
}
|
||||||
|
|
||||||
fun fetchMetadata(url: String, source: Long): SearchableGalleryMetadata? {
|
fun fetchMetadata(url: String, source: Long): SearchableGalleryMetadata? {
|
||||||
if(isExSource(source) || isEhSource(source)) {
|
if(isExSource(source) || isEhSource(source)) {
|
||||||
return fetchEhMetadata(url, isExSource(source))
|
return fetchEhMetadata(url, isExSource(source))
|
||||||
} else if(isPervEdenSource(source)) {
|
} else if(isPervEdenSource(source)) {
|
||||||
return fetchPervEdenMetadata(url, source)
|
return fetchPervEdenMetadata(url, source)
|
||||||
|
} else if(isNhentaiSource(source)) {
|
||||||
|
return fetchNhentaiMetadata(url)
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -57,4 +66,6 @@ class MetadataHelper {
|
|||||||
fun exGalleryBook() = Paper.book("gallery-ex")!!
|
fun exGalleryBook() = Paper.book("gallery-ex")!!
|
||||||
|
|
||||||
fun pervEdenGalleryBook() = Paper.book("gallery-perveden")!!
|
fun pervEdenGalleryBook() = Paper.book("gallery-perveden")!!
|
||||||
|
|
||||||
|
fun nhentaiGalleryBook() = Paper.book("gallery-nhentai")!!
|
||||||
}
|
}
|
@ -4,10 +4,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.all.PervEden
|
import eu.kanade.tachiyomi.source.online.all.PervEden
|
||||||
import exh.metadata.models.ExGalleryMetadata
|
import exh.metadata.models.*
|
||||||
import exh.metadata.models.PervEdenGalleryMetadata
|
|
||||||
import exh.metadata.models.SearchableGalleryMetadata
|
|
||||||
import exh.metadata.models.Tag
|
|
||||||
import exh.plusAssign
|
import exh.plusAssign
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -17,8 +14,11 @@ import java.util.*
|
|||||||
* Copies gallery metadata to a manga object
|
* Copies gallery metadata to a manga object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private const val ARTIST_NAMESPACE = "artist"
|
private const val EH_ARTIST_NAMESPACE = "artist"
|
||||||
private const val AUTHOR_NAMESPACE = "author"
|
private const val EH_AUTHOR_NAMESPACE = "author"
|
||||||
|
|
||||||
|
private const val NHENTAI_ARTIST_NAMESPACE = "artist"
|
||||||
|
private const val NHENTAI_CATEGORIES_NAMESPACE = "category"
|
||||||
|
|
||||||
private val ONGOING_SUFFIX = arrayOf(
|
private val ONGOING_SUFFIX = arrayOf(
|
||||||
"[ongoing]",
|
"[ongoing]",
|
||||||
@ -43,17 +43,17 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
|||||||
|
|
||||||
//No title bug?
|
//No title bug?
|
||||||
val titleObj = if(prefs.useJapaneseTitle().getOrDefault())
|
val titleObj = if(prefs.useJapaneseTitle().getOrDefault())
|
||||||
altTitles.firstOrNull() ?: title
|
altTitle ?: title
|
||||||
else
|
else
|
||||||
title
|
title
|
||||||
titleObj?.let { manga.title = it }
|
titleObj?.let { manga.title = it }
|
||||||
|
|
||||||
//Set artist (if we can find one)
|
//Set artist (if we can find one)
|
||||||
tags[ARTIST_NAMESPACE]?.let {
|
tags[EH_ARTIST_NAMESPACE]?.let {
|
||||||
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = Tag::name)
|
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = Tag::name)
|
||||||
}
|
}
|
||||||
//Set author (if we can find one)
|
//Set author (if we can find one)
|
||||||
tags[AUTHOR_NAMESPACE]?.let {
|
tags[EH_AUTHOR_NAMESPACE]?.let {
|
||||||
if(it.isNotEmpty()) manga.author = it.joinToString(transform = Tag::name)
|
if(it.isNotEmpty()) manga.author = it.joinToString(transform = Tag::name)
|
||||||
}
|
}
|
||||||
//Set genre
|
//Set genre
|
||||||
@ -73,7 +73,7 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
|||||||
//Build a nice looking description out of what we know
|
//Build a nice looking description out of what we know
|
||||||
val titleDesc = StringBuilder()
|
val titleDesc = StringBuilder()
|
||||||
title?.let { titleDesc += "Title: $it\n" }
|
title?.let { titleDesc += "Title: $it\n" }
|
||||||
altTitles.firstOrNull()?.let { titleDesc += "Alternate Title: $it\n" }
|
altTitle?.let { titleDesc += "Alternate Title: $it\n" }
|
||||||
|
|
||||||
val detailsDesc = StringBuilder()
|
val detailsDesc = StringBuilder()
|
||||||
uploader?.let { detailsDesc += "Uploader: $it\n" }
|
uploader?.let { detailsDesc += "Uploader: $it\n" }
|
||||||
@ -93,7 +93,6 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
|
|||||||
detailsDesc += "\n"
|
detailsDesc += "\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val tagsDesc = buildTagsDescription(this)
|
val tagsDesc = buildTagsDescription(this)
|
||||||
|
|
||||||
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||||
@ -146,6 +145,55 @@ fun PervEdenGalleryMetadata.copyTo(manga: SManga) {
|
|||||||
.joinToString(separator = "\n")
|
.joinToString(separator = "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun NHentaiMetadata.copyTo(manga: SManga) {
|
||||||
|
url?.let { manga.url = it }
|
||||||
|
|
||||||
|
//TODO next update allow this to be changed to use HD covers
|
||||||
|
if(mediaId != null)
|
||||||
|
NHentaiMetadata.typeToExtension(thumbnailImageType)?.let {
|
||||||
|
manga.thumbnail_url = "https://t.nhentai.net/galleries/$mediaId/thumb.$it"
|
||||||
|
}
|
||||||
|
|
||||||
|
manga.title = englishTitle ?: japaneseTitle ?: shortTitle!!
|
||||||
|
|
||||||
|
//Set artist (if we can find one)
|
||||||
|
tags[NHENTAI_ARTIST_NAMESPACE]?.let {
|
||||||
|
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = Tag::name)
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[NHENTAI_CATEGORIES_NAMESPACE]?.let {
|
||||||
|
if(it.isNotEmpty()) manga.genre = it.joinToString(transform = Tag::name)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Try to automatically identify if it is ongoing, we try not to be too lenient here to avoid making mistakes
|
||||||
|
//We default to completed
|
||||||
|
manga.status = SManga.COMPLETED
|
||||||
|
englishTitle?.let { t ->
|
||||||
|
ONGOING_SUFFIX.find {
|
||||||
|
t.endsWith(it, ignoreCase = true)
|
||||||
|
}?.let {
|
||||||
|
manga.status = SManga.ONGOING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val titleDesc = StringBuilder()
|
||||||
|
englishTitle?.let { titleDesc += "English Title: $it\n" }
|
||||||
|
japaneseTitle?.let { titleDesc += "Japanese Title: $it\n" }
|
||||||
|
shortTitle?.let { titleDesc += "Short Title: $it\n" }
|
||||||
|
|
||||||
|
val detailsDesc = StringBuilder()
|
||||||
|
uploadDate?.let { detailsDesc += "Upload Date: ${EX_DATE_FORMAT.format(Date(it))}\n" }
|
||||||
|
pageImageTypes.size.let { detailsDesc += "Length: $it pages\n" }
|
||||||
|
favoritesCount?.let { detailsDesc += "Favorited: $it times\n" }
|
||||||
|
scanlator?.nullIfBlank().let { detailsDesc += "Scanlator: $it\n" }
|
||||||
|
|
||||||
|
val tagsDesc = buildTagsDescription(this)
|
||||||
|
|
||||||
|
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||||
|
.filter(String::isNotBlank)
|
||||||
|
.joinToString(separator = "\n")
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildTagsDescription(metadata: SearchableGalleryMetadata)
|
private fun buildTagsDescription(metadata: SearchableGalleryMetadata)
|
||||||
= StringBuilder("Tags:\n").apply {
|
= StringBuilder("Tags:\n").apply {
|
||||||
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
|
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
|
||||||
|
@ -14,6 +14,9 @@ class ExGalleryMetadata : SearchableGalleryMetadata() {
|
|||||||
|
|
||||||
var thumbnailUrl: String? = null
|
var thumbnailUrl: String? = null
|
||||||
|
|
||||||
|
var title: String? = null
|
||||||
|
var altTitle: String? = null
|
||||||
|
|
||||||
var genre: String? = null
|
var genre: String? = null
|
||||||
|
|
||||||
var datePosted: Long? = null
|
var datePosted: Long? = null
|
||||||
@ -27,6 +30,7 @@ class ExGalleryMetadata : SearchableGalleryMetadata() {
|
|||||||
var ratingCount: Int? = null
|
var ratingCount: Int? = null
|
||||||
var averageRating: Double? = null
|
var averageRating: Double? = null
|
||||||
|
|
||||||
|
override fun getTitles() = listOf(title, altTitle).filterNotNull()
|
||||||
|
|
||||||
private fun splitGalleryUrl()
|
private fun splitGalleryUrl()
|
||||||
= url?.let {
|
= url?.let {
|
||||||
|
48
app/src/main/java/exh/metadata/models/NHentaiMetadata.kt
Normal file
48
app/src/main/java/exh/metadata/models/NHentaiMetadata.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package exh.metadata.models
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NHentai metadata
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NHentaiMetadata : SearchableGalleryMetadata() {
|
||||||
|
|
||||||
|
var id: Long? = null
|
||||||
|
|
||||||
|
var url get() = id?.let { "$BASE_URL/g/$it" }
|
||||||
|
set(a) {
|
||||||
|
a?.let {
|
||||||
|
id = a.split("/").last().toLong()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploadDate: Long? = null
|
||||||
|
|
||||||
|
var favoritesCount: Long? = null
|
||||||
|
|
||||||
|
var mediaId: String? = null
|
||||||
|
|
||||||
|
var japaneseTitle: String? = null
|
||||||
|
var englishTitle: String? = null
|
||||||
|
var shortTitle: String? = null
|
||||||
|
|
||||||
|
var coverImageType: String? = null
|
||||||
|
var pageImageTypes: MutableList<String> = mutableListOf()
|
||||||
|
var thumbnailImageType: String? = null
|
||||||
|
|
||||||
|
var scanlator: String? = null
|
||||||
|
|
||||||
|
override fun galleryUniqueIdentifier(): String? = "NHENTAI-$id"
|
||||||
|
|
||||||
|
override fun getTitles() = listOf(japaneseTitle, englishTitle, shortTitle).filterNotNull()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val BASE_URL = "https://nhentai.net"
|
||||||
|
|
||||||
|
fun typeToExtension(t: String?) =
|
||||||
|
when(t) {
|
||||||
|
"p" -> "png"
|
||||||
|
"j" -> "jpg"
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,9 @@ class PervEdenGalleryMetadata : SearchableGalleryMetadata() {
|
|||||||
var url: String? = null
|
var url: String? = null
|
||||||
var thumbnailUrl: String? = null
|
var thumbnailUrl: String? = null
|
||||||
|
|
||||||
|
var title: String? = null
|
||||||
|
var altTitles: MutableList<String> = mutableListOf()
|
||||||
|
|
||||||
var artist: String? = null
|
var artist: String? = null
|
||||||
|
|
||||||
var type: String? = null
|
var type: String? = null
|
||||||
@ -16,6 +19,8 @@ class PervEdenGalleryMetadata : SearchableGalleryMetadata() {
|
|||||||
|
|
||||||
var lang: String? = null
|
var lang: String? = null
|
||||||
|
|
||||||
|
override fun getTitles() = listOf(title).plus(altTitles).filterNotNull()
|
||||||
|
|
||||||
private fun splitGalleryUrl()
|
private fun splitGalleryUrl()
|
||||||
= url?.let {
|
= url?.let {
|
||||||
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
|
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
|
||||||
|
@ -9,11 +9,10 @@ import java.util.HashMap
|
|||||||
abstract class SearchableGalleryMetadata {
|
abstract class SearchableGalleryMetadata {
|
||||||
var uploader: String? = null
|
var uploader: String? = null
|
||||||
|
|
||||||
var title: String? = null
|
|
||||||
val altTitles: MutableList<String> = mutableListOf()
|
|
||||||
|
|
||||||
//Being specific about which classes are used in generics to make deserialization easier
|
//Being specific about which classes are used in generics to make deserialization easier
|
||||||
val tags: HashMap<String, ArrayList<Tag>> = HashMap()
|
val tags: HashMap<String, ArrayList<Tag>> = HashMap()
|
||||||
|
|
||||||
abstract fun galleryUniqueIdentifier(): String?
|
abstract fun galleryUniqueIdentifier(): String?
|
||||||
|
|
||||||
|
abstract fun getTitles(): List<String>
|
||||||
}
|
}
|
@ -28,14 +28,12 @@ class SearchEngine {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
val cachedLowercaseTitle = metadata.title?.toLowerCase()
|
val cachedTitles = metadata.getTitles().map(String::toLowerCase)
|
||||||
val cachedLowercaseAltTitles = metadata.altTitles.map(String::toLowerCase)
|
|
||||||
|
|
||||||
for(component in query) {
|
for(component in query) {
|
||||||
if(component is Text) {
|
if(component is Text) {
|
||||||
//Match title
|
//Match title
|
||||||
if (component.asRegex().test(cachedLowercaseTitle)
|
if (cachedTitles.find { component.asRegex().test(it) } != null) {
|
||||||
|| cachedLowercaseAltTitles.find { component.asRegex().test(it) } != null) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
//Match tags
|
//Match tags
|
||||||
|
Loading…
x
Reference in New Issue
Block a user