mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-07 16:50:00 +00:00
WIP Channel content cache
This commit is contained in:
parent
c49b9f7841
commit
aeb29c54cd
3 changed files with 170 additions and 3 deletions
106
app/src/main/java/com/futo/platformplayer/states/StateCache.kt
Normal file
106
app/src/main/java/com/futo/platformplayer/states/StateCache.kt
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package com.futo.platformplayer.states
|
||||||
|
|
||||||
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||||
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
|
||||||
|
import com.futo.platformplayer.api.media.structures.DedupContentPager
|
||||||
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
|
import com.futo.platformplayer.api.media.structures.MultiChronoContentPager
|
||||||
|
import com.futo.platformplayer.api.media.structures.PlatformContentPager
|
||||||
|
import com.futo.platformplayer.cache.ChannelContentCache
|
||||||
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||||
|
import com.futo.platformplayer.resolveChannelUrl
|
||||||
|
import com.futo.platformplayer.serializers.PlatformContentSerializer
|
||||||
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||||
|
import com.futo.platformplayer.stores.db.types.DBChannelCache
|
||||||
|
import com.futo.platformplayer.stores.db.types.DBHistory
|
||||||
|
import com.futo.platformplayer.toSafeFileName
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
class StateCache {
|
||||||
|
private val _channelCache = ManagedDBStore.create("channelCache", DBChannelCache.Descriptor(), PlatformContentSerializer())
|
||||||
|
.load();
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
_channelCache.deleteAll();
|
||||||
|
}
|
||||||
|
fun clearToday() {
|
||||||
|
val today = _channelCache.queryGreater(DBChannelCache.Index::datetime, OffsetDateTime.now().toEpochSecond());
|
||||||
|
for(content in today)
|
||||||
|
_channelCache.delete(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getChannelCachePager(channelUrl: String): IPager<IPlatformContent> {
|
||||||
|
return _channelCache.queryPager(DBChannelCache.Index::channelUrl, channelUrl, 20) {
|
||||||
|
it.obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun getSubscriptionCachePager(): DedupContentPager {
|
||||||
|
Logger.i(TAG, "Subscriptions CachePager get subscriptions");
|
||||||
|
val subs = StateSubscriptions.instance.getSubscriptions();
|
||||||
|
Logger.i(TAG, "Subscriptions CachePager polycentric urls");
|
||||||
|
val allUrls = subs.map {
|
||||||
|
val otherUrls = PolycentricCache.instance.getCachedProfile(it.channel.url)?.profile?.ownedClaims?.mapNotNull { c -> c.claim.resolveChannelUrl() } ?: listOf();
|
||||||
|
if(!otherUrls.contains(it.channel.url))
|
||||||
|
return@map listOf(listOf(it.channel.url), otherUrls).flatten();
|
||||||
|
else
|
||||||
|
return@map otherUrls;
|
||||||
|
}.flatten().distinct();
|
||||||
|
Logger.i(TAG, "Subscriptions CachePager compiling");
|
||||||
|
|
||||||
|
val pagers = MultiChronoContentPager(allUrls.map { getChannelCachePager(it) }, false, 20);
|
||||||
|
return DedupContentPager(pagers, StatePlatform.instance.getEnabledClients().map { it.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun getCachedContent(url: String): DBChannelCache.Index? {
|
||||||
|
return _channelCache.query(DBChannelCache.Index::url, url).firstOrNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
fun uncacheContent(content: SerializedPlatformContent) {
|
||||||
|
val item = getCachedContent(content.url);
|
||||||
|
if(item != null)
|
||||||
|
_channelCache.delete(item);
|
||||||
|
}
|
||||||
|
fun cacheContents(contents: List<IPlatformContent>): List<IPlatformContent> {
|
||||||
|
return contents.filter { cacheContent(it) };
|
||||||
|
}
|
||||||
|
fun cacheContent(content: IPlatformContent, doUpdate: Boolean = false): Boolean {
|
||||||
|
if(content.author.url.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
val serialized = SerializedPlatformContent.fromContent(content);
|
||||||
|
val existing = getCachedContent(content.url);
|
||||||
|
|
||||||
|
if(existing != null && doUpdate) {
|
||||||
|
_channelCache.update(existing.id!!, serialized);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(existing == null) {
|
||||||
|
_channelCache.insert(serialized);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = "StateCache";
|
||||||
|
|
||||||
|
private var _instance : StateCache? = null;
|
||||||
|
val instance : StateCache
|
||||||
|
get(){
|
||||||
|
if(_instance == null)
|
||||||
|
_instance = StateCache();
|
||||||
|
return _instance!!;
|
||||||
|
};
|
||||||
|
|
||||||
|
fun finish() {
|
||||||
|
_instance?.let {
|
||||||
|
_instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -225,12 +225,32 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
|
||||||
return deserializeIndexes(dbDaoBase.getMultiple(_sqlGetAll(id)));
|
return deserializeIndexes(dbDaoBase.getMultiple(_sqlGetAll(id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun query(field: KProperty<*>, obj: Any): List<I> = query(validateFieldName(field), obj);
|
||||||
fun query(field: String, obj: Any): List<I> {
|
fun query(field: String, obj: Any): List<I> {
|
||||||
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} = ?";
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} = ?";
|
||||||
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj));
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj));
|
||||||
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
}
|
}
|
||||||
fun query(field: KProperty<*>, obj: Any): List<I> = query(validateFieldName(field), obj);
|
fun queryGreater(field: KProperty<*>, obj: Any): List<I> = queryGreater(validateFieldName(field), obj);
|
||||||
|
fun queryGreater(field: String, obj: Any): List<I> {
|
||||||
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} > ?";
|
||||||
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj));
|
||||||
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
|
}
|
||||||
|
fun querySmaller(field: KProperty<*>, obj: Any): List<I> = querySmaller(validateFieldName(field), obj);
|
||||||
|
fun querySmaller(field: String, obj: Any): List<I> {
|
||||||
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} < ?";
|
||||||
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj));
|
||||||
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
|
}
|
||||||
|
fun queryBetween(field: KProperty<*>, greaterThan: Any, smallerThan: Any): List<I> = queryBetween(validateFieldName(field), greaterThan, smallerThan);
|
||||||
|
fun queryBetween(field: String, greaterThan: Any, smallerThan: Any): List<I> {
|
||||||
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} > ? AND ${field} < ?";
|
||||||
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(greaterThan, smallerThan));
|
||||||
|
return deserializeIndexes(dbDaoBase.getMultiple(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun queryPage(field: String, obj: Any, page: Int, pageSize: Int): List<I> {
|
fun queryPage(field: String, obj: Any, page: Int, pageSize: Int): List<I> {
|
||||||
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} = ? ${_orderSQL} LIMIT ? OFFSET ?";
|
val queryStr = "SELECT * FROM ${descriptor.table_name} WHERE ${field} = ? ${_orderSQL} LIMIT ? OFFSET ?";
|
||||||
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj, pageSize, page * pageSize));
|
val query = SimpleSQLiteQuery(queryStr, arrayOf(obj, pageSize, page * pageSize));
|
||||||
|
@ -247,6 +267,12 @@ class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA
|
||||||
queryPage(field, obj, it - 1, pageSize);
|
queryPage(field, obj, it - 1, pageSize);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
fun <X> queryPager(field: KProperty<*>, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> = queryPager(validateFieldName(field), obj, pageSize, convert);
|
||||||
|
fun <X> queryPager(field: String, obj: Any, pageSize: Int, convert: (I)->X): IPager<X> {
|
||||||
|
return AdhocPager({
|
||||||
|
queryPage(field, obj, it - 1, pageSize).map(convert);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fun queryObjectPager(field: KProperty<*>, obj: Any, pageSize: Int): IPager<T> = queryObjectPager(validateFieldName(field), obj, pageSize);
|
fun queryObjectPager(field: KProperty<*>, obj: Any, pageSize: Int): IPager<T> = queryObjectPager(validateFieldName(field), obj, pageSize);
|
||||||
fun queryObjectPager(field: String, obj: Any, pageSize: Int): IPager<T> {
|
fun queryObjectPager(field: String, obj: Any, pageSize: Int): IPager<T> {
|
||||||
|
|
|
@ -1,30 +1,65 @@
|
||||||
package com.futo.platformplayer.stores.db.types
|
package com.futo.platformplayer.stores.db.types
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Database
|
||||||
import androidx.room.Ignore
|
import androidx.room.Ignore
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
|
||||||
import com.futo.platformplayer.models.HistoryVideo
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
|
import com.futo.platformplayer.stores.db.ColumnIndex
|
||||||
|
import com.futo.platformplayer.stores.db.ColumnOrdered
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDAOBase
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDatabase
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDescriptor
|
||||||
import com.futo.platformplayer.stores.db.ManagedDBIndex
|
import com.futo.platformplayer.stores.db.ManagedDBIndex
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class DBChannelCache {
|
class DBChannelCache {
|
||||||
companion object {
|
companion object {
|
||||||
const val TABLE_NAME = "channelCache";
|
const val TABLE_NAME = "feed_cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//These classes solely exist for bounding generics for type erasure
|
||||||
|
@Dao
|
||||||
|
interface DBDAO: ManagedDBDAOBase<SerializedPlatformContent, Index> {}
|
||||||
|
@Database(entities = [Index::class], version = 2)
|
||||||
|
abstract class DB: ManagedDBDatabase<SerializedPlatformContent, Index, DBDAO>() {
|
||||||
|
abstract override fun base(): DBDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Descriptor: ManagedDBDescriptor<SerializedPlatformContent, Index, DB, DBDAO>() {
|
||||||
|
override val table_name: String = TABLE_NAME;
|
||||||
|
override fun create(obj: SerializedPlatformContent): Index = Index(obj);
|
||||||
|
override fun dbClass(): KClass<DB> = DB::class;
|
||||||
|
override fun indexClass(): KClass<Index> = Index::class;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Index: ManagedDBIndex<SerializedPlatformContent> {
|
class Index: ManagedDBIndex<SerializedPlatformContent> {
|
||||||
|
@ColumnIndex
|
||||||
@PrimaryKey(true)
|
@PrimaryKey(true)
|
||||||
|
@ColumnOrdered(1)
|
||||||
override var id: Long? = null;
|
override var id: Long? = null;
|
||||||
|
|
||||||
var feedType: String? = null;
|
@ColumnIndex
|
||||||
|
var url: String? = null;
|
||||||
|
@ColumnIndex
|
||||||
var channelUrl: String? = null;
|
var channelUrl: String? = null;
|
||||||
|
|
||||||
|
@ColumnIndex
|
||||||
|
@ColumnOrdered(0)
|
||||||
|
var datetime: Long? = null;
|
||||||
|
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
constructor(sCache: SerializedPlatformContent) {
|
constructor(sCache: SerializedPlatformContent) {
|
||||||
id = null;
|
id = null;
|
||||||
serialized = null;
|
serialized = null;
|
||||||
|
url = sCache.url;
|
||||||
channelUrl = sCache.author.url;
|
channelUrl = sCache.author.url;
|
||||||
|
datetime = sCache.datetime?.toEpochSecond();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue