dependency update
This commit is contained in:
@@ -17,7 +17,6 @@ import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.Clock.System.now
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
@@ -41,7 +40,7 @@ const val nozomiURLIndexDir = "nozomiurlindex"
|
||||
|
||||
data class Suggestion(
|
||||
val tag: SearchQuery.Tag,
|
||||
val count: Int
|
||||
val count: Int,
|
||||
)
|
||||
|
||||
fun IntBuffer.toSet(): Set<Int> {
|
||||
@@ -92,7 +91,7 @@ class ImagePathResolver(ggjs: String) {
|
||||
|
||||
class ExpirableEntry<T>(
|
||||
private val expiryDuration: Duration,
|
||||
private val action: suspend () -> T
|
||||
private val action: suspend () -> T,
|
||||
) {
|
||||
private var value: T? = null
|
||||
private var expiresAt: Instant = now()
|
||||
@@ -121,7 +120,8 @@ object HitomiHttpClient {
|
||||
}
|
||||
|
||||
private val tagIndexVersion = ExpirableEntry(1.minutes) { getIndexVersion("tagindex") }
|
||||
private val galleriesIndexVersion = ExpirableEntry(1.minutes) { getIndexVersion("galleriesindex") }
|
||||
private val galleriesIndexVersion =
|
||||
ExpirableEntry(1.minutes) { getIndexVersion("galleriesindex") }
|
||||
|
||||
private suspend fun getIndexVersion(name: String): String = withContext(Dispatchers.IO) {
|
||||
httpClient.get("https://$domain/$name/version?_=${System.currentTimeMillis()}").bodyAsText()
|
||||
@@ -144,18 +144,18 @@ object HitomiHttpClient {
|
||||
"galleries" -> "https://$domain/$galleriesIndexDir/galleries.${galleriesIndexVersion.getValue()}.index"
|
||||
"languages" -> "https://$domain/$galleriesIndexDir/languages.${galleriesIndexVersion.getValue()}.index"
|
||||
"nozomiurl" -> "https://$domain/$galleriesIndexDir/nozomiurl.${galleriesIndexVersion.getValue()}.index"
|
||||
else -> "https://$domain/$indexDir/$field.${HitomiHttpClient.tagIndexVersion.getValue()}.index"
|
||||
else -> "https://$domain/$indexDir/$field.${tagIndexVersion.getValue()}.index"
|
||||
}
|
||||
|
||||
return Node.decodeNode(
|
||||
getURLAtRange(url, address ..< address + maxNodeSize)
|
||||
getURLAtRange(url, address..<address + maxNodeSize)
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun bSearch(
|
||||
field: String,
|
||||
key: Node.Key,
|
||||
node: Node
|
||||
node: Node,
|
||||
): Node.Data? {
|
||||
if (node.keys.isEmpty()) {
|
||||
return null
|
||||
@@ -174,12 +174,13 @@ object HitomiHttpClient {
|
||||
}
|
||||
|
||||
private suspend fun getGalleryIDsFromData(offset: Long, length: Int): IntBuffer {
|
||||
val url = "https://$domain/$galleriesIndexDir/galleries.${galleriesIndexVersion.getValue()}.data"
|
||||
val url =
|
||||
"https://$domain/$galleriesIndexDir/galleries.${galleriesIndexVersion.getValue()}.data"
|
||||
if (length > 100000000 || length <= 0) {
|
||||
error("length $length is too long")
|
||||
}
|
||||
|
||||
return getURLAtRange(url, offset until (offset+length)).asIntBuffer()
|
||||
return getURLAtRange(url, offset until (offset + length)).asIntBuffer()
|
||||
}
|
||||
|
||||
private suspend fun getSuggestionsFromData(field: String, data: Node.Data): List<Suggestion> {
|
||||
@@ -188,14 +189,14 @@ object HitomiHttpClient {
|
||||
|
||||
check(data.length in 1..10000) { "Invalid length ${data.length}" }
|
||||
|
||||
val buffer = getURLAtRange(url, offset..<offset+length).order(ByteOrder.BIG_ENDIAN)
|
||||
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" }
|
||||
check(numberOfSuggestions in 1..100) { "Number of suggestions $numberOfSuggestions is too long" }
|
||||
|
||||
return buildList {
|
||||
for (i in 0 ..< numberOfSuggestions) {
|
||||
for (i in 0..<numberOfSuggestions) {
|
||||
val namespaceLen = buffer.int
|
||||
val namespace = ByteArray(namespaceLen).apply {
|
||||
buffer.get(this)
|
||||
@@ -216,7 +217,7 @@ object HitomiHttpClient {
|
||||
private suspend fun getGalleryIDsFromNozomi(
|
||||
area: String?,
|
||||
tag: String,
|
||||
language: String
|
||||
language: String,
|
||||
): IntBuffer {
|
||||
val nozomiAddress = if (area == null) {
|
||||
"https://$domain/$compressedNozomiPrefix/$tag-$language$nozomiExtension"
|
||||
@@ -233,7 +234,10 @@ object HitomiHttpClient {
|
||||
return ByteBuffer.wrap(result).asIntBuffer()
|
||||
}
|
||||
|
||||
private 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)
|
||||
"language" -> getGalleryIDsFromNozomi(null, "index", query.tag)
|
||||
null -> {
|
||||
@@ -242,12 +246,17 @@ object HitomiHttpClient {
|
||||
val node = getNodeAtAddress("galleries", 0)
|
||||
val data = bSearch("galleries", key, node)
|
||||
|
||||
if (data != null) getGalleryIDsFromData(data.offset, data.length) else IntBuffer.allocate(0)
|
||||
if (data != null) getGalleryIDsFromData(
|
||||
data.offset,
|
||||
data.length
|
||||
) else IntBuffer.allocate(0)
|
||||
}
|
||||
|
||||
else -> getGalleryIDsFromNozomi(query.namespace, query.tag, language)
|
||||
}
|
||||
|
||||
suspend fun getSuggestionsForQuery(query: SearchQuery.Tag): Result<List<Suggestion>> = runCatching {
|
||||
suspend fun getSuggestionsForQuery(query: SearchQuery.Tag): Result<List<Suggestion>> =
|
||||
runCatching {
|
||||
val field = query.namespace ?: "global"
|
||||
val key = Node.Key(query.tag)
|
||||
val node = getNodeAtAddress(field, 0)
|
||||
@@ -277,7 +286,7 @@ object HitomiHttpClient {
|
||||
|
||||
val result = LinkedHashSet<Int>()
|
||||
|
||||
with (allGalleries.await()) {
|
||||
with(allGalleries.await()) {
|
||||
while (this.hasRemaining()) {
|
||||
val gallery = this.get()
|
||||
|
||||
@@ -289,6 +298,7 @@ object HitomiHttpClient {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
is SearchQuery.And -> coroutineScope {
|
||||
val queries = query.queries.map { query ->
|
||||
async {
|
||||
@@ -306,6 +316,7 @@ object HitomiHttpClient {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
is SearchQuery.Or -> coroutineScope {
|
||||
val queries = query.queries.map { query ->
|
||||
async {
|
||||
@@ -322,11 +333,13 @@ object HitomiHttpClient {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
null -> getGalleryIDsFromNozomi(null, "index", "all").toSet()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getImageURL(galleryFile: GalleryFile, thumbnail: Boolean = false): List<String> = buildList {
|
||||
suspend fun getImageURL(galleryFile: GalleryFile, thumbnail: Boolean = false): List<String> =
|
||||
buildList {
|
||||
val imagePathResolver = imagePathResolver.getValue()
|
||||
|
||||
listOf("webp", "avif", "jxl").forEach { type ->
|
||||
@@ -360,11 +373,12 @@ object HitomiHttpClient {
|
||||
galleryFile: GalleryFile,
|
||||
thumbnail: Boolean = false,
|
||||
acceptImage: (String) -> Boolean = { true },
|
||||
onDownload: (bytesSentTotal: Long, contentLength: Long) -> Unit = { _, _ -> }
|
||||
onDownload: (bytesSentTotal: Long, contentLength: Long?) -> Unit = { _, _ -> },
|
||||
): Result<Pair<ByteReadChannel, String>> {
|
||||
return runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
val url = getImageURL(galleryFile, thumbnail).firstOrNull(acceptImage) ?: error("No available image")
|
||||
val url = getImageURL(galleryFile, thumbnail).firstOrNull(acceptImage)
|
||||
?: error("No available image")
|
||||
val channel: ByteReadChannel = httpClient.get(url) { onDownload(onDownload) }.body()
|
||||
Pair(channel, url)
|
||||
}
|
||||
|
||||
@@ -17,20 +17,24 @@ import java.io.File
|
||||
|
||||
sealed class ImageLoadProgress {
|
||||
data object NotStarted : ImageLoadProgress()
|
||||
data class Progress(val bytesSent: Long, val contentLength: Long) : ImageLoadProgress()
|
||||
data class Progress(val bytesSent: Long, val contentLength: Long?) : ImageLoadProgress()
|
||||
data class Finished(val file: File) : ImageLoadProgress()
|
||||
data class Error(val exception: Throwable) : ImageLoadProgress()
|
||||
}
|
||||
|
||||
interface ImageCache {
|
||||
suspend fun load(galleryFile: GalleryFile, forceDownload: Boolean = false): StateFlow<ImageLoadProgress>
|
||||
suspend fun load(
|
||||
galleryFile: GalleryFile,
|
||||
forceDownload: Boolean = false,
|
||||
): StateFlow<ImageLoadProgress>
|
||||
|
||||
suspend fun free(vararg files: GalleryFile)
|
||||
suspend fun clear()
|
||||
}
|
||||
|
||||
class FileImageCache(
|
||||
private val cacheDir: File,
|
||||
private val cacheLimit: Long = 128 * 1024 * 1024 // 128MB
|
||||
private val cacheLimit: Long = 128 * 1024 * 1024, // 128MB
|
||||
) : ImageCache {
|
||||
private val mutex = Mutex()
|
||||
|
||||
@@ -74,7 +78,10 @@ class FileImageCache(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(galleryFile: GalleryFile, forceDownload: Boolean): StateFlow<ImageLoadProgress> {
|
||||
override suspend fun load(
|
||||
galleryFile: GalleryFile,
|
||||
forceDownload: Boolean,
|
||||
): StateFlow<ImageLoadProgress> {
|
||||
val hash = galleryFile.hash
|
||||
|
||||
mutex.withLock {
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
[versions]
|
||||
agp = "8.8.1"
|
||||
android-compileSdk = "34"
|
||||
agp = "8.8.2"
|
||||
android-compileSdk = "35"
|
||||
android-minSdk = "21"
|
||||
android-targetSdk = "34"
|
||||
android-targetSdk = "35"
|
||||
kotlin = "2.0.0"
|
||||
ksp = "2.0.0-1.0.21"
|
||||
androidxComposeBom = "2024.06.00"
|
||||
androidxCore = "1.13.1"
|
||||
androidxActivity = "1.9.0"
|
||||
androidxLifecycle = "2.8.3"
|
||||
androidxNavigation = "2.7.7"
|
||||
accompanist = "0.34.0"
|
||||
material3 = "1.2.1"
|
||||
material-icons = "1.6.8"
|
||||
firebase = "33.1.1"
|
||||
crashlytics = "3.0.2"
|
||||
androidxComposeBom = "2025.02.00"
|
||||
androidxCore = "1.15.0"
|
||||
androidxActivity = "1.10.1"
|
||||
androidxLifecycle = "2.8.7"
|
||||
androidxNavigation = "2.8.8"
|
||||
accompanist = "0.37.2"
|
||||
material3 = "1.3.1"
|
||||
material-icons = "1.7.8"
|
||||
firebase = "33.10.0"
|
||||
crashlytics = "3.0.3"
|
||||
hilt = "2.51.1"
|
||||
ktor = "2.3.8"
|
||||
ktor = "3.1.0"
|
||||
coil = "2.6.0"
|
||||
room = "2.6.1"
|
||||
|
||||
kotlinx-serialization = "1.3.2"
|
||||
kotlinx-coroutines = "1.8.0"
|
||||
kotlinx-serialization = "1.8.0"
|
||||
kotlinx-coroutines = "1.10.1"
|
||||
kotlinx-datetime = "0.6.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
Reference in New Issue
Block a user