/* * This file is a part of the SteamWar software. * * Copyright (C) 2025 SteamWar.de-Serverteam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package de.steamwar.data import io.ktor.client.* import io.ktor.client.engine.java.* import io.ktor.client.plugins.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.serialization.kotlinx.json.* import io.ktor.utils.io.jvm.javaio.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.cbor.Cbor import kotlinx.serialization.decodeFromByteArray import kotlinx.serialization.encodeToByteArray import java.io.File import java.time.Instant import java.time.temporal.ChronoUnit const val kCacheFolder: String = "skins" val kCacheFolderFile: File = File(kCacheFolder) const val kCacheConfigName: String = "cache.cbor" val kCacheConfigFile: File = File(kCacheFolder, kCacheConfigName) @Serializable data class CacheConfig(val lastUpdate: MutableMap) { @OptIn(ExperimentalSerializationApi::class) companion object { private var config: CacheConfig = if (kCacheConfigFile.exists()) { kCacheConfigFile.inputStream().use { Cbor.decodeFromByteArray(it.readBytes()) } } else { kCacheConfigFile.createNewFile() kCacheConfigFile.outputStream().use { it.write(Cbor.encodeToByteArray(CacheConfig(mutableMapOf()))) } CacheConfig(mutableMapOf()) } private fun save() { kCacheConfigFile.outputStream().use { it.write(Cbor.encodeToByteArray(config)) } } fun update(uuid: String) { config.lastUpdate[uuid] = Instant.now().toEpochMilli() save() } fun isOutdated(uuid: String): Boolean { return config.lastUpdate[uuid]?.let { it < Instant.now().minus(1, ChronoUnit.DAYS).toEpochMilli() } ?: true } } } val client = HttpClient(Java) { install(ContentNegotiation) { json() } defaultRequest { header("User-Agent", "SteamWar/1.0") } } suspend fun getCachedSkin(uuid: String): Pair { val file = File(kCacheFolderFile, "$uuid.webp") if (file.exists()) { if (CacheConfig.isOutdated(uuid)) { val skin = client.get("https://vzge.me/bust/150/$uuid") skin.bodyAsChannel().copyTo(file.outputStream()) CacheConfig.update(uuid) return file to false } return file to true } withContext(Dispatchers.IO) { file.createNewFile() } val skin = client.get("https://vzge.me/bust/150/$uuid") skin.bodyAsChannel().copyTo(file.outputStream()) CacheConfig.update(uuid) return file to false }