mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-03 15:48:56 +01:00
Various changes
This commit is contained in:
@@ -15,6 +15,8 @@ val PERV_EDEN_IT_SOURCE_ID = LEWD_SOURCE_SERIES + 6
|
||||
|
||||
val NHENTAI_SOURCE_ID = LEWD_SOURCE_SERIES + 7
|
||||
|
||||
val HENTAI_CAFE_SOURCE_ID = LEWD_SOURCE_SERIES + 8
|
||||
|
||||
fun isLewdSource(source: Long) = source in 6900..6999
|
||||
|
||||
fun isEhSource(source: Long) = source == EH_SOURCE_ID
|
||||
|
||||
@@ -10,19 +10,16 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||
import exh.metadata.copyTo
|
||||
import exh.metadata.loadEh
|
||||
import exh.metadata.loadNhentai
|
||||
import exh.metadata.models.ExGalleryMetadata
|
||||
import exh.metadata.models.NHentaiMetadata
|
||||
import exh.metadata.models.PervEdenGalleryMetadata
|
||||
import exh.metadata.models.PervEdenLang
|
||||
import exh.util.defRealm
|
||||
import io.realm.Realm
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
@@ -70,10 +67,19 @@ class GalleryAdder {
|
||||
forceSource: Long? = null): GalleryAddEvent {
|
||||
try {
|
||||
val urlObj = Uri.parse(url)
|
||||
val source = when (urlObj.host) {
|
||||
val lowercasePs = urlObj.pathSegments.map(String::toLowerCase)
|
||||
val firstPathSegment = lowercasePs[0]
|
||||
val source = when (urlObj.host.toLowerCase()) {
|
||||
"g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID
|
||||
"exhentai.org" -> EXH_SOURCE_ID
|
||||
"nhentai.net" -> NHENTAI_SOURCE_ID
|
||||
"www.perveden.com" -> {
|
||||
when(lowercasePs[1]) {
|
||||
"en-manga" -> PERV_EDEN_EN_SOURCE_ID
|
||||
"it-manga" -> PERV_EDEN_IT_SOURCE_ID
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
}
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
|
||||
@@ -81,7 +87,6 @@ class GalleryAdder {
|
||||
return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
|
||||
val firstPathSegment = urlObj.pathSegments.firstOrNull()?.toLowerCase()
|
||||
val realUrl = when(source) {
|
||||
EH_SOURCE_ID, EXH_SOURCE_ID -> when (firstPathSegment) {
|
||||
"g" -> {
|
||||
@@ -94,10 +99,19 @@ class GalleryAdder {
|
||||
}
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
NHENTAI_SOURCE_ID -> when {
|
||||
firstPathSegment == "g" -> url
|
||||
urlObj.pathSegments.size >= 3 -> "https://nhentai.net/g/${urlObj.pathSegments[1]}/"
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
NHENTAI_SOURCE_ID -> {
|
||||
if(firstPathSegment != "g")
|
||||
return GalleryAddEvent.Fail.UnknownType(url)
|
||||
|
||||
"https://nhentai.net/g/${urlObj.pathSegments[1]}/"
|
||||
}
|
||||
PERV_EDEN_EN_SOURCE_ID,
|
||||
PERV_EDEN_IT_SOURCE_ID -> {
|
||||
val uri = Uri.parse("http://www.perveden.com/").buildUpon()
|
||||
urlObj.pathSegments.take(3).forEach {
|
||||
uri.appendPath(it)
|
||||
}
|
||||
uri.toString()
|
||||
}
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
@@ -108,6 +122,8 @@ class GalleryAdder {
|
||||
val cleanedUrl = when(source) {
|
||||
EH_SOURCE_ID, EXH_SOURCE_ID -> getUrlWithoutDomain(realUrl)
|
||||
NHENTAI_SOURCE_ID -> realUrl //nhentai uses URLs directly (oops, my bad when implementing this source)
|
||||
PERV_EDEN_EN_SOURCE_ID,
|
||||
PERV_EDEN_IT_SOURCE_ID -> getUrlWithoutDomain(realUrl)
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
|
||||
@@ -119,17 +135,27 @@ class GalleryAdder {
|
||||
}
|
||||
|
||||
//Copy basics
|
||||
manga.copyFrom(sourceObj.fetchMangaDetails(manga).toBlocking().first())
|
||||
val newManga = sourceObj.fetchMangaDetails(manga).toBlocking().first()
|
||||
manga.copyFrom(newManga)
|
||||
manga.title = newManga.title //Forcibly copy title as copyFrom does not copy title
|
||||
|
||||
//Apply metadata
|
||||
defRealm { realm ->
|
||||
when (source) {
|
||||
EH_SOURCE_ID, EXH_SOURCE_ID ->
|
||||
realm.loadEh(ExGalleryMetadata.galleryId(realUrl),
|
||||
ExGalleryMetadata.galleryToken(realUrl),
|
||||
isExSource(source))?.copyTo(manga)
|
||||
ExGalleryMetadata.UrlQuery(realUrl, isExSource(source))
|
||||
.query(realm)
|
||||
.findFirst()?.copyTo(manga)
|
||||
NHENTAI_SOURCE_ID ->
|
||||
realm.loadNhentai(NHentaiMetadata.nhIdFromUrl(realUrl))
|
||||
NHentaiMetadata.UrlQuery(realUrl)
|
||||
.query(realm)
|
||||
.findFirst()
|
||||
?.copyTo(manga)
|
||||
PERV_EDEN_EN_SOURCE_ID,
|
||||
PERV_EDEN_IT_SOURCE_ID ->
|
||||
PervEdenGalleryMetadata.UrlQuery(realUrl, PervEdenLang.source(source))
|
||||
.query(realm)
|
||||
.findFirst()
|
||||
?.copyTo(manga)
|
||||
else -> return GalleryAddEvent.Fail.UnknownType(url)
|
||||
}
|
||||
@@ -160,16 +186,16 @@ class GalleryAdder {
|
||||
}
|
||||
|
||||
private fun getUrlWithoutDomain(orig: String): String {
|
||||
try {
|
||||
return try {
|
||||
val uri = URI(orig)
|
||||
var out = uri.path
|
||||
if (uri.query != null)
|
||||
out += "?" + uri.query
|
||||
if (uri.fragment != null)
|
||||
out += "#" + uri.fragment
|
||||
return out
|
||||
out
|
||||
} catch (e: URISyntaxException) {
|
||||
return orig
|
||||
orig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,32 @@
|
||||
package exh.metadata
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.LewdSource
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryItem
|
||||
import exh.*
|
||||
import exh.metadata.models.ExGalleryMetadata
|
||||
import exh.metadata.models.NHentaiMetadata
|
||||
import exh.metadata.models.PervEdenGalleryMetadata
|
||||
import exh.metadata.models.SearchableGalleryMetadata
|
||||
import exh.metadata.models.*
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.RealmResults
|
||||
import rx.Observable
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun Realm.ehMetaQueryFromUrl(url: String,
|
||||
exh: Boolean,
|
||||
meta: RealmQuery<ExGalleryMetadata>? = null) =
|
||||
ehMetadataQuery(
|
||||
ExGalleryMetadata.galleryId(url),
|
||||
ExGalleryMetadata.galleryToken(url),
|
||||
exh,
|
||||
meta
|
||||
)
|
||||
|
||||
fun Realm.ehMetadataQuery(gId: String,
|
||||
gToken: String,
|
||||
exh: Boolean,
|
||||
meta: RealmQuery<ExGalleryMetadata>? = null)
|
||||
= (meta ?: where(ExGalleryMetadata::class.java))
|
||||
.equalTo(ExGalleryMetadata::gId.name, gId)
|
||||
.equalTo(ExGalleryMetadata::gToken.name, gToken)
|
||||
.equalTo(ExGalleryMetadata::exh.name, exh)
|
||||
|
||||
fun Realm.loadEh(gId: String, gToken: String, exh: Boolean): ExGalleryMetadata?
|
||||
= ehMetadataQuery(gId, gToken, exh)
|
||||
.findFirst()
|
||||
|
||||
fun Realm.loadEhAsync(gId: String, gToken: String, exh: Boolean): Observable<ExGalleryMetadata?>
|
||||
= ehMetadataQuery(gId, gToken, exh)
|
||||
.findFirstAsync()
|
||||
.asObservable()
|
||||
|
||||
private fun pervEdenSourceToLang(source: Long)
|
||||
= when (source) {
|
||||
PERV_EDEN_EN_SOURCE_ID -> "en"
|
||||
PERV_EDEN_IT_SOURCE_ID -> "it"
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
fun Realm.pervEdenMetaQueryFromUrl(url: String,
|
||||
source: Long,
|
||||
meta: RealmQuery<PervEdenGalleryMetadata>? = null) =
|
||||
pervEdenMetadataQuery(
|
||||
PervEdenGalleryMetadata.pvIdFromUrl(url),
|
||||
source,
|
||||
meta
|
||||
)
|
||||
|
||||
fun Realm.pervEdenMetadataQuery(pvId: String,
|
||||
source: Long,
|
||||
meta: RealmQuery<PervEdenGalleryMetadata>? = null)
|
||||
= (meta ?: where(PervEdenGalleryMetadata::class.java))
|
||||
.equalTo(PervEdenGalleryMetadata::lang.name, pervEdenSourceToLang(source))
|
||||
.equalTo(PervEdenGalleryMetadata::pvId.name, pvId)
|
||||
|
||||
fun Realm.loadPervEden(pvId: String, source: Long): PervEdenGalleryMetadata?
|
||||
= pervEdenMetadataQuery(pvId, source)
|
||||
.findFirst()
|
||||
|
||||
fun Realm.loadPervEdenAsync(pvId: String, source: Long): Observable<PervEdenGalleryMetadata?>
|
||||
= pervEdenMetadataQuery(pvId, source)
|
||||
.findFirstAsync()
|
||||
.asObservable()
|
||||
|
||||
fun Realm.nhentaiMetaQueryFromUrl(url: String,
|
||||
meta: RealmQuery<NHentaiMetadata>? = null) =
|
||||
nhentaiMetadataQuery(
|
||||
NHentaiMetadata.nhIdFromUrl(url),
|
||||
meta
|
||||
)
|
||||
|
||||
fun Realm.nhentaiMetadataQuery(nhId: Long,
|
||||
meta: RealmQuery<NHentaiMetadata>? = null)
|
||||
= (meta ?: where(NHentaiMetadata::class.java))
|
||||
.equalTo(NHentaiMetadata::nhId.name, nhId)
|
||||
|
||||
fun Realm.loadNhentai(nhId: Long): NHentaiMetadata?
|
||||
= nhentaiMetadataQuery(nhId)
|
||||
.findFirst()
|
||||
|
||||
fun Realm.loadNhentaiAsync(nhId: Long): Observable<NHentaiMetadata?>
|
||||
= nhentaiMetadataQuery(nhId)
|
||||
.findFirstAsync()
|
||||
.asObservable()
|
||||
|
||||
fun Realm.loadAllMetadata(): Map<KClass<out SearchableGalleryMetadata>, RealmResults<out SearchableGalleryMetadata>> =
|
||||
listOf<Pair<KClass<out SearchableGalleryMetadata>, RealmQuery<out SearchableGalleryMetadata>>>(
|
||||
Pair(ExGalleryMetadata::class, where(ExGalleryMetadata::class.java)),
|
||||
Pair(NHentaiMetadata::class, where(NHentaiMetadata::class.java)),
|
||||
Pair(PervEdenGalleryMetadata::class, where(PervEdenGalleryMetadata::class.java))
|
||||
).map {
|
||||
Pair(it.first, it.second.findAllSorted(SearchableGalleryMetadata::mangaId.name))
|
||||
Injekt.get<SourceManager>().getOnlineSources().filterIsInstance<LewdSource<*, *>>().map {
|
||||
it.queryAll()
|
||||
}.associate {
|
||||
it.clazz to it.query(this@loadAllMetadata).findAllSorted(SearchableGalleryMetadata::mangaId.name)
|
||||
}.toMap()
|
||||
|
||||
fun Realm.queryMetadataFromManga(manga: Manga,
|
||||
meta: RealmQuery<out SearchableGalleryMetadata>? = null): RealmQuery<out SearchableGalleryMetadata> =
|
||||
when(manga.source) {
|
||||
EH_SOURCE_ID -> ehMetaQueryFromUrl(manga.url, false, meta as? RealmQuery<ExGalleryMetadata>)
|
||||
EXH_SOURCE_ID -> ehMetaQueryFromUrl(manga.url, true, meta as? RealmQuery<ExGalleryMetadata>)
|
||||
PERV_EDEN_EN_SOURCE_ID,
|
||||
PERV_EDEN_IT_SOURCE_ID ->
|
||||
pervEdenMetaQueryFromUrl(manga.url, manga.source, meta as? RealmQuery<PervEdenGalleryMetadata>)
|
||||
NHENTAI_SOURCE_ID -> nhentaiMetaQueryFromUrl(manga.url, meta as? RealmQuery<NHentaiMetadata>)
|
||||
else -> throw IllegalArgumentException("Unknown source type!")
|
||||
}
|
||||
meta: RealmQuery<SearchableGalleryMetadata>? = null):
|
||||
RealmQuery<out SearchableGalleryMetadata> =
|
||||
Injekt.get<SourceManager>().get(manga.source)?.let {
|
||||
(it as LewdSource<*, *>).queryFromUrl(manga.url) as GalleryQuery<SearchableGalleryMetadata>
|
||||
}?.query(this, meta) ?: throw IllegalArgumentException("Unknown source type!")
|
||||
|
||||
fun Realm.syncMangaIds(mangas: List<LibraryItem>) {
|
||||
Timber.d("--> EH: Begin syncing ${mangas.size} manga IDs...")
|
||||
@@ -138,11 +49,4 @@ fun Realm.syncMangaIds(mangas: List<LibraryItem>) {
|
||||
}
|
||||
|
||||
val Manga.metadataClass
|
||||
get() = when (source) {
|
||||
EH_SOURCE_ID,
|
||||
EXH_SOURCE_ID -> ExGalleryMetadata::class
|
||||
PERV_EDEN_IT_SOURCE_ID,
|
||||
PERV_EDEN_EN_SOURCE_ID -> PervEdenGalleryMetadata::class
|
||||
NHENTAI_SOURCE_ID -> NHentaiMetadata::class
|
||||
else -> null
|
||||
}
|
||||
get() = (Injekt.get<SourceManager>().get(source) as? LewdSource<*, *>)?.queryAll()?.clazz
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package exh.metadata
|
||||
|
||||
import exh.metadata.models.SearchableGalleryMetadata
|
||||
import exh.plusAssign
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Metadata utils
|
||||
*/
|
||||
@@ -44,4 +49,37 @@ fun <T> ignore(expr: () -> T): T? {
|
||||
|
||||
fun <K,V> Set<Map.Entry<K,V>>.forEach(action: (K, V) -> Unit) {
|
||||
forEach { action(it.key, it.value) }
|
||||
}
|
||||
}
|
||||
|
||||
val ONGOING_SUFFIX = arrayOf(
|
||||
"[ongoing]",
|
||||
"(ongoing)",
|
||||
"{ongoing}",
|
||||
"<ongoing>",
|
||||
"ongoing",
|
||||
"[incomplete]",
|
||||
"(incomplete)",
|
||||
"{incomplete}",
|
||||
"<incomplete>",
|
||||
"incomplete",
|
||||
"[wip]",
|
||||
"(wip)",
|
||||
"{wip}",
|
||||
"<wip>",
|
||||
"wip"
|
||||
)
|
||||
|
||||
val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
|
||||
|
||||
fun buildTagsDescription(metadata: SearchableGalleryMetadata)
|
||||
= StringBuilder("Tags:\n").apply {
|
||||
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
|
||||
metadata.tags.groupBy {
|
||||
it.namespace
|
||||
}.entries.forEach { namespace, tags ->
|
||||
if (tags.isNotEmpty()) {
|
||||
val joinedTags = tags.joinToString(separator = " ", transform = { "<${it.name}>" })
|
||||
this += "▪ $namespace: $joinedTags\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package exh.metadata
|
||||
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.source.online.all.PervEden
|
||||
import exh.metadata.models.*
|
||||
import exh.plusAssign
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Copies gallery metadata to a manga object
|
||||
*/
|
||||
|
||||
private const val EH_ARTIST_NAMESPACE = "artist"
|
||||
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(
|
||||
"[ongoing]",
|
||||
"(ongoing)",
|
||||
"{ongoing}"
|
||||
)
|
||||
|
||||
val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
|
||||
|
||||
private val prefs: PreferencesHelper by injectLazy()
|
||||
|
||||
fun ExGalleryMetadata.copyTo(manga: SManga) {
|
||||
//TODO Find some way to do this with SManga
|
||||
/*exh?.let {
|
||||
manga.source = if(it)
|
||||
2
|
||||
else
|
||||
1
|
||||
}*/
|
||||
url?.let { manga.url = it }
|
||||
thumbnailUrl?.let { manga.thumbnail_url = it }
|
||||
|
||||
//No title bug?
|
||||
val titleObj = if(prefs.useJapaneseTitle().getOrDefault())
|
||||
altTitle ?: title
|
||||
else
|
||||
title
|
||||
titleObj?.let { manga.title = it }
|
||||
|
||||
//Set artist (if we can find one)
|
||||
tags.filter { it.namespace == EH_ARTIST_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
//Set author (if we can find one)
|
||||
tags.filter { it.namespace == EH_AUTHOR_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.author = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
//Set genre
|
||||
genre?.let { manga.genre = it }
|
||||
|
||||
//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
|
||||
title?.let { t ->
|
||||
ONGOING_SUFFIX.find {
|
||||
t.endsWith(it, ignoreCase = true)
|
||||
}?.let {
|
||||
manga.status = SManga.ONGOING
|
||||
}
|
||||
}
|
||||
|
||||
//Build a nice looking description out of what we know
|
||||
val titleDesc = StringBuilder()
|
||||
title?.let { titleDesc += "Title: $it\n" }
|
||||
altTitle?.let { titleDesc += "Alternate Title: $it\n" }
|
||||
|
||||
val detailsDesc = StringBuilder()
|
||||
uploader?.let { detailsDesc += "Uploader: $it\n" }
|
||||
datePosted?.let { detailsDesc += "Posted: ${EX_DATE_FORMAT.format(Date(it))}\n" }
|
||||
visible?.let { detailsDesc += "Visible: $it\n" }
|
||||
language?.let {
|
||||
detailsDesc += "Language: $it"
|
||||
if(translated == true) detailsDesc += " TR"
|
||||
detailsDesc += "\n"
|
||||
}
|
||||
size?.let { detailsDesc += "File Size: ${humanReadableByteCount(it, true)}\n" }
|
||||
length?.let { detailsDesc += "Length: $it pages\n" }
|
||||
favorites?.let { detailsDesc += "Favorited: $it times\n" }
|
||||
averageRating?.let {
|
||||
detailsDesc += "Rating: $it"
|
||||
ratingCount?.let { detailsDesc += " ($it)" }
|
||||
detailsDesc += "\n"
|
||||
}
|
||||
|
||||
val tagsDesc = buildTagsDescription(this)
|
||||
|
||||
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
fun PervEdenGalleryMetadata.copyTo(manga: SManga) {
|
||||
url?.let { manga.url = it }
|
||||
thumbnailUrl?.let { manga.thumbnail_url = it }
|
||||
|
||||
val titleDesc = StringBuilder()
|
||||
title?.let {
|
||||
manga.title = it
|
||||
titleDesc += "Title: $it\n"
|
||||
}
|
||||
if(altTitles.isNotEmpty())
|
||||
titleDesc += "Alternate Titles: \n" + altTitles.map {
|
||||
"▪ ${it.title}"
|
||||
}.joinToString(separator = "\n", postfix = "\n")
|
||||
|
||||
val detailsDesc = StringBuilder()
|
||||
artist?.let {
|
||||
manga.artist = it
|
||||
detailsDesc += "Artist: $it\n"
|
||||
}
|
||||
|
||||
type?.let {
|
||||
manga.genre = it
|
||||
detailsDesc += "Type: $it\n"
|
||||
}
|
||||
|
||||
status?.let {
|
||||
manga.status = when(it) {
|
||||
"Ongoing" -> SManga.ONGOING
|
||||
"Completed", "Suspended" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
detailsDesc += "Status: $it\n"
|
||||
}
|
||||
|
||||
rating?.let {
|
||||
detailsDesc += "Rating: %.2\n".format(it)
|
||||
}
|
||||
|
||||
val tagsDesc = buildTagsDescription(this)
|
||||
|
||||
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.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.filter { it.namespace == NHENTAI_ARTIST_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
|
||||
tags.filter { it.namespace == NHENTAI_CATEGORIES_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.genre = it.joinToString(transform = { it.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")
|
||||
}
|
||||
|
||||
fun SearchableGalleryMetadata.genericCopyTo(manga: SManga): Boolean {
|
||||
when(this) {
|
||||
is ExGalleryMetadata -> this.copyTo(manga)
|
||||
is PervEdenGalleryMetadata -> this.copyTo(manga)
|
||||
is NHentaiMetadata -> this.copyTo(manga)
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun buildTagsDescription(metadata: SearchableGalleryMetadata)
|
||||
= StringBuilder("Tags:\n").apply {
|
||||
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
|
||||
metadata.tags.groupBy {
|
||||
it.namespace
|
||||
}.entries.forEach { namespace, tags ->
|
||||
if (tags.isNotEmpty()) {
|
||||
val joinedTags = tags.joinToString(separator = " ", transform = { "<${it.name}>" })
|
||||
this += "▪ $namespace: $joinedTags\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.EX_DATE_FORMAT
|
||||
import exh.metadata.ONGOING_SUFFIX
|
||||
import exh.metadata.buildTagsDescription
|
||||
import exh.metadata.humanReadableByteCount
|
||||
import exh.plusAssign
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Ignore
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -61,12 +71,99 @@ open class ExGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
|
||||
@Index
|
||||
override var mangaId: Long? = null
|
||||
|
||||
class EmptyQuery : GalleryQuery<ExGalleryMetadata>(ExGalleryMetadata::class)
|
||||
|
||||
class UrlQuery(
|
||||
val url: String,
|
||||
val exh: Boolean
|
||||
) : GalleryQuery<ExGalleryMetadata>(ExGalleryMetadata::class) {
|
||||
override fun transform() = Query(
|
||||
galleryId(url),
|
||||
galleryToken(url),
|
||||
exh
|
||||
)
|
||||
}
|
||||
|
||||
class Query(val gId: String,
|
||||
val gToken: String,
|
||||
val exh: Boolean
|
||||
) : GalleryQuery<ExGalleryMetadata>(ExGalleryMetadata::class) {
|
||||
override fun map() = mapOf(
|
||||
ExGalleryMetadata::gId to Query::gId,
|
||||
ExGalleryMetadata::gToken to Query::gToken,
|
||||
ExGalleryMetadata::exh to Query::exh
|
||||
)
|
||||
}
|
||||
|
||||
override fun copyTo(manga: SManga) {
|
||||
url?.let { manga.url = it }
|
||||
thumbnailUrl?.let { manga.thumbnail_url = it }
|
||||
|
||||
//No title bug?
|
||||
val titleObj = if(Injekt.get<PreferencesHelper>().useJapaneseTitle().getOrDefault())
|
||||
altTitle ?: title
|
||||
else
|
||||
title
|
||||
titleObj?.let { manga.title = it }
|
||||
|
||||
//Set artist (if we can find one)
|
||||
tags.filter { it.namespace == EH_ARTIST_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
//Set author (if we can find one)
|
||||
tags.filter { it.namespace == EH_AUTHOR_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.author = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
//Set genre
|
||||
genre?.let { manga.genre = it }
|
||||
|
||||
//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
|
||||
title?.let { t ->
|
||||
ONGOING_SUFFIX.find {
|
||||
t.endsWith(it, ignoreCase = true)
|
||||
}?.let {
|
||||
manga.status = SManga.ONGOING
|
||||
}
|
||||
}
|
||||
|
||||
//Build a nice looking description out of what we know
|
||||
val titleDesc = StringBuilder()
|
||||
title?.let { titleDesc += "Title: $it\n" }
|
||||
altTitle?.let { titleDesc += "Alternate Title: $it\n" }
|
||||
|
||||
val detailsDesc = StringBuilder()
|
||||
uploader?.let { detailsDesc += "Uploader: $it\n" }
|
||||
datePosted?.let { detailsDesc += "Posted: ${EX_DATE_FORMAT.format(Date(it))}\n" }
|
||||
visible?.let { detailsDesc += "Visible: $it\n" }
|
||||
language?.let {
|
||||
detailsDesc += "Language: $it"
|
||||
if(translated == true) detailsDesc += " TR"
|
||||
detailsDesc += "\n"
|
||||
}
|
||||
size?.let { detailsDesc += "File Size: ${humanReadableByteCount(it, true)}\n" }
|
||||
length?.let { detailsDesc += "Length: $it pages\n" }
|
||||
favorites?.let { detailsDesc += "Favorited: $it times\n" }
|
||||
averageRating?.let {
|
||||
detailsDesc += "Rating: $it"
|
||||
ratingCount?.let { detailsDesc += " ($it)" }
|
||||
detailsDesc += "\n"
|
||||
}
|
||||
|
||||
val tagsDesc = buildTagsDescription(this)
|
||||
|
||||
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun splitGalleryUrl(url: String)
|
||||
= url.let {
|
||||
Uri.parse(it).pathSegments
|
||||
.filterNot(String::isNullOrBlank)
|
||||
}
|
||||
Uri.parse(it).pathSegments
|
||||
.filterNot(String::isNullOrBlank)
|
||||
}
|
||||
|
||||
fun galleryId(url: String) = splitGalleryUrl(url).let { it[it.size - 2] }
|
||||
|
||||
@@ -77,5 +174,9 @@ open class ExGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
|
||||
ExGalleryMetadata::title.name,
|
||||
ExGalleryMetadata::altTitle.name
|
||||
)
|
||||
|
||||
private const val EH_ARTIST_NAMESPACE = "artist"
|
||||
private const val EH_AUTHOR_NAMESPACE = "author"
|
||||
|
||||
}
|
||||
}
|
||||
68
app/src/main/java/exh/metadata/models/GalleryQuery.kt
Executable file
68
app/src/main/java/exh/metadata/models/GalleryQuery.kt
Executable file
@@ -0,0 +1,68 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import io.realm.*
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty1
|
||||
|
||||
abstract class GalleryQuery<T : SearchableGalleryMetadata>(val clazz: KClass<T>) {
|
||||
open fun map(): Map<*, *> = emptyMap<KProperty<T>, KProperty1<GalleryQuery<T>, *>>()
|
||||
|
||||
open fun transform(): GalleryQuery<T>? = this
|
||||
|
||||
open fun override(meta: RealmQuery<T>): RealmQuery<T> = meta
|
||||
|
||||
fun query(realm: Realm, meta: RealmQuery<T>? = null): RealmQuery<T>
|
||||
= (meta ?: realm.where(clazz.java)).let {
|
||||
val visited = mutableListOf<GalleryQuery<T>>()
|
||||
|
||||
var top: GalleryQuery<T>? = null
|
||||
var newMeta = it
|
||||
while(true) {
|
||||
//DIFFERENT BEHAVIOR from: top?.transform() ?: this
|
||||
top = if(top != null) top.transform() else this
|
||||
|
||||
if(top == null) break
|
||||
|
||||
if(top in visited) break
|
||||
|
||||
newMeta = top.applyMap(newMeta)
|
||||
newMeta = top.override(newMeta)
|
||||
|
||||
visited += top
|
||||
}
|
||||
|
||||
newMeta
|
||||
}!!
|
||||
|
||||
fun applyMap(meta: RealmQuery<T>): RealmQuery<T> {
|
||||
var newMeta = meta
|
||||
|
||||
map().forEach { (t, u) ->
|
||||
t as KProperty<T>
|
||||
u as KProperty1<GalleryQuery<T>, *>
|
||||
|
||||
val v = u.get(this)
|
||||
val n = t.name
|
||||
|
||||
if(v != null) {
|
||||
newMeta = when (v) {
|
||||
is Date -> newMeta.equalTo(n, v)
|
||||
is Boolean -> newMeta.equalTo(n, v)
|
||||
is Byte -> newMeta.equalTo(n, v)
|
||||
is ByteArray -> newMeta.equalTo(n, v)
|
||||
is Double -> newMeta.equalTo(n, v)
|
||||
is Float -> newMeta.equalTo(n, v)
|
||||
is Int -> newMeta.equalTo(n, v)
|
||||
is Long -> newMeta.equalTo(n, v)
|
||||
is Short -> newMeta.equalTo(n, v)
|
||||
is String -> newMeta.equalTo(n, v, Case.INSENSITIVE)
|
||||
else -> throw IllegalArgumentException("Unknown type: ${v::class.qualifiedName}!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newMeta
|
||||
}
|
||||
}
|
||||
91
app/src/main/java/exh/metadata/models/HentaiCafeMetadata.kt
Normal file
91
app/src/main/java/exh/metadata/models/HentaiCafeMetadata.kt
Normal file
@@ -0,0 +1,91 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.buildTagsDescription
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Ignore
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import java.util.*
|
||||
|
||||
@RealmClass
|
||||
open class HentaiCafeMetadata : RealmObject(), SearchableGalleryMetadata {
|
||||
@PrimaryKey
|
||||
override var uuid: String = UUID.randomUUID().toString()
|
||||
|
||||
@Index
|
||||
var hcId: String? = null
|
||||
var readerId: String? = null
|
||||
|
||||
var url get() = hcId?.let { "$BASE_URL/$it" }
|
||||
set(a) {
|
||||
a?.let {
|
||||
hcId = hcIdFromUrl(a)
|
||||
}
|
||||
}
|
||||
|
||||
var title: String? = null
|
||||
|
||||
var artist: String? = null
|
||||
|
||||
override var uploader: String? = null
|
||||
|
||||
override var tags: RealmList<Tag> = RealmList()
|
||||
|
||||
override fun getTitles() = listOf(title).filterNotNull()
|
||||
|
||||
@Ignore
|
||||
override val titleFields = listOf(
|
||||
HentaiCafeMetadata::title.name
|
||||
)
|
||||
|
||||
@Index
|
||||
override var mangaId: Long? = null
|
||||
|
||||
override fun copyTo(manga: SManga) {
|
||||
manga.title = title!!
|
||||
manga.artist = artist
|
||||
manga.author = artist
|
||||
|
||||
//Not available
|
||||
manga.status = SManga.UNKNOWN
|
||||
|
||||
val detailsDesc = "Title: $title\n" +
|
||||
"Artist: $artist\n"
|
||||
|
||||
val tagsDesc = buildTagsDescription(this)
|
||||
|
||||
manga.genre = tags.filter { it.namespace == "tag" }.joinToString {
|
||||
it.name!!
|
||||
}
|
||||
|
||||
manga.description = listOf(detailsDesc, tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
class EmptyQuery : GalleryQuery<HentaiCafeMetadata>(HentaiCafeMetadata::class)
|
||||
|
||||
class UrlQuery(
|
||||
val url: String
|
||||
) : GalleryQuery<HentaiCafeMetadata>(HentaiCafeMetadata::class) {
|
||||
override fun transform() = Query(
|
||||
hcIdFromUrl(url)
|
||||
)
|
||||
}
|
||||
|
||||
class Query(val hcId: String): GalleryQuery<HentaiCafeMetadata>(HentaiCafeMetadata::class) {
|
||||
override fun map() = mapOf(
|
||||
HentaiCafeMetadata::hcId to Query::hcId
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val BASE_URL = "https://hentai.cafe"
|
||||
|
||||
fun hcIdFromUrl(url: String)
|
||||
= url.split("/").last { it.isNotBlank() }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.EX_DATE_FORMAT
|
||||
import exh.metadata.ONGOING_SUFFIX
|
||||
import exh.metadata.buildTagsDescription
|
||||
import exh.metadata.nullIfBlank
|
||||
import exh.plusAssign
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Ignore
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@@ -58,18 +68,92 @@ open class NHentaiMetadata : RealmObject(), SearchableGalleryMetadata {
|
||||
@Index
|
||||
override var mangaId: Long? = null
|
||||
|
||||
class EmptyQuery : GalleryQuery<NHentaiMetadata>(NHentaiMetadata::class)
|
||||
|
||||
class UrlQuery(
|
||||
val url: String
|
||||
) : GalleryQuery<NHentaiMetadata>(NHentaiMetadata::class) {
|
||||
override fun transform() = Query(
|
||||
nhIdFromUrl(url)
|
||||
)
|
||||
}
|
||||
|
||||
class Query(
|
||||
val nhId: Long
|
||||
) : GalleryQuery<NHentaiMetadata>(NHentaiMetadata::class) {
|
||||
override fun map() = mapOf(
|
||||
NHentaiMetadata::nhId to Query::nhId
|
||||
)
|
||||
}
|
||||
|
||||
override fun copyTo(manga: SManga) {
|
||||
url?.let { manga.url = it }
|
||||
|
||||
if(mediaId != null)
|
||||
NHentaiMetadata.typeToExtension(thumbnailImageType)?.let {
|
||||
manga.thumbnail_url = "https://t.nhentai.net/galleries/$mediaId/${
|
||||
if(Injekt.get<PreferencesHelper>().eh_useHighQualityThumbs().getOrDefault())
|
||||
"cover"
|
||||
else
|
||||
"thumb"
|
||||
}.$it"
|
||||
}
|
||||
|
||||
manga.title = englishTitle ?: japaneseTitle ?: shortTitle!!
|
||||
|
||||
//Set artist (if we can find one)
|
||||
tags.filter { it.namespace == NHENTAI_ARTIST_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = { it.name!! })
|
||||
}
|
||||
|
||||
tags.filter { it.namespace == NHENTAI_CATEGORIES_NAMESPACE }.let {
|
||||
if(it.isNotEmpty()) manga.genre = it.joinToString(transform = { it.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 * 1000))}\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")
|
||||
}
|
||||
|
||||
companion object {
|
||||
val BASE_URL = "https://nhentai.net"
|
||||
|
||||
private const val NHENTAI_ARTIST_NAMESPACE = "artist"
|
||||
private const val NHENTAI_CATEGORIES_NAMESPACE = "category"
|
||||
|
||||
fun typeToExtension(t: String?) =
|
||||
when(t) {
|
||||
"p" -> "png"
|
||||
"j" -> "jpg"
|
||||
else -> null
|
||||
}
|
||||
when(t) {
|
||||
"p" -> "png"
|
||||
"j" -> "jpg"
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun nhIdFromUrl(url: String)
|
||||
= url.split("/").last { it.isNotBlank() }.toLong()
|
||||
= url.split("/").last { it.isNotBlank() }.toLong()
|
||||
|
||||
val TITLE_FIELDS = listOf(
|
||||
NHentaiMetadata::japaneseTitle.name,
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.PERV_EDEN_EN_SOURCE_ID
|
||||
import exh.PERV_EDEN_IT_SOURCE_ID
|
||||
import exh.metadata.buildTagsDescription
|
||||
import exh.plusAssign
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.annotations.Ignore
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
@@ -50,11 +56,79 @@ open class PervEdenGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
|
||||
@Index
|
||||
override var mangaId: Long? = null
|
||||
|
||||
override fun copyTo(manga: SManga) {
|
||||
url?.let { manga.url = it }
|
||||
thumbnailUrl?.let { manga.thumbnail_url = it }
|
||||
|
||||
val titleDesc = StringBuilder()
|
||||
title?.let {
|
||||
manga.title = it
|
||||
titleDesc += "Title: $it\n"
|
||||
}
|
||||
if(altTitles.isNotEmpty())
|
||||
titleDesc += "Alternate Titles: \n" + altTitles.map {
|
||||
"▪ ${it.title}"
|
||||
}.joinToString(separator = "\n", postfix = "\n")
|
||||
|
||||
val detailsDesc = StringBuilder()
|
||||
artist?.let {
|
||||
manga.artist = it
|
||||
detailsDesc += "Artist: $it\n"
|
||||
}
|
||||
|
||||
type?.let {
|
||||
manga.genre = it
|
||||
detailsDesc += "Type: $it\n"
|
||||
}
|
||||
|
||||
status?.let {
|
||||
manga.status = when(it) {
|
||||
"Ongoing" -> SManga.ONGOING
|
||||
"Completed", "Suspended" -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
detailsDesc += "Status: $it\n"
|
||||
}
|
||||
|
||||
rating?.let {
|
||||
detailsDesc += "Rating: %.2\n".format(it)
|
||||
}
|
||||
|
||||
val tagsDesc = buildTagsDescription(this)
|
||||
|
||||
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
class EmptyQuery : GalleryQuery<PervEdenGalleryMetadata>(PervEdenGalleryMetadata::class)
|
||||
|
||||
class UrlQuery(
|
||||
val url: String,
|
||||
val lang: PervEdenLang
|
||||
) : GalleryQuery<PervEdenGalleryMetadata>(PervEdenGalleryMetadata::class) {
|
||||
override fun transform() = Query(
|
||||
pvIdFromUrl(url),
|
||||
lang
|
||||
)
|
||||
}
|
||||
|
||||
class Query(val pvId: String,
|
||||
val lang: PervEdenLang
|
||||
) : GalleryQuery<PervEdenGalleryMetadata>(PervEdenGalleryMetadata::class) {
|
||||
override fun map() = mapOf(
|
||||
PervEdenGalleryMetadata::pvId to Query::pvId
|
||||
)
|
||||
|
||||
override fun override(meta: RealmQuery<PervEdenGalleryMetadata>)
|
||||
= meta.equalTo(PervEdenGalleryMetadata::lang.name, lang.name)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun splitGalleryUrl(url: String)
|
||||
= url.let {
|
||||
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
|
||||
}
|
||||
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
|
||||
}
|
||||
|
||||
fun pvIdFromUrl(url: String) = splitGalleryUrl(url).last()
|
||||
|
||||
@@ -88,3 +162,14 @@ open class PervEdenTitle(var metadata: PervEdenGalleryMetadata? = null,
|
||||
|
||||
override fun toString() = "PervEdenTitle(metadata=$metadata, title=$title)"
|
||||
}
|
||||
|
||||
enum class PervEdenLang(val id: Long) {
|
||||
en(PERV_EDEN_EN_SOURCE_ID),
|
||||
it(PERV_EDEN_IT_SOURCE_ID);
|
||||
|
||||
companion object {
|
||||
fun source(id: Long)
|
||||
= PervEdenLang.values().find { it.id == id }
|
||||
?: throw IllegalArgumentException("Unknown source ID: $id!")
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
package exh.metadata.models
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmModel
|
||||
import io.realm.annotations.Index
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
import kotlin.reflect.KCallable
|
||||
|
||||
/**
|
||||
* A gallery that can be searched using the EH search engine
|
||||
@@ -23,4 +20,6 @@ interface SearchableGalleryMetadata: RealmModel {
|
||||
val titleFields: List<String>
|
||||
|
||||
var mangaId: Long?
|
||||
|
||||
fun copyTo(manga: SManga)
|
||||
}
|
||||
@@ -18,12 +18,11 @@ class SearchEngine {
|
||||
fun matchTagList(namespace: String?,
|
||||
component: Text?,
|
||||
excluded: Boolean) {
|
||||
if(excluded)
|
||||
rQuery.not()
|
||||
else if (queryEmpty)
|
||||
queryEmpty = false
|
||||
else
|
||||
rQuery.or()
|
||||
when {
|
||||
excluded -> rQuery.not()
|
||||
queryEmpty -> queryEmpty = false
|
||||
else -> rQuery.or()
|
||||
}
|
||||
|
||||
rQuery.beginGroup()
|
||||
//Match namespace if specified
|
||||
|
||||
@@ -11,10 +11,8 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import exh.isExSource
|
||||
import exh.isLewdSource
|
||||
import exh.metadata.genericCopyTo
|
||||
import exh.metadata.queryMetadataFromManga
|
||||
import exh.util.defRealm
|
||||
import exh.util.realmTrans
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.concurrent.thread
|
||||
@@ -64,7 +62,7 @@ class MetadataFetchDialog {
|
||||
val source = sourceManager.get(manga.source)
|
||||
source?.let {
|
||||
manga.copyFrom(it.fetchMangaDetails(manga).toBlocking().first())
|
||||
realm.queryMetadataFromManga(manga).findFirst()?.genericCopyTo(manga)
|
||||
realm.queryMetadataFromManga(manga).findFirst()?.copyTo(manga)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
Timber.e(t, "Could not migrate manga!")
|
||||
|
||||
@@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import exh.isExSource
|
||||
import exh.isLewdSource
|
||||
import exh.metadata.ehMetaQueryFromUrl
|
||||
import exh.metadata.models.ExGalleryMetadata
|
||||
import exh.util.realmTrans
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@@ -43,7 +43,9 @@ class UrlMigrator {
|
||||
//Build fixed URL
|
||||
val urlWithSlash = "/" + manga.url
|
||||
//Fix metadata if required
|
||||
val metadata = realm.ehMetaQueryFromUrl(manga.url, isExSource(manga.source)).findFirst()
|
||||
val metadata = ExGalleryMetadata.UrlQuery(manga.url, isExSource(manga.source))
|
||||
.query(realm)
|
||||
.findFirst()
|
||||
metadata?.url?.let {
|
||||
if (it.startsWith("g/")) { //Check if metadata URL has no slash
|
||||
metadata.url = urlWithSlash //Fix it
|
||||
|
||||
@@ -480,19 +480,19 @@ class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) {
|
||||
return query.average(fieldName)
|
||||
}
|
||||
|
||||
fun min(fieldName: String): Number {
|
||||
fun min(fieldName: String): Number? {
|
||||
return query.min(fieldName)
|
||||
}
|
||||
|
||||
fun minimumDate(fieldName: String): Date {
|
||||
fun minimumDate(fieldName: String): Date? {
|
||||
return query.minimumDate(fieldName)
|
||||
}
|
||||
|
||||
fun max(fieldName: String): Number {
|
||||
fun max(fieldName: String): Number? {
|
||||
return query.max(fieldName)
|
||||
}
|
||||
|
||||
fun maximumDate(fieldName: String): Date {
|
||||
fun maximumDate(fieldName: String): Date? {
|
||||
return query.maximumDate(fieldName)
|
||||
}
|
||||
|
||||
@@ -540,7 +540,7 @@ class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) {
|
||||
return query.findAllSortedAsync(fieldName1, sortOrder1, fieldName2, sortOrder2)
|
||||
}
|
||||
|
||||
fun findFirst(): E {
|
||||
fun findFirst(): E? {
|
||||
return query.findFirst()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,24 +7,8 @@ import java.util.*
|
||||
|
||||
inline fun <T> realmTrans(block: (Realm) -> T): T {
|
||||
return defRealm {
|
||||
it.beginTransaction()
|
||||
try {
|
||||
val res = block(it)
|
||||
it.commitTransaction()
|
||||
res
|
||||
} catch(t: Throwable) {
|
||||
if (it.isInTransaction) {
|
||||
it.cancelTransaction()
|
||||
} else {
|
||||
RealmLog.warn("Could not cancel transaction, not currently in a transaction.")
|
||||
}
|
||||
|
||||
throw t
|
||||
} finally {
|
||||
//Just in case
|
||||
if (it.isInTransaction) {
|
||||
it.cancelTransaction()
|
||||
}
|
||||
it.trans {
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,5 +19,27 @@ inline fun <T> defRealm(block: (Realm) -> T): T {
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Realm.trans(block: () -> T): T {
|
||||
beginTransaction()
|
||||
try {
|
||||
val res = block()
|
||||
commitTransaction()
|
||||
return res
|
||||
} catch(t: Throwable) {
|
||||
if (isInTransaction) {
|
||||
cancelTransaction()
|
||||
} else {
|
||||
RealmLog.warn("Could not cancel transaction, not currently in a transaction.")
|
||||
}
|
||||
|
||||
throw t
|
||||
} finally {
|
||||
//Just in case
|
||||
if (isInTransaction) {
|
||||
cancelTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>)
|
||||
= createObject(clazz, UUID.randomUUID().toString())
|
||||
= createObject(clazz, UUID.randomUUID().toString())!!
|
||||
|
||||
Reference in New Issue
Block a user