From 420a3e423008cdcfe3ee580e9b28df5b60457f93 Mon Sep 17 00:00:00 2001 From: zarazaex69 Date: Tue, 7 Apr 2026 12:19:58 +0300 Subject: [PATCH] refactor(subscriptions): migrate from MMKV library to file-based storage --- AddSubscription.kt | 82 --------------- V2rayNG/app/src/main/assets/mmkv/SUB | Bin 8192 -> 8777 bytes build.gradle.kts | 2 - src/main/kotlin/AddSubscription.kt | 144 +++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 84 deletions(-) delete mode 100644 AddSubscription.kt create mode 100644 src/main/kotlin/AddSubscription.kt diff --git a/AddSubscription.kt b/AddSubscription.kt deleted file mode 100644 index 3582b28e..00000000 --- a/AddSubscription.kt +++ /dev/null @@ -1,82 +0,0 @@ -import com.google.gson.Gson -import com.tencent.mmkv.MMKV -import java.util.UUID - -data class SubscriptionItem( - var remarks: String = "", - var url: String = "", - var enabled: Boolean = true, - val addedTime: Long = System.currentTimeMillis(), - var lastUpdated: Long = -1, - var autoUpdate: Boolean = false, - val updateInterval: Int? = null, - var prevProfile: String? = null, - var nextProfile: String? = null, - var filter: String? = null, - var allowInsecureUrl: Boolean = false, - var userAgent: String? = null -) - -class SubscriptionManager { - private val subStorage = MMKV.mmkvWithID("SUB") - private val gson = Gson() - - fun addSubscription(remarks: String, url: String, autoUpdate: Boolean = true): String { - val guid = UUID.randomUUID().toString().replace("-", "") - - val subItem = SubscriptionItem( - remarks = remarks, - url = url, - enabled = true, - addedTime = System.currentTimeMillis(), - lastUpdated = -1, - autoUpdate = autoUpdate, - filter = "", - allowInsecureUrl = false, - userAgent = "" - ) - - encodeSubscription(guid, subItem) - return guid - } - - private fun encodeSubscription(guid: String, subItem: SubscriptionItem) { - val json = gson.toJson(subItem) - subStorage.encode(guid, json) - - val subsList = decodeSubsList() - if (!subsList.contains(guid)) { - subsList.add(guid) - encodeSubsList(subsList) - } - } - - private fun decodeSubsList(): MutableList { - val json = subStorage.decodeString("SUBS_LIST") ?: return mutableListOf() - return gson.fromJson(json, Array::class.java)?.toMutableList() ?: mutableListOf() - } - - private fun encodeSubsList(subsList: List) { - subStorage.encode("SUBS_LIST", gson.toJson(subsList)) - } -} - -fun main() { - MMKV.initialize("/home/zarazaex/Projects/olcng/V2rayNG/app/src/main/assets/mmkv") - - val manager = SubscriptionManager() - - manager.addSubscription( - remarks = "БЕЛЫЕ Z", - url = "https://raw.githubusercontent.com/zieng2/wl/refs/heads/main/vless_universal.txt", - autoUpdate = true - ) - - manager.addSubscription( - remarks = "БЕЛЫЕ W", - url = "https://raw.githubusercontent.com/whoahaow/rjsxrd/refs/heads/main/githubmirror/bypass/bypass-all.txt", - autoUpdate = true - ) - - println("Подписки успешно добавлены") -} diff --git a/V2rayNG/app/src/main/assets/mmkv/SUB b/V2rayNG/app/src/main/assets/mmkv/SUB index 8cbabd7465abef2ee9129ee1297c0f24322b83d2..b2d799e943e431b85532e7a2f15b3c3be1e4e3fd 100644 GIT binary patch delta 363 zcma)%K}y3w7=_bSLDWSN(V|eND={_GWG0hlWx*pTg1dh*f0{_zl$j}Q5z#x$1$rHq z9>JwsPhg9Rh#Oar2M^x&JzYFst~#fl=XIor*#Hun(J*1aLdb=p5zA>5NmLlGH}CiE zwYA^4b;Bm#(wLi|Kv2f5j*k+YfUHoF)an`f1e8K`RgRIQeIYpK5vMff)F%}fdp%WP zH{)L4owWDd+rhT$?i`AlK|LJd#FCVdhYb~GY<(^uL@Jn~R8p3)fl%vCAFRN7)D$EDV_4&-tQ#(YH&s_NV>Eb+;|T$1>Y Pi%(p)-TJoK{cZCB?7(qF delta 6 NcmX@<(%`T_0RRdx0*(Lx diff --git a/build.gradle.kts b/build.gradle.kts index 56f1e80c..21087f5e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,12 +8,10 @@ version = "1.0" repositories { mavenCentral() - google() } dependencies { implementation("com.google.code.gson:gson:2.10.1") - implementation("com.tencent:mmkv:1.3.9") } application { diff --git a/src/main/kotlin/AddSubscription.kt b/src/main/kotlin/AddSubscription.kt new file mode 100644 index 00000000..defc8004 --- /dev/null +++ b/src/main/kotlin/AddSubscription.kt @@ -0,0 +1,144 @@ +import com.google.gson.Gson +import java.io.File +import java.io.RandomAccessFile +import java.net.URL +import java.util.UUID + +data class SubscriptionItem( + var remarks: String = "", + var url: String = "", + var enabled: Boolean = true, + val addedTime: Long = System.currentTimeMillis(), + var lastUpdated: Long = -1, + var autoUpdate: Boolean = false, + val updateInterval: Int? = null, + var prevProfile: String? = null, + var nextProfile: String? = null, + var filter: String? = null, + var allowInsecureUrl: Boolean = false, + var userAgent: String? = null +) + +class MMKVWriter(private val filePath: String) { + private val gson = Gson() + + fun writeEntry(key: String, json: String) { + val file = RandomAccessFile(filePath, "rw") + file.seek(file.length()) + + val keyBytes = key.toByteArray(Charsets.UTF_8) + val jsonBytes = json.toByteArray(Charsets.UTF_8) + + file.writeInt(keyBytes.size) + file.write(keyBytes) + file.writeInt(jsonBytes.size) + file.write(jsonBytes) + + file.close() + } +} + +class SubscriptionManager(private val mmkvPath: String) { + private val gson = Gson() + private val writer = MMKVWriter(mmkvPath) + + fun addSubscription(remarks: String, url: String, autoUpdate: Boolean = true): String { + val guid = UUID.randomUUID().toString().replace("-", "") + + val subItem = SubscriptionItem( + remarks = remarks, + url = url, + enabled = true, + addedTime = System.currentTimeMillis(), + lastUpdated = -1, + autoUpdate = autoUpdate, + filter = "", + allowInsecureUrl = false, + userAgent = "" + ) + + val json = gson.toJson(subItem) + writer.writeEntry(guid, json) + + return guid + } + + fun updateSubscription(guid: String, url: String): Boolean { + return try { + println("Обновление подписки $guid...") + val content = URL(url).readText() + val lines = content.lines().filter { it.isNotBlank() } + println("Загружено ${lines.size} конфигураций") + + val subItem = readSubscription(guid) ?: return false + subItem.lastUpdated = System.currentTimeMillis() + + val json = gson.toJson(subItem) + writer.writeEntry(guid, json) + + true + } catch (e: Exception) { + println("Ошибка обновления: ${e.message}") + false + } + } + + private fun readSubscription(guid: String): SubscriptionItem? { + val file = File(mmkvPath) + if (!file.exists()) return null + + val data = file.readBytes() + var pos = 0 + + while (pos < data.size) { + if (pos + 4 > data.size) break + + val keyLen = java.nio.ByteBuffer.wrap(data, pos, 4).int + pos += 4 + if (pos + keyLen > data.size) break + + val key = String(data, pos, keyLen, Charsets.UTF_8) + pos += keyLen + + if (pos + 4 > data.size) break + val jsonLen = java.nio.ByteBuffer.wrap(data, pos, 4).int + pos += 4 + if (pos + jsonLen > data.size) break + + val json = String(data, pos, jsonLen, Charsets.UTF_8) + pos += jsonLen + + if (key == guid) { + return gson.fromJson(json, SubscriptionItem::class.java) + } + } + + return null + } +} + +fun main() { + val mmkvPath = "/home/zarazaex/Projects/olcng/V2rayNG/app/src/main/assets/mmkv/SUB" + + val manager = SubscriptionManager(mmkvPath) + + val guid1 = manager.addSubscription( + remarks = "БЕЛЫЕ Z", + url = "https://raw.githubusercontent.com/zieng2/wl/refs/heads/main/vless_universal.txt", + autoUpdate = true + ) + println("Добавлена подписка БЕЛЫЕ Z: $guid1") + + val guid2 = manager.addSubscription( + remarks = "БЕЛЫЕ W", + url = "https://raw.githubusercontent.com/whoahaow/rjsxrd/refs/heads/main/githubmirror/bypass/bypass-all.txt", + autoUpdate = true + ) + println("Добавлена подписка БЕЛЫЕ W: $guid2") + + println("\nОбновление подписок...") + manager.updateSubscription(guid1, "https://raw.githubusercontent.com/zieng2/wl/refs/heads/main/vless_universal.txt") + manager.updateSubscription(guid2, "https://raw.githubusercontent.com/whoahaow/rjsxrd/refs/heads/main/githubmirror/bypass/bypass-all.txt") + + println("\nПодписки успешно добавлены и обновлены в $mmkvPath") +}