Suggestion
This commit is contained in:
@@ -13,7 +13,9 @@ import kotlinx.coroutines.coroutineScope
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import xyz.quaver.pupil.hitomi.max_node_size
|
import xyz.quaver.pupil.hitomi.max_node_size
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
import java.nio.IntBuffer
|
import java.nio.IntBuffer
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
|
||||||
const val domain = "ltn.hitomi.la"
|
const val domain = "ltn.hitomi.la"
|
||||||
const val galleryBlockExtension = ".html"
|
const val galleryBlockExtension = ".html"
|
||||||
@@ -28,6 +30,11 @@ const val galleriesIndexDir = "galleriesindex"
|
|||||||
const val languagesIndexDir = "languagesindex"
|
const val languagesIndexDir = "languagesindex"
|
||||||
const val nozomiURLIndexDir = "nozomiurlindex"
|
const val nozomiURLIndexDir = "nozomiurlindex"
|
||||||
|
|
||||||
|
data class Suggestion(
|
||||||
|
val tag: SearchQuery.Tag,
|
||||||
|
val count: Int
|
||||||
|
)
|
||||||
|
|
||||||
fun IntBuffer.toSet(): Set<Int> {
|
fun IntBuffer.toSet(): Set<Int> {
|
||||||
val result = mutableSetOf<Int>()
|
val result = mutableSetOf<Int>()
|
||||||
|
|
||||||
@@ -78,7 +85,7 @@ class HitomiHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Node.decodeNode(
|
return Node.decodeNode(
|
||||||
getURLAtRange(url, address until (address+max_node_size))
|
getURLAtRange(url, address ..< address+max_node_size)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +119,38 @@ class HitomiHttpClient {
|
|||||||
return getURLAtRange(url, offset until (offset+length)).asIntBuffer()
|
return getURLAtRange(url, offset until (offset+length)).asIntBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getGalleryIDsFromNozomi(
|
private suspend fun getSuggestionsFromData(field: String, data: Node.Data): List<Suggestion> {
|
||||||
|
val url = "https://$domain/$indexDir/$field.${getTagIndexVersion()}.data"
|
||||||
|
val (offset, length) = data
|
||||||
|
|
||||||
|
check(data.length in 1..10000) { "Invalid length ${data.length}" }
|
||||||
|
|
||||||
|
val buffer = getURLAtRange(url, offset..<offset+length).order(ByteOrder.BIG_ENDIAN)
|
||||||
|
|
||||||
|
val numberOfSuggestions = buffer.int
|
||||||
|
|
||||||
|
check(numberOfSuggestions in 1 .. 100) { "Number of suggestions $numberOfSuggestions is too long" }
|
||||||
|
|
||||||
|
return buildList {
|
||||||
|
for (i in 0 ..< numberOfSuggestions) {
|
||||||
|
val namespaceLen = buffer.int
|
||||||
|
val namespace = ByteArray(namespaceLen).apply {
|
||||||
|
buffer.get(this)
|
||||||
|
}.toString(charset("UTF-8"))
|
||||||
|
|
||||||
|
val tagLen = buffer.int
|
||||||
|
val tag = ByteArray(tagLen).apply {
|
||||||
|
buffer.get(this)
|
||||||
|
}.toString(charset("UTF-8"))
|
||||||
|
|
||||||
|
val count = buffer.int
|
||||||
|
|
||||||
|
add(Suggestion(SearchQuery.Tag(namespace, tag), count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getGalleryIDsFromNozomi(
|
||||||
area: String?,
|
area: String?,
|
||||||
tag: String,
|
tag: String,
|
||||||
language: String
|
language: String
|
||||||
@@ -132,7 +170,7 @@ class HitomiHttpClient {
|
|||||||
return ByteBuffer.wrap(result).asIntBuffer()
|
return ByteBuffer.wrap(result).asIntBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getGalleryIDsForQuery(query: SearchQuery.Tag, language: String = "all"): IntBuffer = when (query.namespace) {
|
private suspend fun getGalleryIDsForQuery(query: SearchQuery.Tag, language: String = "all"): IntBuffer = when (query.namespace) {
|
||||||
"female", "male" -> getGalleryIDsFromNozomi("tag", query.toString(), language)
|
"female", "male" -> getGalleryIDsFromNozomi("tag", query.toString(), language)
|
||||||
"language" -> getGalleryIDsFromNozomi(null, "index", query.tag)
|
"language" -> getGalleryIDsFromNozomi(null, "index", query.tag)
|
||||||
null -> {
|
null -> {
|
||||||
@@ -146,62 +184,73 @@ class HitomiHttpClient {
|
|||||||
else -> getGalleryIDsFromNozomi(query.namespace, query.tag, language)
|
else -> getGalleryIDsFromNozomi(query.namespace, query.tag, language)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun search(query: SearchQuery?): Set<Int> = when (query) {
|
suspend fun getSuggestionsForQuery(query: SearchQuery.Tag): Result<List<Suggestion>> = runCatching {
|
||||||
is SearchQuery.Tag -> getGalleryIDsForQuery(query).toSet()
|
val field = query.namespace ?: "global"
|
||||||
is SearchQuery.Not -> coroutineScope {
|
val key = Node.Key(field)
|
||||||
val allGalleries = async {
|
val node = getNodeAtAddress(field, 0)
|
||||||
getGalleryIDsFromNozomi(null, "index", "all")
|
val data = bSearch(field, key, node)
|
||||||
}
|
|
||||||
|
|
||||||
val queriedGalleries = search(query.query)
|
data?.let { getSuggestionsFromData(field, data) } ?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val result = mutableSetOf<Int>()
|
suspend fun search(query: SearchQuery?): Result<Set<Int>> = runCatching {
|
||||||
|
when (query) {
|
||||||
|
is SearchQuery.Tag -> getGalleryIDsForQuery(query).toSet()
|
||||||
|
is SearchQuery.Not -> coroutineScope {
|
||||||
|
val allGalleries = async {
|
||||||
|
getGalleryIDsFromNozomi(null, "index", "all")
|
||||||
|
}
|
||||||
|
|
||||||
with (allGalleries.await()) {
|
val queriedGalleries = search(query.query).getOrThrow()
|
||||||
while (this.hasRemaining()) {
|
|
||||||
val gallery = this.get()
|
|
||||||
|
|
||||||
if (gallery in queriedGalleries) {
|
val result = mutableSetOf<Int>()
|
||||||
result.add(gallery)
|
|
||||||
|
with (allGalleries.await()) {
|
||||||
|
while (this.hasRemaining()) {
|
||||||
|
val gallery = this.get()
|
||||||
|
|
||||||
|
if (gallery in queriedGalleries) {
|
||||||
|
result.add(gallery)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
is SearchQuery.And -> coroutineScope {
|
is SearchQuery.And -> coroutineScope {
|
||||||
val queries = query.queries.map { query ->
|
val queries = query.queries.map { query ->
|
||||||
async {
|
async {
|
||||||
search(query)
|
search(query).getOrThrow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val result = queries.first().await().toMutableSet()
|
val result = queries.first().await().toMutableSet()
|
||||||
|
|
||||||
queries.drop(1).forEach {
|
queries.drop(1).forEach {
|
||||||
val queryResult = it.await()
|
val queryResult = it.await()
|
||||||
|
|
||||||
result.retainAll(queryResult)
|
result.retainAll(queryResult)
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
is SearchQuery.Or -> coroutineScope {
|
|
||||||
val queries = query.queries.map { query ->
|
|
||||||
async {
|
|
||||||
search(query)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
is SearchQuery.Or -> coroutineScope {
|
||||||
|
val queries = query.queries.map { query ->
|
||||||
|
async {
|
||||||
|
search(query).getOrThrow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val result = mutableSetOf<Int>()
|
val result = mutableSetOf<Int>()
|
||||||
|
|
||||||
queries.forEach {
|
queries.forEach {
|
||||||
val queryResult = it.await()
|
val queryResult = it.await()
|
||||||
result.addAll(queryResult)
|
result.addAll(queryResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
null -> getGalleryIDsFromNozomi(null, "index", "all").toSet()
|
||||||
result
|
|
||||||
}
|
}
|
||||||
null -> getGalleryIDsFromNozomi(null, "index", "all").toSet()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user