komikku/source-api/src/commonMain/kotlin/exh/metadata/metadata/NHentaiSearchMetadata.kt
MediocreLegion 80ea323281
fix(delegate): migrate NH to the v2 api (#1582)
* fix(delegate): migrate NH to the v2 api

* bump versionId

* remove versionId requirement

* gemini review suggestions

* remove dead code

* remove unused imports

* linging

* unneeded comment

* fix(delegate): migrate NH to the v2 api (jobobby04/TachiyomiSY#1581)

* fix(delegate): migrate NH to the v2 api

* remove extra comment

* remove redundant data

* linting

* Code cleanup

---------

Co-authored-by: Jobobby04 <jobobby04@users.noreply.github.com>
(cherry picked from commit eec1236b8b5904b46d79bb716e29f073c6fe9b34)

* close `response`

* Avoid empty server list

---------

Co-authored-by: Jobobby04 <jobobby04@users.noreply.github.com>
Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
2026-04-22 13:31:05 +07:00

131 lines
4.6 KiB
Kotlin

package exh.metadata.metadata
import android.content.Context
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.copy
import exh.metadata.MetadataUtil
import kotlinx.serialization.Serializable
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
@Serializable
class NHentaiSearchMetadata : RaisedSearchMetadata() {
var url get() = nhId?.let { BASE_URL + nhIdToPath(it) }
set(a) {
a?.let {
nhId = nhUrlToId(a)
}
}
var nhId: Long? = null
var uploadDate: Long? = null
var favoritesCount: Long? = null
var mediaId: String? = null
var japaneseTitle by titleDelegate(TITLE_TYPE_JAPANESE)
var englishTitle by titleDelegate(TITLE_TYPE_ENGLISH)
var shortTitle by titleDelegate(TITLE_TYPE_SHORT)
var coverImageUrl: String? = null
var pageImagePreviewUrls: List<String> = emptyList()
var scanlator: String? = null
var preferredTitle: Int? = null
override fun createMangaInfo(manga: SManga): SManga {
val key = nhId?.let { nhIdToPath(it) }
val title = when (preferredTitle) {
TITLE_TYPE_SHORT -> shortTitle ?: englishTitle ?: japaneseTitle ?: manga.title
0, TITLE_TYPE_ENGLISH -> englishTitle ?: japaneseTitle ?: shortTitle ?: manga.title
else -> englishTitle ?: japaneseTitle ?: shortTitle ?: manga.title
}
// Set artist (if we can find one)
val artist = tags.ofNamespace(NHENTAI_ARTIST_NAMESPACE).let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}
// Set group (if we can find one)
val group = tags.ofNamespace(NHENTAI_GROUP_NAMESPACE).let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}
// Copy tags -> genres
val genres = tagsToGenreString()
// Try to automatically identify if it is ongoing, we try not to be too lenient here to avoid making mistakes
// We default to completed
var status = SManga.COMPLETED
englishTitle?.let { t ->
MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true)
}?.let {
status = SManga.ONGOING
}
}
return manga.copy(
url = key ?: manga.url,
thumbnail_url = coverImageUrl ?: manga.thumbnail_url,
title = title,
artist = group ?: manga.artist,
author = artist ?: manga.artist,
genre = genres,
status = status,
description = null,
)
}
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
return with(context) {
listOfNotNull(
getItem(nhId) { stringResource(SYMR.strings.id) },
getItem(
uploadDate,
{
MetadataUtil.EX_DATE_FORMAT
.format(ZonedDateTime.ofInstant(Instant.ofEpochSecond(it), ZoneId.systemDefault()))
},
) {
stringResource(SYMR.strings.date_posted)
},
getItem(favoritesCount) { stringResource(SYMR.strings.total_favorites) },
getItem(mediaId) { stringResource(SYMR.strings.media_id) },
getItem(japaneseTitle) { stringResource(SYMR.strings.japanese_title) },
getItem(englishTitle) { stringResource(SYMR.strings.english_title) },
getItem(shortTitle) { stringResource(SYMR.strings.short_title) },
getItem(coverImageUrl) { stringResource(SYMR.strings.thumbnail_url) },
getItem(pageImagePreviewUrls.size) { stringResource(SYMR.strings.page_count) },
getItem(scanlator) { stringResource(MR.strings.scanlator) },
)
}
}
companion object {
private const val TITLE_TYPE_JAPANESE = 0
const val TITLE_TYPE_ENGLISH = 1
const val TITLE_TYPE_SHORT = 2
const val TAG_TYPE_DEFAULT = 0
const val BASE_URL = "https://nhentai.net"
private const val NHENTAI_ARTIST_NAMESPACE = "artist"
private const val NHENTAI_GROUP_NAMESPACE = "group"
const val NHENTAI_CATEGORIES_NAMESPACE = "category"
fun nhUrlToId(url: String) =
url.split("/").last { it.isNotBlank() }.toLong()
fun nhIdToPath(id: Long) = "/g/$id/"
}
}