From ae904b4cd863906d6a29f2cf235221520a23fee1 Mon Sep 17 00:00:00 2001 From: Kelvin K Date: Thu, 13 Jun 2024 17:46:22 +0200 Subject: [PATCH] Content recommendation api support, removing old CachedPlatformClient --- .../api/media/CachedPlatformClient.kt | 109 ------------------ .../api/media/IPlatformClient.kt | 5 + .../api/media/PlatformClientCapabilities.kt | 3 +- .../contents/IPlatformContentDetails.kt | 2 + .../video/SerializedPlatformVideoDetails.kt | 2 + .../api/media/platforms/js/JSClient.kt | 18 ++- .../platforms/js/models/JSPostDetails.kt | 21 ++++ .../platforms/js/models/JSVideoDetails.kt | 21 ++++ .../platformplayer/downloads/VideoLocal.kt | 3 + .../mainactivity/main/TutorialFragment.kt | 7 +- .../platformplayer/states/StatePlatform.kt | 9 ++ 11 files changed, 84 insertions(+), 116 deletions(-) delete mode 100644 app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt diff --git a/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt deleted file mode 100644 index c604a7e6..00000000 --- a/app/src/main/java/com/futo/platformplayer/api/media/CachedPlatformClient.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.futo.platformplayer.api.media - -import androidx.collection.LruCache -import com.futo.platformplayer.api.media.models.ResultCapabilities -import com.futo.platformplayer.api.media.models.channels.IPlatformChannel -import com.futo.platformplayer.api.media.models.chapters.IChapter -import com.futo.platformplayer.api.media.models.comments.IPlatformComment -import com.futo.platformplayer.api.media.models.contents.IPlatformContent -import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails -import com.futo.platformplayer.api.media.models.live.ILiveChatWindowDescriptor -import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent -import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker -import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist -import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails -import com.futo.platformplayer.api.media.structures.IPager -import com.futo.platformplayer.models.ImageVariable - -/** - * A temporary class that caches video results - * In future this should be part of a bigger system - */ -class CachedPlatformClient : IPlatformClient { - private val _client : IPlatformClient; - override val id: String get() = _client.id; - override val name: String get() = _client.name; - override val icon: ImageVariable? get() = _client.icon; - - private val _cache: LruCache; - - override val capabilities: PlatformClientCapabilities - get() = _client.capabilities; - - constructor(client : IPlatformClient, cacheSize : Int = 10 * 1024 * 1024) { - this._client = client; - this._cache = LruCache(cacheSize); - } - override fun initialize() { _client.initialize() } - override fun disable() { _client.disable() } - - override fun isContentDetailsUrl(url: String): Boolean = _client.isContentDetailsUrl(url); - override fun getContentDetails(url: String): IPlatformContentDetails { - var result = _cache.get(url); - if(result == null) { - result = _client.getContentDetails(url); - _cache.put(url, result); - } - return result; - } - - override fun getContentChapters(url: String): List = _client.getContentChapters(url); - override fun getPlaybackTracker(url: String): IPlaybackTracker? = _client.getPlaybackTracker(url); - - override fun isChannelUrl(url: String): Boolean = _client.isChannelUrl(url); - override fun getChannel(channelUrl: String): IPlatformChannel = _client.getChannel(channelUrl); - - override fun getChannelCapabilities(): ResultCapabilities = _client.getChannelCapabilities(); - override fun getChannelContents( - channelUrl: String, - type: String?, - order: String?, - filters: Map>? - ): IPager = _client.getChannelContents(channelUrl); - - override fun getChannelPlaylists(channelUrl: String): IPager = _client.getChannelPlaylists(channelUrl); - - override fun getPeekChannelTypes(): List = _client.getPeekChannelTypes(); - override fun peekChannelContents(channelUrl: String, type: String?): List = _client.peekChannelContents(channelUrl, type); - - override fun getChannelUrlByClaim(claimType: Int, claimValues: Map): String? = _client.getChannelUrlByClaim(claimType, claimValues) - - override fun searchSuggestions(query: String): Array = _client.searchSuggestions(query); - override fun getSearchCapabilities(): ResultCapabilities = _client.getSearchCapabilities(); - override fun search( - query: String, - type: String?, - order: String?, - filters: Map>? - ): IPager = _client.search(query, type, order, filters); - - override fun getSearchChannelContentsCapabilities(): ResultCapabilities = _client.getSearchChannelContentsCapabilities(); - override fun searchChannelContents( - channelUrl: String, - query: String, - type: String?, - order: String?, - filters: Map>? - ): IPager = _client.searchChannelContents(channelUrl, query, type, order, filters); - - override fun searchChannels(query: String) = _client.searchChannels(query); - - override fun getComments(url: String): IPager = _client.getComments(url); - override fun getSubComments(comment: IPlatformComment): IPager = _client.getSubComments(comment); - - override fun getLiveChatWindow(url: String): ILiveChatWindowDescriptor? = _client.getLiveChatWindow(url); - override fun getLiveEvents(url: String): IPager? = _client.getLiveEvents(url); - - override fun getHome(): IPager = _client.getHome(); - - override fun getUserSubscriptions(): Array { return arrayOf(); }; - - override fun searchPlaylists(query: String, type: String?, order: String?, filters: Map>?): IPager = _client.searchPlaylists(query, type, order, filters); - override fun isPlaylistUrl(url: String): Boolean = _client.isPlaylistUrl(url); - override fun getPlaylist(url: String): IPlatformPlaylistDetails = _client.getPlaylist(url); - override fun getUserPlaylists(): Array { return arrayOf(); }; - - override fun isClaimTypeSupported(claimType: Int): Boolean { - return _client.isClaimTypeSupported(claimType); - } -} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt index 9aaf08c4..590ecc32 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/IPlatformClient.kt @@ -121,6 +121,11 @@ interface IPlatformClient { */ fun getPlaybackTracker(url: String): IPlaybackTracker?; + /** + * Get content recommendations + */ + fun getContentRecommendations(url: String): IPager?; + //Comments /** diff --git a/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt b/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt index 1b76ae28..cb62b66c 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/PlatformClientCapabilities.kt @@ -19,7 +19,8 @@ data class PlatformClientCapabilities( val hasGetLiveChatWindow: Boolean = false, val hasGetContentChapters: Boolean = false, val hasPeekChannelContents: Boolean = false, - val hasGetChannelPlaylists: Boolean = false + val hasGetChannelPlaylists: Boolean = false, + val hasGetContentRecommendations: Boolean = false ) { } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/contents/IPlatformContentDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/contents/IPlatformContentDetails.kt index 781e4665..0f642764 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/contents/IPlatformContentDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/contents/IPlatformContentDetails.kt @@ -10,4 +10,6 @@ interface IPlatformContentDetails : IPlatformContent { fun getComments(client: IPlatformClient): IPager?; fun getPlaybackTracker(): IPlaybackTracker?; + + fun getContentRecommendations(client: IPlatformClient): IPager?; } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformVideoDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformVideoDetails.kt index 39851441..1a0435d2 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformVideoDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/video/SerializedPlatformVideoDetails.kt @@ -7,6 +7,7 @@ import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.comments.IPlatformComment 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.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.streams.sources.* @@ -56,6 +57,7 @@ open class SerializedPlatformVideoDetails( override fun getComments(client: IPlatformClient): IPager? = null; override fun getPlaybackTracker(): IPlaybackTracker? = null; + override fun getContentRecommendations(client: IPlatformClient): IPager? = null; companion object { fun fromVideo(video : IPlatformVideoDetails, subtitleSources: List) : SerializedPlatformVideoDetails { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt index aeec0ea3..0c3288fb 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/JSClient.kt @@ -560,7 +560,7 @@ open class JSClient : IPlatformClient { plugin.executeTyped("source.getSubComments(${Json.encodeToString(comment as JSComment)})")); } - @JSDocs(16, "source.getLiveChatWindow(url)", "Gets live events for a livestream") + @JSDocs(18, "source.getLiveChatWindow(url)", "Gets live events for a livestream") @JSDocsParameter("url", "Url of live stream") override fun getLiveChatWindow(url: String): ILiveChatWindowDescriptor? = isBusyWith("getLiveChatWindow") { if(!capabilities.hasGetLiveChatWindow) @@ -569,7 +569,7 @@ open class JSClient : IPlatformClient { return@isBusyWith JSLiveChatWindowDescriptor(config, plugin.executeTyped("source.getLiveChatWindow(${Json.encodeToString(url)})")); } - @JSDocs(16, "source.getLiveEvents(url)", "Gets live events for a livestream") + @JSDocs(19, "source.getLiveEvents(url)", "Gets live events for a livestream") @JSDocsParameter("url", "Url of live stream") override fun getLiveEvents(url: String): IPager? = isBusyWith("getLiveEvents") { if(!capabilities.hasGetLiveEvents) @@ -578,6 +578,20 @@ open class JSClient : IPlatformClient { return@isBusyWith JSLiveEventPager(config, this, plugin.executeTyped("source.getLiveEvents(${Json.encodeToString(url)})")); } + + + @JSDocs(19, "source.getContentRecommendations(url)", "Gets recommendations of a content page") + @JSDocsParameter("url", "Url of content") + override fun getContentRecommendations(url: String): IPager? = isBusyWith("getContentRecommendations") { + if(!capabilities.hasGetContentRecommendations) + return@isBusyWith null; + ensureEnabled(); + return@isBusyWith JSContentPager(config, this, + plugin.executeTyped("source.getContentRecommendations(${Json.encodeToString(url)})")); + } + + + @JSDocs(19, "source.searchPlaylists(query)", "Searches for playlists on the platform") @JSDocsParameter("query", "Query that search results should match") @JSDocsParameter("type", "(optional) Type of contents to get from search ") diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPostDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPostDetails.kt index bc455fcc..6c80d7dc 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPostDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPostDetails.kt @@ -3,6 +3,7 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.models.comments.IPlatformComment +import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.post.IPlatformPost import com.futo.platformplayer.api.media.models.post.IPlatformPostDetails @@ -18,6 +19,7 @@ import com.futo.platformplayer.states.StateDeveloper class JSPostDetails : JSPost, IPlatformPost, IPlatformPostDetails { private val _hasGetComments: Boolean; + private val _hasGetContentRecommendations: Boolean; override val rating: IRating; @@ -34,6 +36,7 @@ class JSPostDetails : JSPost, IPlatformPost, IPlatformPostDetails { content = obj.getOrDefault(config, "content", contextName, "") ?: ""; _hasGetComments = _content.has("getComments"); + _hasGetContentRecommendations = _content.has("getContentRecommendations"); } override fun getComments(client: IPlatformClient): IPager? { @@ -51,9 +54,27 @@ class JSPostDetails : JSPost, IPlatformPost, IPlatformPostDetails { } override fun getPlaybackTracker(): IPlaybackTracker? = null; + override fun getContentRecommendations(client: IPlatformClient): IPager? { + if(!_hasGetContentRecommendations || _content.isClosed) + return null; + + if(client is DevJSClient) + return StateDeveloper.instance.handleDevCall(client.devID, "postDetail.getContentRecommendations()") { + return@handleDevCall getContentRecommendationsJS(client); + } + else if(client is JSClient) + return getContentRecommendationsJS(client); + + return null; + } + private fun getContentRecommendationsJS(client: JSClient): JSContentPager { + val contentPager = _content.invoke("getContentRecommendations", arrayOf()); + return JSContentPager(_pluginConfig, client, contentPager); + } private fun getCommentsJS(client: JSClient): JSCommentPager { val commentPager = _content.invoke("getComments", arrayOf()); return JSCommentPager(_pluginConfig, client, commentPager); } + } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoDetails.kt index 83137f23..da495498 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoDetails.kt @@ -6,6 +6,7 @@ import com.caoccao.javet.values.reference.V8ValueArray import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.models.comments.IPlatformComment +import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.ratings.RatingLikes @@ -27,6 +28,7 @@ import com.futo.platformplayer.states.StateDeveloper class JSVideoDetails : JSVideo, IPlatformVideoDetails { private val _hasGetComments: Boolean; + private val _hasGetContentRecommendations: Boolean; private val _hasGetPlaybackTracker: Boolean; //Details @@ -66,6 +68,7 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails { _hasGetComments = _content.has("getComments"); _hasGetPlaybackTracker = _content.has("getPlaybackTracker"); + _hasGetContentRecommendations = _content.has("getContentRecommendations"); } override fun getPlaybackTracker(): IPlaybackTracker? { @@ -89,6 +92,24 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails { }; } + override fun getContentRecommendations(client: IPlatformClient): IPager? { + if(!_hasGetContentRecommendations || _content.isClosed) + return null; + + if(client is DevJSClient) + return StateDeveloper.instance.handleDevCall(client.devID, "videoDetail.getContentRecommendations()") { + return@handleDevCall getContentRecommendationsJS(client); + } + else if(client is JSClient) + return getContentRecommendationsJS(client); + + return null; + } + private fun getContentRecommendationsJS(client: JSClient): JSContentPager { + val contentPager = _content.invoke("getContentRecommendations", arrayOf()); + return JSContentPager(_pluginConfig, client, contentPager); + } + override fun getComments(client: IPlatformClient): IPager? { if(client !is JSClient || !_hasGetComments || _content.isClosed) return null; diff --git a/app/src/main/java/com/futo/platformplayer/downloads/VideoLocal.kt b/app/src/main/java/com/futo/platformplayer/downloads/VideoLocal.kt index 3b985796..7308b1c6 100644 --- a/app/src/main/java/com/futo/platformplayer/downloads/VideoLocal.kt +++ b/app/src/main/java/com/futo/platformplayer/downloads/VideoLocal.kt @@ -6,6 +6,7 @@ import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.comments.IPlatformComment 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.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor @@ -81,6 +82,8 @@ class VideoLocal: IPlatformVideoDetails, IStoreItem { override fun getComments(client: IPlatformClient): IPager? = null; override fun getPlaybackTracker(): IPlaybackTracker? = null; + override fun getContentRecommendations(client: IPlatformClient): IPager? = null; + fun toPlatformVideo() : IPlatformVideoDetails { throw NotImplementedError(); diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/TutorialFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/TutorialFragment.kt index 20cdcaa2..5139c0f8 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/TutorialFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/TutorialFragment.kt @@ -15,6 +15,7 @@ import com.futo.platformplayer.api.media.models.Thumbnail import com.futo.platformplayer.api.media.models.Thumbnails import com.futo.platformplayer.api.media.models.comments.IPlatformComment 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.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.ratings.RatingLikes @@ -144,10 +145,8 @@ class TutorialFragment : MainFragment() { override fun getComments(client: IPlatformClient): IPager { return EmptyPager() } - - override fun getPlaybackTracker(): IPlaybackTracker? { - return null - } + override fun getPlaybackTracker(): IPlaybackTracker? = null; + override fun getContentRecommendations(client: IPlatformClient): IPager? = null; } companion object { diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt index dbfa8886..3e14ff2f 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlatform.kt @@ -647,6 +647,15 @@ class StatePlatform { return client.getPlaybackTracker(url); } + fun getContentRecommendations(url: String): IPager? { + val baseClient = getContentClientOrNull(url) ?: return null; + if (baseClient !is JSClient) { + return baseClient.getContentRecommendations(url); + } + val client = _mainClientPool.getClientPooled(baseClient); + return client.getContentRecommendations(url); + } + fun hasEnabledChannelClient(url : String) : Boolean = getEnabledClients().any { it.isChannelUrl(url) }; fun getChannelClient(url : String, exclude: List? = null) : IPlatformClient = getChannelClientOrNull(url, exclude) ?: throw NoPlatformClientException("No client enabled that supports this channel url (${url})");