From 47d96a6ba9bfca60beec480bad9983ad85c5c820 Mon Sep 17 00:00:00 2001 From: tom5079 <7948651+tom5079@users.noreply.github.com> Date: Sat, 8 Mar 2025 15:27:57 -0800 Subject: [PATCH] dependency update --- .../pupil/networking/HitomiHttpClient.kt | 116 ++++++++++-------- .../xyz/quaver/pupil/networking/ImageCache.kt | 17 ++- gradle/libs.versions.toml | 32 ++--- 3 files changed, 93 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/xyz/quaver/pupil/networking/HitomiHttpClient.kt b/app/src/main/java/xyz/quaver/pupil/networking/HitomiHttpClient.kt index 7e578730..335247b3 100644 --- a/app/src/main/java/xyz/quaver/pupil/networking/HitomiHttpClient.kt +++ b/app/src/main/java/xyz/quaver/pupil/networking/HitomiHttpClient.kt @@ -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 { @@ -92,7 +91,7 @@ class ImagePathResolver(ggjs: String) { class ExpirableEntry( 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..
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 { @@ -188,14 +189,14 @@ object HitomiHttpClient { check(data.length in 1..10000) { "Invalid length ${data.length}" } - val buffer = getURLAtRange(url, offset.. getGalleryIDsFromNozomi("tag", query.toString(), language) "language" -> getGalleryIDsFromNozomi(null, "index", query.tag) null -> { @@ -242,19 +246,24 @@ 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> = runCatching { - val field = query.namespace ?: "global" - val key = Node.Key(query.tag) - val node = getNodeAtAddress(field, 0) - val data = bSearch(field, key, node) + suspend fun getSuggestionsForQuery(query: SearchQuery.Tag): Result> = + runCatching { + val field = query.namespace ?: "global" + val key = Node.Key(query.tag) + val node = getNodeAtAddress(field, 0) + val data = bSearch(field, key, node) - data?.let { getSuggestionsFromData(field, data) } ?: emptyList() - } + data?.let { getSuggestionsFromData(field, data) } ?: emptyList() + } suspend fun getGalleryInfo(galleryID: Int) = runCatching { withContext(Dispatchers.IO) { @@ -277,7 +286,7 @@ object HitomiHttpClient { val result = LinkedHashSet() - 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,49 +333,52 @@ object HitomiHttpClient { result } + null -> getGalleryIDsFromNozomi(null, "index", "all").toSet() } } - suspend fun getImageURL(galleryFile: GalleryFile, thumbnail: Boolean = false): List = buildList { - val imagePathResolver = imagePathResolver.getValue() + suspend fun getImageURL(galleryFile: GalleryFile, thumbnail: Boolean = false): List = + buildList { + val imagePathResolver = imagePathResolver.getValue() - listOf("webp", "avif", "jxl").forEach { type -> - val available = when { - thumbnail && type != "jxl" -> true - type == "webp" -> galleryFile.hasWebP != 0 - type == "avif" -> galleryFile.hasAVIF != 0 - !thumbnail && type == "jxl" -> galleryFile.hasJXL != 0 - else -> false + listOf("webp", "avif", "jxl").forEach { type -> + val available = when { + thumbnail && type != "jxl" -> true + type == "webp" -> galleryFile.hasWebP != 0 + type == "avif" -> galleryFile.hasAVIF != 0 + !thumbnail && type == "jxl" -> galleryFile.hasJXL != 0 + else -> false + } + + if (!available) return@forEach + + val url = buildString { + append("https://") + append(imagePathResolver.decodeSubdomain(galleryFile.hash, thumbnail)) + append(".hitomi.la/") + append(type) + if (thumbnail) append("bigtn") + append('/') + append(imagePathResolver.decodeImagePath(galleryFile.hash, thumbnail)) + append('.') + append(type) + } + + add(url) } - - if (!available) return@forEach - - val url = buildString { - append("https://") - append(imagePathResolver.decodeSubdomain(galleryFile.hash, thumbnail)) - append(".hitomi.la/") - append(type) - if (thumbnail) append("bigtn") - append('/') - append(imagePathResolver.decodeImagePath(galleryFile.hash, thumbnail)) - append('.') - append(type) - } - - add(url) } - } suspend fun loadImage( galleryFile: GalleryFile, thumbnail: Boolean = false, acceptImage: (String) -> Boolean = { true }, - onDownload: (bytesSentTotal: Long, contentLength: Long) -> Unit = { _, _ -> } + onDownload: (bytesSentTotal: Long, contentLength: Long?) -> Unit = { _, _ -> }, ): Result> { 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) } diff --git a/app/src/main/java/xyz/quaver/pupil/networking/ImageCache.kt b/app/src/main/java/xyz/quaver/pupil/networking/ImageCache.kt index bcc0b561..65a3e0e0 100644 --- a/app/src/main/java/xyz/quaver/pupil/networking/ImageCache.kt +++ b/app/src/main/java/xyz/quaver/pupil/networking/ImageCache.kt @@ -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 + suspend fun load( + galleryFile: GalleryFile, + forceDownload: Boolean = false, + ): StateFlow + 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() @@ -56,7 +60,7 @@ class FileImageCache( files.forEach { file -> val hash = file.hash - requests[hash]?.let { (job, _) -> + requests[hash]?.let { (job, _) -> job.cancel() } @@ -74,7 +78,10 @@ class FileImageCache( } } - override suspend fun load(galleryFile: GalleryFile, forceDownload: Boolean): StateFlow { + override suspend fun load( + galleryFile: GalleryFile, + forceDownload: Boolean, + ): StateFlow { val hash = galleryFile.hash mutex.withLock { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 703f1477..684ec982 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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]