Remove unused metadata objects and misc code cleanup

This commit is contained in:
NerdNumber9 2019-04-12 04:18:57 -04:00
parent 6f36331818
commit cadd389658
24 changed files with 27 additions and 1218 deletions

View File

@ -17,7 +17,6 @@ import exh.metadata.metadata.EHentaiSearchMetadata.Companion.EH_GENRE_NAMESPACE
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_NORMAL
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.nullIfBlank
import exh.metadata.parseHumanReadableByteCount
import exh.ui.login.LoginController
@ -253,9 +252,9 @@ class EHentai(override val id: Long,
override fun parseIntoMetadata(metadata: EHentaiSearchMetadata, input: Response) {
with(metadata) {
with(input.asJsoup()) {
val url = input.request().url().encodedPath()!!
gId = ExGalleryMetadata.galleryId(url)
gToken = ExGalleryMetadata.galleryToken(url)
val url = input.request().url().encodedPath()
gId = EHentaiSearchMetadata.galleryId(url)
gToken = EHentaiSearchMetadata.galleryToken(url)
exh = this@EHentai.exh
title = select("#gn").text().nullIfBlank()?.trim()

View File

@ -18,7 +18,6 @@ import exh.NHENTAI_SOURCE_ID
import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.NHentaiSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedTag
import exh.metadata.models.NHentaiMetadata
import exh.util.*
import okhttp3.Request
import okhttp3.Response
@ -226,7 +225,7 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
override fun fetchImageUrl(page: Page) = Observable.just(page.imageUrl!!)!!
fun imageUrlFromType(mediaId: String, page: Int, t: String) = NHentaiMetadata.typeToExtension(t)?.let {
fun imageUrlFromType(mediaId: String, page: Int, t: String) = NHentaiSearchMetadata.typeToExtension(t)?.let {
"https://i.nhentai.net/galleries/$mediaId/$page.$it"
}

View File

@ -13,7 +13,6 @@ import exh.metadata.metadata.PervEdenSearchMetadata
import exh.metadata.metadata.PervEdenSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag
import exh.metadata.models.PervEdenGalleryMetadata
import exh.util.UriFilter
import exh.util.UriGroup
import exh.util.urlImportFetchSearchManga
@ -138,7 +137,7 @@ class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSour
with(metadata) {
url = Uri.parse(input.location()).path
pvId = PervEdenGalleryMetadata.pvIdFromUrl(url!!)
pvId = PervEdenSearchMetadata.pvIdFromUrl(url!!)
lang = this@PervEden.lang

View File

@ -10,7 +10,7 @@ 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.models.ExGalleryMetadata
import exh.metadata.metadata.EHentaiSearchMetadata
import okhttp3.MediaType
import okhttp3.Request
import okhttp3.RequestBody
@ -137,7 +137,7 @@ class GalleryAdder {
?: return GalleryAddEvent.Fail.Error(url, "Could not find EH source!")
val cleanedUrl = when(source) {
EH_SOURCE_ID, EXH_SOURCE_ID -> ExGalleryMetadata.normalizeUrl(getUrlWithoutDomain(realUrl))
EH_SOURCE_ID, EXH_SOURCE_ID -> EHentaiSearchMetadata.normalizeUrl(getUrlWithoutDomain(realUrl))
NHENTAI_SOURCE_ID -> getUrlWithoutDomain(realUrl)
PERV_EDEN_EN_SOURCE_ID,
PERV_EDEN_IT_SOURCE_ID -> getUrlWithoutDomain(realUrl)

View File

@ -1,6 +1,6 @@
package exh.favorites
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.metadata.EHentaiSearchMetadata
import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.PrimaryKey
@ -19,5 +19,5 @@ open class FavoriteEntry : RealmObject() {
@Index var category: Int = -1
fun getUrl() = ExGalleryMetadata.normalizeUrl(gid, token)
fun getUrl() = EHentaiSearchMetadata.idAndTokenToUrl(gid, token)
}

View File

@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.online.all.EHentai
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.metadata.EHentaiSearchMetadata
import io.realm.Realm
import io.realm.RealmConfiguration
import uy.kohesive.injekt.injectLazy
@ -111,8 +111,8 @@ class LocalFavoritesStorage {
}.mapNotNull {
FavoriteEntry().apply {
title = it.second.title
gid = ExGalleryMetadata.galleryId(it.second.url)
token = ExGalleryMetadata.galleryToken(it.second.url)
gid = EHentaiSearchMetadata.galleryId(it.second.url)
token = EHentaiSearchMetadata.galleryToken(it.second.url)
category = it.first
// TODO Throw error here

View File

@ -1,52 +0,0 @@
package exh.metadata
import eu.kanade.tachiyomi.data.database.models.Manga
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.*
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.reflect.KClass
//fun Realm.loadAllMetadata(): Map<KClass<out SearchableGalleryMetadata>, RealmResults<out SearchableGalleryMetadata>> =
// Injekt.get<SourceManager>().getOnlineSources().filterIsInstance<LewdSource<*, *>>().map {
// it.queryAll()
// }.associate {
// it.clazz to it.query(this@loadAllMetadata).sort(SearchableGalleryMetadata::mangaId.name).findAll()
// }.toMap()
//fun Realm.queryMetadataFromManga(manga: Manga,
// 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...")
executeTransaction {
mangas.forEach { manga ->
if(isLewdSource(manga.manga.source)) {
try {
manga.hasMetadata =
queryMetadataFromManga(manga.manga).findFirst()?.let { meta ->
meta.mangaId = manga.manga.id
true
} ?: false
} catch (e: Exception) {
Timber.w(e, "Error syncing manga IDs! Ignoring...")
}
}
}
}
Timber.d("--> EH: Finish syncing ${mangas.size} manga IDs!")
}*/
//val Manga.metadataClass
// get() = (Injekt.get<SourceManager>().get(source) as? LewdSource<*, *>)?.queryAll()?.clazz

View File

@ -1,7 +1,5 @@
package exh.metadata
import exh.metadata.models.SearchableGalleryMetadata
import exh.plusAssign
import java.text.SimpleDateFormat
import java.util.*
@ -10,18 +8,18 @@ import java.util.*
*/
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
val unit = if (si) 1000 else 1024
if (bytes < unit) return bytes.toString() + " B"
if (bytes < unit) return "$bytes B"
val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt()
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre)
}
private val KB_FACTOR: Long = 1000
private val KIB_FACTOR: Long = 1024
private val MB_FACTOR = 1000 * KB_FACTOR
private val MIB_FACTOR = 1024 * KIB_FACTOR
private val GB_FACTOR = 1000 * MB_FACTOR
private val GIB_FACTOR = 1024 * MIB_FACTOR
private const val KB_FACTOR: Long = 1000
private const val KIB_FACTOR: Long = 1024
private const val MB_FACTOR = 1000 * KB_FACTOR
private const val MIB_FACTOR = 1024 * KIB_FACTOR
private const val GB_FACTOR = 1000 * MB_FACTOR
private const val GIB_FACTOR = 1024 * MIB_FACTOR
fun parseHumanReadableByteCount(arg0: String): Double? {
val spaceNdx = arg0.indexOf(" ")
@ -66,24 +64,3 @@ val ONGOING_SUFFIX = arrayOf(
)
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"
}
}
}
fun joinTagsToGenreString(metadata: SearchableGalleryMetadata)
= metadata.tags.joinToString { "${it.namespace}: ${it.name}" }
fun joinEmulatedTagsToGenreString(metadata: SearchableGalleryMetadata)
= metadata.tags.filter { it.namespace == EMULATED_TAG_NAMESPACE }.joinToString { it.name.toString() }
val EMULATED_TAG_NAMESPACE = "tag"

View File

@ -47,7 +47,7 @@ class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
const val TAG_TYPE_DEFAULT = 0
val BASE_URL = "https://hentai.cafe"
const val BASE_URL = "https://hentai.cafe"
fun hcIdFromUrl(url: String)
= url.split("/").last { it.isNotBlank() }

View File

@ -89,9 +89,8 @@ class HitomiSearchMetadata: RaisedSearchMetadata() {
const val TAG_TYPE_DEFAULT = 0
val LTN_BASE_URL = "https://ltn.hitomi.la"
val BASE_URL = "https://hitomi.la"
val IMG_BASE_URL = "https://aa.hitomi.la/galleries"
const val LTN_BASE_URL = "https://ltn.hitomi.la"
const val BASE_URL = "https://hitomi.la"
fun hlIdFromUrl(url: String)
= url.split('/').last().substringBeforeLast('.')

View File

@ -100,7 +100,7 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
const val TAG_TYPE_DEFAULT = 0
val BASE_URL = "https://nhentai.net"
const val BASE_URL = "https://nhentai.net"
private const val NHENTAI_ARTIST_NAMESPACE = "artist"
private const val NHENTAI_CATEGORIES_NAMESPACE = "category"

View File

@ -42,9 +42,10 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
titleDesc += "Title: $it\n"
}
if(altTitles.isNotEmpty())
titleDesc += "Alternate Titles: \n" + altTitles.map {
titleDesc += "Alternate Titles: \n" + altTitles
.joinToString(separator = "\n", postfix = "\n") {
"$it"
}.joinToString(separator = "\n", postfix = "\n")
}
val detailsDesc = StringBuilder()
artist?.let {
@ -102,7 +103,7 @@ enum class PervEdenLang(val id: Long) {
companion object {
fun source(id: Long)
= PervEdenLang.values().find { it.id == id }
= values().find { it.id == id }
?: throw IllegalArgumentException("Unknown source ID: $id!")
}
}

View File

@ -3,8 +3,6 @@ package exh.metadata.metadata
import android.net.Uri
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.buildTagsDescription
import exh.metadata.joinEmulatedTagsToGenreString
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.plusAssign
import java.util.*

View File

@ -1,190 +0,0 @@
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.*
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.*
/**
* Gallery metadata storage model
*/
@RealmClass
open class ExGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
var url: String? = null
set(value) {
//Ensure that URLs are always formatted in the same way to reduce duplicate galleries
field = value?.let { normalizeUrl(it) }
}
@Index
var gId: String? = null
@Index
var gToken: String? = null
@Index
var exh: Boolean? = null
var thumbnailUrl: String? = null
@Index
var title: String? = null
@Index
var altTitle: String? = null
@Index
override var uploader: String? = null
var genre: String? = null
var datePosted: Long? = null
var parent: String? = null
var visible: String? = null //Not a boolean
var language: String? = null
var translated: Boolean? = null
var size: Long? = null
var length: Int? = null
var favorites: Int? = null
var ratingCount: Int? = null
var averageRating: Double? = null
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOfNotNull(title, altTitle)
@Ignore
override val titleFields = TITLE_FIELDS
@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(
::gId to Query::gId,
::gToken to Query::gToken,
::exh to Query::exh
)
}
override fun copyTo(manga: SManga) {
url?.let { manga.url = normalizeUrl(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!! })
}
//Copy tags -> genres
manga.genre = joinTagsToGenreString(this)
//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()
genre?.let { detailsDesc += "Genre: $it\n" }
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 {
//Only parse URL if is full URL
val pathSegments = if(it.startsWith("http"))
Uri.parse(it).pathSegments
else
it.split('/')
pathSegments.filterNot(String::isNullOrBlank)
}
fun galleryId(url: String) = splitGalleryUrl(url)[1]
fun galleryToken(url: String) =
splitGalleryUrl(url)[2]
fun normalizeUrl(id: String, token: String)
= "/g/$id/$token/?nw=always"
fun normalizeUrl(url: String)
= normalizeUrl(galleryId(url), galleryToken(url))
val TITLE_FIELDS = listOf(
ExGalleryMetadata::title.name,
ExGalleryMetadata::altTitle.name
)
private const val EH_ARTIST_NAMESPACE = "artist"
}
}

View File

@ -1,70 +0,0 @@
package exh.metadata.models
import io.realm.Case
import io.realm.Realm
import io.realm.RealmQuery
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.java.name}!")
}
}
}
return newMeta
}
}

View File

@ -1,95 +0,0 @@
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 thumbnailUrl: String? = null
var title: String? = null
var artist: String? = null
override var uploader: String? = null //Always will be null as this is unknown
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOfNotNull(title)
@Ignore
override val titleFields = listOf(
::title.name
)
@Index
override var mangaId: Long? = null
override fun copyTo(manga: SManga) {
thumbnailUrl?.let { manga.thumbnail_url = it }
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() }
}
}

View File

@ -1,152 +0,0 @@
package exh.metadata.models
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.buildTagsDescription
import exh.metadata.joinTagsToGenreString
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 java.util.*
@RealmClass
open class HitomiGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
@Index
var hlId: String? = null
var thumbnailUrl: String? = null
var artist: String? = null
var group: String? = null
var type: String? = null
var language: String? = null
var languageSimple: String? = null
var series: RealmList<String> = RealmList()
var characters: RealmList<String> = RealmList()
var buyLink: String? = null
var uploadDate: Long? = null
override var tags: RealmList<Tag> = RealmList()
// Sites does not show uploader
override var uploader: String? = "admin"
var url get() = hlId?.let { urlFromHlId(it) }
set(a) {
a?.let {
hlId = hlIdFromUrl(a)
}
}
@Index
override var mangaId: Long? = null
@Index
var title: String? = null
override fun getTitles() = listOfNotNull(title)
@Ignore
override val titleFields = listOf(
::title.name
)
class EmptyQuery : GalleryQuery<HitomiGalleryMetadata>(HitomiGalleryMetadata::class)
class UrlQuery(
val url: String
) : GalleryQuery<HitomiGalleryMetadata>(HitomiGalleryMetadata::class) {
override fun transform() = Query(
hlIdFromUrl(url)
)
}
class Query(val hlId: String): GalleryQuery<HitomiGalleryMetadata>(HitomiGalleryMetadata::class) {
override fun map() = mapOf(
HitomiGalleryMetadata::hlId to Query::hlId
)
}
override fun copyTo(manga: SManga) {
thumbnailUrl?.let { manga.thumbnail_url = it }
val titleDesc = StringBuilder()
title?.let {
manga.title = it
titleDesc += "Title: $it\n"
}
val detailsDesc = StringBuilder()
artist?.let {
manga.artist = it
manga.author = it
detailsDesc += "Artist: $it\n"
}
group?.let {
detailsDesc += "Group: $it\n"
}
type?.let {
detailsDesc += "Type: $it\n"
}
(language ?: languageSimple ?: "none").let {
detailsDesc += "Language: $it\n"
}
if(series.isNotEmpty())
detailsDesc += "Series: ${series.joinToString()}\n"
if(characters.isNotEmpty())
detailsDesc += "Characters: ${characters.joinToString()}\n"
uploadDate?.let {
detailsDesc += "Upload date: ${EX_DATE_FORMAT.format(Date(it))}\n"
}
buyLink?.let {
detailsDesc += "Buy at: $it"
}
manga.status = SManga.UNKNOWN
//Copy tags -> genres
manga.genre = joinTagsToGenreString(this)
val tagsDesc = buildTagsDescription(this)
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank)
.joinToString(separator = "\n")
}
companion object {
val LTN_BASE_URL = "https://ltn.hitomi.la"
val BASE_URL = "https://hitomi.la"
fun hlIdFromUrl(url: String)
= url.split('/').last().substringBeforeLast('.')
fun urlFromHlId(id: String)
= "$BASE_URL/galleries/$id.html"
}
}

View File

@ -1,14 +0,0 @@
package exh.metadata.models
import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.RealmClass
@RealmClass
open class HitomiPage: RealmObject() {
@Index lateinit var gallery: String
@Index var index: Int = -1
lateinit var url: String
}

View File

@ -1,67 +0,0 @@
package exh.metadata.models
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.models.HitomiGalleryMetadata.Companion.hlIdFromUrl
import exh.metadata.models.HitomiGalleryMetadata.Companion.urlFromHlId
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.Index
import io.realm.annotations.RealmClass
@RealmClass
open class HitomiSkeletonGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
override var uuid: String
set(value) {}
get() = throw UnsupportedOperationException()
var hlId: String? = null
var thumbnailUrl: String? = null
var artist: String? = null
var group: String? = null
var type: String? = null
var language: String? = null
var languageSimple: String? = null
var series: RealmList<String> = RealmList()
var characters: RealmList<String> = RealmList()
var buyLink: String? = null
var uploadDate: Long? = null
override var tags: RealmList<Tag> = RealmList()
// Sites does not show uploader
override var uploader: String? = "admin"
var url get() = hlId?.let { urlFromHlId(it) }
set(a) {
a?.let {
hlId = hlIdFromUrl(a)
}
}
override var mangaId: Long? = null
@Index
var title: String? = null
override fun getTitles() = listOfNotNull(title)
@Ignore
override val titleFields = listOf(
::title.name
)
override fun copyTo(manga: SManga) {
throw UnsupportedOperationException("This operation cannot be performed on skeleton galleries!")
}
}

View File

@ -1,185 +0,0 @@
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.*
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.*
/**
* NHentai metadata
*/
@RealmClass
open class NHentaiMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
var nhId: Long? = null
var url get() = nhId?.let { "$BASE_URL/g/$it" }
set(a) {
a?.let {
nhId = nhUrlToId(a)
}
}
@Index
override var uploader: String? = null
var uploadDate: Long? = null
var favoritesCount: Long? = null
var mediaId: String? = null
@Index
var japaneseTitle: String? = null
@Index
var englishTitle: String? = null
@Index
var shortTitle: String? = null
var coverImageType: String? = null
var pageImageTypes: RealmList<PageImageType> = RealmList()
var thumbnailImageType: String? = null
var scanlator: String? = null
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOf(japaneseTitle, englishTitle, shortTitle).filterNotNull()
@Ignore
override val titleFields = TITLE_FIELDS
@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(
nhUrlToId(url)
)
}
class Query(
val nhId: Long
) : GalleryQuery<NHentaiMetadata>(NHentaiMetadata::class) {
override fun map() = mapOf(
::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_nh_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!! })
}
var category: String? = null
tags.filter { it.namespace == NHENTAI_CATEGORIES_NAMESPACE }.let {
if(it.isNotEmpty()) category = it.joinToString(transform = { it.name!! })
}
//Copy tags -> genres
manga.genre = joinEmulatedTagsToGenreString(this)
//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()
category?.let { detailsDesc += "Category: $it\n" }
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
}
fun nhUrlToId(url: String)
= url.split("/").last { it.isNotBlank() }.toLong()
val TITLE_FIELDS = listOf(
NHentaiMetadata::japaneseTitle.name,
NHentaiMetadata::englishTitle.name,
NHentaiMetadata::shortTitle.name
)
}
}
@RealmClass
open class PageImageType(var type: String? = null): RealmObject() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as PageImageType
if (type != other.type) return false
return true
}
override fun hashCode() = type?.hashCode() ?: 0
override fun toString() = "PageImageType(type=$type)"
}

View File

@ -1,144 +0,0 @@
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.metadata.joinEmulatedTagsToGenreString
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
import io.realm.annotations.RealmClass
import java.util.*
@RealmClass
open class PervEdenGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
@Index
var pvId: String? = null
var url: String? = null
var thumbnailUrl: String? = null
@Index
var title: String? = null
var altTitles: RealmList<PervEdenTitle> = RealmList()
@Index
override var uploader: String? = null
@Index
var artist: String? = null
var type: String? = null
var rating: Float? = null
var status: String? = null
var lang: String? = null
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOf(title).plus(altTitles.map {
it.title
}).filterNotNull()
@Ignore
override val titleFields = TITLE_FIELDS
@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 {
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)
}
//Copy tags -> genres
manga.genre = joinEmulatedTagsToGenreString(this)
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)
}
fun pvIdFromUrl(url: String) = splitGalleryUrl(url).last()
val TITLE_FIELDS = listOf(
//TODO Somehow include altTitles
PervEdenGalleryMetadata::title.name
)
}
}
@RealmClass
open class PervEdenTitle(var metadata: PervEdenGalleryMetadata? = null,
@Index var title: String? = null): RealmObject() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as PervEdenTitle
if (metadata != other.metadata) return false
if (title != other.title) return false
return true
}
override fun hashCode(): Int {
var result = metadata?.hashCode() ?: 0
result = 31 * result + (title?.hashCode() ?: 0)
return result
}
override fun toString() = "PervEdenTitle(metadata=$metadata, title=$title)"
}

View File

@ -1,25 +0,0 @@
package exh.metadata.models
import eu.kanade.tachiyomi.source.model.SManga
import io.realm.RealmList
import io.realm.RealmModel
/**
* A gallery that can be searched using the EH search engine
*/
interface SearchableGalleryMetadata: RealmModel {
var uuid: String
var uploader: String?
//Being specific about which classes are used in generics to make deserialization easier
var tags: RealmList<Tag>
fun getTitles(): List<String>
val titleFields: List<String>
var mangaId: Long?
fun copyTo(manga: SManga)
}

View File

@ -1,36 +0,0 @@
package exh.metadata.models
import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.RealmClass
/**
* Simple tag model
*/
@RealmClass
open class Tag(@Index var namespace: String? = null,
@Index var name: String? = null,
var light: Boolean? = null): RealmObject() {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Tag
if (namespace != other.namespace) return false
if (name != other.name) return false
if (light != other.light) return false
return true
}
override fun hashCode(): Int {
var result = namespace?.hashCode() ?: 0
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + (light?.hashCode() ?: 0)
return result
}
override fun toString() = "Tag(namespace=$namespace, name=$name, light=$light)"
}

View File

@ -1,133 +0,0 @@
package exh.metadata.models
import android.net.Uri
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.buildTagsDescription
import exh.metadata.joinEmulatedTagsToGenreString
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 java.util.*
@RealmClass
open class TsuminoMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
@Index
var tmId: String? = null
var url get() = tmId?.let { mangaUrlFromId(it) }
set(a) {
a?.let {
tmId = tmIdFromUrl(a)
}
}
var title: String? = null
var artist: String? = null
override var uploader: String? = null
var uploadDate: Long? = null
var length: Int? = null
var ratingString: String? = null
var category: String? = null
var collection: String? = null
var group: String? = null
var parody: RealmList<String> = RealmList()
var character: RealmList<String> = RealmList()
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOfNotNull(title)
@Ignore
override val titleFields = listOf(
::title.name
)
@Index
override var mangaId: Long? = null
class EmptyQuery : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class)
class UrlQuery(
val url: String
) : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class) {
override fun transform() = Query(
tmIdFromUrl(url)
)
}
class Query(
val tmId: String
) : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class) {
override fun map() = mapOf(
::tmId to Query::tmId
)
}
override fun copyTo(manga: SManga) {
title?.let { manga.title = it }
manga.thumbnail_url = thumbUrlFromId(tmId.toString())
artist?.let { manga.artist = it }
manga.status = SManga.UNKNOWN
val titleDesc = "Title: $title\n"
val detailsDesc = StringBuilder()
uploader?.let { detailsDesc += "Uploader: $it\n" }
uploadDate?.let { detailsDesc += "Uploaded: ${EX_DATE_FORMAT.format(Date(it))}\n" }
length?.let { detailsDesc += "Length: $it pages\n" }
ratingString?.let { detailsDesc += "Rating: $it\n" }
category?.let {
detailsDesc += "Category: $it\n"
}
collection?.let { detailsDesc += "Collection: $it\n" }
group?.let { detailsDesc += "Group: $it\n" }
val parodiesString = parody.joinToString()
if(parodiesString.isNotEmpty()) {
detailsDesc += "Parody: $parodiesString\n"
}
val charactersString = character.joinToString()
if(charactersString.isNotEmpty()) {
detailsDesc += "Character: $charactersString\n"
}
//Copy tags -> genres
manga.genre = joinEmulatedTagsToGenreString(this)
val tagsDesc = buildTagsDescription(this)
manga.description = listOf(titleDesc, detailsDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank)
.joinToString(separator = "\n")
}
companion object {
val BASE_URL = "http://www.tsumino.com"
fun tmIdFromUrl(url: String)
= Uri.parse(url).pathSegments[2]
fun mangaUrlFromId(id: String) = "$BASE_URL/Book/Info/$id"
fun thumbUrlFromId(id: String) = "$BASE_URL/Image/Thumb/$id"
}
}