Migrate to realm for metadata

This commit is contained in:
NerdNumber9
2017-08-25 17:31:38 -04:00
parent bb6b88a703
commit cd291f0a27
31 changed files with 1394 additions and 588 deletions

View File

@@ -1,62 +1,114 @@
package exh.metadata
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.model.SManga
import exh.*
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.NHentaiMetadata
import exh.metadata.models.PervEdenGalleryMetadata
import exh.metadata.models.SearchableGalleryMetadata
import io.paperdb.Paper
import io.realm.Realm
import io.realm.RealmQuery
import io.realm.RealmResults
import rx.Observable
import kotlin.reflect.KClass
class MetadataHelper {
fun Realm.ehMetaQueryFromUrl(url: String,
exh: Boolean,
meta: RealmQuery<ExGalleryMetadata>? = null) =
ehMetadataQuery(
ExGalleryMetadata.galleryId(url),
ExGalleryMetadata.galleryToken(url),
exh,
meta
)
fun writeGallery(galleryMetadata: SearchableGalleryMetadata, source: Long)
= (if(isExSource(source) || isEhSource(source)) exGalleryBook()
else if(isPervEdenSource(source)) pervEdenGalleryBook()
else if(isNhentaiSource(source)) nhentaiGalleryBook()
else null)?.write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata)!!
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 fetchEhMetadata(url: String, exh: Boolean): ExGalleryMetadata?
= ExGalleryMetadata().let {
it.url = url
it.exh = exh
return exGalleryBook().read<ExGalleryMetadata>(it.galleryUniqueIdentifier())
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>?) =
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>?) =
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>> =
mapOf(
Pair(ExGalleryMetadata::class, where(ExGalleryMetadata::class.java).findAll()),
Pair(NHentaiMetadata::class, where(NHentaiMetadata::class.java).findAll()),
Pair(PervEdenGalleryMetadata::class, where(PervEdenGalleryMetadata::class.java).findAll())
)
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!")
}
fun fetchPervEdenMetadata(url: String, source: Long): PervEdenGalleryMetadata?
= PervEdenGalleryMetadata().let {
it.url = url
if(source == PERV_EDEN_EN_SOURCE_ID)
it.lang = "en"
else if(source == PERV_EDEN_IT_SOURCE_ID)
it.lang = "it"
else throw IllegalArgumentException("Invalid source id!")
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? {
if(isExSource(source) || isEhSource(source)) {
return fetchEhMetadata(url, isExSource(source))
} else if(isPervEdenSource(source)) {
return fetchPervEdenMetadata(url, source)
} else if(isNhentaiSource(source)) {
return fetchNhentaiMetadata(url)
} else {
return null
}
}
fun getAllGalleries() = exGalleryBook().allKeys.map {
exGalleryBook().read<ExGalleryMetadata>(it)
}
fun exGalleryBook() = Paper.book("gallery-ex")!!
fun pervEdenGalleryBook() = Paper.book("gallery-perveden")!!
fun nhentaiGalleryBook() = Paper.book("gallery-nhentai")!!
}

View File

@@ -4,7 +4,6 @@ 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.EHentaiMetadata
import eu.kanade.tachiyomi.source.online.all.PervEden
import exh.metadata.models.*
import exh.plusAssign
@@ -51,12 +50,12 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
titleObj?.let { manga.title = it }
//Set artist (if we can find one)
tags[EH_ARTIST_NAMESPACE]?.let {
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = Tag::name)
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[EH_AUTHOR_NAMESPACE]?.let {
if(it.isNotEmpty()) manga.author = it.joinToString(transform = Tag::name)
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 }
@@ -159,12 +158,12 @@ fun NHentaiMetadata.copyTo(manga: SManga) {
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.filter { it.namespace == NHENTAI_ARTIST_NAMESPACE }.let {
if(it.isNotEmpty()) manga.artist = it.joinToString(transform = { it.name!! })
}
tags[NHENTAI_CATEGORIES_NAMESPACE]?.let {
if(it.isNotEmpty()) manga.genre = it.joinToString(transform = Tag::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
@@ -209,7 +208,9 @@ fun SearchableGalleryMetadata.genericCopyTo(manga: SManga): Boolean {
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.entries.forEach { namespace, 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"

View File

@@ -1,22 +1,43 @@
package exh.metadata.models
import android.net.Uri
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.*
/**
* Gallery metadata storage model
*/
class ExGalleryMetadata : SearchableGalleryMetadata() {
@RealmClass
open class ExGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
var url: String? = null
@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
@@ -30,22 +51,26 @@ class ExGalleryMetadata : SearchableGalleryMetadata() {
var ratingCount: Int? = null
var averageRating: Double? = null
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOf(title, altTitle).filterNotNull()
private fun splitGalleryUrl()
= url?.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
}
@Ignore
override val titleFields = listOf(
ExGalleryMetadata::title.name,
ExGalleryMetadata::altTitle.name
)
fun galleryId() = splitGalleryUrl()?.let { it[it.size - 2] }
companion object {
private fun splitGalleryUrl(url: String)
= url.let {
Uri.parse(it).pathSegments
.filterNot(String::isNullOrBlank)
}
fun galleryToken() =
splitGalleryUrl()?.last()
fun galleryId(url: String) = splitGalleryUrl(url).let { it[it.size - 2] }
override fun galleryUniqueIdentifier() = exh?.let { exh ->
url?.let {
//Fuck, this should be EXH and EH but it's too late to change it now...
"${if(exh) "EXH" else "EX"}-${galleryId()}-${galleryToken()}"
}
fun galleryToken(url: String) =
splitGalleryUrl(url).last()
}
}

View File

@@ -1,40 +1,64 @@
package exh.metadata.models
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.*
/**
* NHentai metadata
*/
class NHentaiMetadata : SearchableGalleryMetadata() {
@RealmClass
open class NHentaiMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
var id: Long? = null
var nhId: Long? = null
var url get() = id?.let { "$BASE_URL/g/$it" }
var url get() = nhId?.let { "$BASE_URL/g/$it" }
set(a) {
a?.let {
id = a.split("/").last { it.isNotBlank() }.toLong()
nhId = nhIdFromUrl(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: MutableList<String> = mutableListOf()
var pageImageTypes: RealmList<PageImageType> = RealmList()
var thumbnailImageType: String? = null
var scanlator: String? = null
override fun galleryUniqueIdentifier(): String? = "NHENTAI-$id"
override var tags: RealmList<Tag> = RealmList()
override fun getTitles() = listOf(japaneseTitle, englishTitle, shortTitle).filterNotNull()
@Ignore
override val titleFields = listOf(
NHentaiMetadata::japaneseTitle.name,
NHentaiMetadata::englishTitle.name,
NHentaiMetadata::shortTitle.name
)
companion object {
val BASE_URL = "https://nhentai.net"
@@ -44,5 +68,27 @@ class NHentaiMetadata : SearchableGalleryMetadata() {
"j" -> "jpg"
else -> null
}
fun nhIdFromUrl(url: String)
= url.split("/").last { it.isNotBlank() }.toLong()
}
}
@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,14 +1,33 @@
package exh.metadata.models
import android.net.Uri
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 PervEdenGalleryMetadata : RealmObject(), SearchableGalleryMetadata {
@PrimaryKey
override var uuid: String = UUID.randomUUID().toString()
@Index
var pvId: String? = null
class PervEdenGalleryMetadata : SearchableGalleryMetadata() {
var url: String? = null
var thumbnailUrl: String? = null
@Index
var title: String? = null
var altTitles: MutableList<String> = mutableListOf()
var altTitles: RealmList<PervEdenTitle> = RealmList()
@Index
override var uploader: String? = null
@Index
var artist: String? = null
var type: String? = null
@@ -19,14 +38,48 @@ class PervEdenGalleryMetadata : SearchableGalleryMetadata() {
var lang: String? = null
override fun getTitles() = listOf(title).plus(altTitles).filterNotNull()
override var tags: RealmList<Tag> = RealmList()
private fun splitGalleryUrl()
= url?.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
}
override fun getTitles() = listOf(title).plus(altTitles.map {
it.title
}).filterNotNull()
override fun galleryUniqueIdentifier() = splitGalleryUrl()?.let {
"PERVEDEN-${lang?.toUpperCase()}-${it.last()}"
@Ignore
override val titleFields = listOf(
//TODO Somehow include altTitles
PervEdenGalleryMetadata::title.name
)
companion object {
private fun splitGalleryUrl(url: String)
= url.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
}
fun pvIdFromUrl(url: String) = splitGalleryUrl(url).last()
}
}
@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,18 +1,24 @@
package exh.metadata.models
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
*/
abstract class SearchableGalleryMetadata {
var uploader: String? = null
interface SearchableGalleryMetadata: RealmModel {
var uuid: String
var uploader: String?
//Being specific about which classes are used in generics to make deserialization easier
val tags: HashMap<String, ArrayList<Tag>> = HashMap()
var tags: RealmList<Tag>
abstract fun galleryUniqueIdentifier(): String?
fun getTitles(): List<String>
abstract fun getTitles(): List<String>
val titleFields: List<String>
}

View File

@@ -1,7 +1,36 @@
package exh.metadata.models
import io.realm.RealmObject
import io.realm.annotations.Index
import io.realm.annotations.RealmClass
/**
* Simple tag model
*/
data class Tag(var name: String, var light: Boolean)
@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)"
}