mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Basic History backup & export, Minor pooling cleanup, some docs
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user