ManagedDBSTore delete corrupted items, Fix serialized content serializer, Fix notifications wrong intent

This commit is contained in:
Kelvin 2023-12-04 20:06:24 +01:00
parent 7cde8ed538
commit cbf2712654
15 changed files with 64 additions and 16 deletions

View File

@ -6,9 +6,13 @@ import com.futo.platformplayer.api.media.models.locked.IPlatformLockedContent
import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent import com.futo.platformplayer.api.media.models.nested.IPlatformNestedContent
import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.post.IPlatformPost
import com.futo.platformplayer.serializers.PlatformContentSerializer import com.futo.platformplayer.serializers.PlatformContentSerializer
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.SerialName
@kotlinx.serialization.Serializable(with = PlatformContentSerializer::class) @kotlinx.serialization.Serializable(with = PlatformContentSerializer::class)
interface SerializedPlatformContent: IPlatformContent { interface SerializedPlatformContent: IPlatformContent {
override val contentType: ContentType;
fun toJson() : String; fun toJson() : String;
fun fromJson(str : String) : SerializedPlatformContent; fun fromJson(str : String) : SerializedPlatformContent;
fun fromJsonArray(str : String) : Array<SerializedPlatformContent>; fun fromJsonArray(str : String) : Array<SerializedPlatformContent>;

View File

@ -30,7 +30,7 @@ open class SerializedPlatformLockedContent(
override val unlockUrl: String? = null, override val unlockUrl: String? = null,
override val contentThumbnails: Thumbnails override val contentThumbnails: Thumbnails
) : IPlatformLockedContent, SerializedPlatformContent { ) : IPlatformLockedContent, SerializedPlatformContent {
final override val contentType: ContentType get() = ContentType.LOCKED; override val contentType: ContentType = ContentType.LOCKED;
override fun toJson() : String { override fun toJson() : String {
return Json.encodeToString(this); return Json.encodeToString(this);

View File

@ -30,7 +30,7 @@ open class SerializedPlatformNestedContent(
override val contentProvider: String?, override val contentProvider: String?,
override val contentThumbnails: Thumbnails override val contentThumbnails: Thumbnails
) : IPlatformNestedContent, SerializedPlatformContent { ) : IPlatformNestedContent, SerializedPlatformContent {
final override val contentType: ContentType get() = ContentType.NESTED_VIDEO; final override val contentType: ContentType = ContentType.NESTED_VIDEO;
override val contentPlugin: String? = StatePlatform.instance.getContentClientOrNull(contentUrl)?.id; override val contentPlugin: String? = StatePlatform.instance.getContentClientOrNull(contentUrl)?.id;
override val contentSupported: Boolean get() = contentPlugin != null; override val contentSupported: Boolean get() = contentPlugin != null;

View File

@ -8,6 +8,7 @@ import com.futo.platformplayer.api.media.models.contents.ContentType
import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.post.IPlatformPost
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
import com.futo.polycentric.core.combineHashCodes import com.futo.polycentric.core.combineHashCodes
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -26,7 +27,7 @@ open class SerializedPlatformPost(
override val thumbnails: List<Thumbnails?>, override val thumbnails: List<Thumbnails?>,
override val images: List<String> override val images: List<String>
) : IPlatformPost, SerializedPlatformContent { ) : IPlatformPost, SerializedPlatformContent {
final override val contentType: ContentType get() = ContentType.POST; override val contentType: ContentType = ContentType.POST;
override fun toJson() : String { override fun toJson() : String {
return Json.encodeToString(this); return Json.encodeToString(this);

View File

@ -26,7 +26,7 @@ open class SerializedPlatformVideo(
override val duration: Long, override val duration: Long,
override val viewCount: Long, override val viewCount: Long,
) : IPlatformVideo, SerializedPlatformContent { ) : IPlatformVideo, SerializedPlatformContent {
final override val contentType: ContentType get() = ContentType.MEDIA; override val contentType: ContentType = ContentType.MEDIA;
override val isLive: Boolean = false; override val isLive: Boolean = false;

View File

@ -122,7 +122,7 @@ class BackgroundWorker(private val appContext: Context, private val workerParams
//Only for testing notifications //Only for testing notifications
val testNotifs = 0; val testNotifs = 0;
if(contentNotifs.size == 0 && testNotifs > 0) { if(contentNotifs.size == 0 && testNotifs > 0) {
results.first.getResults().filter { it is IPlatformVideo && it.datetime?.let { it < now } == true } results.first.getResults().filter { it is IPlatformVideo }
.take(testNotifs).forEach { .take(testNotifs).forEach {
contentNotifs.add(Pair(StateSubscriptions.instance.getSubscriptions().first(), it)); contentNotifs.add(Pair(StateSubscriptions.instance.getSubscriptions().first(), it));
} }

View File

@ -18,6 +18,7 @@ import com.futo.platformplayer.engine.internal.V8BindObject
import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.getOrThrow
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import kotlin.streams.asSequence
import kotlin.streams.toList import kotlin.streams.toList
class PackageHttp: V8Package { class PackageHttp: V8Package {
@ -171,7 +172,9 @@ class PackageHttp: V8Package {
return@map it.first.requestWithBody(it.second.method, it.second.url, it.second.body!!, it.second.headers); return@map it.first.requestWithBody(it.second.method, it.second.url, it.second.body!!, it.second.headers);
else else
return@map it.first.request(it.second.method, it.second.url, it.second.headers); return@map it.first.request(it.second.method, it.second.url, it.second.headers);
}.toList(); }
.asSequence()
.toList();
} }
} }

View File

@ -3,6 +3,7 @@ package com.futo.platformplayer.serializers
import com.futo.platformplayer.api.media.models.contents.ContentType import com.futo.platformplayer.api.media.models.contents.ContentType
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
import com.futo.platformplayer.api.media.models.video.SerializedPlatformNestedContent import com.futo.platformplayer.api.media.models.video.SerializedPlatformNestedContent
import com.futo.platformplayer.api.media.models.video.SerializedPlatformPost
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
@ -22,7 +23,7 @@ class PlatformContentSerializer() : JsonContentPolymorphicSerializer<SerializedP
"MEDIA" -> SerializedPlatformVideo.serializer(); "MEDIA" -> SerializedPlatformVideo.serializer();
"NESTED_VIDEO" -> SerializedPlatformNestedContent.serializer(); "NESTED_VIDEO" -> SerializedPlatformNestedContent.serializer();
"ARTICLE" -> throw NotImplementedError("Articles not yet implemented"); "ARTICLE" -> throw NotImplementedError("Articles not yet implemented");
"POST" -> throw NotImplementedError("Post not yet implemented"); "POST" -> SerializedPlatformPost.serializer();
else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.contentOrNull}") else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.contentOrNull}")
}; };
else else
@ -30,7 +31,7 @@ class PlatformContentSerializer() : JsonContentPolymorphicSerializer<SerializedP
ContentType.MEDIA.value -> SerializedPlatformVideo.serializer(); ContentType.MEDIA.value -> SerializedPlatformVideo.serializer();
ContentType.NESTED_VIDEO.value -> SerializedPlatformNestedContent.serializer(); ContentType.NESTED_VIDEO.value -> SerializedPlatformNestedContent.serializer();
ContentType.ARTICLE.value -> throw NotImplementedError("Articles not yet implemented"); ContentType.ARTICLE.value -> throw NotImplementedError("Articles not yet implemented");
ContentType.POST.value -> throw NotImplementedError("Post not yet implemented"); ContentType.POST.value -> SerializedPlatformPost.serializer();
else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.int}") else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.int}")
}; };
} }

View File

@ -13,6 +13,7 @@ import android.net.NetworkRequest
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Xml
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -22,6 +23,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.activities.CaptchaActivity import com.futo.platformplayer.activities.CaptchaActivity
import com.futo.platformplayer.activities.IWithResultLauncher import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.api.media.platforms.js.DevJSClient import com.futo.platformplayer.api.media.platforms.js.DevJSClient
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
@ -37,9 +39,12 @@ import com.futo.platformplayer.logging.LogLevel
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.HistoryVideo import com.futo.platformplayer.models.HistoryVideo
import com.futo.platformplayer.receivers.AudioNoisyReceiver import com.futo.platformplayer.receivers.AudioNoisyReceiver
import com.futo.platformplayer.serializers.PlatformContentSerializer
import com.futo.platformplayer.services.DownloadService import com.futo.platformplayer.services.DownloadService
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.db.ManagedDBStore
import com.futo.platformplayer.stores.db.types.DBHistory import com.futo.platformplayer.stores.db.types.DBHistory
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
import com.futo.platformplayer.stores.v2.ManagedStore import com.futo.platformplayer.stores.v2.ManagedStore
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
@ -548,6 +553,7 @@ class StateApp {
} }
*/ */
} }
} }
fun mainAppStartedWithExternalFiles(context: Context) { fun mainAppStartedWithExternalFiles(context: Context) {

View File

@ -1,5 +1,6 @@
package com.futo.platformplayer.states package com.futo.platformplayer.states
import com.futo.platformplayer.api.media.models.contents.ContentType
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
import com.futo.platformplayer.api.media.structures.DedupContentPager import com.futo.platformplayer.api.media.structures.DedupContentPager
@ -11,10 +12,15 @@ import com.futo.platformplayer.resolveChannelUrl
import com.futo.platformplayer.serializers.PlatformContentSerializer import com.futo.platformplayer.serializers.PlatformContentSerializer
import com.futo.platformplayer.stores.db.ManagedDBStore import com.futo.platformplayer.stores.db.ManagedDBStore
import com.futo.platformplayer.stores.db.types.DBSubscriptionCache import com.futo.platformplayer.stores.db.types.DBSubscriptionCache
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.streams.asSequence
import kotlin.streams.toList
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
class StateCache { class StateCache {
@ -34,6 +40,8 @@ class StateCache {
fun getChannelCachePager(channelUrl: String): IPager<IPlatformContent> { fun getChannelCachePager(channelUrl: String): IPager<IPlatformContent> {
return _subscriptionCache.queryPager(DBSubscriptionCache.Index::channelUrl, channelUrl, 20) { return _subscriptionCache.queryPager(DBSubscriptionCache.Index::channelUrl, channelUrl, 20) {
if(it.objOrNull?.contentType == ContentType.POST)
Logger.i(TAG, "FOUND CACHED POST\n (${it.objOrNull?.name})");
it.obj; it.obj;
} }
} }
@ -56,7 +64,10 @@ class StateCache {
}.flatten().distinct(); }.flatten().distinct();
Logger.i(TAG, "Subscriptions CachePager get pagers"); Logger.i(TAG, "Subscriptions CachePager get pagers");
val pagers = allUrls.parallelStream().map { getChannelCachePager(it) }.toList(); val pagers = allUrls.parallelStream()
.map { getChannelCachePager(it) }
.asSequence()
.toList();
Logger.i(TAG, "Subscriptions CachePager compiling"); Logger.i(TAG, "Subscriptions CachePager compiling");
val pager = MultiChronoContentPager(pagers, false, 20); val pager = MultiChronoContentPager(pagers, false, 20);

View File

@ -117,7 +117,7 @@ class StateNotifications {
.setContentText("${content.name}") .setContentText("${content.name}")
.setSubText(content.datetime?.toHumanNowDiffStringMinDay()) .setSubText(content.datetime?.toHumanNowDiffStringMinDay())
.setSilent(true) .setSilent(true)
.setContentIntent(PendingIntent.getActivity(context, 0, MainActivity.getVideoIntent(context, content.url), .setContentIntent(PendingIntent.getActivity(context, content.hashCode(), MainActivity.getVideoIntent(context, content.url),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)) PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE))
.setChannelId(notificationChannel.id); .setChannelId(notificationChannel.id);
if(thumbnail != null) { if(thumbnail != null) {

View File

@ -42,6 +42,7 @@ import kotlinx.coroutines.*
import okhttp3.internal.concat import okhttp3.internal.concat
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping import kotlin.reflect.jvm.internal.impl.builtins.jvm.JavaToKotlinClassMap.PlatformMutabilityMapping
import kotlin.streams.asSequence
import kotlin.streams.toList import kotlin.streams.toList
/*** /***
@ -389,6 +390,7 @@ class StatePlatform {
} }
return@map homeResult; return@map homeResult;
} }
.asSequence()
.toList() .toList()
.associateWith { 1f }; .associateWith { 1f };
@ -709,6 +711,7 @@ class StatePlatform {
} }
return@map results; return@map results;
} }
.asSequence()
.toList(); .toList();
val pager = MultiChronoContentPager(pagers.toTypedArray()); val pager = MultiChronoContentPager(pagers.toTypedArray());

View File

@ -38,6 +38,7 @@ import java.util.concurrent.ForkJoinTask
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.streams.asSequence
import kotlin.streams.toList import kotlin.streams.toList
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -258,7 +259,9 @@ class StateSubscriptions {
Pair(it, StatePolycentric.instance.getChannelUrls(it.channel.url, it.channel.id)); Pair(it, StatePolycentric.instance.getChannelUrls(it.channel.url, it.channel.id));
else else
Pair(it, listOf(it.channel.url)); Pair(it, listOf(it.channel.url));
}.toList().associate { it }; }.asSequence()
.toList()
.associate { it };
val result = algo.getSubscriptions(subUrls); val result = algo.getSubscriptions(subUrls);
return Pair(result.pager, result.exceptions); return Pair(result.pager, result.exceptions);

View File

@ -14,10 +14,15 @@ open class ManagedDBIndex<T> {
@Ignore @Ignore
private var _obj: T? = null; private var _obj: T? = null;
@Ignore
var isCorrupted: Boolean = false;
@get:Ignore @get:Ignore
val obj: T get() = _obj ?: throw IllegalStateException("Attempted to access serialized object on a index-only instance"); val obj: T get() = _obj ?: throw IllegalStateException("Attempted to access serialized object on a index-only instance");
@get:Ignore
val objOrNull: T? get() = _obj;
fun setInstance(obj: T) { fun setInstance(obj: T) {
this._obj = obj; this._obj = obj;
} }

View File

@ -361,22 +361,33 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
} }
fun convertObject(index: I): T? { fun convertObject(index: I): T? {
return index.obj ?: deserializeIndex(index).obj; return index.objOrNull ?: deserializeIndex(index).obj;
} }
fun convertObjects(indexes: List<I>): List<T> { fun convertObjects(indexes: List<I>): List<T> {
return indexes.mapNotNull { it.obj ?: convertObject(it) }; return indexes.mapNotNull { it.objOrNull ?: convertObject(it) };
} }
fun deserializeIndex(index: I): I { fun deserializeIndex(index: I): I {
if(index.isCorrupted)
return index;
if(index.serialized == null) throw IllegalStateException("Cannot deserialize index-only items from [${name}]"); if(index.serialized == null) throw IllegalStateException("Cannot deserialize index-only items from [${name}]");
val obj = _serializer.deserialize(_class, index.serialized!!); try {
index.setInstance(obj); val obj = _serializer.deserialize(_class, index.serialized!!);
index.setInstance(obj);
}
catch(ex: Throwable) {
if(index.serialized != null && index.serialized!!.size > 0) {
Logger.w("ManagedDBStore", "Corrupted object in ${name} found [${index.id}], deleting due to ${ex.message}", ex);
index.isCorrupted = true;
delete(index.id!!);
}
}
index.serialized = null; index.serialized = null;
return index; return index;
} }
fun deserializeIndexes(indexes: List<I>): List<I> { fun deserializeIndexes(indexes: List<I>): List<I> {
for(index in indexes) for(index in indexes)
deserializeIndex(index); deserializeIndex(index);
return indexes; return indexes.filter { !it.isCorrupted }
} }
fun serialize(obj: T): ByteArray { fun serialize(obj: T): ByteArray {