diff --git a/.gitmodules b/.gitmodules index 3c5b4994..1db1e78b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,3 +61,6 @@ [submodule "dep/futopay"] path = dep/futopay url = ../futopayclientlibraries.git +[submodule "app/src/unstable/assets/sources/niconico"] + path = app/src/unstable/assets/sources/niconico + url = git@gitlab.futo.org:videostreaming/plugins/niconico.git diff --git a/app/src/main/java/com/futo/platformplayer/Extensions_Content.kt b/app/src/main/java/com/futo/platformplayer/Extensions_Content.kt index 39944ee5..d05db15c 100644 --- a/app/src/main/java/com/futo/platformplayer/Extensions_Content.kt +++ b/app/src/main/java/com/futo/platformplayer/Extensions_Content.kt @@ -5,11 +5,23 @@ import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescri import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails +import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource import com.futo.platformplayer.helpers.VideoHelper +import com.futo.platformplayer.views.video.datasources.JSHttpDataSource +import com.google.android.exoplayer2.upstream.DefaultHttpDataSource +import com.google.android.exoplayer2.upstream.HttpDataSource fun IPlatformVideoDetails.isDownloadable(): Boolean = VideoHelper.isDownloadable(this); fun IVideoSource.isDownloadable(): Boolean = VideoHelper.isDownloadable(this); fun IAudioSource.isDownloadable(): Boolean = VideoHelper.isDownloadable(this); +fun JSSource.getHttpDataSourceFactory(): HttpDataSource.Factory { + val requestModifier = getRequestModifier(); + return if (requestModifier != null) { + JSHttpDataSource.Factory().setRequestModifier(requestModifier); + } else { + DefaultHttpDataSource.Factory(); + } +} fun IVideoSourceDescriptor.hasAnySource(): Boolean = this.videoSources.any() || (this is VideoUnMuxedSourceDescriptor && this.audioSources.any()); \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt new file mode 100644 index 00000000..ea250ae4 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/AdhocRequestModifier.kt @@ -0,0 +1,14 @@ +package com.futo.platformplayer.api.media.models.modifier + +class AdhocRequestModifier: IRequestModifier { + val _handler: (String, Map)->IRequest; + override var allowByteSkip: Boolean = false; + + constructor(modifyReq: (String, Map)->IRequest) { + _handler = modifyReq; + } + + override fun modifyRequest(url: String, headers: Map): IRequest { + return _handler(url, headers); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt new file mode 100644 index 00000000..1a7e4ce6 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IModifierOptions.kt @@ -0,0 +1,6 @@ +package com.futo.platformplayer.api.media.models.modifier + +interface IModifierOptions { + val applyAuthClient: String?; + val applyCookieClient: String?; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt new file mode 100644 index 00000000..43cd502c --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequest.kt @@ -0,0 +1,6 @@ +package com.futo.platformplayer.api.media.models.modifier + +interface IRequest { + val url: String?; + val headers: Map; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt new file mode 100644 index 00000000..f15ee477 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/api/media/models/modifier/IRequestModifier.kt @@ -0,0 +1,7 @@ +package com.futo.platformplayer.api.media.models.modifier + + +interface IRequestModifier { + var allowByteSkip: Boolean; + fun modifyRequest(url: String, headers: Map): IRequest +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/DevJSClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/DevJSClient.kt index 1b929293..47dfb1cb 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/DevJSClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/DevJSClient.kt @@ -20,7 +20,7 @@ class DevJSClient : JSClient { val devID: String; - constructor(context: Context, config: SourcePluginConfig, script: String, auth: SourceAuth? = null, captcha: SourceCaptchaData? = null, devID: String? = null): super(context, SourcePluginDescriptor(config, auth?.toEncrypted(), captcha?.toEncrypted(), listOf("DEV")), null, script) { + constructor(context: Context, config: SourcePluginConfig, script: String, auth: SourceAuth? = null, captcha: SourceCaptchaData? = null, devID: String? = null, settings: HashMap? = null): super(context, SourcePluginDescriptor(config, auth?.toEncrypted(), captcha?.toEncrypted(), listOf("DEV"), settings), null, script) { _devScript = script; _auth = auth; _captcha = captcha; @@ -49,7 +49,7 @@ class DevJSClient : JSClient { _auth = auth; } fun recreate(context: Context): DevJSClient { - return DevJSClient(context, config, _devScript, _auth, _captcha, devID); + return DevJSClient(context, config, _devScript, _auth, _captcha, devID, descriptor.settings); } override fun getCopy(): JSClient { 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 f5f72686..7106f02f 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 @@ -7,6 +7,7 @@ import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.reference.V8ValueArray import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.PlatformClientCapabilities import com.futo.platformplayer.api.media.models.PlatformAuthorLink @@ -67,8 +68,8 @@ open class JSClient : IPlatformClient { var descriptor: SourcePluginDescriptor private set; - private val _client: JSHttpClient; - private val _clientAuth: JSHttpClient?; + private val _httpClient: JSHttpClient; + private val _httpClientAuth: JSHttpClient?; private var _searchCapabilities: ResultCapabilities? = null; private var _searchChannelContentsCapabilities: ResultCapabilities? = null; private var _channelCapabilities: ResultCapabilities? = null; @@ -131,9 +132,9 @@ open class JSClient : IPlatformClient { _captcha = descriptor.getCaptchaData(); flags = descriptor.flags.toTypedArray(); - _client = JSHttpClient(this, null, _captcha); - _clientAuth = JSHttpClient(this, _auth, _captcha); - _plugin = V8Plugin(context, descriptor.config, null, _client, _clientAuth); + _httpClient = JSHttpClient(this, null, _captcha); + _httpClientAuth = JSHttpClient(this, _auth, _captcha); + _plugin = V8Plugin(context, descriptor.config, null, _httpClient, _httpClientAuth); _plugin.withDependency(context, "scripts/polyfil.js"); _plugin.withDependency(context, "scripts/source.js"); @@ -160,9 +161,9 @@ open class JSClient : IPlatformClient { _captcha = descriptor.getCaptchaData(); flags = descriptor.flags.toTypedArray(); - _client = JSHttpClient(this, null, _captcha); - _clientAuth = JSHttpClient(this, _auth, _captcha); - _plugin = V8Plugin(context, descriptor.config, script, _client, _clientAuth); + _httpClient = JSHttpClient(this, null, _captcha); + _httpClientAuth = JSHttpClient(this, _auth, _captcha); + _plugin = V8Plugin(context, descriptor.config, script, _httpClient, _httpClientAuth); _plugin.withDependency(context, "scripts/polyfil.js"); _plugin.withDependency(context, "scripts/source.js"); _plugin.withScript(script); @@ -181,6 +182,13 @@ open class JSClient : IPlatformClient { fun getUnderlyingPlugin(): V8Plugin { return _plugin; } + fun getHttpClientById(id: String): JSHttpClient? { + if(_httpClient.clientId == id) + return _httpClient; + if(_httpClientAuth?.clientId == id) + return _httpClientAuth; + return plugin.httpClientOthers[id]; + } override fun initialize() { Logger.i(TAG, "Plugin [${config.name}] initializing"); @@ -254,7 +262,7 @@ open class JSClient : IPlatformClient { @JSDocs(2, "source.getHome()", "Gets the HomeFeed of the platform") override fun getHome(): IPager = isBusyWith { ensureEnabled(); - return@isBusyWith JSContentPager(config, plugin, + return@isBusyWith JSContentPager(config, this, plugin.executeTyped("source.getHome()")); } @@ -292,7 +300,7 @@ open class JSClient : IPlatformClient { @JSDocsParameter("channelId", "(optional) Channel id to search in") override fun search(query: String, type: String?, order: String?, filters: Map>?): IPager = isBusyWith { ensureEnabled(); - return@isBusyWith JSContentPager(config, plugin, + return@isBusyWith JSContentPager(config, this, plugin.executeTyped("source.search(${Json.encodeToString(query)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})")); } @@ -316,7 +324,7 @@ open class JSClient : IPlatformClient { if(!capabilities.hasSearchChannelContents) throw IllegalStateException("This plugin does not support channel search"); - return@isBusyWith JSContentPager(config, plugin, + return@isBusyWith JSContentPager(config, this, plugin.executeTyped("source.searchChannelContents(${Json.encodeToString(channelUrl)}, ${Json.encodeToString(query)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})")); } @@ -325,7 +333,7 @@ open class JSClient : IPlatformClient { @JSDocsParameter("query", "Query that channels should match") override fun searchChannels(query: String): IPager = isBusyWith { ensureEnabled(); - return@isBusyWith JSChannelPager(config, plugin, + return@isBusyWith JSChannelPager(config, this, plugin.executeTyped("source.searchChannels(${Json.encodeToString(query)})")); } @@ -372,7 +380,7 @@ open class JSClient : IPlatformClient { @JSDocsParameter("filters", "(optional) Filters to apply on contents") override fun getChannelContents(channelUrl: String, type: String?, order: String?, filters: Map>?): IPager = isBusyWith { ensureEnabled(); - return@isBusyWith JSContentPager(config, plugin, + return@isBusyWith JSContentPager(config, this, plugin.executeTyped("source.getChannelContents(${Json.encodeToString(channelUrl)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})")); } @@ -438,7 +446,7 @@ open class JSClient : IPlatformClient { @JSDocsParameter("url", "A content url (this platform)") override fun getContentDetails(url: String): IPlatformContentDetails = isBusyWith { ensureEnabled(); - return@isBusyWith IJSContentDetails.fromV8(config, + return@isBusyWith IJSContentDetails.fromV8(this, plugin.executeTyped("source.getContentDetails(${Json.encodeToString(url)})")); } @@ -476,13 +484,13 @@ open class JSClient : IPlatformClient { if (pager !is V8ValueObject) { //TODO: Maybe solve this better return@isBusyWith EmptyPager(); } - return@isBusyWith JSCommentPager(config, plugin, pager); + return@isBusyWith JSCommentPager(config, this, pager); } @JSDocs(17, "source.getSubComments(comment)", "Gets replies for a given comment") @JSDocsParameter("comment", "Comment object that was returned by getComments") override fun getSubComments(comment: IPlatformComment): IPager { ensureEnabled(); - return comment.getReplies(this) ?: JSCommentPager(config, plugin, + return comment.getReplies(this) ?: JSCommentPager(config, this, plugin.executeTyped("source.getSubComments(${Json.encodeToString(comment as JSComment)})")); } @@ -501,7 +509,7 @@ open class JSClient : IPlatformClient { if(!capabilities.hasGetLiveEvents) return@isBusyWith null; ensureEnabled(); - return@isBusyWith JSLiveEventPager(config, plugin, + return@isBusyWith JSLiveEventPager(config, this, plugin.executeTyped("source.getLiveEvents(${Json.encodeToString(url)})")); } @JSDocs(19, "source.searchPlaylists(query)", "Searches for playlists on the platform") @@ -514,7 +522,7 @@ open class JSClient : IPlatformClient { ensureEnabled(); if(!capabilities.hasSearchPlaylists) throw IllegalStateException("This plugin does not support playlist search"); - return@isBusyWith JSContentPager(config, plugin, plugin.executeTyped("source.searchPlaylists(${Json.encodeToString(query)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})")); + return@isBusyWith JSContentPager(config, this, plugin.executeTyped("source.searchPlaylists(${Json.encodeToString(query)}, ${Json.encodeToString(type)}, ${Json.encodeToString(order)}, ${Json.encodeToString(filters)})")); } @JSOptional @JSDocs(20, "source.isPlaylistUrl(url)", "Validates if a playlist url is for this platform") @@ -530,7 +538,7 @@ open class JSClient : IPlatformClient { @JSDocsParameter("url", "Url of playlist") override fun getPlaylist(url: String): IPlatformPlaylistDetails = isBusyWith { ensureEnabled(); - return@isBusyWith JSPlaylistDetails(plugin, plugin.config as SourcePluginConfig, plugin.executeTyped("source.getPlaylist(${Json.encodeToString(url)})")); + return@isBusyWith JSPlaylistDetails(this, plugin.config as SourcePluginConfig, plugin.executeTyped("source.getPlaylist(${Json.encodeToString(url)})")); } @JSOptional diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt index 5833295e..e6a49777 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/SourcePluginDescriptor.kt @@ -26,17 +26,19 @@ class SourcePluginDescriptor { @kotlinx.serialization.Transient val onCaptchaChanged = Event0(); - constructor(config :SourcePluginConfig, authEncrypted: String? = null, captchaEncrypted: String? = null) { + constructor(config :SourcePluginConfig, authEncrypted: String? = null, captchaEncrypted: String? = null, settings: HashMap? = null) { this.config = config; this.authEncrypted = authEncrypted; this.captchaEncrypted = captchaEncrypted; this.flags = listOf(); + this.settings = settings ?: hashMapOf(); } - constructor(config :SourcePluginConfig, authEncrypted: String? = null, captchaEncrypted: String? = null, flags: List) { + constructor(config :SourcePluginConfig, authEncrypted: String? = null, captchaEncrypted: String? = null, flags: List, settings: HashMap? = null) { this.config = config; this.authEncrypted = authEncrypted; this.captchaEncrypted = captchaEncrypted; this.flags = flags; + this.settings = settings ?: hashMapOf(); } fun getSettingsWithDefaults(): HashMap { diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt index 05df143f..8496dfc7 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/internal/JSHttpClient.kt @@ -1,12 +1,16 @@ package com.futo.platformplayer.api.media.platforms.js.internal +import android.net.Uri import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourceAuth import com.futo.platformplayer.api.media.platforms.js.SourceCaptchaData import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig +import com.futo.platformplayer.api.media.platforms.js.models.JSRequest +import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.matchesDomain +import java.util.UUID class JSHttpClient : ManagedHttpClient { private val _jsClient: JSClient?; @@ -14,12 +18,15 @@ class JSHttpClient : ManagedHttpClient { private val _auth: SourceAuth?; private val _captcha: SourceCaptchaData?; + val clientId = UUID.randomUUID().toString(); + var doUpdateCookies: Boolean = true; var doApplyCookies: Boolean = true; var doAllowNewCookies: Boolean = true; val isLoggedIn: Boolean get() = _auth != null; private var _currentCookieMap: HashMap>; + private var _otherCookieMap: HashMap>; constructor(jsClient: JSClient?, auth: SourceAuth? = null, captcha: SourceCaptchaData? = null, config: SourcePluginConfig? = null) : super() { _jsClient = jsClient; @@ -28,6 +35,7 @@ class JSHttpClient : ManagedHttpClient { _captcha = captcha; _currentCookieMap = hashMapOf(); + _otherCookieMap = hashMapOf(); if(!auth?.cookieMap.isNullOrEmpty()) { for(domainCookies in auth!!.cookieMap!!) _currentCookieMap.put(domainCookies.key, HashMap(domainCookies.value)); @@ -49,6 +57,45 @@ class JSHttpClient : ManagedHttpClient { return newClient; } + //TODO: Use this in beforeRequest to remove dup code + fun applyHeaders(url: Uri, headers: MutableMap, applyAuth: Boolean = false, applyOtherCookies: Boolean = false) { + val domain = url.host!!.lowercase(); + val auth = _auth; + if (applyAuth && auth != null) { + //TODO: Possibly add doApplyHeaders + for (header in auth.headers.filter { domain.matchesDomain(it.key) }.flatMap { it.value.entries }) + headers.put(header.key, header.value); + } + + if(doApplyCookies && (applyAuth || applyOtherCookies)) { + val cookiesToApply = hashMapOf(); + if(applyOtherCookies) + synchronized(_otherCookieMap) { + for(cookie in _otherCookieMap + .filter { domain.matchesDomain(it.key) } + .flatMap { it.value.toList() }) + cookiesToApply[cookie.first] = cookie.second; + } + if(applyAuth) + synchronized(_currentCookieMap) { + for(cookie in _currentCookieMap + .filter { domain.matchesDomain(it.key) } + .flatMap { it.value.toList() }) + cookiesToApply[cookie.first] = cookie.second; + }; + + if(cookiesToApply.size > 0) { + val cookieString = cookiesToApply.map { it.key + "=" + it.value }.joinToString("; "); + + val existingCookies = headers["Cookie"]; + if(!existingCookies.isNullOrEmpty()) + headers.put("Cookie", existingCookies.trim(';') + "; " + cookieString); + else + headers.put("Cookie", cookieString); + } + } + } + override fun beforeRequest(request: okhttp3.Request): okhttp3.Request { val domain = request.url.host.lowercase(); val auth = _auth; @@ -101,10 +148,10 @@ class JSHttpClient : ManagedHttpClient { val defaultCookieDomain = "." + domainParts.drop(domainParts.size - 2).joinToString("."); for (header in resp.headers) { - if ((_auth != null || _currentCookieMap.isNotEmpty()) && header.first.lowercase() == "set-cookie") { + if(header.first.lowercase() == "set-cookie") { + var domainToUse = domain; val cookie = cookieStringToPair(header.second); var cookieValue = cookie.second; - var domainToUse = domain; if (cookie.first.isNotEmpty() && cookie.second.isNotEmpty()) { val cookieParts = cookie.second.split(";"); @@ -124,17 +171,33 @@ class JSHttpClient : ManagedHttpClient { domainToUse = if (cookieVariables.containsKey("domain")) cookieVariables["domain"]!!.lowercase(); else defaultCookieDomain; + //TODO: Make sure this has no negative effect besides apply cookies to root domain + if(!domainToUse.startsWith(".")) + domainToUse = ".${domainToUse}"; } - val cookieMap = if (_currentCookieMap.containsKey(domainToUse)) - _currentCookieMap[domainToUse]!!; - else { - val newMap = hashMapOf(); - _currentCookieMap[domainToUse] = newMap - newMap; + if ((_auth != null || _currentCookieMap.isNotEmpty())) { + val cookieMap = if (_currentCookieMap.containsKey(domainToUse)) + _currentCookieMap[domainToUse]!!; + else { + val newMap = hashMapOf(); + _currentCookieMap[domainToUse] = newMap + newMap; + } + if (cookieMap.containsKey(cookie.first) || doAllowNewCookies) + cookieMap[cookie.first] = cookieValue; + } + else { + val cookieMap = if (_otherCookieMap.containsKey(domainToUse)) + _otherCookieMap[domainToUse]!!; + else { + val newMap = hashMapOf(); + _otherCookieMap[domainToUse] = newMap + newMap; + } + if (cookieMap.containsKey(cookie.first) || doAllowNewCookies) + cookieMap[cookie.first] = cookieValue; } - if(cookieMap.containsKey(cookie.first) || doAllowNewCookies) - cookieMap[cookie.first] = cookieValue; } } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt index 09da4562..a6a15fb6 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContent.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.models.contents.ContentType import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrThrow @@ -10,13 +11,14 @@ import com.futo.platformplayer.getOrThrow interface IJSContent: IPlatformContent { companion object { - fun fromV8(config: SourcePluginConfig, obj: V8ValueObject): IPlatformContent { + fun fromV8(plugin: JSClient, obj: V8ValueObject): IPlatformContent { + val config = plugin.config; val type: Int = obj.getOrThrow(config, "contentType", "ContentItem"); val pluginType: String? = obj.getOrDefault(config, "plugin_type", "ContentItem", null); //TODO: Temporary workaround for intercepting details in lists if(pluginType != null && pluginType.endsWith("Details")) - return IJSContentDetails.fromV8(config, obj); + return IJSContentDetails.fromV8(plugin, obj); return when(ContentType.fromInt(type)) { ContentType.MEDIA -> JSVideo(config, obj); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContentDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContentDetails.kt index fad34868..6ec2fd98 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContentDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/IJSContentDetails.kt @@ -4,17 +4,18 @@ import com.caoccao.javet.values.reference.V8ValueObject 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.IPlatformContentDetails +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.getOrThrow interface IJSContentDetails: IPlatformContent { companion object { - fun fromV8(config: SourcePluginConfig, obj: V8ValueObject): IPlatformContentDetails { - val type: Int = obj.getOrThrow(config, "contentType", "ContentDetails"); + fun fromV8(plugin: JSClient, obj: V8ValueObject): IPlatformContentDetails { + val type: Int = obj.getOrThrow(plugin.config, "contentType", "ContentDetails"); return when(ContentType.fromInt(type)) { - ContentType.MEDIA -> JSVideoDetails(config, obj); - ContentType.POST -> JSPostDetails(config, obj); + ContentType.MEDIA -> JSVideoDetails(plugin, obj); + ContentType.POST -> JSPostDetails(plugin.config, obj); else -> throw NotImplementedError("Unknown content type ${type}"); } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannelPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannelPager.kt index 01253896..683c64af 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannelPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSChannelPager.kt @@ -2,13 +2,14 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.PlatformAuthorLink +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin class JSChannelPager : JSPager, IPager { - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) {} + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) {} override fun convertResult(obj: V8ValueObject): PlatformAuthorLink { return PlatformAuthorLink.fromV8(config, obj); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSComment.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSComment.kt index 04146582..ab847b6b 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSComment.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSComment.kt @@ -5,6 +5,7 @@ import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.comments.IPlatformComment import com.futo.platformplayer.api.media.models.ratings.IRating +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin @@ -60,6 +61,7 @@ class JSComment : IPlatformComment { return null; val obj = _comment!!.invoke("getReplies", arrayOf()); - return JSCommentPager(_config!!, _plugin!!, obj); + val plugin = if(client is JSClient) client else throw NotImplementedError("Only implemented for JSClient"); + return JSCommentPager(_config!!, plugin, obj); } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSCommentPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSCommentPager.kt index 13d44fe9..94205d35 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSCommentPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSCommentPager.kt @@ -2,15 +2,16 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.comments.IPlatformComment +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin class JSCommentPager : JSPager, IPager { - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) { } + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) { } override fun convertResult(obj: V8ValueObject): IPlatformComment { - return JSComment(config, plugin, obj); + return JSComment(config, plugin.getUnderlyingPlugin(), obj); } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSContentPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSContentPager.kt index f7a8fbbc..490fa7c4 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSContentPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSContentPager.kt @@ -3,15 +3,16 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.IPluginSourced import com.futo.platformplayer.api.media.models.contents.IPlatformContent +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.engine.V8Plugin class JSContentPager : JSPager, IPluginSourced { override val sourceConfig: SourcePluginConfig get() = config; - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) {} + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) {} override fun convertResult(obj: V8ValueObject): IPlatformContent { - return IJSContent.fromV8(config, obj); + return IJSContent.fromV8(plugin, obj); } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLiveEventPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLiveEventPager.kt index 1d8ead85..27731fea 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLiveEventPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSLiveEventPager.kt @@ -2,6 +2,7 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPlatformLiveEventPager import com.futo.platformplayer.engine.V8Plugin @@ -10,7 +11,7 @@ import com.futo.platformplayer.getOrThrow class JSLiveEventPager : JSPager, IPlatformLiveEventPager { override var nextRequest: Int; - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) { + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) { nextRequest = pager.getOrThrow(config, "nextRequest", "LiveEventPager"); } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt index 831cc93d..a7672823 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPager.kt @@ -4,6 +4,7 @@ import android.os.Looper import com.caoccao.javet.values.reference.V8ValueArray import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.BuildConfig +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin @@ -12,7 +13,7 @@ import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.warnIfMainThread abstract class JSPager : IPager { - protected val plugin: V8Plugin; + protected val plugin: JSClient; protected val config: SourcePluginConfig; protected var pager: V8ValueObject; @@ -21,9 +22,9 @@ abstract class JSPager : IPager { private var _hasMorePages: Boolean = false; //private var _morePagesWasFalse: Boolean = false; - val isAvailable get() = plugin._runtime?.let { !it.isClosed && !it.isDead } ?: false; + val isAvailable get() = plugin.getUnderlyingPlugin()._runtime?.let { !it.isClosed && !it.isDead } ?: false; - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) { + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) { this.plugin = plugin; this.pager = pager; this.config = config; @@ -43,7 +44,7 @@ abstract class JSPager : IPager { override fun nextPage() { warnIfMainThread("JSPager.nextPage"); - pager = plugin.catchScriptErrors("[${plugin.config.name}] JSPager", "pager.nextPage()") { + pager = plugin.getUnderlyingPlugin().catchScriptErrors("[${plugin.config.name}] JSPager", "pager.nextPage()") { pager.invoke("nextPage", arrayOf()); }; _hasMorePages = pager.getOrDefault(config, "hasMore", "Pager", false) ?: false; diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistDetails.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistDetails.kt index 97b3f29b..cbd4e013 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistDetails.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistDetails.kt @@ -4,6 +4,7 @@ import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin @@ -13,7 +14,7 @@ import com.futo.platformplayer.models.Playlist class JSPlaylistDetails: JSPlaylist, IPlatformPlaylistDetails { override val contents: IPager; - constructor(plugin: V8Plugin, config: SourcePluginConfig, obj: V8ValueObject): super(config, obj) { + constructor(plugin: JSClient, config: SourcePluginConfig, obj: V8ValueObject): super(config, obj) { contents = JSVideoPager(config, plugin, obj.getOrThrow(config, "contents", "PlaylistDetails")); } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistPager.kt index a0df057b..151bfe2a 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSPlaylistPager.kt @@ -2,13 +2,14 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylist +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.engine.V8Plugin class JSPlaylistPager : JSPager, IPager { - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) {} + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) {} override fun convertResult(obj: V8ValueObject): IPlatformPlaylist { return JSPlaylist(config, obj); 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 e8982889..bc455fcc 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 @@ -54,6 +54,6 @@ class JSPostDetails : JSPost, IPlatformPost, IPlatformPostDetails { private fun getCommentsJS(client: JSClient): JSCommentPager { val commentPager = _content.invoke("getComments", arrayOf()); - return JSCommentPager(_pluginConfig, client.getUnderlyingPlugin(), commentPager); + 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/JSRequest.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt index 960982bf..1a9ef32b 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequest.kt @@ -1,18 +1,81 @@ package com.futo.platformplayer.api.media.platforms.js.models +import android.net.Uri import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.modifier.IModifierOptions +import com.futo.platformplayer.api.media.models.modifier.IRequest +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig -import com.futo.platformplayer.getOrThrow -import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.getOrDefault @kotlinx.serialization.Serializable -class JSRequest : JSRequestModifier.IRequest { - override val url: String; - override val headers: Map; +class JSRequest : IRequest { + private val _v8Url: String?; + private val _v8Headers: Map?; + private val _v8Options: Options?; - constructor(config: IV8PluginConfig, obj: V8ValueObject) { + override var url: String? = null; + override lateinit var headers: Map; + + constructor(plugin: JSClient, url: String?, headers: Map?, options: Options?, originalUrl: String?, originalHeaders: Map?) { + _v8Url = url; + _v8Headers = headers; + _v8Options = options; + initialize(plugin, originalUrl, originalHeaders); + } + constructor(plugin: JSClient, obj: V8ValueObject, originalUrl: String?, originalHeaders: Map?) { val contextName = "ModifyRequestResponse"; - url = obj.getOrThrow(config, "url", contextName); - headers = obj.getOrThrow(config, "headers", contextName); + val config = plugin.config; + _v8Url = obj.getOrDefault(config, "url", contextName, null); + _v8Headers = obj.getOrDefault>(config, "headers", contextName, null); + _v8Options = obj.getOrDefault(config, "options", "JSRequestModifier.options", null)?.let { + Options(config, it); + } + initialize(plugin, originalUrl, originalHeaders); + } + + private fun initialize(plugin: JSClient, originalUrl: String?, originalHeaders: Map?) { + val config = plugin.config; + url = _v8Url ?: originalUrl; + headers = _v8Headers ?: originalHeaders ?: mapOf(); + + if(_v8Options != null) { + if(_v8Options.applyCookieClient != null && url != null) { + val client = plugin.getHttpClientById(_v8Options.applyCookieClient); + if(client != null) { + val toModifyHeaders = headers.toMutableMap(); + client.applyHeaders(Uri.parse(url), toModifyHeaders, false, true); + headers = toModifyHeaders; + } + } + if(_v8Options.applyAuthClient != null && url != null) { + val client = plugin.getHttpClientById(_v8Options.applyAuthClient); + if(client != null) { + val toModifyHeaders = headers.toMutableMap(); + client.applyHeaders(Uri.parse(url), toModifyHeaders, true, false); + headers = toModifyHeaders; + } + } + } + } + + fun modify(plugin: JSClient, originalUrl: String?, originalHeaders: Map?): JSRequest { + return JSRequest(plugin, _v8Url, _v8Headers, _v8Options, originalUrl, originalHeaders); + } + + + @kotlinx.serialization.Serializable + class Options: IModifierOptions { + override val applyAuthClient: String?; + override val applyCookieClient: String?; + + constructor(config: IV8PluginConfig, obj: V8ValueObject) { + applyAuthClient = obj.getOrDefault(config, "applyAuthClient", "JSRequestModifier.options.applyAuthClient", null); + applyCookieClient = obj.getOrDefault(config, "applyCookieClient", "JSRequestModifier.options.applyCookieClient", null); + } + } + + companion object { + } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt index 0d71057a..cffbb474 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSRequestModifier.kt @@ -1,19 +1,28 @@ package com.futo.platformplayer.api.media.platforms.js.models +import android.net.Uri import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.modifier.IRequest +import com.futo.platformplayer.api.media.models.modifier.IRequestModifier +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.exceptions.ScriptImplementationException +import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrNull +import com.futo.platformplayer.getOrThrow -class JSRequestModifier { +class JSRequestModifier: IRequestModifier { + private val _plugin: JSClient; private val _config: IV8PluginConfig; private var _modifier: V8ValueObject; - val allowByteSkip: Boolean; + override var allowByteSkip: Boolean; - constructor(config: IV8PluginConfig, modifier: V8ValueObject) { + constructor(plugin: JSClient, modifier: V8ValueObject) { + this._plugin = plugin; this._modifier = modifier; - this._config = config; + this._config = plugin.config; + val config = plugin.config; allowByteSkip = modifier.getOrNull(config, "allowByteSkip", "JSRequestModifier") ?: true; @@ -21,22 +30,19 @@ class JSRequestModifier { throw ScriptImplementationException(config, "RequestModifier is missing modifyRequest", null); } - fun modifyRequest(url: String, headers: Map): IRequest { + override fun modifyRequest(url: String, headers: Map): IRequest { if (_modifier.isClosed) { return Request(url, headers); } val result = V8Plugin.catchScriptErrors(_config, "[${_config.name}] JSRequestModifier", "builder.modifyRequest()") { _modifier.invoke("modifyRequest", url, headers); - }; + } as V8ValueObject; - return JSRequest(_config, result as V8ValueObject); + val req = JSRequest(_plugin, result, url, headers); + return req; } - interface IRequest { - val url: String; - val headers: Map; - } data class Request(override val url: String, override val headers: Map) : IRequest; } \ 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 815452ee..83137f23 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 @@ -44,13 +44,14 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails { override val subtitles: List; - constructor(config: SourcePluginConfig, obj: V8ValueObject) : super(config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin.config, obj) { val contextName = "VideoDetails"; + val config = plugin.config; description = _content.getOrThrow(config, "description", contextName); - video = JSVideoSourceDescriptor.fromV8(config, _content.getOrThrow(config, "video", contextName)); - dash = JSSource.fromV8DashNullable(config, _content.getOrThrowNullable(config, "dash", contextName)); - hls = JSSource.fromV8HLSNullable(config, _content.getOrThrowNullable(config, "hls", contextName)); - live = JSSource.fromV8VideoNullable(config, _content.getOrThrowNullable(config, "live", contextName)); + video = JSVideoSourceDescriptor.fromV8(plugin, _content.getOrThrow(config, "video", contextName)); + dash = JSSource.fromV8DashNullable(plugin, _content.getOrThrowNullable(config, "dash", contextName)); + hls = JSSource.fromV8HLSNullable(plugin, _content.getOrThrowNullable(config, "hls", contextName)); + live = JSSource.fromV8VideoNullable(plugin, _content.getOrThrowNullable(config, "live", contextName)); rating = IRating.fromV8OrDefault(config, _content.getOrDefault(config, "rating", contextName, null), RatingLikes(0)); if(!_content.has("subtitles")) @@ -105,6 +106,6 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails { if (commentPager !is V8ValueObject) //TODO: Maybe handle this better? return null; - return JSCommentPager(_pluginConfig, client.getUnderlyingPlugin(), commentPager); + 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/JSVideoPager.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoPager.kt index 1cc24a2d..89117138 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoPager.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/JSVideoPager.kt @@ -2,12 +2,13 @@ package com.futo.platformplayer.api.media.platforms.js.models import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.video.IPlatformVideo +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.engine.V8Plugin class JSVideoPager : JSPager { - constructor(config: SourcePluginConfig, plugin: V8Plugin, pager: V8ValueObject) : super(config, plugin, pager) {} + constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) {} override fun convertResult(obj: V8ValueObject): IPlatformVideo { return JSVideo(config, obj); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt index 39d80032..09de1f35 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioUrlSource.kt @@ -2,7 +2,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.getOrThrow @@ -19,8 +21,9 @@ open class JSAudioUrlSource : IAudioUrlSource, JSSource { override var priority: Boolean = false; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(TYPE_AUDIOURL, config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_AUDIOURL, plugin, obj) { val contextName = "AudioUrlSource"; + val config = plugin.config; bitrate = _obj.getOrThrow(config, "bitrate", contextName); container = _obj.getOrThrow(config, "container", contextName); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioWithMetadataSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioWithMetadataSource.kt index 9eafafee..42eeffce 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioWithMetadataSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSAudioWithMetadataSource.kt @@ -3,7 +3,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource import com.futo.platformplayer.api.media.models.streams.sources.other.StreamMetaData +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrDefault class JSAudioUrlRangeSource : JSAudioUrlSource, IStreamMetaDataSource { @@ -22,8 +24,9 @@ class JSAudioUrlRangeSource : JSAudioUrlSource, IStreamMetaDataSource { && indexEnd != null) StreamMetaData(initStart, initEnd, indexStart, indexEnd) else null; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) { val contextName = "JSAudioUrlRangeSource"; + val config = plugin.config; itagId = _obj.getOrDefault(config, "itagId", contextName, null); initStart = _obj.getOrDefault(config, "initStart", contextName, null); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestSource.kt index 4f50cbf6..3070a2d4 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSDashManifestSource.kt @@ -3,7 +3,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow @@ -19,9 +21,9 @@ class JSDashManifestSource : IVideoUrlSource, IDashManifestSource, JSSource { override var priority: Boolean = false; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(TYPE_DASH, config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_DASH, plugin, obj) { val contextName = "DashSource"; - + val config = plugin.config; name = _obj.getOrThrow(config, "name", contextName); url = _obj.getOrThrow(config, "url", contextName); duration = _obj.getOrThrow(config, "duration", contextName); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt index 8636cbb8..41948802 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestAudioSource.kt @@ -4,7 +4,9 @@ import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.orNull @@ -20,8 +22,9 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource { override var priority: Boolean = false; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(TYPE_HLS, config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_HLS, plugin, obj) { val contextName = "HLSAudioSource"; + val config = plugin.config; name = _obj.getOrThrow(config, "name", contextName); url = _obj.getOrThrow(config, "url", contextName); @@ -33,7 +36,7 @@ class JSHLSManifestAudioSource : IHLSManifestAudioSource, JSSource { companion object { - fun fromV8HLSNullable(config: IV8PluginConfig, obj: V8Value?) : JSHLSManifestAudioSource? = obj.orNull { fromV8HLS(config, it as V8ValueObject) }; - fun fromV8HLS(config: IV8PluginConfig, obj: V8ValueObject) : JSHLSManifestAudioSource = JSHLSManifestAudioSource(config, obj); + fun fromV8HLSNullable(plugin: JSClient, obj: V8Value?) : JSHLSManifestAudioSource? = obj.orNull { fromV8HLS(plugin, it as V8ValueObject) }; + fun fromV8HLS(plugin: JSClient, obj: V8ValueObject) : JSHLSManifestAudioSource = JSHLSManifestAudioSource(plugin, obj); } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestSource.kt index 8c785c17..606d107c 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSHLSManifestSource.kt @@ -3,7 +3,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow @@ -19,8 +21,9 @@ class JSHLSManifestSource : IHLSManifestSource, JSSource { override var priority: Boolean = false; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(TYPE_HLS, config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(TYPE_HLS, plugin, obj) { val contextName = "HLSSource"; + val config = plugin.config; name = _obj.getOrThrow(config, "name", contextName); url = _obj.getOrThrow(config, "url", contextName); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt index 9bf35ad2..ccd9c223 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSSource.kt @@ -4,31 +4,47 @@ import androidx.media3.datasource.DefaultHttpDataSource import androidx.media3.datasource.HttpDataSource import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.reference.V8ValueObject +import com.futo.platformplayer.api.media.models.modifier.AdhocRequestModifier +import com.futo.platformplayer.api.media.models.modifier.IRequestModifier import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource +import com.futo.platformplayer.api.media.platforms.js.JSClient +import com.futo.platformplayer.api.media.platforms.js.models.JSRequest import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.V8Plugin +import com.futo.platformplayer.getOrDefault import com.futo.platformplayer.orNull import com.futo.platformplayer.views.video.datasources.JSHttpDataSource abstract class JSSource { + protected val _plugin: JSClient; protected val _config: IV8PluginConfig; protected val _obj: V8ValueObject; - private val _hasRequestModifier: Boolean; + val hasRequestModifier: Boolean; + private val _requestModifier: JSRequest?; val type : String; - constructor(type: String, config: IV8PluginConfig, obj: V8ValueObject) { - this._config = config; + constructor(type: String, plugin: JSClient, obj: V8ValueObject) { + this._plugin = plugin; + this._config = plugin.config; this._obj = obj; this.type = type; - _hasRequestModifier = obj.has("getRequestModifier"); + _requestModifier = obj.getOrDefault(_config, "requestModifier", "JSSource.requestModifier", null)?.let { + JSRequest(plugin, it, null, null); + } + hasRequestModifier = _requestModifier != null || obj.has("getRequestModifier"); } - fun getRequestModifier(): JSRequestModifier? { - if (!_hasRequestModifier || _obj.isClosed) { + fun getRequestModifier(): IRequestModifier? { + if(_requestModifier != null) + return AdhocRequestModifier { url, headers -> + return@AdhocRequestModifier _requestModifier.modify(_plugin, url, headers); + }; + + if (!hasRequestModifier || _obj.isClosed) { return null; } @@ -40,16 +56,7 @@ abstract class JSSource { return null; } - return JSRequestModifier(_config, result) - } - - fun getHttpDataSourceFactory(): HttpDataSource.Factory { - val requestModifier = getRequestModifier(); - return if (requestModifier != null) { - JSHttpDataSource.Factory().setRequestModifier(requestModifier); - } else { - DefaultHttpDataSource.Factory(); - } + return JSRequestModifier(_plugin, result) } companion object { @@ -60,28 +67,28 @@ abstract class JSSource { const val TYPE_DASH = "DashSource"; const val TYPE_HLS = "HLSSource"; - fun fromV8VideoNullable(config: IV8PluginConfig, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(config, it as V8ValueObject) }; - fun fromV8Video(config: IV8PluginConfig, obj: V8ValueObject) : IVideoSource { + fun fromV8VideoNullable(plugin: JSClient, obj: V8Value?) : IVideoSource? = obj.orNull { fromV8Video(plugin, it as V8ValueObject) }; + fun fromV8Video(plugin: JSClient, obj: V8ValueObject) : IVideoSource { val type = obj.getString("plugin_type"); return when(type) { - TYPE_VIDEOURL -> JSVideoUrlSource(config, obj); - TYPE_VIDEO_WITH_METADATA -> JSVideoUrlRangeSource(config, obj); - TYPE_HLS -> fromV8HLS(config, obj); - TYPE_DASH -> fromV8Dash(config, obj); + TYPE_VIDEOURL -> JSVideoUrlSource(plugin, obj); + TYPE_VIDEO_WITH_METADATA -> JSVideoUrlRangeSource(plugin, obj); + TYPE_HLS -> fromV8HLS(plugin, obj); + TYPE_DASH -> fromV8Dash(plugin, obj); else -> throw NotImplementedError("Unknown type ${type}"); } } - fun fromV8DashNullable(config: IV8PluginConfig, obj: V8Value?) : JSDashManifestSource? = obj.orNull { fromV8Dash(config, it as V8ValueObject) }; - fun fromV8Dash(config: IV8PluginConfig, obj: V8ValueObject) : JSDashManifestSource = JSDashManifestSource(config, obj); - fun fromV8HLSNullable(config: IV8PluginConfig, obj: V8Value?) : JSHLSManifestSource? = obj.orNull { fromV8HLS(config, it as V8ValueObject) }; - fun fromV8HLS(config: IV8PluginConfig, obj: V8ValueObject) : JSHLSManifestSource = JSHLSManifestSource(config, obj); + fun fromV8DashNullable(plugin: JSClient, obj: V8Value?) : JSDashManifestSource? = obj.orNull { fromV8Dash(plugin, it as V8ValueObject) }; + fun fromV8Dash(plugin: JSClient, obj: V8ValueObject) : JSDashManifestSource = JSDashManifestSource(plugin, obj); + fun fromV8HLSNullable(plugin: JSClient, obj: V8Value?) : JSHLSManifestSource? = obj.orNull { fromV8HLS(plugin, it as V8ValueObject) }; + fun fromV8HLS(plugin: JSClient, obj: V8ValueObject) : JSHLSManifestSource = JSHLSManifestSource(plugin, obj); - fun fromV8Audio(config: IV8PluginConfig, obj: V8ValueObject) : IAudioSource { + fun fromV8Audio(plugin: JSClient, obj: V8ValueObject) : IAudioSource { val type = obj.getString("plugin_type"); return when(type) { - TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(config, obj); - TYPE_AUDIOURL -> JSAudioUrlSource(config, obj); - TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(config, obj); + TYPE_HLS -> JSHLSManifestAudioSource.fromV8HLS(plugin, obj); + TYPE_AUDIOURL -> JSAudioUrlSource(plugin, obj); + TYPE_AUDIO_WITH_METADATA -> JSAudioUrlRangeSource(plugin, obj); else -> throw NotImplementedError("Unknown type ${type}"); } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSUnMuxVideoSourceDescriptor.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSUnMuxVideoSourceDescriptor.kt index 08d4911d..035f5fb6 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSUnMuxVideoSourceDescriptor.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSUnMuxVideoSourceDescriptor.kt @@ -5,6 +5,7 @@ import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.getOrThrow @@ -15,15 +16,16 @@ class JSUnMuxVideoSourceDescriptor: VideoUnMuxedSourceDescriptor { override val videoSources: Array; override val audioSources: Array; - constructor(config: IV8PluginConfig, obj: V8ValueObject) { + constructor(plugin: JSClient, obj: V8ValueObject) { this._obj = obj; + val config = plugin.config; val contextName = "UnMuxVideoSource" this.isUnMuxed = obj.getOrThrow(config, "isUnMuxed", contextName); this.videoSources = obj.getOrThrow(config, "videoSources", contextName).toArray() - .map { JSSource.fromV8Video(config, it as V8ValueObject) } + .map { JSSource.fromV8Video(plugin, it as V8ValueObject) } .toTypedArray(); this.audioSources = obj.getOrThrow(config, "audioSources", contextName).toArray() - .map { JSSource.fromV8Audio(config, it as V8ValueObject) } + .map { JSSource.fromV8Audio(plugin, it as V8ValueObject) } .toTypedArray(); } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoSourceDescriptor.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoSourceDescriptor.kt index 463100b0..131a5794 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoSourceDescriptor.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoSourceDescriptor.kt @@ -5,7 +5,9 @@ import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrThrow class JSVideoSourceDescriptor: VideoMuxedSourceDescriptor { @@ -14,12 +16,13 @@ class JSVideoSourceDescriptor: VideoMuxedSourceDescriptor { override val isUnMuxed: Boolean; override val videoSources: Array; - constructor(config: IV8PluginConfig, obj: V8ValueObject) { + constructor(plugin: JSClient, obj: V8ValueObject) { this._obj = obj; + val config = plugin.config; val contextName = "VideoSourceDescriptor"; this.isUnMuxed = obj.getOrThrow(config, "isUnMuxed", contextName); this.videoSources = obj.getOrThrow(config, "videoSources", contextName).toArray() - .map { JSSource.fromV8Video(config, it as V8ValueObject) } + .map { JSSource.fromV8Video(plugin, it as V8ValueObject) } .toTypedArray(); } @@ -28,11 +31,11 @@ class JSVideoSourceDescriptor: VideoMuxedSourceDescriptor { const val TYPE_UNMUXED = "UnMuxVideoSourceDescriptor"; - fun fromV8(config: IV8PluginConfig, obj: V8ValueObject) : IVideoSourceDescriptor { + fun fromV8(plugin: JSClient, obj: V8ValueObject) : IVideoSourceDescriptor { val type = obj.getString("plugin_type") return when(type) { - TYPE_MUXED -> JSVideoSourceDescriptor(config, obj); - TYPE_UNMUXED -> JSUnMuxVideoSourceDescriptor(config, obj); + TYPE_MUXED -> JSVideoSourceDescriptor(plugin, obj); + TYPE_UNMUXED -> JSUnMuxVideoSourceDescriptor(plugin, obj); else -> throw NotImplementedError("Unknown type: ${type}"); } } diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoUrlSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoUrlSource.kt index a4fbf0e8..66246f56 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoUrlSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoUrlSource.kt @@ -2,7 +2,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrNull import com.futo.platformplayer.getOrThrow @@ -18,8 +20,9 @@ open class JSVideoUrlSource : IVideoUrlSource, JSSource { override var priority: Boolean = false; - constructor(config: IV8PluginConfig, obj: V8ValueObject): super(TYPE_VIDEOURL, config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject): super(TYPE_VIDEOURL, plugin, obj) { val contextName = "JSVideoUrlSource"; + val config = plugin.config; width = _obj.getOrThrow(config, "width", contextName); height = _obj.getOrThrow(config, "height", contextName); diff --git a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoWithMetadataSource.kt b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoWithMetadataSource.kt index 90b6edee..0e86c2fc 100644 --- a/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoWithMetadataSource.kt +++ b/app/src/main/java/com/futo/platformplayer/api/media/platforms/js/models/sources/JSVideoWithMetadataSource.kt @@ -3,7 +3,9 @@ package com.futo.platformplayer.api.media.platforms.js.models.sources import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource import com.futo.platformplayer.api.media.models.streams.sources.other.StreamMetaData +import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.engine.IV8PluginConfig +import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.getOrDefault class JSVideoUrlRangeSource : JSVideoUrlSource, IStreamMetaDataSource { @@ -21,8 +23,9 @@ class JSVideoUrlRangeSource : JSVideoUrlSource, IStreamMetaDataSource { && indexEnd != null) StreamMetaData(initStart, initEnd, indexStart, indexEnd) else null; - constructor(config: IV8PluginConfig, obj: V8ValueObject) : super(config, obj) { + constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin, obj) { val contextName = "JSVideoUrlRangeSource"; + val config = plugin.config; itagId = _obj.getOrDefault(config, "itagId", contextName, null); initStart = _obj.getOrDefault(config, "initStart", contextName, null); diff --git a/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt b/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt index 95ad1b3a..8b7c08da 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/V8Plugin.kt @@ -11,6 +11,7 @@ import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.reference.V8ValueObject import com.futo.platformplayer.api.http.ManagedHttpClient +import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.engine.exceptions.NoInternetException import com.futo.platformplayer.engine.exceptions.PluginEngineStoppedException @@ -34,15 +35,24 @@ import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateAssets import com.futo.platformplayer.warnIfMainThread +import java.util.concurrent.ConcurrentHashMap class V8Plugin { val config: IV8PluginConfig; private val _client: ManagedHttpClient; private val _clientAuth: ManagedHttpClient; + private val _clientOthers: ConcurrentHashMap = ConcurrentHashMap(); val httpClient: ManagedHttpClient get() = _client; val httpClientAuth: ManagedHttpClient get() = _clientAuth; + val httpClientOthers: Map get() = _clientOthers; + + fun registerHttpClient(client: JSHttpClient) { + synchronized(_clientOthers) { + _clientOthers.put(client.clientId, client); + } + } private val _runtimeLock = Object(); var _runtime : V8Runtime? = null; diff --git a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt index 68a74202..937e2d23 100644 --- a/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt +++ b/app/src/main/java/com/futo/platformplayer/engine/packages/PackageHttp.kt @@ -45,7 +45,12 @@ class PackageHttp: V8Package { @V8Function fun newClient(withAuth: Boolean): PackageHttpClient { - return PackageHttpClient(this, if(withAuth) _clientAuth.clone() else _client.clone()); + val httpClient = if(withAuth) _clientAuth.clone() else _client.clone(); + if(httpClient is JSHttpClient) + _plugin.registerHttpClient(httpClient); + val client = PackageHttpClient(this, httpClient); + + return client; } @V8Function fun getDefaultClient(withAuth: Boolean): PackageHttpClient { @@ -187,10 +192,19 @@ class PackageHttp: V8Package { @Transient private val _defaultHeaders = mutableMapOf(); + @Transient + private val _clientId: String?; + + @V8Property + fun clientId(): String? { + return _clientId; + } + constructor(pack: PackageHttp, baseClient: ManagedHttpClient): super() { _package = pack; _client = baseClient; + _clientId = if(_client is JSHttpClient) _client.clientId else null; } @V8Function diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SourceDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SourceDetailFragment.kt index a28e8016..3af3f01d 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SourceDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/SourceDetailFragment.kt @@ -23,6 +23,7 @@ import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateDeveloper import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.views.buttons.BigButton @@ -294,7 +295,9 @@ class SourceDetailFragment : MainFragment() { } } - val clientIfExists = StatePlugins.instance.getPlugin(config.id); + val clientIfExists = if(config.id != StateDeveloper.DEV_ID) + StatePlugins.instance.getPlugin(config.id); + else null; groups.add( BigButtonGroup(c, context.getString(R.string.management), BigButton(c, context.getString(R.string.uninstall), context.getString(R.string.removes_the_plugin_from_the_app), R.drawable.ic_block) { diff --git a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt index 1625b1e4..3d72d32f 100644 --- a/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt +++ b/app/src/main/java/com/futo/platformplayer/helpers/VideoHelper.kt @@ -20,6 +20,7 @@ import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource +import com.futo.platformplayer.getHttpDataSourceFactory import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.others.Language import kotlin.math.abs diff --git a/app/src/main/java/com/futo/platformplayer/states/StateDeveloper.kt b/app/src/main/java/com/futo/platformplayer/states/StateDeveloper.kt index 8d9ceff6..7b6628e6 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateDeveloper.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateDeveloper.kt @@ -1,5 +1,6 @@ package com.futo.platformplayer.states +import android.content.Context import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.api.http.server.ManagedHttpServer import com.futo.platformplayer.developer.DeveloperEndpoints @@ -93,6 +94,13 @@ class StateDeveloper { } } + fun setDevClientSettings(settings: HashMap) { + val client = StatePlatform.instance.getDevClient(); + client?.let { + it.descriptor.settings = settings; + }; + } + fun runServer() { if(_server != null) diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt index 85b16fd0..2f08937d 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlugins.kt @@ -10,6 +10,7 @@ import com.futo.platformplayer.api.media.platforms.js.SourceAuth import com.futo.platformplayer.api.media.platforms.js.SourceCaptchaData import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginDescriptor +import com.futo.platformplayer.developer.DeveloperEndpoints import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.models.ImageVariable import com.futo.platformplayer.stores.FragmentedStorage @@ -411,6 +412,16 @@ class StatePlugins { fun setPluginSettings(id: String, map: Map) { val newSettings = HashMap(map); + if(id == StateDeveloper.DEV_ID) + { + val decConfig = StatePlatform.instance.getDevClient()?.config ?: return; + for(setting in decConfig.settings) { + if(!newSettings.containsKey(setting.variableOrName) || newSettings[setting.variableOrName] == null) + newSettings[setting.variableOrName] = setting.default; + } + StateDeveloper.instance.setDevClientSettings(newSettings); + return; + } val plugin = getPlugin(id); if(plugin != null) { diff --git a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt index abe86c62..02a3ab52 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt +++ b/app/src/main/java/com/futo/platformplayer/views/video/FutoVideoPlayerBase.kt @@ -38,8 +38,10 @@ import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.platforms.js.models.sources.JSAudioUrlRangeSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSHLSManifestAudioSource +import com.futo.platformplayer.api.media.platforms.js.models.sources.JSSource import com.futo.platformplayer.api.media.platforms.js.models.sources.JSVideoUrlRangeSource import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.getHttpDataSourceFactory import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.states.StateApp @@ -402,22 +404,31 @@ abstract class FutoVideoPlayerBase : RelativeLayout { @OptIn(UnstableApi::class) private fun swapVideoSourceUrl(videoSource: IVideoUrlSource) { Logger.i(TAG, "Loading VideoSource [Url]"); - _lastVideoMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory() - .setUserAgent(DEFAULT_USER_AGENT)) + val dataSource = if(videoSource is JSSource && videoSource.hasRequestModifier) + videoSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); + _lastVideoMediaSource = ProgressiveMediaSource.Factory(dataSource) .createMediaSource(MediaItem.fromUri(videoSource.getVideoUrl())); } @OptIn(UnstableApi::class) private fun swapVideoSourceDash(videoSource: IDashManifestSource) { Logger.i(TAG, "Loading VideoSource [Dash]"); - _lastVideoMediaSource = DashMediaSource.Factory(DefaultHttpDataSource.Factory() - .setUserAgent(DEFAULT_USER_AGENT)) + val dataSource = if(videoSource is JSSource && videoSource.hasRequestModifier) + videoSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); + _lastVideoMediaSource = DashMediaSource.Factory(dataSource) .createMediaSource(MediaItem.fromUri(videoSource.url)) } @OptIn(UnstableApi::class) private fun swapVideoSourceHLS(videoSource: IHLSManifestSource) { Logger.i(TAG, "Loading VideoSource [HLS]"); - _lastVideoMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory() - .setUserAgent(DEFAULT_USER_AGENT)) + val dataSource = if(videoSource is JSSource && videoSource.hasRequestModifier) + videoSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); + _lastVideoMediaSource = HlsMediaSource.Factory(dataSource) .createMediaSource(MediaItem.fromUri(videoSource.url)); } @@ -455,15 +466,21 @@ abstract class FutoVideoPlayerBase : RelativeLayout { @OptIn(UnstableApi::class) private fun swapAudioSourceUrl(audioSource: IAudioUrlSource) { Logger.i(TAG, "Loading AudioSource [Url]"); - _lastAudioMediaSource = ProgressiveMediaSource.Factory(DefaultHttpDataSource.Factory() - .setUserAgent(DEFAULT_USER_AGENT)) + val dataSource = if(audioSource is JSSource && audioSource.hasRequestModifier) + audioSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); + _lastAudioMediaSource = ProgressiveMediaSource.Factory(dataSource) .createMediaSource(MediaItem.fromUri(audioSource.getAudioUrl())); } @OptIn(UnstableApi::class) private fun swapAudioSourceHLS(audioSource: IHLSManifestAudioSource) { Logger.i(TAG, "Loading AudioSource [HLS]"); - _lastAudioMediaSource = HlsMediaSource.Factory(DefaultHttpDataSource.Factory() - .setUserAgent(DEFAULT_USER_AGENT)) + val dataSource = if(audioSource is JSSource && audioSource.hasRequestModifier) + audioSource.getHttpDataSourceFactory() + else + DefaultHttpDataSource.Factory().setUserAgent(DEFAULT_USER_AGENT); + _lastAudioMediaSource = HlsMediaSource.Factory(dataSource) .createMediaSource(MediaItem.fromUri(audioSource.url)); } diff --git a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java index d153c440..4c99ccb9 100644 --- a/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java +++ b/app/src/main/java/com/futo/platformplayer/views/video/datasources/JSHttpDataSource.java @@ -11,6 +11,8 @@ import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.futo.platformplayer.api.media.models.modifier.IRequest; +import com.futo.platformplayer.api.media.models.modifier.IRequestModifier; import com.futo.platformplayer.api.media.platforms.js.models.JSRequestModifier; import androidx.media3.common.C; import androidx.media3.common.PlaybackException; @@ -60,7 +62,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { private int readTimeoutMs; private boolean allowCrossProtocolRedirects; private boolean keepPostFor302Redirects; - @Nullable private JSRequestModifier requestModifier = null; + @Nullable private IRequestModifier requestModifier = null; /** Creates an instance. */ public Factory() { @@ -83,7 +85,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { * @param requestModifier The request modifier that will be used, or {@code null} to use no request modifier * @return This factory. */ - public Factory setRequestModifier(@Nullable JSRequestModifier requestModifier) { + public Factory setRequestModifier(@Nullable IRequestModifier requestModifier) { this.requestModifier = requestModifier; return this; } @@ -228,7 +230,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { private int responseCode; private long bytesToRead; private long bytesRead; - @Nullable private JSRequestModifier requestModifier; + @Nullable private IRequestModifier requestModifier; private JSHttpDataSource( @Nullable String userAgent, @@ -238,7 +240,7 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { @Nullable RequestProperties defaultRequestProperties, @Nullable Predicate contentTypePredicate, boolean keepPostFor302Redirects, - @Nullable JSRequestModifier requestModifier) { + @Nullable IRequestModifier requestModifier) { super(/* isNetwork= */ true); this.userAgent = userAgent; this.connectTimeoutMillis = connectTimeoutMillis; @@ -574,8 +576,9 @@ public class JSHttpDataSource extends BaseDataSource implements HttpDataSource { String requestUrl = url.toString(); if (requestModifier != null) { - JSRequestModifier.IRequest result = requestModifier.modifyRequest(requestUrl, requestHeaders); - requestUrl = result.getUrl(); + IRequest result = requestModifier.modifyRequest(requestUrl, requestHeaders); + String modifiedUrl = result.getUrl(); + requestUrl = (modifiedUrl != null) ? modifiedUrl : requestUrl; requestHeaders = result.getHeaders(); } diff --git a/app/src/unstable/assets/sources/niconico b/app/src/unstable/assets/sources/niconico new file mode 160000 index 00000000..f031e10d --- /dev/null +++ b/app/src/unstable/assets/sources/niconico @@ -0,0 +1 @@ +Subproject commit f031e10d3852dd7138f2ed312c5e74466f23da32