Basic History backup & export, Minor pooling cleanup, some docs

This commit is contained in:
Kelvin
2023-10-10 20:32:39 +02:00
parent 7ebd8f13c2
commit b1aae244de
8 changed files with 424 additions and 37 deletions
@@ -6,6 +6,7 @@ import com.futo.platformplayer.getOrDefault
import com.futo.platformplayer.getOrThrow
import com.futo.platformplayer.getOrThrowNullable
import com.futo.polycentric.core.combineHashCodes
import okhttp3.internal.platform.Platform
@kotlinx.serialization.Serializable
class PlatformID {
@@ -40,6 +41,8 @@ class PlatformID {
}
companion object {
val NONE = PlatformID("Unknown", null);
fun fromV8(config: SourcePluginConfig, value: V8ValueObject): PlatformID {
val contextName = "PlatformID";
return PlatformID(
@@ -49,5 +52,9 @@ class PlatformID {
value.getOrDefault(config, "claimType", contextName, 0) ?: 0,
value.getOrDefault(config, "claimFieldType", contextName, -1) ?: -1);
}
fun asUrlID(url: String): PlatformID {
return PlatformID("URL", url, null);
}
}
}
@@ -0,0 +1,28 @@
package com.futo.platformplayer.api.media
class PlatformMultiClientPool {
private val _maxCap: Int;
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
constructor(maxCap: Int = -1) {
_maxCap = if(maxCap > 0)
maxCap
else 99;
}
fun getClientPooled(parentClient: IPlatformClient, capacity: Int): IPlatformClient {
val pool = synchronized(_clientPools) {
if(!_clientPools.containsKey(parentClient))
_clientPools[parentClient] = PlatformClientPool(parentClient).apply {
this.onDead.subscribe { client, pool ->
synchronized(_clientPools) {
if(_clientPools[parentClient] == pool)
_clientPools.remove(parentClient);
}
}
}
_clientPools[parentClient]!!;
};
return pool.getClient(capacity.coerceAtMost(_maxCap));
}
}
@@ -1,8 +1,13 @@
package com.futo.platformplayer.models
import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
import com.futo.platformplayer.api.media.models.Thumbnails
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
@kotlinx.serialization.Serializable
class HistoryVideo {
@@ -18,4 +23,41 @@ class HistoryVideo {
this.position = position;
this.date = date;
}
fun toReconString(): String {
return "${video.url}|||${date.toEpochSecond()}|||${position}|||${video.name}";
}
companion object {
fun fromReconString(str: String, resolve: ((url: String)->SerializedPlatformVideo)? = null): HistoryVideo {
var index = str.indexOf("|||");
if(index < 0) throw IllegalArgumentException("Invalid history string: " + str);
val url = str.substring(0, index);
var indexNext = str.indexOf("|||", index + 3);
if(indexNext < 0) throw IllegalArgumentException("Invalid history string: " + str);
val dateSec = str.substring(index + 3, indexNext).toLong();
index = indexNext;
indexNext = str.indexOf("|||", index + 3);
if(indexNext < 0) throw IllegalArgumentException("Invalid history string: " + str);
val position = str.substring(index + 3, indexNext).toLong();
val name = str.substring(indexNext + 3);
val video = resolve?.invoke(url) ?: SerializedPlatformVideo(
id = PlatformID.asUrlID(url),
name = name,
thumbnails = Thumbnails(),
author = PlatformAuthorLink(PlatformID.NONE, "Unknown", ""),
datetime = null,
url = url,
shareUrl = url,
duration = 0,
viewCount = -1
);
return HistoryVideo(video, position, OffsetDateTime.of(LocalDateTime.ofEpochSecond(dateSec, 0, ZoneOffset.UTC), ZoneOffset.UTC));
}
}
}
@@ -16,6 +16,7 @@ import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.activities.SettingsActivity
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.encryption.EncryptionProvider
import com.futo.platformplayer.getNowDiffHours
import com.futo.platformplayer.logging.Logger
@@ -201,7 +202,8 @@ class StateBackup {
);
val storesToSave = getAllMigrationStores()
.associateBy { it.name }
.mapValues { it.value.getAllReconstructionStrings() };
.mapValues { it.value.getAllReconstructionStrings() }
.toMutableMap();
val settings = Settings.instance.encode();
val pluginSettings = StatePlugins.instance.getPlugins()
.associateBy { it.config.id }
@@ -211,7 +213,12 @@ class StateBackup {
.associateBy { it.config.id }
.mapValues { it.value.config.sourceUrl!! };
return ExportStructure(exportInfo, settings, storesToSave, pluginUrls, pluginSettings);
val export = ExportStructure(exportInfo, settings, storesToSave, pluginUrls, pluginSettings);
//export.videoCache = StatePlaylists.instance.getHistory()
// .distinctBy { it.video.url }
// .map { it.video };
return export;
}
@@ -387,6 +394,7 @@ class StateBackup {
val plugins: Map<String, String>,
val pluginSettings: Map<String, Map<String, String?>>,
) {
var videoCache: List<SerializedPlatformVideo>? = null;
fun asZip(): ByteArray {
return ByteArrayOutputStream().use { byteStream ->
@@ -8,6 +8,7 @@ import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.IPluginSourced
import com.futo.platformplayer.api.media.PlatformClientPool
import com.futo.platformplayer.api.media.PlatformMultiClientPool
import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException
import com.futo.platformplayer.api.media.models.FilterGroup
import com.futo.platformplayer.api.media.models.PlatformAuthorLink
@@ -38,6 +39,7 @@ import com.futo.platformplayer.stores.*
import kotlinx.coroutines.*
import okhttp3.internal.concat
import java.time.OffsetDateTime
import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping
import kotlin.streams.toList
/***
@@ -61,8 +63,8 @@ class StatePlatform {
private val _availableClients : ArrayList<IPlatformClient> = ArrayList();
private val _enabledClients : ArrayList<IPlatformClient> = ArrayList();
private val _clientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
private val _trackerClientPools: HashMap<IPlatformClient, PlatformClientPool> = hashMapOf();
private val _channelClientPool = PlatformMultiClientPool(15);
private val _trackerClientPool = PlatformMultiClientPool(1);
private val _primaryClientPersistent = FragmentedStorage.get<StringStorage>("primaryClient");
private var _primaryClientObj : IPlatformClient? = null;
@@ -233,36 +235,6 @@ class StatePlatform {
fun getClient(id: String): IPlatformClient {
return getClientOrNull(id) ?: throw IllegalArgumentException("Client with id $id does not exist");
}
fun getClientPooled(parentClient: IPlatformClient, capacity: Int): IPlatformClient {
val pool = synchronized(_clientPools) {
if(!_clientPools.containsKey(parentClient))
_clientPools[parentClient] = PlatformClientPool(parentClient).apply {
this.onDead.subscribe { client, pool ->
synchronized(_clientPools) {
if(_clientPools[parentClient] == pool)
_clientPools.remove(parentClient);
}
}
}
_clientPools[parentClient]!!;
};
return pool.getClient(capacity);
}
fun getTrackerClientPooled(parentClient: IPlatformClient, capacity: Int): IPlatformClient {
val pool = synchronized(_trackerClientPools) {
if(!_trackerClientPools.containsKey(parentClient))
_trackerClientPools[parentClient] = PlatformClientPool(parentClient).apply {
this.onDead.subscribe { client, pool ->
synchronized(_trackerClientPools) {
if(_trackerClientPools[parentClient] == pool)
_trackerClientPools.remove(parentClient);
}
}
}
_trackerClientPools[parentClient]!!;
};
return pool.getClient(capacity);
}
fun getClientsByClaimType(claimType: Int): List<IPlatformClient> {
return getEnabledClients().filter { it.isClaimTypeSupported(claimType) };
@@ -627,7 +599,7 @@ class StatePlatform {
if (baseClient !is JSClient) {
return baseClient.getPlaybackTracker(url);
}
val client = getTrackerClientPooled(baseClient, 1);
val client = _trackerClientPool.getClientPooled(baseClient, 1);
return client.getPlaybackTracker(url);
}
@@ -651,7 +623,7 @@ class StatePlatform {
val clientCapabilities = baseClient.getChannelCapabilities();
val client = if(usePooledClients > 1)
getClientPooled(baseClient, usePooledClients);
_channelClientPool.getClientPooled(baseClient, usePooledClients);
else baseClient;
var lastStream: OffsetDateTime? = null;
@@ -4,6 +4,7 @@ import android.content.Context
import android.net.Uri
import androidx.core.content.FileProvider
import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
@@ -38,6 +39,11 @@ class StatePlaylists {
})
.load();
private val _historyStore = FragmentedStorage.storeJson<HistoryVideo>("history")
.withRestore(object: ReconstructStore<HistoryVideo>() {
override fun toReconstruction(obj: HistoryVideo): String = obj.toReconString();
override suspend fun toObject(id: String, backup: String, reconstructionBuilder: Builder): HistoryVideo
= HistoryVideo.fromReconString(backup, null);
})
.load();
val playlistStore = FragmentedStorage.storeJson<Playlist>("playlists")
.withRestore(PlaylistBackup())
@@ -49,7 +55,7 @@ class StatePlaylists {
val onWatchLaterChanged = Event0();
fun toMigrateCheck(): List<ManagedStore<*>> {
return listOf(playlistStore, _watchlistStore);
return listOf(playlistStore, _watchlistStore, _historyStore);
}
fun getWatchLater() : List<SerializedPlatformVideo> {
@@ -122,6 +128,11 @@ class StatePlaylists {
}
if (shouldUpdate) {
//A unrecovered item
if(historyVideo.video.author.id.value == null && historyVideo.video.duration == 0L)
historyVideo.video = SerializedPlatformVideo.fromVideo(video);
historyVideo.position = pos;
historyVideo.date = OffsetDateTime.now();
_historyStore.saveAsync(historyVideo);