Reduced amount of warnings.

This commit is contained in:
Koen 2023-12-11 11:51:43 +01:00
parent 150a7d5006
commit fa12f8277c
172 changed files with 1502 additions and 1603 deletions

View File

@ -7,8 +7,8 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/> <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>

View File

@ -169,7 +169,7 @@ private fun parseHextet(ipString: String, start: Int, end: Int): Short {
var hextet = 0 var hextet = 0
for (i in start until end) { for (i in start until end) {
hextet = hextet shl 4 hextet = hextet shl 4
hextet = hextet or ipString[i].digitToIntOrNull(16)!! ?: -1 hextet = hextet or ipString[i].digitToIntOrNull(16)!!
} }
return hextet.toShort() return hextet.toShort()
} }

View File

@ -27,7 +27,7 @@ fun <R> V8Value?.orDefault(default: R, handler: (V8Value)->R): R {
inline fun <reified T> V8Value.expectOrThrow(config: IV8PluginConfig, contextName: String): T { inline fun <reified T> V8Value.expectOrThrow(config: IV8PluginConfig, contextName: String): T {
if(this !is T) if(this !is T)
throw ScriptImplementationException(config, "Expected ${contextName} to be of type ${T::class.simpleName}, but found ${this::class.simpleName}"); throw ScriptImplementationException(config, "Expected ${contextName} to be of type ${T::class.simpleName}, but found ${this::class.simpleName}");
return this as T; return this;
} }
//Singles //Singles

View File

@ -2,15 +2,9 @@ package com.futo.platformplayer
import android.content.Context import android.content.Context
import android.webkit.CookieManager import android.webkit.CookieManager
import androidx.lifecycle.lifecycleScope
import androidx.work.Constraints
import androidx.work.Data import androidx.work.Data
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueInteger
import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.primitive.V8ValueString
import com.futo.platformplayer.activities.DeveloperActivity import com.futo.platformplayer.activities.DeveloperActivity
@ -36,7 +30,6 @@ import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.FragmentedStorageFileJson import com.futo.platformplayer.stores.FragmentedStorageFileJson
import com.futo.platformplayer.stores.db.types.DBHistory
import com.futo.platformplayer.views.fields.ButtonField import com.futo.platformplayer.views.fields.ButtonField
import com.futo.platformplayer.views.fields.FieldForm import com.futo.platformplayer.views.fields.FieldForm
import com.futo.platformplayer.views.fields.FormField import com.futo.platformplayer.views.fields.FormField
@ -44,11 +37,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.* import kotlinx.serialization.Contextual
import kotlinx.serialization.json.* import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.UUID
import java.util.concurrent.TimeUnit
import java.util.stream.IntStream.range import java.util.stream.IntStream.range
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -109,14 +103,14 @@ class SettingsDev : FragmentedStorageFileJson() {
StateApp.instance.scope.launch(Dispatchers.IO) { StateApp.instance.scope.launch(Dispatchers.IO) {
try { try {
val subsCache = val subsCache =
StateSubscriptions.instance.getSubscriptionsFeedWithExceptions(cacheScope = this)?.first; StateSubscriptions.instance.getSubscriptionsFeedWithExceptions(cacheScope = this).first;
var total = 0; var total = 0;
var page = 0; var page = 0;
var lastToast = System.currentTimeMillis(); var lastToast = System.currentTimeMillis();
while(subsCache!!.hasMorePages() && total < 5000) { while(subsCache.hasMorePages() && total < 5000) {
subsCache!!.nextPage(); subsCache.nextPage();
total += subsCache!!.getResults().size; total += subsCache.getResults().size;
page++; page++;
if(page % 10 == 0) if(page % 10 == 0)
@ -174,9 +168,9 @@ class SettingsDev : FragmentedStorageFileJson() {
var total = 0; var total = 0;
var page = 0; var page = 0;
var lastToast = System.currentTimeMillis(); var lastToast = System.currentTimeMillis();
while(subsCache!!.hasMorePages() && total < 5000) { while(subsCache.hasMorePages() && total < 5000) {
subsCache!!.nextPage(); subsCache.nextPage();
total += subsCache!!.getResults().size; total += subsCache.getResults().size;
page++; page++;
for(item in subsCache.getResults().filterIsInstance<IPlatformVideo>()) { for(item in subsCache.getResults().filterIsInstance<IPlatformVideo>()) {
@ -375,9 +369,9 @@ class SettingsDev : FragmentedStorageFileJson() {
@FormField(R.string.getHome, FieldForm.BUTTON, R.string.attempts_to_fetch_2_pages_from_getHome, 2) @FormField(R.string.getHome, FieldForm.BUTTON, R.string.attempts_to_fetch_2_pages_from_getHome, 2)
fun testV8Home() { fun testV8Home() {
runTestPlugin(_currentPlugin) { runTestPlugin(_currentPlugin) {
var home: IPager<IPlatformContent>? = null; var home: IPager<IPlatformContent>?;
var resultPage1: String = ""; val resultPage1: String;
var resultPage2: String = ""; val resultPage2: String;
val page1Time = measureTimeMillis { val page1Time = measureTimeMillis {
home = it.getHome(); home = it.getHome();
val results = home!!.getResults(); val results = home!!.getResults();

View File

@ -22,20 +22,25 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.Playlist import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.models.Subscription import com.futo.platformplayer.models.Subscription
import com.futo.platformplayer.parsers.HLS import com.futo.platformplayer.parsers.HLS
import com.futo.platformplayer.states.* import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StateMeta
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.views.LoaderView import com.futo.platformplayer.views.LoaderView
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuFilters
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuGroup import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuGroup
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
import com.futo.platformplayer.views.pills.RoundButton import com.futo.platformplayer.views.pills.RoundButton
import com.futo.platformplayer.views.pills.RoundButtonGroup import com.futo.platformplayer.views.pills.RoundButtonGroup
import com.futo.platformplayer.views.overlays.slideup.*
import com.futo.platformplayer.views.video.FutoVideoPlayerBase import com.futo.platformplayer.views.video.FutoVideoPlayerBase
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.lang.IllegalStateException
class UISlideOverlays { class UISlideOverlays {
companion object { companion object {
@ -242,7 +247,7 @@ class UISlideOverlays {
val audioSources = if(descriptor is VideoUnMuxedSourceDescriptor) descriptor.audioSources else null; val audioSources = if(descriptor is VideoUnMuxedSourceDescriptor) descriptor.audioSources else null;
val subtitleSources = video.subtitles; val subtitleSources = video.subtitles;
if(videoSources.size == 0 && (audioSources?.size ?: 0) == 0) { if(videoSources.isEmpty() && (audioSources?.size ?: 0) == 0) {
UIDialogs.toast(container.context.getString(R.string.no_downloads_available), false); UIDialogs.toast(container.context.getString(R.string.no_downloads_available), false);
return null; return null;
} }
@ -263,24 +268,30 @@ class UISlideOverlays {
videoSources videoSources
.filter { it.isDownloadable() } .filter { it.isDownloadable() }
.map { .map {
if (it is IVideoUrlSource) { when (it) {
SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "${it.width}x${it.height}", it, { is IVideoUrlSource -> {
selectedVideo = it SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "${it.width}x${it.height}", it, {
menu?.selectOption(videoSources, it); selectedVideo = it
if(selectedAudio != null || !requiresAudio) menu?.selectOption(videoSources, it);
menu?.setOk(container.context.getString(R.string.download)); if(selectedAudio != null || !requiresAudio)
}, false) menu?.setOk(container.context.getString(R.string.download));
} else if (it is IHLSManifestSource) { }, false)
SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "HLS", it, { }
showHlsPicker(video, it, it.url, container)
}, false) is IHLSManifestSource -> {
} else { SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "HLS", it, {
throw Exception("Unhandled source type") showHlsPicker(video, it, it.url, container)
}, false)
}
else -> {
throw Exception("Unhandled source type")
}
} }
}).flatten().toList() }).flatten().toList()
)); ));
if(Settings.instance.downloads.getDefaultVideoQualityPixels() > 0 && videoSources.size > 0) { if(Settings.instance.downloads.getDefaultVideoQualityPixels() > 0 && videoSources.isNotEmpty()) {
//TODO: Add HLS support here //TODO: Add HLS support here
selectedVideo = VideoHelper.selectBestVideoSource( selectedVideo = VideoHelper.selectBestVideoSource(
videoSources.filter { it is IVideoUrlSource && it.isDownloadable() }.asIterable(), videoSources.filter { it is IVideoUrlSource && it.isDownloadable() }.asIterable(),
@ -289,30 +300,30 @@ class UISlideOverlays {
) as IVideoUrlSource; ) as IVideoUrlSource;
} }
audioSources?.let { audioSources -> if (audioSources != null) {
items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.audio), audioSources, audioSources items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.audio), audioSources, audioSources
.filter { VideoHelper.isDownloadable(it) } .filter { VideoHelper.isDownloadable(it) }
.map { .map {
if (it is IAudioUrlSource) { when (it) {
SlideUpMenuItem(container.context, R.drawable.ic_music, it.name, "${it.bitrate}", it, { is IAudioUrlSource -> {
selectedAudio = it SlideUpMenuItem(container.context, R.drawable.ic_music, it.name, "${it.bitrate}", it, {
menu?.selectOption(audioSources, it); selectedAudio = it
menu?.setOk(container.context.getString(R.string.download)); menu?.selectOption(audioSources, it);
}, false); menu?.setOk(container.context.getString(R.string.download));
} else if (it is IHLSManifestAudioSource) { }, false);
SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "HLS Audio", it, { }
showHlsPicker(video, it, it.url, container)
}, false) is IHLSManifestAudioSource -> {
} else { SlideUpMenuItem(container.context, R.drawable.ic_movie, it.name, "HLS Audio", it, {
throw Exception("Unhandled source type") showHlsPicker(video, it, it.url, container)
}, false)
}
else -> {
throw Exception("Unhandled source type")
}
} }
})); }));
val asources = audioSources;
val preferredAudioSource = VideoHelper.selectBestAudioSource(asources.asIterable(),
FutoVideoPlayerBase.PREFERED_AUDIO_CONTAINERS,
Settings.instance.playback.getPrimaryLanguage(container.context),
if(Settings.instance.downloads.isHighBitrateDefault()) 99999999 else 1);
menu?.selectOption(asources, preferredAudioSource);
//TODO: Add HLS support here //TODO: Add HLS support here
selectedAudio = VideoHelper.selectBestAudioSource(audioSources.filter { it is IAudioUrlSource && it.isDownloadable() }.asIterable(), selectedAudio = VideoHelper.selectBestAudioSource(audioSources.filter { it is IAudioUrlSource && it.isDownloadable() }.asIterable(),
@ -321,10 +332,8 @@ class UISlideOverlays {
if(Settings.instance.downloads.isHighBitrateDefault()) 9999999 else 1) as IAudioUrlSource?; if(Settings.instance.downloads.isHighBitrateDefault()) 9999999 else 1) as IAudioUrlSource?;
} }
//ContentResolver is required for subtitles..
if(contentResolver != null && subtitleSources.isNotEmpty()) { if(contentResolver != null && subtitleSources.isNotEmpty()) {
items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.subtitles), subtitleSources, subtitleSources items.add(SlideUpMenuGroup(container.context, container.context.getString(R.string.subtitles), subtitleSources, subtitleSources.map {
.map {
SlideUpMenuItem(container.context, R.drawable.ic_edit, it.name, "", it, { SlideUpMenuItem(container.context, R.drawable.ic_edit, it.name, "", it, {
if (selectedSubtitle == it) { if (selectedSubtitle == it) {
selectedSubtitle = null; selectedSubtitle = null;
@ -334,7 +343,8 @@ class UISlideOverlays {
menu?.selectOption(subtitleSources, it); menu?.selectOption(subtitleSources, it);
} }
}, false); }, false);
})); })
);
} }
menu = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.download_video), null, true, items); menu = SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.download_video), null, true, items);
@ -645,10 +655,11 @@ class UISlideOverlays {
val visible = buttonGroup.getVisibleButtons().filter { !ignoreTags.contains(it.tagRef) }; val visible = buttonGroup.getVisibleButtons().filter { !ignoreTags.contains(it.tagRef) };
val hidden = buttonGroup.getInvisibleButtons().filter { !ignoreTags.contains(it.tagRef) }; val hidden = buttonGroup.getInvisibleButtons().filter { !ignoreTags.contains(it.tagRef) };
val views = arrayOf(hidden val views = arrayOf(
.map { btn -> SlideUpMenuItem(container.context, btn.iconResource, btn.text.text.toString(), "", "", { hidden
btn.handler?.invoke(btn); .map { btn -> SlideUpMenuItem(container.context, btn.iconResource, btn.text.text.toString(), "", "", {
}, true) as View }.toTypedArray() ?: arrayOf(), btn.handler?.invoke(btn);
}, true) as View }.toTypedArray(),
arrayOf(SlideUpMenuItem(container.context, R.drawable.ic_pin, container.context.getString(R.string.change_pins), container.context.getString(R.string.decide_which_buttons_should_be_pinned), "", { arrayOf(SlideUpMenuItem(container.context, R.drawable.ic_pin, container.context.getString(R.string.change_pins), container.context.getString(R.string.decide_which_buttons_should_be_pinned), "", {
showOrderOverlay(container, container.context.getString(R.string.select_your_pins_in_order), (visible + hidden).map { Pair(it.text.text.toString(), it.tagRef!!) }) { showOrderOverlay(container, container.context.getString(R.string.select_your_pins_in_order), (visible + hidden).map { Pair(it.text.text.toString(), it.tagRef!!) }) {
val selected = it val selected = it

View File

@ -143,6 +143,7 @@ fun InputStream.copyToOutputStream(inputStreamLength: Long, outputStream: Output
} }
} }
@Suppress("DEPRECATION")
fun Activity.setNavigationBarColorAndIcons() { fun Activity.setNavigationBarColorAndIcons() {
window.navigationBarColor = ContextCompat.getColor(this, android.R.color.black); window.navigationBarColor = ContextCompat.getColor(this, android.R.color.black);

View File

@ -5,13 +5,20 @@ import android.content.Intent
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.* import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.setNavigationBarColorAndIcons
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.states.StatePlugins
@ -194,7 +201,7 @@ class AddSourceActivity : AppCompatActivity() {
config.allowUrls, true) config.allowUrls, true)
) )
val pastelRed = resources.getColor(R.color.pastel_red); val pastelRed = ContextCompat.getColor(this, R.color.pastel_red);
for(warning in config.getWarnings(script)) for(warning in config.getWarnings(script))
_sourceWarnings.addView( _sourceWarnings.addView(

View File

@ -11,7 +11,6 @@ import com.futo.platformplayer.*
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.views.buttons.BigButton import com.futo.platformplayer.views.buttons.BigButton
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.journeyapps.barcodescanner.CaptureActivity
class AddSourceOptionsActivity : AppCompatActivity() { class AddSourceOptionsActivity : AppCompatActivity() {
lateinit var _buttonBack: ImageButton; lateinit var _buttonBack: ImageButton;

View File

@ -26,7 +26,7 @@ class DeveloperActivity : AppCompatActivity() {
_form = findViewById(R.id.settings_form); _form = findViewById(R.id.settings_form);
_form.fromObject(SettingsDev.instance); _form.fromObject(SettingsDev.instance);
_form.onChanged.subscribe { field, value -> _form.onChanged.subscribe { _, _ ->
_form.setObjectValues(); _form.setObjectValues();
SettingsDev.instance.save(); SettingsDev.instance.save();
}; };

View File

@ -2,7 +2,6 @@ package com.futo.platformplayer.activities
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
interface IWithResultLauncher { interface IWithResultLauncher {
fun launchForResult(intent: Intent, code: Int, handler: (ActivityResult)->Unit); fun launchForResult(intent: Intent, code: Int, handler: (ActivityResult)->Unit);

View File

@ -3,24 +3,22 @@ package com.futo.platformplayer.activities
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.webkit.ConsoleMessage
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebChromeClient
import android.webkit.WebView import android.webkit.WebView
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.platforms.js.SourceAuth import com.futo.platformplayer.api.media.platforms.js.SourceAuth
import com.futo.platformplayer.api.media.platforms.js.SourcePluginAuthConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginAuthConfig
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.others.LoginWebViewClient import com.futo.platformplayer.others.LoginWebViewClient
import com.futo.platformplayer.setNavigationBarColorAndIcons
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -102,7 +100,7 @@ class LoginActivity : AppCompatActivity() {
override fun finish() { override fun finish() {
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
_webView?.loadUrl("about:blank"); _webView.loadUrl("about:blank");
} }
_callback?.let { _callback?.let {
_callback = null; _callback = null;

View File

@ -26,7 +26,6 @@ import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.* import com.futo.platformplayer.*
import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.dialogs.ConnectCastingDialog
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
import com.futo.platformplayer.fragment.mainactivity.main.* import com.futo.platformplayer.fragment.mainactivity.main.*
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
@ -44,7 +43,6 @@ import com.futo.platformplayer.stores.v2.ManagedStore
import com.google.gson.JsonParser import com.google.gson.JsonParser
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.io.PrintWriter import java.io.PrintWriter
@ -649,7 +647,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
if(file.lowercase().endsWith(".json") || mime == "application/json") { if(file.lowercase().endsWith(".json") || mime == "application/json") {
var recon = String(data); var recon = String(data);
if(!recon.trim().startsWith("[")) if(!recon.trim().startsWith("["))
return handleUnknownJson(file, recon); return handleUnknownJson(recon);
val reconLines = Json.decodeFromString<List<String>>(recon); val reconLines = Json.decodeFromString<List<String>>(recon);
recon = reconLines.joinToString("\n"); recon = reconLines.joinToString("\n");
@ -671,7 +669,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
if(file.lowercase().endsWith(".json")) { if(file.lowercase().endsWith(".json")) {
val recon = String(readSharedFile(file)); val recon = String(readSharedFile(file));
if(!recon.startsWith("[")) if(!recon.startsWith("["))
return handleUnknownJson(file, recon); return handleUnknownJson(recon);
Logger.i(TAG, "Opened shared playlist reconstruction\n${recon}"); Logger.i(TAG, "Opened shared playlist reconstruction\n${recon}");
handleReconstruction(recon); handleReconstruction(recon);
@ -723,7 +721,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
} }
return false; return false;
} }
fun handleUnknownJson(name: String?, json: String): Boolean { fun handleUnknownJson(json: String): Boolean {
val context = this; val context = this;
@ -832,7 +830,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
val isStop: Boolean = lifecycle.currentState == Lifecycle.State.CREATED; val isStop: Boolean = lifecycle.currentState == Lifecycle.State.CREATED;
Logger.v(TAG, "onPictureInPictureModeChanged isInPictureInPictureMode=$isInPictureInPictureMode isStop=$isStop") Logger.v(TAG, "onPictureInPictureModeChanged isInPictureInPictureMode=$isInPictureInPictureMode isStop=$isStop")
_fragVideoDetail?.onPictureInPictureModeChanged(isInPictureInPictureMode, isStop, newConfig); _fragVideoDetail.onPictureInPictureModeChanged(isInPictureInPictureMode, isStop, newConfig);
Logger.v(TAG, "onPictureInPictureModeChanged Ready"); Logger.v(TAG, "onPictureInPictureModeChanged Ready");
} }
@ -889,7 +887,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
transaction = transaction.replace(R.id.fragment_main, segment); transaction = transaction.replace(R.id.fragment_main, segment);
val extraBottomDP = if(_fragVideoDetail.state == VideoDetailFragment.State.MINIMIZED) HEIGHT_VIDEO_MINIMIZED_DP else 0f
if (segment.hasBottomBar) { if (segment.hasBottomBar) {
if (!fragCurrent.hasBottomBar) if (!fragCurrent.hasBottomBar)
transaction = transaction.show(_fragBotBarMenu); transaction = transaction.show(_fragBotBarMenu);
@ -899,8 +896,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
transaction = transaction.hide(_fragBotBarMenu); transaction = transaction.hide(_fragBotBarMenu);
} }
transaction.commitNow(); transaction.commitNow();
} } else {
else {
//Special cases //Special cases
if(segment is VideoDetailFragment) { if(segment is VideoDetailFragment) {
_fragContainerVideoDetail.visibility = View.VISIBLE; _fragContainerVideoDetail.visibility = View.VISIBLE;

View File

@ -8,20 +8,19 @@ import android.widget.ImageButton
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.setNavigationBarColorAndIcons import com.futo.platformplayer.setNavigationBarColorAndIcons
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StatePolycentric
import com.futo.polycentric.core.* import com.futo.polycentric.core.KeyPair
import com.futo.polycentric.core.Process
import com.futo.polycentric.core.ProcessSecret
import com.futo.polycentric.core.SignedEvent
import com.futo.polycentric.core.Store
import com.futo.polycentric.core.base64UrlToByteArray
import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator
import com.journeyapps.barcodescanner.CaptureActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import userpackage.Protocol import userpackage.Protocol
import userpackage.Protocol.ExportBundle import userpackage.Protocol.ExportBundle

View File

@ -17,7 +17,6 @@ import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.dialogs.CommentDialog
import com.futo.platformplayer.dp import com.futo.platformplayer.dp
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
@ -30,7 +29,6 @@ import com.futo.platformplayer.views.buttons.BigButton
import com.futo.polycentric.core.Store import com.futo.polycentric.core.Store
import com.futo.polycentric.core.Synchronization import com.futo.polycentric.core.Synchronization
import com.futo.polycentric.core.SystemState import com.futo.polycentric.core.SystemState
import com.futo.polycentric.core.toURLInfoDataLink
import com.futo.polycentric.core.toURLInfoSystemLinkUrl import com.futo.polycentric.core.toURLInfoSystemLinkUrl
import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.ImagePicker
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -250,7 +248,7 @@ class PolycentricProfileActivity : AppCompatActivity() {
} }
private fun getMimeType(contentResolver: ContentResolver, uri: Uri): String? { private fun getMimeType(contentResolver: ContentResolver, uri: Uri): String? {
var mimeType: String? = null; var mimeType: String?;
// Try to get MIME type from the content URI // Try to get MIME type from the content URI
mimeType = contentResolver.getType(uri); mimeType = contentResolver.getType(uri);

View File

@ -49,7 +49,7 @@ class SettingsActivity : AppCompatActivity(), IWithResultLauncher {
_loaderView = findViewById(R.id.loader); _loaderView = findViewById(R.id.loader);
overlay = findViewById(R.id.overlay_container); overlay = findViewById(R.id.overlay_container);
_form.onChanged.subscribe { field, value -> _form.onChanged.subscribe { field, _ ->
Logger.i("SettingsActivity", "Setting [${field.field?.name}] changed, saving"); Logger.i("SettingsActivity", "Setting [${field.field?.name}] changed, saving");
_form.setObjectValues(); _form.setObjectValues();
Settings.instance.save(); Settings.instance.save();

View File

@ -13,8 +13,6 @@ import okhttp3.Response
import okhttp3.ResponseBody import okhttp3.ResponseBody
import okhttp3.WebSocket import okhttp3.WebSocket
import okhttp3.WebSocketListener import okhttp3.WebSocketListener
import java.util.Dictionary
import java.util.concurrent.TimeUnit
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
open class ManagedHttpClient { open class ManagedHttpClient {
@ -60,7 +58,7 @@ open class ManagedHttpClient {
val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder() val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
.url(url); .url(url);
if(user_agent != null && !user_agent.isEmpty() && !headers.any { it.key.lowercase() == "user-agent" }) if(user_agent.isNotEmpty() && !headers.any { it.key.lowercase() == "user-agent" })
requestBuilder.addHeader("User-Agent", user_agent) requestBuilder.addHeader("User-Agent", user_agent)
for (pair in headers.entries) for (pair in headers.entries)
@ -137,7 +135,7 @@ open class ManagedHttpClient {
val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder() val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
.method(request.method, requestBody) .method(request.method, requestBody)
.url(request.url); .url(request.url);
if(user_agent != null && !user_agent.isEmpty() && !request.headers.any { it.key.lowercase() == "user-agent" }) if(user_agent.isNotEmpty() && !request.headers.any { it.key.lowercase() == "user-agent" })
requestBuilder.addHeader("User-Agent", user_agent) requestBuilder.addHeader("User-Agent", user_agent)
for (pair in request.headers.entries) for (pair in request.headers.entries)
@ -148,7 +146,7 @@ open class ManagedHttpClient {
val time = measureTimeMillis { val time = measureTimeMillis {
val call = client.newCall(requestBuilder.build()); val call = client.newCall(requestBuilder.build());
request.onCallCreated?.emit(call); request.onCallCreated.emit(call);
response = call.execute() response = call.execute()
resp = Response( resp = Response(
response.code, response.code,

View File

@ -1,11 +1,11 @@
package com.futo.platformplayer.api.http.server package com.futo.platformplayer.api.http.server
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.http.server.exceptions.EmptyRequestException import com.futo.platformplayer.api.http.server.exceptions.EmptyRequestException
import com.futo.platformplayer.api.http.server.handlers.HttpFuntionHandler import com.futo.platformplayer.api.http.server.handlers.HttpFuntionHandler
import com.futo.platformplayer.api.http.server.handlers.HttpHandler import com.futo.platformplayer.api.http.server.handlers.HttpHandler
import com.futo.platformplayer.api.http.server.handlers.HttpOptionsAllowHandler import com.futo.platformplayer.api.http.server.handlers.HttpOptionsAllowHandler
import com.futo.platformplayer.logging.Logger
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.OutputStream import java.io.OutputStream
import java.lang.reflect.Field import java.lang.reflect.Field
@ -14,11 +14,10 @@ import java.net.InetAddress
import java.net.NetworkInterface import java.net.NetworkInterface
import java.net.ServerSocket import java.net.ServerSocket
import java.net.Socket import java.net.Socket
import java.util.* import java.util.UUID
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.stream.IntStream.range import java.util.stream.IntStream.range
import kotlin.collections.HashMap
class ManagedHttpServer(private val _requestedPort: Int = 0) { class ManagedHttpServer(private val _requestedPort: Int = 0) {
private val _client : ManagedHttpClient = ManagedHttpClient(); private val _client : ManagedHttpClient = ManagedHttpClient();
@ -212,13 +211,13 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
addHandler(HttpFuntionHandler("GET", getMethod.second.path) { getMethod.first.invoke(obj, it) }).apply { addHandler(HttpFuntionHandler("GET", getMethod.second.path) { getMethod.first.invoke(obj, it) }).apply {
if(!getMethod.second.contentType.isEmpty()) if(!getMethod.second.contentType.isEmpty())
this.withContentType(getMethod.second.contentType); this.withContentType(getMethod.second.contentType);
}.withContentType(getMethod.second.contentType ?: ""); }.withContentType(getMethod.second.contentType);
for(postMethod in postMethods) for(postMethod in postMethods)
if(postMethod.first.parameterTypes.firstOrNull() == HttpContext::class.java && postMethod.first.parameterCount == 1) if(postMethod.first.parameterTypes.firstOrNull() == HttpContext::class.java && postMethod.first.parameterCount == 1)
addHandler(HttpFuntionHandler("POST", postMethod.second.path) { postMethod.first.invoke(obj, it) }).apply { addHandler(HttpFuntionHandler("POST", postMethod.second.path) { postMethod.first.invoke(obj, it) }).apply {
if(!postMethod.second.contentType.isEmpty()) if(!postMethod.second.contentType.isEmpty())
this.withContentType(postMethod.second.contentType); this.withContentType(postMethod.second.contentType);
}.withContentType(postMethod.second.contentType ?: ""); }.withContentType(postMethod.second.contentType);
for(getField in getFields) { for(getField in getFields) {
getField.first.isAccessible = true; getField.first.isAccessible = true;
@ -232,13 +231,13 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
} }
else else
it.respondCode(204); it.respondCode(204);
}).withContentType(getField.second.contentType ?: ""); }).withContentType(getField.second.contentType);
} }
} }
private fun keepAliveLoop(requestReader: BufferedInputStream, responseStream: OutputStream, requestId: String, handler: (HttpContext)->Unit) { private fun keepAliveLoop(requestReader: BufferedInputStream, responseStream: OutputStream, requestId: String, handler: (HttpContext)->Unit) {
val stopCount = _stopCount; val stopCount = _stopCount;
var keepAlive = false; var keepAlive: Boolean;
var requestsMax = 0; var requestsMax = 0;
var requestsTotal = 0; var requestsTotal = 0;
do { do {
@ -288,11 +287,13 @@ class ManagedHttpServer(private val _requestedPort: Int = 0) {
for (intf in NetworkInterface.getNetworkInterfaces()) { for (intf in NetworkInterface.getNetworkInterfaces()) {
for (addr in intf.inetAddresses) { for (addr in intf.inetAddresses) {
if (!addr.isLoopbackAddress) { if (!addr.isLoopbackAddress) {
val ipString: String = addr.hostAddress; val ipString: String = addr.hostAddress ?: continue
val isIPv4 = ipString.indexOf(':') < 0; val isIPv4 = ipString.indexOf(':') < 0
if (!isIPv4) if (!isIPv4) {
continue; continue
addresses.add(addr); }
addresses.add(addr)
} }
} }
} }

View File

@ -1,6 +1,3 @@
package com.futo.platformplayer.api.http.server.exceptions package com.futo.platformplayer.api.http.server.exceptions
import java.net.SocketTimeoutException
import java.util.concurrent.TimeoutException
class EmptyRequestException(msg: String) : Exception(msg) {} class EmptyRequestException(msg: String) : Exception(msg) {}

View File

@ -10,12 +10,9 @@ 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.ILiveChatWindowDescriptor
import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent 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.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.models.playlists.IPlatformPlaylistDetails
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.models.ImageVariable import com.futo.platformplayer.models.ImageVariable
import com.futo.platformplayer.models.Playlist
/** /**
* A temporary class that caches video results * A temporary class that caches video results
@ -44,8 +41,7 @@ class CachedPlatformClient : IPlatformClient {
var result = _cache.get(url); var result = _cache.get(url);
if(result == null) { if(result == null) {
result = _client.getContentDetails(url); result = _client.getContentDetails(url);
if (result != null) _cache.put(url, result);
_cache.put(url, result);
} }
return result; return result;
} }

View File

@ -10,11 +10,9 @@ 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.ILiveChatWindowDescriptor
import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent 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.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.models.playlists.IPlatformPlaylistDetails
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.models.ImageVariable import com.futo.platformplayer.models.ImageVariable
import com.futo.platformplayer.models.Playlist
/** /**
* A client for a specific platform * A client for a specific platform

View File

@ -9,7 +9,6 @@ import com.caverock.androidsvg.SVG
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
import com.futo.platformplayer.api.media.models.live.LiveEventComment import com.futo.platformplayer.api.media.models.live.LiveEventComment
import com.futo.platformplayer.api.media.models.live.LiveEventDonation
import com.futo.platformplayer.api.media.models.live.LiveEventEmojis import com.futo.platformplayer.api.media.models.live.LiveEventEmojis
import com.futo.platformplayer.api.media.platforms.js.models.JSLiveEventPager import com.futo.platformplayer.api.media.platforms.js.models.JSLiveEventPager
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
@ -195,7 +194,7 @@ class LiveChatManager {
fun getEmojiDrawable(emoji: String, cb: (drawable: Drawable?)->Unit) { fun getEmojiDrawable(emoji: String, cb: (drawable: Drawable?)->Unit) {
var drawable: Drawable? = null; var drawable: Drawable? = null;
var url: String? = null; var url: String?;
synchronized(_cache_lock) { synchronized(_cache_lock) {
url = _cache_urls[emoji]; url = _cache_urls[emoji];
if(url != null) if(url != null)

View File

@ -20,7 +20,7 @@ class PlatformMultiClientPool {
val pool = synchronized(_clientPools) { val pool = synchronized(_clientPools) {
if(!_clientPools.containsKey(parentClient)) if(!_clientPools.containsKey(parentClient))
_clientPools[parentClient] = PlatformClientPool(parentClient, _name).apply { _clientPools[parentClient] = PlatformClientPool(parentClient, _name).apply {
this.onDead.subscribe { client, pool -> this.onDead.subscribe { _, pool ->
synchronized(_clientPools) { synchronized(_clientPools) {
if(_clientPools[parentClient] == pool) if(_clientPools[parentClient] == pool)
_clientPools.remove(parentClient); _clientPools.remove(parentClient);

View File

@ -64,7 +64,6 @@ class FilterGroup(
val isMultiSelect: Boolean, val isMultiSelect: Boolean,
val id: String? = null val id: String? = null
) { ) {
@kotlinx.serialization.Transient
val idOrName: String get() = id ?: name; val idOrName: String get() = id ?: name;
companion object { companion object {

View File

@ -1,31 +1,23 @@
package com.futo.platformplayer.api.media.models.live package com.futo.platformplayer.api.media.models.live
import com.caoccao.javet.values.V8Value
import com.caoccao.javet.values.reference.V8ValueObject import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
import com.futo.platformplayer.api.media.models.ratings.RatingLikes
import com.futo.platformplayer.api.media.models.ratings.RatingScaler
import com.futo.platformplayer.api.media.models.ratings.RatingType
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.getOrThrow
import com.futo.platformplayer.orDefault
interface IPlatformLiveEvent { interface IPlatformLiveEvent {
val type : LiveEventType; val type : LiveEventType;
companion object { companion object {
fun fromV8(config: IV8PluginConfig, obj: V8ValueObject, contextName: String = "Unknown") : IPlatformLiveEvent { fun fromV8(config: IV8PluginConfig, obj: V8ValueObject, contextName: String = "LiveEvent") : IPlatformLiveEvent {
val contextName = "LiveEvent"; val t = LiveEventType.fromInt(obj.getOrThrow<Int>(config, "type", contextName));
val type = LiveEventType.fromInt(obj.getOrThrow<Int>(config, "type", contextName)); return when(t) {
return when(type) {
LiveEventType.COMMENT -> LiveEventComment.fromV8(config, obj); LiveEventType.COMMENT -> LiveEventComment.fromV8(config, obj);
LiveEventType.EMOJIS -> LiveEventEmojis.fromV8(config, obj); LiveEventType.EMOJIS -> LiveEventEmojis.fromV8(config, obj);
LiveEventType.DONATION -> LiveEventDonation.fromV8(config, obj); LiveEventType.DONATION -> LiveEventDonation.fromV8(config, obj);
LiveEventType.VIEWCOUNT -> LiveEventViewCount.fromV8(config, obj); LiveEventType.VIEWCOUNT -> LiveEventViewCount.fromV8(config, obj);
LiveEventType.RAID -> LiveEventRaid.fromV8(config, obj); LiveEventType.RAID -> LiveEventRaid.fromV8(config, obj);
else -> throw NotImplementedError("Unknown type ${type}"); else -> throw NotImplementedError("Unknown type $t");
} }
} }
} }

View File

@ -1,9 +1,6 @@
package com.futo.platformplayer.api.media.models.playlists package com.futo.platformplayer.api.media.models.playlists
import com.futo.platformplayer.api.media.models.Thumbnails
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.structures.IPager
interface IPlatformPlaylist : IPlatformContent { interface IPlatformPlaylist : IPlatformContent {
val thumbnail: String?; val thumbnail: String?;

View File

@ -2,10 +2,6 @@ package com.futo.platformplayer.api.media.models.post
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.*
import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
/** /**
* A detailed video model with data including video/audio sources * A detailed video model with data including video/audio sources

View File

@ -14,14 +14,13 @@ interface IRating {
companion object { companion object {
fun fromV8OrDefault(config: IV8PluginConfig, obj: V8Value?, default: IRating) = obj.orDefault(default) { fromV8(config, it as V8ValueObject) }; fun fromV8OrDefault(config: IV8PluginConfig, obj: V8Value?, default: IRating) = obj.orDefault(default) { fromV8(config, it as V8ValueObject) };
fun fromV8(config: IV8PluginConfig, obj: V8ValueObject, contextName: String = "Unknown") : IRating { fun fromV8(config: IV8PluginConfig, obj: V8ValueObject, contextName: String = "Rating") : IRating {
val contextName = "Rating"; val t = RatingType.fromInt(obj.getOrThrow<Int>(config, "type", contextName));
val type = RatingType.fromInt(obj.getOrThrow<Int>(config, "type", contextName)); return when(t) {
return when(type) {
RatingType.LIKES -> RatingLikes.fromV8(config, obj); RatingType.LIKES -> RatingLikes.fromV8(config, obj);
RatingType.LIKEDISLIKES -> RatingLikeDislikes.fromV8(config, obj); RatingType.LIKEDISLIKES -> RatingLikeDislikes.fromV8(config, obj);
RatingType.SCALE -> RatingScaler.fromV8(config, obj); RatingType.SCALE -> RatingScaler.fromV8(config, obj);
else -> throw NotImplementedError("Unknown type ${type}"); else -> throw NotImplementedError("Unknown type $t");
} }
} }
} }

View File

@ -1,8 +1,6 @@
package com.futo.platformplayer.api.media.models.subtitles package com.futo.platformplayer.api.media.models.subtitles
import android.net.Uri import android.net.Uri
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
interface ISubtitleSource { interface ISubtitleSource {
val name: String; val name: String;

View File

@ -1,13 +1,12 @@
package com.futo.platformplayer.api.media.models.video package com.futo.platformplayer.api.media.models.video
import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.models.comments.IPlatformComment
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
import com.futo.platformplayer.api.media.models.ratings.IRating import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
import com.futo.platformplayer.api.media.structures.IPager
/** /**
* A detailed video model with data including video/audio sources * A detailed video model with data including video/audio sources

View File

@ -8,6 +8,5 @@ import com.futo.platformplayer.api.media.models.streams.sources.VideoUrlSource
class SerializedVideoMuxedSourceDescriptor( class SerializedVideoMuxedSourceDescriptor(
val _videoSources: Array<VideoUrlSource> val _videoSources: Array<VideoUrlSource>
): VideoMuxedSourceDescriptor(), ISerializedVideoSourceDescriptor { ): VideoMuxedSourceDescriptor(), ISerializedVideoSourceDescriptor {
@kotlinx.serialization.Transient
override val videoSources: Array<IVideoSource> get() = _videoSources.map { it }.toTypedArray(); override val videoSources: Array<IVideoSource> get() = _videoSources.map { it }.toTypedArray();
}; };

View File

@ -1,15 +1,16 @@
package com.futo.platformplayer.api.media.models.video package com.futo.platformplayer.api.media.models.video
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.media.models.streams.sources.AudioUrlSource
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.streams.sources.VideoUrlSource
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
class SerializedVideoNonMuxedSourceDescriptor( class SerializedVideoNonMuxedSourceDescriptor(
val _videoSources: Array<VideoUrlSource>, val _videoSources: Array<VideoUrlSource>,
val _audioSources: Array<AudioUrlSource> val _audioSources: Array<AudioUrlSource>
): VideoUnMuxedSourceDescriptor(), ISerializedVideoSourceDescriptor { ): VideoUnMuxedSourceDescriptor(), ISerializedVideoSourceDescriptor {
@kotlinx.serialization.Transient
override val videoSources: Array<IVideoSource> get() = _videoSources.map { it }.toTypedArray(); override val videoSources: Array<IVideoSource> get() = _videoSources.map { it }.toTypedArray();
@kotlinx.serialization.Transient
override val audioSources: Array<IAudioSource> get() = _audioSources.map { it }.toTypedArray(); override val audioSources: Array<IAudioSource> get() = _audioSources.map { it }.toTypedArray();
}; };

View File

@ -1,14 +1,14 @@
package com.futo.platformplayer.api.media.platforms.js package com.futo.platformplayer.api.media.platforms.js
import android.content.Context import android.content.Context
import com.futo.platformplayer.states.StateDeveloper
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.api.media.models.comments.IPlatformComment 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.IPlatformContent
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import java.util.* import com.futo.platformplayer.states.StateDeveloper
import java.util.UUID
class DevJSClient : JSClient { class DevJSClient : JSClient {
override val id: String override val id: String
@ -26,8 +26,8 @@ class DevJSClient : JSClient {
_captcha = captcha; _captcha = captcha;
this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5); this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5);
onCaptchaException.subscribe { client, captcha -> onCaptchaException.subscribe { client, c ->
StateApp.instance.handleCaptchaException(client, captcha); StateApp.instance.handleCaptchaException(client, c);
} }
} }
//TODO: Misisng auth/captcha pass on purpose? //TODO: Misisng auth/captcha pass on purpose?
@ -37,8 +37,8 @@ class DevJSClient : JSClient {
_captcha = captcha; _captcha = captcha;
this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5); this.devID = devID ?: UUID.randomUUID().toString().substring(0, 5);
onCaptchaException.subscribe { client, captcha -> onCaptchaException.subscribe { client, c ->
StateApp.instance.handleCaptchaException(client, captcha); StateApp.instance.handleCaptchaException(client, c);
} }
} }

View File

@ -4,12 +4,9 @@ import android.content.Context
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value
import com.caoccao.javet.values.primitive.V8ValueBoolean import com.caoccao.javet.values.primitive.V8ValueBoolean
import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueInteger
import com.caoccao.javet.values.primitive.V8ValueNull
import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.primitive.V8ValueString
import com.caoccao.javet.values.reference.V8ValueArray import com.caoccao.javet.values.reference.V8ValueArray
import com.caoccao.javet.values.reference.V8ValueObject import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.PlatformClientCapabilities import com.futo.platformplayer.api.media.PlatformClientCapabilities
import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.PlatformAuthorLink
@ -23,23 +20,38 @@ 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.live.IPlatformLiveEvent
import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails import com.futo.platformplayer.api.media.models.playlists.IPlatformPlaylistDetails
import com.futo.platformplayer.api.media.platforms.js.internal.* import com.futo.platformplayer.api.media.platforms.js.internal.JSCallDocs
import com.futo.platformplayer.api.media.platforms.js.models.* import com.futo.platformplayer.api.media.platforms.js.internal.JSDocs
import com.futo.platformplayer.api.media.platforms.js.internal.JSDocsParameter
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
import com.futo.platformplayer.api.media.platforms.js.internal.JSOptional
import com.futo.platformplayer.api.media.platforms.js.internal.JSParameterDocs
import com.futo.platformplayer.api.media.platforms.js.models.IJSContentDetails
import com.futo.platformplayer.api.media.platforms.js.models.JSChannel
import com.futo.platformplayer.api.media.platforms.js.models.JSChannelPager
import com.futo.platformplayer.api.media.platforms.js.models.JSChapter
import com.futo.platformplayer.api.media.platforms.js.models.JSComment
import com.futo.platformplayer.api.media.platforms.js.models.JSCommentPager
import com.futo.platformplayer.api.media.platforms.js.models.JSContentPager
import com.futo.platformplayer.api.media.platforms.js.models.JSLiveChatWindowDescriptor
import com.futo.platformplayer.api.media.platforms.js.models.JSLiveEventPager
import com.futo.platformplayer.api.media.platforms.js.models.JSPlaybackTracker
import com.futo.platformplayer.api.media.platforms.js.models.JSPlaylistDetails
import com.futo.platformplayer.api.media.structures.EmptyPager import com.futo.platformplayer.api.media.structures.EmptyPager
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.engine.exceptions.PluginEngineException import com.futo.platformplayer.engine.exceptions.PluginEngineException
import com.futo.platformplayer.engine.exceptions.PluginEngineStoppedException
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptValidationException import com.futo.platformplayer.engine.exceptions.ScriptValidationException
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.ImageVariable import com.futo.platformplayer.models.ImageVariable
import com.futo.platformplayer.states.AnnouncementType import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.OffsetDateTime import java.time.OffsetDateTime
@ -50,7 +62,7 @@ open class JSClient : IPlatformClient {
val config: SourcePluginConfig; val config: SourcePluginConfig;
protected val _context: Context; protected val _context: Context;
private val _plugin: V8Plugin; private val _plugin: V8Plugin;
private val plugin: V8Plugin get() = _plugin ?: throw IllegalStateException("Client not enabled"); private val plugin: V8Plugin get() = _plugin
var descriptor: SourcePluginDescriptor var descriptor: SourcePluginDescriptor
private set; private set;

View File

@ -5,9 +5,8 @@ import com.futo.platformplayer.SignatureProvider
import com.futo.platformplayer.api.media.Serializer import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.states.StatePlugins
import kotlinx.serialization.decodeFromString
import java.net.URL import java.net.URL
import java.util.* import java.util.UUID
@kotlinx.serialization.Serializable @kotlinx.serialization.Serializable
class SourcePluginConfig( class SourcePluginConfig(
@ -149,7 +148,6 @@ class SourcePluginConfig(
val warningDialog: String? = null, val warningDialog: String? = null,
val options: List<String>? = null val options: List<String>? = null
) { ) {
@kotlinx.serialization.Transient
val variableOrName: String get() = variable ?: name; val variableOrName: String get() = variable ?: name;
} }
} }

View File

@ -2,7 +2,6 @@ package com.futo.platformplayer.api.media.platforms.js
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.serializers.FlexibleBooleanSerializer
import com.futo.platformplayer.views.fields.DropdownFieldOptions import com.futo.platformplayer.views.fields.DropdownFieldOptions
import com.futo.platformplayer.views.fields.FieldForm import com.futo.platformplayer.views.fields.FieldForm
import com.futo.platformplayer.views.fields.FormField import com.futo.platformplayer.views.fields.FormField
@ -107,9 +106,9 @@ class SourcePluginDescriptor {
fun loadDefaults(config: SourcePluginConfig) { fun loadDefaults(config: SourcePluginConfig) {
if(tabEnabled.enableHome == null) if(tabEnabled.enableHome == null)
tabEnabled.enableHome = config.enableInHome ?: true; tabEnabled.enableHome = config.enableInHome
if(tabEnabled.enableSearch == null) if(tabEnabled.enableSearch == null)
tabEnabled.enableSearch = config.enableInSearch ?: true; tabEnabled.enableSearch = config.enableInSearch
} }
} }

View File

@ -1,7 +1,5 @@
package com.futo.platformplayer.api.media.platforms.js.internal package com.futo.platformplayer.api.media.platforms.js.internal
import android.net.Uri
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.platforms.js.JSClient 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.SourceAuth
@ -47,10 +45,7 @@ class JSHttpClient : ManagedHttpClient {
override fun clone(): ManagedHttpClient { override fun clone(): ManagedHttpClient {
val newClient = JSHttpClient(_jsClient, _auth); val newClient = JSHttpClient(_jsClient, _auth);
newClient._currentCookieMap = if(_currentCookieMap != null) newClient._currentCookieMap = HashMap(_currentCookieMap.toList().associate { Pair(it.first, HashMap(it.second)) })
HashMap(_currentCookieMap.toList().associate { Pair(it.first, HashMap(it.second)) })
else
hashMapOf();
return newClient; return newClient;
} }
@ -69,10 +64,10 @@ class JSHttpClient : ManagedHttpClient {
} }
if(doApplyCookies) { if(doApplyCookies) {
if (!_currentCookieMap.isNullOrEmpty()) { if (_currentCookieMap.isNotEmpty()) {
val cookiesToApply = hashMapOf<String, String>(); val cookiesToApply = hashMapOf<String, String>();
synchronized(_currentCookieMap!!) { synchronized(_currentCookieMap) {
for(cookie in _currentCookieMap!! for(cookie in _currentCookieMap
.filter { domain.matchesDomain(it.key) } .filter { domain.matchesDomain(it.key) }
.flatMap { it.value.toList() }) .flatMap { it.value.toList() })
cookiesToApply[cookie.first] = cookie.second; cookiesToApply[cookie.first] = cookie.second;
@ -92,11 +87,11 @@ class JSHttpClient : ManagedHttpClient {
} }
if(_jsClient != null) if(_jsClient != null)
_jsClient?.validateUrlOrThrow(request.url.toString()); _jsClient.validateUrlOrThrow(request.url.toString());
else if (_jsConfig != null && !_jsConfig.isUrlAllowed(request.url.toString())) else if (_jsConfig != null && !_jsConfig.isUrlAllowed(request.url.toString()))
throw ScriptImplementationException(_jsConfig, "Attempted to access non-whitelisted url: ${request.url.toString()}\nAdd it to your config"); throw ScriptImplementationException(_jsConfig, "Attempted to access non-whitelisted url: ${request.url.toString()}\nAdd it to your config");
return newBuilder?.let { it.build() } ?: request; return newBuilder?.build() ?: request;
} }
override fun afterRequest(resp: okhttp3.Response): okhttp3.Response { override fun afterRequest(resp: okhttp3.Response): okhttp3.Response {
@ -107,84 +102,48 @@ class JSHttpClient : ManagedHttpClient {
"." + domainParts.drop(domainParts.size - 2).joinToString("."); "." + domainParts.drop(domainParts.size - 2).joinToString(".");
for (header in resp.headers) { for (header in resp.headers) {
if ((_auth != null || _currentCookieMap.isNotEmpty()) && header.first.lowercase() == "set-cookie") { if ((_auth != null || _currentCookieMap.isNotEmpty()) && header.first.lowercase() == "set-cookie") {
//val newCookies = cookieStringToMap(header.second.split("; "));
val cookie = cookieStringToPair(header.second); val cookie = cookieStringToPair(header.second);
//for (cookie in newCookies) { var cookieValue = cookie.second;
var cookieValue = cookie.second; var domainToUse = domain;
var domainToUse = domain;
if (!cookie.first.isNullOrEmpty() && !cookie.second.isNullOrEmpty()) { if (cookie.first.isNotEmpty() && cookie.second.isNotEmpty()) {
val cookieParts = cookie.second.split(";"); val cookieParts = cookie.second.split(";");
if (cookieParts.size == 0) if (cookieParts.size == 0)
continue; continue;
cookieValue = cookieParts[0].trim(); cookieValue = cookieParts[0].trim();
val cookieVariables = cookieParts.drop(1).map { val cookieVariables = cookieParts.drop(1).map {
val splitIndex = it.indexOf("="); val splitIndex = it.indexOf("=");
if (splitIndex < 0) if (splitIndex < 0)
return@map Pair(it.trim().lowercase(), ""); return@map Pair(it.trim().lowercase(), "");
return@map Pair<String, String>( return@map Pair<String, String>(
it.substring(0, splitIndex).lowercase().trim(), it.substring(0, splitIndex).lowercase().trim(),
it.substring(splitIndex + 1).trim() it.substring(splitIndex + 1).trim()
); );
}.toMap(); }.toMap();
domainToUse = if (cookieVariables.containsKey("domain")) domainToUse = if (cookieVariables.containsKey("domain"))
cookieVariables["domain"]!!.lowercase(); cookieVariables["domain"]!!.lowercase();
else defaultCookieDomain; else defaultCookieDomain;
} }
val cookieMap = if (_currentCookieMap!!.containsKey(domainToUse)) val cookieMap = if (_currentCookieMap.containsKey(domainToUse))
_currentCookieMap!![domainToUse]!!; _currentCookieMap[domainToUse]!!;
else { else {
val newMap = hashMapOf<String, String>(); val newMap = hashMapOf<String, String>();
_currentCookieMap!!.put(domainToUse, newMap) _currentCookieMap[domainToUse] = newMap
newMap; newMap;
} }
if(cookieMap.containsKey(cookie.first) || doAllowNewCookies) if(cookieMap.containsKey(cookie.first) || doAllowNewCookies)
cookieMap.put(cookie.first, cookieValue); cookieMap[cookie.first] = cookieValue;
//}
} }
} }
} }
return resp; return resp;
} }
private fun cookieStringToMap(parts: List<String>): Map<String, String> {
val map = hashMapOf<String, String>();
for(cookie in parts) {
val pair = cookieStringToPair(cookie)
map.put(pair.first, pair.second);
}
return map;
}
private fun cookieStringToPair(cookie: String): Pair<String, String> { private fun cookieStringToPair(cookie: String): Pair<String, String> {
val cookieKey = cookie.substring(0, cookie.indexOf("=")); val cookieKey = cookie.substring(0, cookie.indexOf("="));
val cookieVal = cookie.substring(cookie.indexOf("=") + 1); val cookieVal = cookie.substring(cookie.indexOf("=") + 1);
return Pair(cookieKey.trim(), cookieVal.trim()); return Pair(cookieKey.trim(), cookieVal.trim());
} }
//Prints out code for test reproduction..
fun printTestCode(url: String, body: ByteArray?, headers: Map<String, String>, cookieString: String, allHeaders: Map<String, String>? = null) {
var code = "Code: \n";
code += "\nurl = \"${url}\";";
if(body != null)
code += "\nbody = \"${String(body).replace("\"", "\\\"")}\";";
if(headers != null)
for(header in headers) {
code += "\nclient.Headers.Add(\"${header.key}\", \"${header.value}\");";
}
if(cookieString != null)
code += "\nclient.Headers.Add(\"Cookie\", \"${cookieString}\");";
if(allHeaders != null) {
code += "\n//OTHER HEADERS:"
for (header in allHeaders) {
code += "\nclient.Headers.Add(\"${header.key}\", \"${header.value}\");";
}
}
Logger.i("Testing", code);
}
} }

View File

@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")
package com.futo.platformplayer.api.media.platforms.js.models.sources package com.futo.platformplayer.api.media.platforms.js.models.sources
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value

View File

@ -1,7 +1,5 @@
package com.futo.platformplayer.api.media.structures package com.futo.platformplayer.api.media.structures
import kotlinx.coroutines.CoroutineScope
/** /**
* A Pager interface that implements a suspended manner of nextPage * A Pager interface that implements a suspended manner of nextPage
*/ */

View File

@ -1,6 +1,5 @@
package com.futo.platformplayer.api.media.structures package com.futo.platformplayer.api.media.structures
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
/** /**

View File

@ -5,6 +5,7 @@ import com.futo.platformplayer.api.media.structures.ReusablePager.Companion.asRe
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
/** /**
@ -25,6 +26,7 @@ abstract class MultiRefreshPager<T>: IRefreshPager<T>, IPager<T> {
private val _pending: MutableList<Deferred<IPager<T>?>>; private val _pending: MutableList<Deferred<IPager<T>?>>;
@OptIn(ExperimentalCoroutinesApi::class)
constructor(pagers: List<IPager<T>>, pendingPagers: List<Deferred<IPager<T>?>>, placeholderPagers: List<IPager<T>>? = null) { constructor(pagers: List<IPager<T>>, pendingPagers: List<Deferred<IPager<T>?>>, placeholderPagers: List<IPager<T>>? = null) {
_pagersReusable = pagers.map { ReusablePager(it) }.toMutableList(); _pagersReusable = pagers.map { ReusablePager(it) }.toMutableList();
_totalPagers = pagers.size + pendingPagers.size; _totalPagers = pagers.size + pendingPagers.size;
@ -100,7 +102,7 @@ abstract class MultiRefreshPager<T>: IRefreshPager<T>, IPager<T> {
} }
private fun getCurrentSubPagers(): List<IPager<T>> { private fun getCurrentSubPagers(): List<IPager<T>> {
val reusableWindows = _pagersReusable.map { it.getWindow() as IPager<T> }; val reusableWindows = _pagersReusable.map { it.getWindow() };
val placeholderWindows = synchronized(_pending) { val placeholderWindows = synchronized(_pending) {
_placeHolderPagersPaired.filter { _pending.contains(it.key) }.values _placeHolderPagersPaired.filter { _pending.contains(it.key) }.values
} }

View File

@ -100,7 +100,7 @@ class SingleAsyncItemPager<T> {
private fun fillDeferredUntil(i: Int): Int { private fun fillDeferredUntil(i: Int): Int {
val startPos = _requestedPageItems.size; val startPos = _requestedPageItems.size;
for(i in _requestedPageItems.size..i) { for(v in _requestedPageItems.size..i) {
_requestedPageItems.add(CompletableDeferred()); _requestedPageItems.add(CompletableDeferred());
} }
return startPos; return startPos;

View File

@ -2,13 +2,17 @@ package com.futo.platformplayer.casting
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.getConnectedSocket import com.futo.platformplayer.getConnectedSocket
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.CastingDeviceInfo import com.futo.platformplayer.models.CastingDeviceInfo
import com.futo.platformplayer.protos.ChromeCast import com.futo.platformplayer.protos.ChromeCast
import com.futo.platformplayer.toHexString import com.futo.platformplayer.toHexString
import com.futo.platformplayer.toInetAddress import com.futo.platformplayer.toInetAddress
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.json.JSONObject import org.json.JSONObject
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
@ -502,10 +506,10 @@ class ChromecastCastingDevice : CastingDevice {
} }
val volume = status.getJSONObject("volume"); val volume = status.getJSONObject("volume");
val volumeControlType = volume.getString("controlType"); //val volumeControlType = volume.getString("controlType");
val volumeLevel = volume.getString("level").toDouble(); val volumeLevel = volume.getString("level").toDouble();
val volumeMuted = volume.getBoolean("muted"); val volumeMuted = volume.getBoolean("muted");
val volumeStepInterval = volume.getString("stepInterval").toFloat(); //val volumeStepInterval = volume.getString("stepInterval").toFloat();
this.volume = if (volumeMuted) 0.0 else volumeLevel; this.volume = if (volumeMuted) 0.0 else volumeLevel;
Logger.i(TAG, "Status update received volume (level: $volumeLevel, muted: $volumeMuted)"); Logger.i(TAG, "Status update received volume (level: $volumeLevel, muted: $volumeMuted)");

View File

@ -5,12 +5,23 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Looper import android.os.Looper
import android.util.Base64 import android.util.Base64
import com.futo.platformplayer.* import com.futo.platformplayer.BuildConfig
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.http.server.ManagedHttpServer import com.futo.platformplayer.api.http.server.ManagedHttpServer
import com.futo.platformplayer.api.http.server.handlers.* import com.futo.platformplayer.api.http.server.handlers.HttpConstantHandler
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.http.server.handlers.HttpFileHandler
import com.futo.platformplayer.api.http.server.handlers.HttpFuntionHandler
import com.futo.platformplayer.api.http.server.handlers.HttpProxyHandler
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
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.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalSubtitleSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalVideoSource
import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.builders.DashBuilder import com.futo.platformplayer.builders.DashBuilder
@ -21,18 +32,20 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.CastingDeviceInfo import com.futo.platformplayer.models.CastingDeviceInfo
import com.futo.platformplayer.parsers.HLS import com.futo.platformplayer.parsers.HLS
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import kotlinx.coroutines.* import com.futo.platformplayer.stores.CastingDeviceInfoStorage
import com.futo.platformplayer.stores.FragmentedStorage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.net.InetAddress import java.net.InetAddress
import java.util.* import java.util.UUID
import javax.jmdns.JmDNS import javax.jmdns.JmDNS
import javax.jmdns.ServiceEvent import javax.jmdns.ServiceEvent
import javax.jmdns.ServiceListener import javax.jmdns.ServiceListener
import kotlin.collections.HashMap
import com.futo.platformplayer.stores.CastingDeviceInfoStorage
import com.futo.platformplayer.stores.FragmentedStorage
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.jmdns.ServiceTypeListener import javax.jmdns.ServiceTypeListener
class StateCasting { class StateCasting {
@ -213,7 +226,7 @@ class StateCasting {
} }
} }
_castServer.start(); _castServer.start();
enableDeveloper(context.contentResolver, true); enableDeveloper(true);
Logger.i(TAG, "CastingService started."); Logger.i(TAG, "CastingService started.");
} }
@ -1077,7 +1090,6 @@ class StateCasting {
CastProtocolType.FCAST -> { CastProtocolType.FCAST -> {
FCastCastingDevice(deviceInfo); FCastCastingDevice(deviceInfo);
} }
else -> throw Exception("${deviceInfo.type} is not a valid casting protocol")
} }
} }
@ -1172,7 +1184,7 @@ class StateCasting {
invokeEvents?.let { _scopeMain.launch { it(); }; }; invokeEvents?.let { _scopeMain.launch { it(); }; };
} }
fun enableDeveloper(contentResolver: ContentResolver, enableDev: Boolean){ fun enableDeveloper(enableDev: Boolean){
_castServer.removeAllHandlers("dev"); _castServer.removeAllHandlers("dev");
if(enableDev) { if(enableDev) {
_castServer.addHandler(HttpFuntionHandler("GET", "/dashPlayer") { context -> _castServer.addHandler(HttpFuntionHandler("GET", "/dashPlayer") { context ->

View File

@ -1,8 +1,10 @@
package com.futo.platformplayer.constructs package com.futo.platformplayer.constructs
import android.provider.Settings.Global import kotlinx.coroutines.CompletableDeferred
import com.futo.platformplayer.states.StateApp import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.* import kotlinx.coroutines.Deferred
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
class BatchedTaskHandler<TParameter, TResult> { class BatchedTaskHandler<TParameter, TResult> {
@ -25,14 +27,14 @@ class BatchedTaskHandler<TParameter, TResult> {
} }
fun execute(para : TParameter) : Deferred<TResult> { fun execute(para : TParameter) : Deferred<TResult> {
var result: TResult? = null; var result: TResult?;
var taskResult: Deferred<TResult>? = null; var taskResult: Deferred<TResult>? = null;
synchronized(_batchLock) { synchronized(_batchLock) {
result = _taskGetCache?.invoke(para); result = _taskGetCache?.invoke(para);
if(result == null) { if(result == null) {
taskResult = _batchRequest[para]; taskResult = _batchRequest[para];
if(taskResult?.isCancelled ?: false) { if(taskResult?.isCancelled == true) {
_batchRequest.remove(para); _batchRequest.remove(para);
taskResult = null; taskResult = null;
} }

View File

@ -2,7 +2,11 @@ package com.futo.platformplayer.constructs
import android.util.Log import android.util.Log
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class TaskHandler<TParameter, TResult> { class TaskHandler<TParameter, TResult> {
private val TAG = "TaskHandler<TResult>" private val TAG = "TaskHandler<TResult>"
@ -16,7 +20,7 @@ class TaskHandler<TParameter, TResult> {
private val _task: suspend ((parameter: TParameter) -> TResult); private val _task: suspend ((parameter: TParameter) -> TResult);
constructor(claz : Class<TResult>, scope: ()->CoroutineScope) { constructor(claz : Class<TResult>, scope: ()->CoroutineScope) {
_task = { claz.newInstance() }; _task = { claz.getDeclaredConstructor().newInstance() };
_scope = scope; _scope = scope;
_dispatcher = Dispatchers.IO; _dispatcher = Dispatchers.IO;
} }
@ -32,7 +36,7 @@ class TaskHandler<TParameter, TResult> {
} }
inline fun <reified T : Throwable>exception(noinline cb : (T)->Unit) : TaskHandler<TParameter, TResult> { inline fun <reified T : Throwable>exception(noinline cb : (T)->Unit) : TaskHandler<TParameter, TResult> {
onError.subscribeConditional { ex, para -> onError.subscribeConditional { ex, _ ->
if(ex is T) { if(ex is T) {
cb(ex); cb(ex);
return@subscribeConditional true; return@subscribeConditional true;
@ -76,8 +80,7 @@ class TaskHandler<TParameter, TResult> {
try { try {
onSuccess.emit(result); onSuccess.emit(result);
handled = true; handled = true;
} } catch (e: Throwable) {
catch (e: Throwable) {
Logger.w(TAG, "Handled exception in TaskHandler onSuccess.", e); Logger.w(TAG, "Handled exception in TaskHandler onSuccess.", e);
onError.emit(e, parameter); onError.emit(e, parameter);
handled = true; handled = true;

View File

@ -1,9 +1,10 @@
package com.futo.platformplayer.debug package com.futo.platformplayer.debug
import com.google.android.exoplayer2.util.Log import com.futo.platformplayer.logging.Logger
class Stopwatch { class Stopwatch {
var startTime = System.nanoTime() private var startTime = System.nanoTime()
val elapsedMs: Double get() { val elapsedMs: Double get() {
val now = System.nanoTime() val now = System.nanoTime()
@ -19,7 +20,7 @@ class Stopwatch {
val now = System.nanoTime() val now = System.nanoTime()
val diff = now - startTime val diff = now - startTime
val diffMs = diff / 1000000.0 val diffMs = diff / 1000000.0
Log.d(tag, "STOPWATCH $message ${diffMs}ms") Logger.i(tag, "STOPWATCH $message ${diffMs}ms")
startTime = now startTime = now
return diff return diff
} }

View File

@ -80,11 +80,11 @@ class DeveloperEndpoints(private val context: Context) {
//Files //Files
@HttpGET("/dev", "text/html") @HttpGET("/dev", "text/html")
val devTestHtml = StateAssets.readAsset(context, "devportal/index.html", true); val devTestHtml = StateAssets.readAsset(context, "devportal/index.html");
@HttpGET("/source.js", "application/javascript") @HttpGET("/source.js", "application/javascript")
val devSourceJS = StateAssets.readAsset(context, "scripts/source.js", true); val devSourceJS = StateAssets.readAsset(context, "scripts/source.js");
@HttpGET("/dev_bridge.js", "application/javascript") @HttpGET("/dev_bridge.js", "application/javascript")
val devBridgeJS = StateAssets.readAsset(context, "devportal/dev_bridge.js", true); val devBridgeJS = StateAssets.readAsset(context, "devportal/dev_bridge.js");
@HttpGET("/source_docs.json", "application/json") @HttpGET("/source_docs.json", "application/json")
val devSourceDocsJson = Json.encodeToString(JSClient.getJSDocs()); val devSourceDocsJson = Json.encodeToString(JSClient.getJSDocs());
@HttpGET("/source_docs.js", "application/javascript") @HttpGET("/source_docs.js", "application/javascript")
@ -98,7 +98,7 @@ class DeveloperEndpoints(private val context: Context) {
//@HttpGET("/dependencies/vuetify.min.css", "text/css") //@HttpGET("/dependencies/vuetify.min.css", "text/css")
//val depVuetifyCss = StateAssets.readAsset(context, "devportal/dependencies/vuetify.min.css", true); //val depVuetifyCss = StateAssets.readAsset(context, "devportal/dependencies/vuetify.min.css", true);
@HttpGET("/dependencies/FutoMainLogo.svg", "image/svg+xml") @HttpGET("/dependencies/FutoMainLogo.svg", "image/svg+xml")
val depFutoLogo = StateAssets.readAsset(context, "devportal/dependencies/FutoMainLogo.svg", true); val depFutoLogo = StateAssets.readAsset(context, "devportal/dependencies/FutoMainLogo.svg");
@HttpGET("/reference_plugin.d.ts", "text/plain") @HttpGET("/reference_plugin.d.ts", "text/plain")
fun devSourceTSWithRefs(httpContext: HttpContext) { fun devSourceTSWithRefs(httpContext: HttpContext) {
@ -107,7 +107,7 @@ class DeveloperEndpoints(private val context: Context) {
builder.appendLine("//Reference Scriptfile"); builder.appendLine("//Reference Scriptfile");
builder.appendLine("//Intended exclusively for auto-complete in your IDE, not for execution"); builder.appendLine("//Intended exclusively for auto-complete in your IDE, not for execution");
builder.appendLine(StateAssets.readAsset(context, "devportal/plugin.d.ts", true)); builder.appendLine(StateAssets.readAsset(context, "devportal/plugin.d.ts"));
httpContext.respondCode(200, builder.toString(), "text/plain"); httpContext.respondCode(200, builder.toString(), "text/plain");
} }
@ -119,7 +119,7 @@ class DeveloperEndpoints(private val context: Context) {
builder.appendLine("//Reference Scriptfile"); builder.appendLine("//Reference Scriptfile");
builder.appendLine("//Intended exclusively for auto-complete in your IDE, not for execution"); builder.appendLine("//Intended exclusively for auto-complete in your IDE, not for execution");
builder.appendLine(StateAssets.readAsset(context, "scripts/source.js", true)); builder.appendLine(StateAssets.readAsset(context, "scripts/source.js"));
for(pack in testPluginOrThrow.getPackages()) { for(pack in testPluginOrThrow.getPackages()) {
builder.appendLine(); builder.appendLine();
@ -194,7 +194,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondJson(200, testPluginOrThrow.getPackageVariables()); context.respondJson(200, testPluginOrThrow.getPackageVariables());
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@HttpPOST("/plugin/cleanTestPlugin") @HttpPOST("/plugin/cleanTestPlugin")
@ -204,7 +204,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(200); context.respondCode(200);
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@HttpPOST("/plugin/captchaTestPlugin") @HttpPOST("/plugin/captchaTestPlugin")
@ -226,7 +226,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(200, "Captcha started"); context.respondCode(200, "Captcha started");
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@HttpGET("/plugin/loginTestPlugin") @HttpGET("/plugin/loginTestPlugin")
@ -246,7 +246,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(200, "Login started"); context.respondCode(200, "Login started");
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@HttpGET("/plugin/logoutTestPlugin") @HttpGET("/plugin/logoutTestPlugin")
@ -258,7 +258,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(200, "Logged out"); context.respondCode(200, "Logged out");
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@ -269,7 +269,7 @@ class DeveloperEndpoints(private val context: Context) {
context.respondCode(200, if(isLoggedIn) "true" else "false", "application/json"); context.respondCode(200, if(isLoggedIn) "true" else "false", "application/json");
} }
catch(ex: Throwable) { catch(ex: Throwable) {
context.respondCode(500, (ex::class.simpleName + ":" + ex.message) ?: "", "text/plain") context.respondCode(500, (ex::class.simpleName + ":" + ex.message), "text/plain")
} }
} }
@ -319,7 +319,7 @@ class DeveloperEndpoints(private val context: Context) {
catch(invocation: InvocationTargetException) { catch(invocation: InvocationTargetException) {
val innerException = invocation.targetException; val innerException = invocation.targetException;
Logger.e("DeveloperEndpoints", innerException.message, innerException); Logger.e("DeveloperEndpoints", innerException.message, innerException);
context.respondCode(500, innerException::class.simpleName + ":" + innerException.message ?: "", "text/plain") context.respondCode(500, innerException::class.simpleName + ":" + innerException.message, "text/plain")
} }
catch(ilEx: IllegalArgumentException) { catch(ilEx: IllegalArgumentException) {
if(ilEx.message?.contains("does not exist") ?: false) { if(ilEx.message?.contains("does not exist") ?: false) {
@ -327,12 +327,12 @@ class DeveloperEndpoints(private val context: Context) {
} }
else { else {
Logger.e("DeveloperEndpoints", ilEx.message, ilEx); Logger.e("DeveloperEndpoints", ilEx.message, ilEx);
context.respondCode(500, ilEx::class.simpleName + ":" + ilEx.message ?: "", "text/plain") context.respondCode(500, ilEx::class.simpleName + ":" + ilEx.message, "text/plain")
} }
} }
catch(ex: Throwable) { catch(ex: Throwable) {
Logger.e("DeveloperEndpoints", ex.message, ex); Logger.e("DeveloperEndpoints", ex.message, ex);
context.respondCode(500, ex::class.simpleName + ":" + ex.message ?: "", "text/plain") context.respondCode(500, ex::class.simpleName + ":" + ex.message, "text/plain")
} }
} }
@HttpGET("/plugin/remoteProp") @HttpGET("/plugin/remoteProp")
@ -362,12 +362,12 @@ class DeveloperEndpoints(private val context: Context) {
} }
else { else {
Logger.e("DeveloperEndpoints", ilEx.message, ilEx); Logger.e("DeveloperEndpoints", ilEx.message, ilEx);
context.respondCode(500, ilEx::class.simpleName + ":" + ilEx.message ?: "", "text/plain") context.respondCode(500, ilEx::class.simpleName + ":" + ilEx.message, "text/plain")
} }
} }
catch(ex: Throwable) { catch(ex: Throwable) {
Logger.e("DeveloperEndpoints", ex.message, ex); Logger.e("DeveloperEndpoints", ex.message, ex);
context.respondCode(500, ex::class.simpleName + ":" + ex.message ?: "", "text/plain") context.respondCode(500, ex::class.simpleName + ":" + ex.message, "text/plain")
} }
} }
@ -398,7 +398,7 @@ class DeveloperEndpoints(private val context: Context) {
fun pluginLoadDevPlugin(context: HttpContext) { fun pluginLoadDevPlugin(context: HttpContext) {
val config = context.readContentJson<SourcePluginConfig>() val config = context.readContentJson<SourcePluginConfig>()
try { try {
val script = _client.get(config.absoluteScriptUrl!!); val script = _client.get(config.absoluteScriptUrl);
if(!script.isOk) if(!script.isOk)
throw IllegalStateException("URL ${config.scriptUrl} return code ${script.code}"); throw IllegalStateException("URL ${config.scriptUrl} return code ${script.code}");
if(script.body == null) if(script.body == null)
@ -409,7 +409,7 @@ class DeveloperEndpoints(private val context: Context) {
} }
catch(ex: Exception) { catch(ex: Exception) {
Logger.e("DeveloperEndpoints", ex.message, ex); Logger.e("DeveloperEndpoints", ex.message, ex);
context.respondCode(500, ex::class.simpleName + ":" + ex.message ?: "", "text/plain") context.respondCode(500, ex::class.simpleName + ":" + ex.message, "text/plain")
} }
} }

View File

@ -1,7 +1,9 @@
package com.futo.platformplayer.dialogs package com.futo.platformplayer.dialogs
import android.app.AlertDialog import android.app.AlertDialog
import android.app.PendingIntent.* import android.app.PendingIntent.FLAG_MUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.PendingIntent.getBroadcast
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
@ -14,12 +16,18 @@ import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.receivers.InstallReceiver import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.copyToOutputStream
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.receivers.InstallReceiver
import com.futo.platformplayer.states.StateUpdate import com.futo.platformplayer.states.StateUpdate
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@ -100,7 +108,7 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) {
window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
_text.text = context.resources.getText(R.string.downloading_update); _text.text = context.resources.getText(R.string.downloading_update);
(_updateSpinner?.drawable as Animatable?)?.start(); (_updateSpinner.drawable as Animatable?)?.start();
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
var inputStream: InputStream? = null; var inputStream: InputStream? = null;
@ -193,7 +201,7 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) {
setCancelable(true); setCancelable(true);
setCanceledOnTouchOutside(true); setCanceledOnTouchOutside(true);
_buttonClose.visibility = View.VISIBLE; _buttonClose.visibility = View.VISIBLE;
(_updateSpinner?.drawable as Animatable?)?.stop(); (_updateSpinner.drawable as Animatable?)?.stop();
if (result == null || result.isBlank()) { if (result == null || result.isBlank()) {
_updateSpinner.setImageResource(R.drawable.ic_update_success_251dp); _updateSpinner.setImageResource(R.drawable.ic_update_success_251dp);

View File

@ -7,8 +7,8 @@ import android.graphics.drawable.Animatable
import android.os.Bundle import android.os.Bundle
import android.text.Spannable import android.text.Spannable
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.text.method.ScrollingMovementMethod import android.text.method.ScrollingMovementMethod
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
@ -17,12 +17,16 @@ import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException
import com.futo.platformplayer.assume
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.stores.v2.ManagedStore import com.futo.platformplayer.stores.v2.ManagedStore
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ImportDialog : AlertDialog { class ImportDialog : AlertDialog {
companion object { companion object {
@ -99,7 +103,6 @@ class ImportDialog : AlertDialog {
_textProgress = findViewById(R.id.text_progress); _textProgress = findViewById(R.id.text_progress);
_updateSpinner = findViewById(R.id.update_spinner); _updateSpinner = findViewById(R.id.update_spinner);
val toMigrateCount = _store.getMissingReconstructionCount();
_import_type_text.text = _store.name; _import_type_text.text = _store.name;
_import_name_text.text = _name; _import_name_text.text = _name;

View File

@ -1,29 +1,13 @@
package com.futo.platformplayer.dialogs package com.futo.platformplayer.dialogs
import android.app.AlertDialog import android.app.AlertDialog
import android.app.PendingIntent.*
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.widget.Button
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import com.futo.platformplayer.receivers.InstallReceiver
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.states.StateApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class ProgressDialog : AlertDialog { class ProgressDialog : AlertDialog {
companion object { companion object {
@ -50,7 +34,7 @@ class ProgressDialog : AlertDialog {
setCancelable(false); setCancelable(false);
setCanceledOnTouchOutside(false); setCanceledOnTouchOutside(false);
_text.text = ""; _text.text = "";
(_updateSpinner?.drawable as Animatable?)?.start(); (_updateSpinner.drawable as Animatable?)?.start();
_handler(this); _handler(this);
} }

View File

@ -6,13 +6,20 @@ import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode import com.arthenica.ffmpegkit.ReturnCode
import com.arthenica.ffmpegkit.StatisticsCallback import com.arthenica.ffmpegkit.StatisticsCallback
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.media.models.streams.sources.AudioUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalSubtitleSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.SubtitleRawSource
import com.futo.platformplayer.api.media.models.streams.sources.VideoUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource import com.futo.platformplayer.api.media.models.streams.sources.other.IStreamMetaDataSource
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
@ -24,8 +31,11 @@ import com.futo.platformplayer.hasAnySource
import com.futo.platformplayer.helpers.FileHelper.Companion.sanitizeFileName import com.futo.platformplayer.helpers.FileHelper.Companion.sanitizeFileName
import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.helpers.VideoHelper
import com.futo.platformplayer.isDownloadable import com.futo.platformplayer.isDownloadable
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.parsers.HLS import com.futo.platformplayer.parsers.HLS
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.toHumanBitrate import com.futo.platformplayer.toHumanBitrate
import com.futo.platformplayer.toHumanBytesSpeed import com.futo.platformplayer.toHumanBytesSpeed
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -54,14 +64,9 @@ class VideoDownload {
var video: SerializedPlatformVideo? = null; var video: SerializedPlatformVideo? = null;
var videoDetails: SerializedPlatformVideoDetails? = null; var videoDetails: SerializedPlatformVideoDetails? = null;
@kotlinx.serialization.Transient
val videoEither: IPlatformVideo get() = videoDetails ?: video ?: throw IllegalStateException("Missing video?"); val videoEither: IPlatformVideo get() = videoDetails ?: video ?: throw IllegalStateException("Missing video?");
@kotlinx.serialization.Transient
val id: PlatformID get() = videoEither.id val id: PlatformID get() = videoEither.id
@kotlinx.serialization.Transient
val name: String get() = videoEither.name; val name: String get() = videoEither.name;
@kotlinx.serialization.Transient
val thumbnail: String? get() = videoDetails?.thumbnails?.getHQThumbnail() ?: video?.thumbnails?.getHQThumbnail(); val thumbnail: String? get() = videoDetails?.thumbnails?.getHQThumbnail() ?: video?.thumbnails?.getHQThumbnail();
var targetPixelCount: Long? = null; var targetPixelCount: Long? = null;
@ -385,7 +390,7 @@ class VideoDownload {
Logger.i(TAG, "${name} downloadSource Finished"); Logger.i(TAG, "${name} downloadSource Finished");
} }
catch(ioex: IOException) { catch(ioex: IOException) {
if(targetFile.exists() ?: false) if(targetFile.exists())
targetFile.delete(); targetFile.delete();
if(ioex.message?.contains("ENOSPC") ?: false) if(ioex.message?.contains("ENOSPC") ?: false)
throw Exception("Not enough space on device", ioex); throw Exception("Not enough space on device", ioex);
@ -393,7 +398,7 @@ class VideoDownload {
throw ioex; throw ioex;
} }
catch(ex: Throwable) { catch(ex: Throwable) {
if(targetFile.exists() ?: false) if(targetFile.exists())
targetFile.delete(); targetFile.delete();
throw ex; throw ex;
} }
@ -412,7 +417,7 @@ class VideoDownload {
val cmd = "-f concat -safe 0 -i \"${fileList.absolutePath}\" -c copy \"${targetFile.absolutePath}\"" val cmd = "-f concat -safe 0 -i \"${fileList.absolutePath}\" -c copy \"${targetFile.absolutePath}\""
val statisticsCallback = StatisticsCallback { statistics -> val statisticsCallback = StatisticsCallback { _ ->
//TODO: Show progress? //TODO: Show progress?
} }
@ -449,8 +454,7 @@ class VideoDownload {
targetFile.createNewFile(); targetFile.createNewFile();
var sourceLength: Long? = null; val sourceLength: Long?;
val fileStream = FileOutputStream(targetFile); val fileStream = FileOutputStream(targetFile);
try{ try{
@ -458,17 +462,17 @@ class VideoDownload {
if(Settings.instance.downloads.byteRangeDownload && head?.containsKey("accept-ranges") == true && head.containsKey("content-length")) if(Settings.instance.downloads.byteRangeDownload && head?.containsKey("accept-ranges") == true && head.containsKey("content-length"))
{ {
val concurrency = Settings.instance.downloads.getByteRangeThreadCount(); val concurrency = Settings.instance.downloads.getByteRangeThreadCount();
Logger.i(TAG, "Download ${name} ByteRange Parallel (${concurrency})"); Logger.i(TAG, "Download $name ByteRange Parallel (${concurrency})");
sourceLength = head["content-length"]!!.toLong(); sourceLength = head["content-length"]!!.toLong();
onProgress(sourceLength, 0, 0); onProgress(sourceLength, 0, 0);
downloadSource_Ranges(name, client, fileStream, videoUrl, sourceLength, 1024*512, concurrency, onProgress); downloadSource_Ranges(name, client, fileStream, videoUrl, sourceLength, 1024*512, concurrency, onProgress);
} }
else { else {
Logger.i(TAG, "Download ${name} Sequential"); Logger.i(TAG, "Download $name Sequential");
sourceLength = downloadSource_Sequential(client, fileStream, videoUrl, onProgress); sourceLength = downloadSource_Sequential(client, fileStream, videoUrl, onProgress);
} }
Logger.i(TAG, "${name} downloadSource Finished"); Logger.i(TAG, "$name downloadSource Finished");
} }
catch(ioex: IOException) { catch(ioex: IOException) {
if(targetFile.exists() ?: false) if(targetFile.exists() ?: false)
@ -484,7 +488,7 @@ class VideoDownload {
throw ex; throw ex;
} }
finally { finally {
fileStream?.close(); fileStream.close();
} }
return sourceLength!!; return sourceLength!!;
} }
@ -507,7 +511,7 @@ class VideoDownload {
val sourceStream = result.body.byteStream(); val sourceStream = result.body.byteStream();
var totalRead: Long = 0; var totalRead: Long = 0;
var read = 0; var read: Int;
val buffer = ByteArray(4096); val buffer = ByteArray(4096);

View File

@ -11,7 +11,13 @@ import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.LocalVideoMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.LocalVideoMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.LocalVideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.LocalVideoUnMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalSubtitleSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.SubtitleRawSource
import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideoDetails
@ -46,7 +52,7 @@ class VideoLocal: IPlatformVideoDetails, IStoreItem {
override val shareUrl: String get() = videoSerialized.shareUrl; override val shareUrl: String get() = videoSerialized.shareUrl;
@kotlinx.serialization.Transient @kotlinx.serialization.Transient
override val video: IVideoSourceDescriptor get() = if(!audioSource.isEmpty()) override val video: IVideoSourceDescriptor get() = if(audioSource.isNotEmpty())
LocalVideoUnMuxedSourceDescriptor(this) LocalVideoUnMuxedSourceDescriptor(this)
else else
LocalVideoMuxedSourceDescriptor(this); LocalVideoMuxedSourceDescriptor(this);

View File

@ -10,14 +10,30 @@ import com.caoccao.javet.values.primitive.V8ValueBoolean
import com.caoccao.javet.values.primitive.V8ValueInteger import com.caoccao.javet.values.primitive.V8ValueInteger
import com.caoccao.javet.values.primitive.V8ValueString import com.caoccao.javet.values.primitive.V8ValueString
import com.caoccao.javet.values.reference.V8ValueObject import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.*
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.engine.exceptions.* import com.futo.platformplayer.engine.exceptions.NoInternetException
import com.futo.platformplayer.engine.exceptions.PluginEngineStoppedException
import com.futo.platformplayer.engine.exceptions.ScriptAgeException
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptCompilationException
import com.futo.platformplayer.engine.exceptions.ScriptCriticalException
import com.futo.platformplayer.engine.exceptions.ScriptException
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptTimeoutException
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
import com.futo.platformplayer.engine.internal.V8Converter import com.futo.platformplayer.engine.internal.V8Converter
import com.futo.platformplayer.engine.packages.* import com.futo.platformplayer.engine.packages.PackageBridge
import com.futo.platformplayer.engine.packages.PackageDOMParser
import com.futo.platformplayer.engine.packages.PackageHttp
import com.futo.platformplayer.engine.packages.PackageUtilities
import com.futo.platformplayer.engine.packages.V8Package
import com.futo.platformplayer.getOrThrow
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateAssets import com.futo.platformplayer.states.StateAssets
import com.futo.platformplayer.warnIfMainThread
class V8Plugin { class V8Plugin {
val config: IV8PluginConfig; val config: IV8PluginConfig;
@ -61,7 +77,7 @@ class V8Plugin {
withDependency(PackageBridge(this, config)); withDependency(PackageBridge(this, config));
for(pack in config.packages) for(pack in config.packages)
withDependency(getPackage(context, pack)); withDependency(getPackage(pack));
} }
fun withDependency(context: Context, assetPath: String) : V8Plugin { fun withDependency(context: Context, assetPath: String) : V8Plugin {
@ -208,10 +224,10 @@ class V8Plugin {
} }
} }
private fun getPackage(context: Context, packageName: String): V8Package { private fun getPackage(packageName: String): V8Package {
//TODO: Auto get all package types? //TODO: Auto get all package types?
return when(packageName) { return when(packageName) {
"DOMParser" -> PackageDOMParser(context, this) "DOMParser" -> PackageDOMParser(this)
"Http" -> PackageHttp(this, config) "Http" -> PackageHttp(this, config)
"Utilities" -> PackageUtilities(this, config) "Utilities" -> PackageUtilities(this, config)
else -> throw ScriptCompilationException(config, "Unknown package [${packageName}] required for plugin ${config.name}"); else -> throw ScriptCompilationException(config, "Unknown package [${packageName}] required for plugin ${config.name}");

View File

@ -1,9 +1,6 @@
package com.futo.platformplayer.engine.exceptions package com.futo.platformplayer.engine.exceptions
import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.getOrThrow
import java.lang.Exception
open class PluginEngineException(config: IV8PluginConfig, error: String, code: String? = null) : PluginException(config, error, null, code) { open class PluginEngineException(config: IV8PluginConfig, error: String, code: String? = null) : PluginException(config, error, null, code) {

View File

@ -1,9 +1,6 @@
package com.futo.platformplayer.engine.exceptions package com.futo.platformplayer.engine.exceptions
import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.getOrThrow
import java.lang.Exception
class PluginEngineStoppedException(config: IV8PluginConfig, error: String, code: String? = null) : PluginEngineException(config, error, code) { class PluginEngineStoppedException(config: IV8PluginConfig, error: String, code: String? = null) : PluginEngineException(config, error, code) {

View File

@ -1,12 +1,8 @@
package com.futo.platformplayer.engine.internal package com.futo.platformplayer.engine.internal
import com.caoccao.javet.annotations.V8Convert
import com.caoccao.javet.interop.V8Runtime import com.caoccao.javet.interop.V8Runtime
import com.caoccao.javet.interop.converters.JavetObjectConverter import com.caoccao.javet.interop.converters.JavetObjectConverter
import com.caoccao.javet.interop.converters.JavetProxyConverter
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value
import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.engine.V8Plugin
class V8Converter : JavetObjectConverter() { class V8Converter : JavetObjectConverter() {
@ -17,11 +13,11 @@ class V8Converter : JavetObjectConverter() {
return null; return null;
val value: V8Value? = super.toV8Value(v8Runtime, obj, depth) val value: V8Value? = super.toV8Value(v8Runtime, obj, depth)
if (value != null && !value.isUndefined) if (value != null && !value.isUndefined) {
return value as T; return value as T;
if (obj != null) { }
if (obj is IV8Convertable) if (obj is IV8Convertable) {
return obj.toV8(v8Runtime) as T; return obj.toV8(v8Runtime) as T
} }
return null; return null;
} }

View File

@ -1,16 +1,11 @@
package com.futo.platformplayer.engine.packages package com.futo.platformplayer.engine.packages
import android.content.Context
import android.util.Log
import com.caoccao.javet.annotations.V8Allow
import com.caoccao.javet.annotations.V8Convert import com.caoccao.javet.annotations.V8Convert
import com.caoccao.javet.annotations.V8Function import com.caoccao.javet.annotations.V8Function
import com.caoccao.javet.annotations.V8Property import com.caoccao.javet.annotations.V8Property
import com.caoccao.javet.enums.V8ConversionMode import com.caoccao.javet.enums.V8ConversionMode
import com.caoccao.javet.enums.V8ProxyMode import com.caoccao.javet.enums.V8ProxyMode
import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.engine.dev.V8RemoteObject
import com.futo.platformplayer.engine.internal.V8BindObject import com.futo.platformplayer.engine.internal.V8BindObject
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -20,8 +15,8 @@ class PackageDOMParser : V8Package {
override val name: String get() = "DOMParser"; override val name: String get() = "DOMParser";
override val variableName: String = "domParser"; override val variableName: String = "domParser";
constructor(context: Context, v8Plugin: V8Plugin): super(v8Plugin) { constructor(v8Plugin: V8Plugin): super(v8Plugin) {
//v8Plugin.withDependency(context, "/scripts/some/package/path");
} }
@V8Function @V8Function
@ -45,8 +40,7 @@ class PackageDOMParser : V8Package {
@V8Property @V8Property
fun childNodes(): List<DOMNode> { fun childNodes(): List<DOMNode> {
val results = _element.children().map { DOMNode(_package, it) }.toList(); val results = _element.children().map { DOMNode(_package, it) }.toList();
if(results != null) _children.addAll(results);
_children.addAll(results);
return results; return results;
} }
@V8Property @V8Property

View File

@ -8,18 +8,15 @@ import com.caoccao.javet.enums.V8ProxyMode
import com.caoccao.javet.interop.V8Runtime import com.caoccao.javet.interop.V8Runtime
import com.caoccao.javet.values.V8Value import com.caoccao.javet.values.V8Value
import com.caoccao.javet.values.reference.V8ValueObject import com.caoccao.javet.values.reference.V8ValueObject
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
import com.futo.platformplayer.engine.IV8PluginConfig import com.futo.platformplayer.engine.IV8PluginConfig
import com.futo.platformplayer.engine.V8Plugin import com.futo.platformplayer.engine.V8Plugin
import com.futo.platformplayer.engine.internal.IV8Convertable import com.futo.platformplayer.engine.internal.IV8Convertable
import com.futo.platformplayer.engine.internal.V8BindObject import com.futo.platformplayer.engine.internal.V8BindObject
import com.futo.platformplayer.getOrThrow import com.futo.platformplayer.logging.Logger
import kotlinx.coroutines.CoroutineScope
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import kotlin.streams.asSequence import kotlin.streams.asSequence
import kotlin.streams.toList
class PackageHttp: V8Package { class PackageHttp: V8Package {
@Transient @Transient
@ -227,10 +224,10 @@ class PackageHttp: V8Package {
return logExceptions { return logExceptions {
return@logExceptions catchHttp { return@logExceptions catchHttp {
val client = _client; val client = _client;
logRequest(method, url, headers, null); //logRequest(method, url, headers, null);
val resp = client.requestMethod(method, url, headers); val resp = client.requestMethod(method, url, headers);
val responseBody = resp.body?.string(); val responseBody = resp.body?.string();
logResponse(method, url, resp.code, resp.headers, responseBody); //logResponse(method, url, resp.code, resp.headers, responseBody);
return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers)); return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers));
} }
}; };
@ -241,10 +238,10 @@ class PackageHttp: V8Package {
return logExceptions { return logExceptions {
catchHttp { catchHttp {
val client = _client; val client = _client;
logRequest(method, url, headers, body); //logRequest(method, url, headers, body);
val resp = client.requestMethod(method, url, body, headers); val resp = client.requestMethod(method, url, body, headers);
val responseBody = resp.body?.string(); val responseBody = resp.body?.string();
logResponse(method, url, resp.code, resp.headers, responseBody); //logResponse(method, url, resp.code, resp.headers, responseBody);
return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers)); return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers));
} }
}; };
@ -256,10 +253,10 @@ class PackageHttp: V8Package {
return logExceptions { return logExceptions {
catchHttp { catchHttp {
val client = _client; val client = _client;
logRequest("GET", url, headers, null); //logRequest("GET", url, headers, null);
val resp = client.get(url, headers); val resp = client.get(url, headers);
val responseBody = resp.body?.string(); val responseBody = resp.body?.string();
logResponse("GET", url, resp.code, resp.headers, responseBody); //logResponse("GET", url, resp.code, resp.headers, responseBody);
return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers)); return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers));
} }
}; };
@ -270,10 +267,10 @@ class PackageHttp: V8Package {
return logExceptions { return logExceptions {
catchHttp { catchHttp {
val client = _client; val client = _client;
logRequest("POST", url, headers, body); //logRequest("POST", url, headers, body);
val resp = client.post(url, body, headers); val resp = client.post(url, body, headers);
val responseBody = resp.body?.string(); val responseBody = resp.body?.string();
logResponse("POST", url, resp.code, resp.headers, responseBody); //logResponse("POST", url, resp.code, resp.headers, responseBody);
return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers)); return@catchHttp BridgeHttpResponse(resp.url, resp.code, responseBody, sanitizeResponseHeaders(resp.headers));
} }
}; };
@ -283,7 +280,7 @@ class PackageHttp: V8Package {
fun socket(url: String, headers: Map<String, String>? = null): SocketResult { fun socket(url: String, headers: Map<String, String>? = null): SocketResult {
val socketHeaders = headers?.toMutableMap() ?: HashMap(); val socketHeaders = headers?.toMutableMap() ?: HashMap();
applyDefaultHeaders(socketHeaders); applyDefaultHeaders(socketHeaders);
return SocketResult(this, _client, url, socketHeaders ?: HashMap()); return SocketResult(this, _client, url, socketHeaders);
} }
private fun applyDefaultHeaders(headerMap: MutableMap<String, String>) { private fun applyDefaultHeaders(headerMap: MutableMap<String, String>) {
@ -305,9 +302,7 @@ class PackageHttp: V8Package {
return result return result
} }
private fun logRequest(method: String, url: String, headers: Map<String, String> = HashMap(), body: String?) { /*private fun logRequest(method: String, url: String, headers: Map<String, String> = HashMap(), body: String?) {
return;
Logger.v(TAG) { Logger.v(TAG) {
val stringBuilder = StringBuilder(); val stringBuilder = StringBuilder();
stringBuilder.appendLine("HTTP request (useAuth = )"); stringBuilder.appendLine("HTTP request (useAuth = )");
@ -324,11 +319,9 @@ class PackageHttp: V8Package {
return@v stringBuilder.toString(); return@v stringBuilder.toString();
}; };
} }*/
private fun logResponse(method: String, url: String, responseCode: Int? = null, responseHeaders: Map<String, List<String>> = HashMap(), responseBody: String? = null) {
return;
/*private fun logResponse(method: String, url: String, responseCode: Int? = null, responseHeaders: Map<String, List<String>> = HashMap(), responseBody: String? = null) {
Logger.v(TAG) { Logger.v(TAG) {
val stringBuilder = StringBuilder(); val stringBuilder = StringBuilder();
if (responseCode != null) { if (responseCode != null) {
@ -353,7 +346,7 @@ class PackageHttp: V8Package {
return@v stringBuilder.toString(); return@v stringBuilder.toString();
}; };
} }*/
fun <T> logExceptions(handle: ()->T): T { fun <T> logExceptions(handle: ()->T): T {
try { try {

View File

@ -4,6 +4,6 @@ class RateLimitException : Throwable {
val pluginIds: List<String>; val pluginIds: List<String>;
constructor(pluginIds: List<String>): super() { constructor(pluginIds: List<String>): super() {
this.pluginIds = pluginIds ?: listOf(); this.pluginIds = pluginIds;
} }
} }

View File

@ -9,11 +9,17 @@ import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.dp
import com.futo.platformplayer.fixHtmlLinks
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.resolveChannelUrl
import com.futo.platformplayer.selectBestImage
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.views.platform.PlatformLinkView import com.futo.platformplayer.views.platform.PlatformLinkView
import com.futo.polycentric.core.toName import com.futo.polycentric.core.toName
import com.futo.polycentric.core.toURLInfoSystemLinkUrl import com.futo.polycentric.core.toURLInfoSystemLinkUrl
@ -48,7 +54,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment {
setChannel(it); setChannel(it);
}; };
_lastPolycentricProfile?.also { _lastPolycentricProfile?.also {
setPolycentricProfile(it, animate = false); setPolycentricProfile(it);
} }
return view; return view;
@ -108,7 +114,7 @@ class ChannelAboutFragment : Fragment, IChannelTabFragment {
} }
fun setPolycentricProfile(polycentricProfile: PolycentricProfile?, animate: Boolean) { fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) {
_lastPolycentricProfile = polycentricProfile; _lastPolycentricProfile = polycentricProfile;
if (polycentricProfile == null) { if (polycentricProfile == null) {

View File

@ -8,8 +8,6 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
@ -31,13 +29,15 @@ import com.futo.platformplayer.engine.exceptions.PluginException
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.fragment.mainactivity.main.FeedView import com.futo.platformplayer.fragment.mainactivity.main.FeedView
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateCache import com.futo.platformplayer.states.StateCache
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StatePolycentric
import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -215,14 +215,14 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
fun setPager(pager: IPager<IPlatformContent>, cache: FeedView.ItemCache<IPlatformContent>? = null) { fun setPager(pager: IPager<IPlatformContent>, cache: FeedView.ItemCache<IPlatformContent>? = null) {
if (_pager_parent != null && _pager_parent is IRefreshPager<*>) { if (_pager_parent != null && _pager_parent is IRefreshPager<*>) {
(_pager_parent as IRefreshPager<*>).onPagerError?.remove(this); (_pager_parent as IRefreshPager<*>).onPagerError.remove(this);
(_pager_parent as IRefreshPager<*>).onPagerChanged?.remove(this); (_pager_parent as IRefreshPager<*>).onPagerChanged.remove(this);
_pager_parent = null; _pager_parent = null;
} }
if(_pager is IReplacerPager<*>) if(_pager is IReplacerPager<*>)
(_pager as IReplacerPager<*>).onReplaced.remove(this); (_pager as IReplacerPager<*>).onReplaced.remove(this);
var pagerToSet: IPager<IPlatformContent>? = null; var pagerToSet: IPager<IPlatformContent>?;
if(pager is IRefreshPager<*>) { if(pager is IRefreshPager<*>) {
_pager_parent = pager; _pager_parent = pager;
pagerToSet = pager.getCurrentPager() as IPager<IPlatformContent>; pagerToSet = pager.getCurrentPager() as IPager<IPlatformContent>;
@ -305,7 +305,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
_adapterResults?.setLoading(loading); _adapterResults?.setLoading(loading);
} }
fun setPolycentricProfile(polycentricProfile: PolycentricProfile?, animate: Boolean) { fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) {
val p = _lastPolycentricProfile; val p = _lastPolycentricProfile;
if (p != null && polycentricProfile != null && p.system == polycentricProfile.system) { if (p != null && polycentricProfile != null && p.system == polycentricProfile.system) {
Logger.i(TAG, "setPolycentricProfile skipped because previous was same"); Logger.i(TAG, "setPolycentricProfile skipped because previous was same");

View File

@ -1,7 +1,6 @@
package com.futo.platformplayer.fragment.channel.tab package com.futo.platformplayer.fragment.channel.tab
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,7 +8,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.PlatformAuthorLink
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
@ -18,11 +18,10 @@ import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.fragment.mainactivity.main.ChannelFragment import com.futo.platformplayer.fragment.mainactivity.main.ChannelFragment
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.resolveChannelUrl
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.viewholders.CreatorViewHolder import com.futo.platformplayer.views.adapters.viewholders.CreatorViewHolder
import com.futo.polycentric.core.toUrl
import kotlinx.coroutines.runBlocking
class ChannelListFragment : Fragment, IChannelTabFragment { class ChannelListFragment : Fragment, IChannelTabFragment {
private var _channels: ArrayList<IPlatformChannel> = arrayListOf(); private var _channels: ArrayList<IPlatformChannel> = arrayListOf();
@ -84,7 +83,7 @@ class ChannelListFragment : Fragment, IChannelTabFragment {
recyclerCreator.layoutManager = _lm; recyclerCreator.layoutManager = _lm;
_recyclerCreator = recyclerCreator; _recyclerCreator = recyclerCreator;
_lastChannel?.also { setChannel(it); }; _lastChannel?.also { setChannel(it); };
_lastPolycentricProfile?.also { setPolycentricProfile(it, animate = false); } _lastPolycentricProfile?.also { setPolycentricProfile(it); }
return view; return view;
} }
@ -125,7 +124,7 @@ class ChannelListFragment : Fragment, IChannelTabFragment {
} }
} }
fun setPolycentricProfile(polycentricProfile: PolycentricProfile?, animate: Boolean) { fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) {
_taskLoadChannel.cancel(); _taskLoadChannel.cancel();
_lastPolycentricProfile = polycentricProfile; _lastPolycentricProfile = polycentricProfile;

View File

@ -32,7 +32,7 @@ class ChannelMonetizationFragment : Fragment, IChannelTabFragment {
_supportView?.visibility = View.GONE; _supportView?.visibility = View.GONE;
_textMonetization?.visibility = View.GONE; _textMonetization?.visibility = View.GONE;
setPolycentricProfile(_lastPolycentricProfile, animate = false); setPolycentricProfile(_lastPolycentricProfile);
return view; return view;
} }
@ -46,14 +46,14 @@ class ChannelMonetizationFragment : Fragment, IChannelTabFragment {
_lastChannel = channel; _lastChannel = channel;
} }
fun setPolycentricProfile(polycentricProfile: PolycentricProfile?, animate: Boolean) { fun setPolycentricProfile(polycentricProfile: PolycentricProfile?) {
_lastPolycentricProfile = polycentricProfile _lastPolycentricProfile = polycentricProfile
if (polycentricProfile != null) { if (polycentricProfile != null) {
_supportView?.setPolycentricProfile(polycentricProfile, animate) _supportView?.setPolycentricProfile(polycentricProfile)
_supportView?.visibility = View.VISIBLE _supportView?.visibility = View.VISIBLE
_textMonetization?.visibility = View.GONE _textMonetization?.visibility = View.GONE
} else { } else {
_supportView?.setPolycentricProfile(null, animate) _supportView?.setPolycentricProfile(null)
_supportView?.visibility = View.GONE _supportView?.visibility = View.GONE
_textMonetization?.visibility = View.VISIBLE _textMonetization?.visibility = View.VISIBLE
} }

View File

@ -7,11 +7,8 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
@ -19,7 +16,6 @@ import androidx.core.animation.doOnEnd
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.activities.SettingsActivity import com.futo.platformplayer.activities.SettingsActivity
import com.futo.platformplayer.dp import com.futo.platformplayer.dp
@ -31,7 +27,6 @@ import com.futo.platformplayer.states.StatePayment
import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.states.StateSubscriptions
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Collections
import kotlin.math.floor import kotlin.math.floor
import kotlin.math.roundToInt import kotlin.math.roundToInt

View File

@ -63,7 +63,7 @@ class BuyFragment : MainFragment() {
_overlayLoading = findViewById(R.id.overlay_loading); _overlayLoading = findViewById(R.id.overlay_loading);
_overlayPaying = findViewById(R.id.overlay_paying); _overlayPaying = findViewById(R.id.overlay_paying);
_paymentManager = PaymentManager(StatePayment.instance, fragment, _overlayPaying) { success, purchaseId, exception -> _paymentManager = PaymentManager(StatePayment.instance, fragment, _overlayPaying) { success, _, exception ->
if(success) { if(success) {
UIDialogs.showDialog(context, R.drawable.ic_check, context.getString(R.string.payment_succeeded), context.getString(R.string.thanks_for_your_purchase_a_key_will_be_sent_to_your_email_after_your_payment_has_been_received), null, 0, UIDialogs.showDialog(context, R.drawable.ic_check, context.getString(R.string.payment_succeeded), context.getString(R.string.thanks_for_your_purchase_a_key_will_be_sent_to_your_email_after_your_payment_has_been_received), null, 0,
UIDialogs.Action("Ok", {}, UIDialogs.ActionStyle.PRIMARY)); UIDialogs.Action("Ok", {}, UIDialogs.ActionStyle.PRIMARY));
@ -90,7 +90,7 @@ class BuyFragment : MainFragment() {
val currencies = StatePayment.instance.getAvailableCurrencies("grayjay"); val currencies = StatePayment.instance.getAvailableCurrencies("grayjay");
val prices = StatePayment.instance.getAvailableCurrencyPrices("grayjay"); val prices = StatePayment.instance.getAvailableCurrencyPrices("grayjay");
val country = StatePayment.instance.getPaymentCountryFromIP()?.let { c -> PaymentConfigurations.COUNTRIES.find { it.id.equals(c, ignoreCase = true) } }; val country = StatePayment.instance.getPaymentCountryFromIP()?.let { c -> PaymentConfigurations.COUNTRIES.find { it.id.equals(c, ignoreCase = true) } };
val currency = country?.let { c -> PaymentConfigurations.CURRENCIES.find { it.id == c.defaultCurrencyId && (currencies.contains(it.id) ?: true) } }; val currency = country?.let { c -> PaymentConfigurations.CURRENCIES.find { it.id == c.defaultCurrencyId && (currencies.contains(it.id)) } };
if(currency != null && prices.containsKey(currency.id)) { if(currency != null && prices.containsKey(currency.id)) {
val price = prices[currency.id]!!; val price = prices[currency.id]!!;

View File

@ -3,8 +3,6 @@ package com.futo.platformplayer.fragment.mainactivity.main
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.os.Bundle import android.os.Bundle
import android.text.Html
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -30,9 +28,9 @@ import com.futo.platformplayer.api.media.models.post.IPlatformPost
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.fragment.channel.tab.ChannelAboutFragment import com.futo.platformplayer.fragment.channel.tab.ChannelAboutFragment
import com.futo.platformplayer.fragment.channel.tab.ChannelContentsFragment
import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment import com.futo.platformplayer.fragment.channel.tab.ChannelListFragment
import com.futo.platformplayer.fragment.channel.tab.ChannelMonetizationFragment import com.futo.platformplayer.fragment.channel.tab.ChannelMonetizationFragment
import com.futo.platformplayer.fragment.channel.tab.ChannelContentsFragment
import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
@ -43,10 +41,10 @@ import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.platformplayer.views.adapters.ChannelViewPagerAdapter import com.futo.platformplayer.views.adapters.ChannelViewPagerAdapter
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.polycentric.core.* import com.futo.polycentric.core.*
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
@ -54,7 +52,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import okhttp3.internal.platform.Platform
@Serializable @Serializable
data class PolycentricProfile(val system: PublicKey, val systemState: SystemState, val ownedClaims: List<OwnedClaim>); data class PolycentricProfile(val system: PublicKey, val systemState: SystemState, val ownedClaims: List<OwnedClaim>);
@ -450,7 +447,7 @@ class ChannelFragment : MainFragment() {
private fun setPolycentricProfileOr(url: String, or: () -> Unit) { private fun setPolycentricProfileOr(url: String, or: () -> Unit) {
setPolycentricProfile(null, animate = false); setPolycentricProfile(null, animate = false);
val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(it.url) }; val cachedProfile = channel?.let { PolycentricCache.instance.getCachedProfile(url) };
if (cachedProfile != null) { if (cachedProfile != null) {
setPolycentricProfile(cachedProfile, animate = false); setPolycentricProfile(cachedProfile, animate = false);
} else { } else {
@ -492,10 +489,10 @@ class ChannelFragment : MainFragment() {
} }
(_viewPager.adapter as ChannelViewPagerAdapter?)?.let { (_viewPager.adapter as ChannelViewPagerAdapter?)?.let {
it.getFragment<ChannelAboutFragment>().setPolycentricProfile(profile, animate); it.getFragment<ChannelAboutFragment>().setPolycentricProfile(profile);
it.getFragment<ChannelMonetizationFragment>().setPolycentricProfile(profile, animate); it.getFragment<ChannelMonetizationFragment>().setPolycentricProfile(profile);
it.getFragment<ChannelListFragment>().setPolycentricProfile(profile, animate); it.getFragment<ChannelListFragment>().setPolycentricProfile(profile);
it.getFragment<ChannelContentsFragment>().setPolycentricProfile(profile, animate); it.getFragment<ChannelContentsFragment>().setPolycentricProfile(profile);
//TODO: Call on other tabs as needed //TODO: Call on other tabs as needed
} }
} }

View File

@ -6,10 +6,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.api.media.models.ResultCapabilities import com.futo.platformplayer.api.media.models.ResultCapabilities
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
@ -18,6 +16,8 @@ import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
import com.futo.platformplayer.isHttpUrl import com.futo.platformplayer.isHttpUrl
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -32,7 +32,7 @@ class ContentSearchResultsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onHide() { override fun onHide() {
@ -110,7 +110,7 @@ class ContentSearchResultsFragment : MainFragment() {
_taskSearch.cancel(); _taskSearch.cancel();
} }
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?) {
if(parameter is SuggestionsFragmentData) { if(parameter is SuggestionsFragmentData) {
setQuery(parameter.query, false); setQuery(parameter.query, false);
setChannelUrl(parameter.channelUrl, false); setChannelUrl(parameter.channelUrl, false);
@ -127,7 +127,7 @@ class ContentSearchResultsFragment : MainFragment() {
setFilterButtonVisible(true); setFilterButtonVisible(true);
onFilterClick.subscribe(this) { onFilterClick.subscribe(this) {
_overlayContainer?.let { _overlayContainer.let {
val filterValuesCopy = HashMap(_filterValues); val filterValuesCopy = HashMap(_filterValues);
val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy); val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy);
filtersOverlay.onOK.subscribe { enabledClientIds, changed -> filtersOverlay.onOK.subscribe { enabledClientIds, changed ->

View File

@ -6,15 +6,15 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.models.PlatformAuthorLink import com.futo.platformplayer.api.media.models.PlatformAuthorLink
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
class CreatorSearchResultsFragment : MainFragment() { class CreatorSearchResultsFragment : MainFragment() {
@ -26,7 +26,7 @@ class CreatorSearchResultsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onResume() { override fun onResume() {
@ -69,7 +69,7 @@ class CreatorSearchResultsFragment : MainFragment() {
_taskSearch.cancel(); _taskSearch.cancel();
} }
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?) {
if(parameter is String) { if(parameter is String) {
setQuery(parameter); setQuery(parameter);

View File

@ -8,32 +8,24 @@ import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.futo.platformplayer.* import com.futo.platformplayer.*
import com.futo.platformplayer.activities.CaptchaActivity
import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
import com.futo.platformplayer.api.media.structures.EmptyPager import com.futo.platformplayer.api.media.structures.EmptyPager
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptExecutionException import com.futo.platformplayer.engine.exceptions.ScriptExecutionException
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.others.CaptchaWebViewClient
import com.futo.platformplayer.states.AnnouncementType import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StateMeta import com.futo.platformplayer.states.StateMeta
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.views.announcements.AnnouncementView
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.InsertedViewHolder import com.futo.platformplayer.views.adapters.InsertedViewHolder
import com.futo.platformplayer.views.announcements.AnnouncementView
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.UUID import java.util.UUID
@ -159,8 +151,8 @@ class HomeFragment : MainFragment() {
loadResults(); loadResults();
} }
override fun filterResults(contents: List<IPlatformContent>): List<IPlatformContent> { override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
return contents.filter { !StateMeta.instance.isVideoHidden(it.url) && !StateMeta.instance.isCreatorHidden(it.author.url) }; return results.filter { !StateMeta.instance.isVideoHidden(it.url) && !StateMeta.instance.isCreatorHidden(it.author.url) };
} }
private fun loadResults() { private fun loadResults() {

View File

@ -14,12 +14,12 @@ import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.* import com.futo.platformplayer.*
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment
import com.futo.platformplayer.views.AnyAdapterView
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.Playlist import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlaylists import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.views.AnyAdapterView
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
import com.futo.platformplayer.views.adapters.viewholders.ImportPlaylistsViewHolder import com.futo.platformplayer.views.adapters.viewholders.ImportPlaylistsViewHolder
import com.futo.platformplayer.views.adapters.viewholders.SelectablePlaylist import com.futo.platformplayer.views.adapters.viewholders.SelectablePlaylist
@ -32,7 +32,7 @@ class ImportPlaylistsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onHide() { override fun onHide() {
@ -79,7 +79,7 @@ class ImportPlaylistsFragment : MainFragment() {
_spinner = findViewById(R.id.channel_loader); _spinner = findViewById(R.id.channel_loader);
_adapterView = findViewById<RecyclerView>(R.id.recycler_import).asAny( _items) { _adapterView = findViewById<RecyclerView>(R.id.recycler_import).asAny( _items) {
it.onSelectedChange.subscribe { c -> it.onSelectedChange.subscribe {
updateSelected(); updateSelected();
}; };
}; };
@ -123,7 +123,7 @@ class ImportPlaylistsFragment : MainFragment() {
_taskLoadPlaylist.cancel(); _taskLoadPlaylist.cancel();
} }
fun onShown(parameter: Any ?, isBack: Boolean) { fun onShown(parameter: Any?) {
updateSelected(); updateSelected();
val itemsRemoved = _items.size; val itemsRemoved = _items.size;

View File

@ -15,13 +15,13 @@ import com.futo.platformplayer.*
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.ImportTopBarFragment
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.views.AnyAdapterView import com.futo.platformplayer.views.AnyAdapterView
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
import com.futo.platformplayer.views.adapters.viewholders.ImportSubscriptionViewHolder import com.futo.platformplayer.views.adapters.viewholders.ImportSubscriptionViewHolder
import com.futo.platformplayer.views.adapters.viewholders.SelectableIPlatformChannel import com.futo.platformplayer.views.adapters.viewholders.SelectableIPlatformChannel
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -37,7 +37,7 @@ class ImportSubscriptionsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onHide() { override fun onHide() {
@ -93,7 +93,7 @@ class ImportSubscriptionsFragment : MainFragment() {
_loadProgress = findViewById(R.id.text_load_progress); _loadProgress = findViewById(R.id.text_load_progress);
_adapterView = findViewById<RecyclerView>(R.id.recycler_import).asAny( _items) { _adapterView = findViewById<RecyclerView>(R.id.recycler_import).asAny( _items) {
it.onSelectedChange.subscribe { c -> it.onSelectedChange.subscribe {
updateSelected(); updateSelected();
}; };
}; };
@ -152,14 +152,14 @@ class ImportSubscriptionsFragment : MainFragment() {
_taskLoadChannel.cancel(); _taskLoadChannel.cancel();
} }
fun onShown(parameter: Any ?, isBack: Boolean) { fun onShown(parameter: Any?) {
_counter = 0; _counter = 0;
_limitToastShown = false; _limitToastShown = false;
updateSelected(); updateSelected();
val itemsRemoved = _items.size; val itemsRemoved = _items.size;
_items.clear(); _items.clear();
_adapterView?.adapter?.notifyItemRangeRemoved(0, itemsRemoved); _adapterView.adapter?.notifyItemRangeRemoved(0, itemsRemoved);
_links = (parameter as List<String>).filter { i -> !StateSubscriptions.instance.isSubscribed(i) }.toList(); _links = (parameter as List<String>).filter { i -> !StateSubscriptions.instance.isSubscribed(i) }.toList();
_currentLoadIndex = 0; _currentLoadIndex = 0;

View File

@ -41,7 +41,7 @@ class PlaylistFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -150,7 +150,7 @@ class PlaylistFragment : MainFragment() {
}; };
} }
fun onShown(parameter: Any ?, isBack: Boolean) { fun onShown(parameter: Any?) {
_taskLoadPlaylist.cancel(); _taskLoadPlaylist.cancel();
if (parameter is Playlist?) { if (parameter is Playlist?) {

View File

@ -6,14 +6,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
class PlaylistSearchResultsFragment : MainFragment() { class PlaylistSearchResultsFragment : MainFragment() {
@ -25,7 +25,7 @@ class PlaylistSearchResultsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onResume() { override fun onResume() {
@ -78,7 +78,7 @@ class PlaylistSearchResultsFragment : MainFragment() {
_taskSearch.cancel(); _taskSearch.cancel();
} }
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?) {
if(parameter is String) { if(parameter is String) {
setQuery(parameter); setQuery(parameter);

View File

@ -14,21 +14,15 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UISlideOverlays import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.assume
import com.futo.platformplayer.fragment.mainactivity.topbar.NavigationTopBarFragment
import com.futo.platformplayer.models.Playlist import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.models.SearchType import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlatform import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.views.adapters.* import com.futo.platformplayer.views.adapters.*
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTextInput
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import kotlin.collections.ArrayList
class PlaylistsFragment : MainFragment() { class PlaylistsFragment : MainFragment() {
@ -52,7 +46,7 @@ class PlaylistsFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown();
} }
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
@ -133,11 +127,12 @@ class PlaylistsFragment : MainFragment() {
StatePlaylists.instance.onWatchLaterChanged.remove(this); StatePlaylists.instance.onWatchLaterChanged.remove(this);
} }
fun onShown(parameter: Any?, isBack: Boolean) { @SuppressLint("NotifyDataSetChanged")
fun onShown() {
playlists.clear() playlists.clear()
playlists.addAll( playlists.addAll(
StatePlaylists.instance.getPlaylists() StatePlaylists.instance.getPlaylists().sortedByDescending { maxOf(it.datePlayed, it.dateUpdate, it.dateCreation) }
.sortedByDescending { maxOf(it.datePlayed, it.dateUpdate, it.dateCreation) }); );
_adapterPlaylist.notifyDataSetChanged(); _adapterPlaylist.notifyDataSetChanged();
updateWatchLater(); updateWatchLater();

View File

@ -40,15 +40,15 @@ import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StatePolycentric
import com.futo.platformplayer.toHumanNowDiffString import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanNumber import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.views.comments.AddCommentView
import com.futo.platformplayer.views.segments.CommentsList
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.platform.PlatformIndicator
import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.platformplayer.views.others.Toggle
import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView import com.futo.platformplayer.views.adapters.feedtypes.PreviewPostView
import com.futo.platformplayer.views.comments.AddCommentView
import com.futo.platformplayer.views.others.CreatorThumbnail
import com.futo.platformplayer.views.others.Toggle
import com.futo.platformplayer.views.overlays.RepliesOverlay import com.futo.platformplayer.views.overlays.RepliesOverlay
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
import com.futo.platformplayer.views.platform.PlatformIndicator
import com.futo.platformplayer.views.segments.CommentsList
import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.polycentric.core.ApiMethods import com.futo.polycentric.core.ApiMethods
import com.futo.polycentric.core.ContentType import com.futo.polycentric.core.ContentType
import com.futo.polycentric.core.Models import com.futo.polycentric.core.Models
@ -220,7 +220,7 @@ class PostDetailFragment : MainFragment {
root.removeView(layoutTop); root.removeView(layoutTop);
_commentsList.setPrependedView(layoutTop); _commentsList.setPrependedView(layoutTop);
_commentsList.onCommentsLoaded.subscribe { count -> _commentsList.onCommentsLoaded.subscribe {
updateCommentType(false); updateCommentType(false);
}; };

View File

@ -13,7 +13,9 @@ import android.widget.FrameLayout
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.AddSourceActivity import com.futo.platformplayer.activities.AddSourceActivity
import com.futo.platformplayer.activities.LoginActivity import com.futo.platformplayer.activities.LoginActivity
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
@ -25,8 +27,8 @@ import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.views.buttons.BigButton import com.futo.platformplayer.views.buttons.BigButton
import com.futo.platformplayer.views.buttons.BigButtonGroup import com.futo.platformplayer.views.buttons.BigButtonGroup
import com.futo.platformplayer.views.sources.SourceHeaderView
import com.futo.platformplayer.views.fields.FieldForm import com.futo.platformplayer.views.fields.FieldForm
import com.futo.platformplayer.views.sources.SourceHeaderView
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -40,7 +42,7 @@ class SourceDetailFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown(parameter);
} }
override fun onHide() { override fun onHide() {
@ -92,7 +94,7 @@ class SourceDetailFragment : MainFragment() {
updateSourceViews(); updateSourceViews();
} }
fun onShown(parameter: Any?, isBack: Boolean) { fun onShown(parameter: Any?) {
if (parameter is SourcePluginConfig) { if (parameter is SourcePluginConfig) {
loadConfig(parameter); loadConfig(parameter);
updateSourceViews(); updateSourceViews();
@ -135,7 +137,7 @@ class SourceDetailFragment : MainFragment() {
try { try {
_settingsAppForm.fromObject(source.descriptor.appSettings); _settingsAppForm.fromObject(source.descriptor.appSettings);
_settingsAppForm.onChanged.clear(); _settingsAppForm.onChanged.clear();
_settingsAppForm.onChanged.subscribe { field, value -> _settingsAppForm.onChanged.subscribe { _, _ ->
_settingsAppChanged = true; _settingsAppChanged = true;
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@ -150,7 +152,7 @@ class SourceDetailFragment : MainFragment() {
context.getString(R.string.these_settings_are_defined_by_the_plugin) context.getString(R.string.these_settings_are_defined_by_the_plugin)
); );
_settingsForm.onChanged.clear(); _settingsForm.onChanged.clear();
_settingsForm.onChanged.subscribe { field, value -> _settingsForm.onChanged.subscribe { _, _ ->
_settingsChanged = true; _settingsChanged = true;
} }
} catch (e: Throwable) { } catch (e: Throwable) {
@ -478,8 +480,8 @@ class SourceDetailFragment : MainFragment() {
Logger.i(TAG, "Update is available (config.version=${config.version}, source.config.version=${c.version})."); Logger.i(TAG, "Update is available (config.version=${config.version}, source.config.version=${c.version}).");
val c = context ?: return@launch; val ctx = context ?: return@launch;
val intent = Intent(c, AddSourceActivity::class.java).apply { val intent = Intent(ctx, AddSourceActivity::class.java).apply {
data = Uri.parse(sourceUrl) data = Uri.parse(sourceUrl)
}; };

View File

@ -1,5 +1,6 @@
package com.futo.platformplayer.fragment.mainactivity.main package com.futo.platformplayer.fragment.mainactivity.main
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
@ -10,23 +11,23 @@ import android.widget.LinearLayout
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.AddSourceOptionsActivity import com.futo.platformplayer.activities.AddSourceOptionsActivity
import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment import com.futo.platformplayer.fragment.mainactivity.topbar.AddTopBarFragment
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlugins import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.SubscriptionStorage import com.futo.platformplayer.stores.SubscriptionStorage
import com.futo.platformplayer.views.sources.SourceUnderConstructionView
import com.futo.platformplayer.views.adapters.DisabledSourceView import com.futo.platformplayer.views.adapters.DisabledSourceView
import com.futo.platformplayer.views.adapters.EnabledSourceAdapter import com.futo.platformplayer.views.adapters.EnabledSourceAdapter
import com.futo.platformplayer.views.adapters.EnabledSourceViewHolder import com.futo.platformplayer.views.adapters.EnabledSourceViewHolder
import com.futo.platformplayer.views.adapters.ItemMoveCallback import com.futo.platformplayer.views.adapters.ItemMoveCallback
import com.futo.platformplayer.views.sources.SourceUnderConstructionView
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.util.* import java.util.Collections
class SourcesFragment : MainFragment() { class SourcesFragment : MainFragment() {
override val isMainView : Boolean = true; override val isMainView : Boolean = true;
@ -159,15 +160,15 @@ class SourcesFragment : MainFragment() {
_didCreateView = true; _didCreateView = true;
} }
@SuppressLint("NotifyDataSetChanged")
fun reloadSources() { fun reloadSources() {
enabledSources.clear(); enabledSources.clear();
disabledSources.clear(); disabledSources.clear();
enabledSources.addAll(StatePlatform.instance.getSortedEnabledClient()); enabledSources.addAll(StatePlatform.instance.getSortedEnabledClient());
disabledSources.addAll(StatePlatform.instance.getAvailableClients().filter { !enabledSources.contains(it) }); disabledSources.addAll(StatePlatform.instance.getAvailableClients().filter { !enabledSources.contains(it) });
_adapterSourcesEnabled?.notifyDataSetChanged(); _adapterSourcesEnabled.notifyDataSetChanged();
setCanRemove(enabledSources.size > 1); setCanRemove(enabledSources.size > 1);
//_adapterSourcesDisabled?.notifyDataSetChanged();
updateDisabledSources(); updateDisabledSources();
if(_didCreateView) { if(_didCreateView) {
@ -207,18 +208,15 @@ class SourcesFragment : MainFragment() {
} }
private fun setCanRemove(canRemove: Boolean) { private fun setCanRemove(canRemove: Boolean) {
val recyclerSourcesEnabled = _recyclerSourcesEnabled ?: return; for (i in 0 until _recyclerSourcesEnabled.childCount) {
var adapterSourcesEnabled = _adapterSourcesEnabled ?: return; val view: View = _recyclerSourcesEnabled.getChildAt(i)
val viewHolder = _recyclerSourcesEnabled.getChildViewHolder(view)
for (i in 0 until recyclerSourcesEnabled.childCount) {
val view: View = recyclerSourcesEnabled.getChildAt(i)
val viewHolder = recyclerSourcesEnabled.getChildViewHolder(view)
if (viewHolder is EnabledSourceViewHolder) { if (viewHolder is EnabledSourceViewHolder) {
viewHolder.setCanRemove(canRemove); viewHolder.setCanRemove(canRemove);
} }
} }
adapterSourcesEnabled.canRemove = canRemove; _adapterSourcesEnabled.canRemove = canRemove;
} }
private fun onPrimaryChanged(client: IPlatformClient) { private fun onPrimaryChanged(client: IPlatformClient) {

View File

@ -27,12 +27,12 @@ import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StateSubscriptions import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.FragmentedStorageFileJson import com.futo.platformplayer.stores.FragmentedStorageFileJson
import com.futo.platformplayer.views.announcements.AnnouncementView
import com.futo.platformplayer.views.FeedStyle import com.futo.platformplayer.views.FeedStyle
import com.futo.platformplayer.views.NoResultsView import com.futo.platformplayer.views.NoResultsView
import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder import com.futo.platformplayer.views.adapters.ContentPreviewViewHolder
import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
import com.futo.platformplayer.views.adapters.InsertedViewHolder import com.futo.platformplayer.views.adapters.InsertedViewHolder
import com.futo.platformplayer.views.announcements.AnnouncementView
import com.futo.platformplayer.views.buttons.BigButton import com.futo.platformplayer.views.buttons.BigButton
import com.futo.platformplayer.views.subscriptions.SubscriptionBar import com.futo.platformplayer.views.subscriptions.SubscriptionBar
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@ -111,7 +111,7 @@ class SubscriptionsFeedFragment : MainFragment() {
} }
}; };
StateSubscriptions.instance.onSubscriptionsChanged.subscribe(this) { subs, added -> StateSubscriptions.instance.onSubscriptionsChanged.subscribe(this) { _, added ->
if(!added) if(!added)
StateSubscriptions.instance.clearSubscriptionFeed(); StateSubscriptions.instance.clearSubscriptionFeed();
StateApp.instance.scopeOrNull?.let { StateApp.instance.scopeOrNull?.let {
@ -146,7 +146,7 @@ class SubscriptionsFeedFragment : MainFragment() {
val homeTab = Settings.instance.tabs.find { it.id == 0 }; val homeTab = Settings.instance.tabs.find { it.id == 0 };
val isHomeEnabled = homeTab?.enabled == true; val isHomeEnabled = homeTab?.enabled == true;
if (announcementsView != null && isHomeEnabled) { if (announcementsView != null && isHomeEnabled) {
headerView?.removeView(announcementsView); headerView.removeView(announcementsView);
_announcementsView = null; _announcementsView = null;
} }
@ -154,7 +154,7 @@ class SubscriptionsFeedFragment : MainFragment() {
val c = context; val c = context;
if (c != null) { if (c != null) {
_announcementsView = AnnouncementView(c, null).apply { _announcementsView = AnnouncementView(c, null).apply {
headerView?.addView(this) headerView.addView(this)
}; };
} }
} }
@ -272,17 +272,18 @@ class SubscriptionsFeedFragment : MainFragment() {
} }
private fun toggleFilterContentType(contentType: ContentType, isTrue: Boolean) { private fun toggleFilterContentType(contentType: ContentType, isTrue: Boolean) {
synchronized(_filterLock) { synchronized(_filterLock) {
if(!isTrue) if(!isTrue) {
_filterSettings.allowContentTypes.remove(contentType); _filterSettings.allowContentTypes.remove(contentType);
else if(!_filterSettings.allowContentTypes.contains(contentType)) } else if(!_filterSettings.allowContentTypes.contains(contentType)) {
_filterSettings.allowContentTypes.add(contentType) _filterSettings.allowContentTypes.add(contentType)
else null; }
_filterSettings.save(); _filterSettings.save();
}; };
if(Settings.instance.subscriptions.fetchOnTabOpen) //TODO: Do this different, temporary workaround if(Settings.instance.subscriptions.fetchOnTabOpen) { //TODO: Do this different, temporary workaround
loadResults(false); loadResults(false);
else } else {
loadCache(); loadCache();
}
} }
override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> { override fun filterResults(results: List<IPlatformContent>): List<IPlatformContent> {
@ -381,7 +382,7 @@ class SubscriptionsFeedFragment : MainFragment() {
context?.let { context?.let {
fragment.lifecycleScope.launch(Dispatchers.Main) { fragment.lifecycleScope.launch(Dispatchers.Main) {
try { try {
if (exs!!.size <= 8) { if (exs.size <= 8) {
for (ex in exs) { for (ex in exs) {
var toShow = ex; var toShow = ex;
var channel: String? = null; var channel: String? = null;

View File

@ -3,16 +3,17 @@ package com.futo.platformplayer.fragment.mainactivity.main
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.view.* import androidx.core.view.WindowCompat
import com.futo.platformplayer.logging.Logger import androidx.core.view.WindowInsetsCompat
import com.futo.platformplayer.states.StatePlayer import androidx.core.view.WindowInsetsControllerCompat
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.casting.CastConnectionState import com.futo.platformplayer.casting.CastConnectionState
@ -20,8 +21,10 @@ import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.listeners.OrientationManager import com.futo.platformplayer.listeners.OrientationManager
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.PlatformVideoWithTime import com.futo.platformplayer.models.PlatformVideoWithTime
import com.futo.platformplayer.models.UrlVideoWithTime import com.futo.platformplayer.models.UrlVideoWithTime
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StateSaved import com.futo.platformplayer.states.StateSaved
import com.futo.platformplayer.states.VideoToOpen import com.futo.platformplayer.states.VideoToOpen
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
@ -269,7 +272,7 @@ class VideoDetailFragment : MainFragment {
val viewDetail = _viewDetail; val viewDetail = _viewDetail;
Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.allowBackground}"); Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.allowBackground}");
if(viewDetail?.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && viewDetail?.allowBackground != true) { if(viewDetail?.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && !viewDetail.allowBackground) {
_leavingPiP = false; _leavingPiP = false;
val params = _viewDetail?.getPictureInPictureParams(); val params = _viewDetail?.getPictureInPictureParams();

View File

@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")
package com.futo.platformplayer.fragment.mainactivity.main package com.futo.platformplayer.fragment.mainactivity.main
import android.app.PictureInPictureParams import android.app.PictureInPictureParams
@ -23,16 +25,21 @@ import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager import android.view.WindowManager
import android.widget.* import android.widget.FrameLayout
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.futo.platformplayer.*
import com.futo.platformplayer.api.media.IPluginSourced
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.UISlideOverlays
import com.futo.platformplayer.api.media.IPluginSourced
import com.futo.platformplayer.api.media.LiveChatManager import com.futo.platformplayer.api.media.LiveChatManager
import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.api.media.exceptions.ContentNotAvailableYetException import com.futo.platformplayer.api.media.exceptions.ContentNotAvailableYetException
@ -46,12 +53,17 @@ import com.futo.platformplayer.api.media.models.playback.IPlaybackTracker
import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
import com.futo.platformplayer.api.media.models.ratings.RatingLikes import com.futo.platformplayer.api.media.models.ratings.RatingLikes
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.* import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.IDashManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalSubtitleSource
import com.futo.platformplayer.api.media.models.streams.sources.LocalVideoSource
import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.api.media.platforms.js.models.JSVideoDetails import com.futo.platformplayer.api.media.platforms.js.models.JSVideoDetails
import com.futo.platformplayer.api.media.structures.IPager import com.futo.platformplayer.api.media.structures.IPager
@ -61,21 +73,41 @@ import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.TaskHandler import com.futo.platformplayer.constructs.TaskHandler
import com.futo.platformplayer.downloads.VideoLocal import com.futo.platformplayer.downloads.VideoLocal
import com.futo.platformplayer.dp
import com.futo.platformplayer.engine.exceptions.ScriptAgeException import com.futo.platformplayer.engine.exceptions.ScriptAgeException
import com.futo.platformplayer.engine.exceptions.ScriptException import com.futo.platformplayer.engine.exceptions.ScriptException
import com.futo.platformplayer.engine.exceptions.ScriptImplementationException import com.futo.platformplayer.engine.exceptions.ScriptImplementationException
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
import com.futo.platformplayer.exceptions.UnsupportedCastException import com.futo.platformplayer.exceptions.UnsupportedCastException
import com.futo.platformplayer.fixHtmlLinks
import com.futo.platformplayer.fixHtmlWhitespace
import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
import com.futo.platformplayer.getNowDiffSeconds
import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.helpers.VideoHelper
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.Subscription import com.futo.platformplayer.models.Subscription
import com.futo.platformplayer.polycentric.PolycentricCache import com.futo.platformplayer.polycentric.PolycentricCache
import com.futo.platformplayer.receivers.MediaControlReceiver import com.futo.platformplayer.receivers.MediaControlReceiver
import com.futo.platformplayer.states.* import com.futo.platformplayer.selectBestImage
import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.states.StateHistory
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.states.StatePlugins
import com.futo.platformplayer.states.StatePolycentric
import com.futo.platformplayer.states.StateSubscriptions
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringArrayStorage import com.futo.platformplayer.stores.StringArrayStorage
import com.futo.platformplayer.stores.db.types.DBHistory import com.futo.platformplayer.stores.db.types.DBHistory
import com.futo.platformplayer.toHumanBitrate
import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.MonetizationView import com.futo.platformplayer.views.MonetizationView
import com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout import com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout
import com.futo.platformplayer.views.casting.CastView import com.futo.platformplayer.views.casting.CastView
@ -87,7 +119,11 @@ import com.futo.platformplayer.views.overlays.LiveChatOverlay
import com.futo.platformplayer.views.overlays.QueueEditorOverlay import com.futo.platformplayer.views.overlays.QueueEditorOverlay
import com.futo.platformplayer.views.overlays.RepliesOverlay import com.futo.platformplayer.views.overlays.RepliesOverlay
import com.futo.platformplayer.views.overlays.SupportOverlay import com.futo.platformplayer.views.overlays.SupportOverlay
import com.futo.platformplayer.views.overlays.slideup.* import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuButtonList
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuGroup
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuTitle
import com.futo.platformplayer.views.pills.PillRatingLikesDislikes import com.futo.platformplayer.views.pills.PillRatingLikesDislikes
import com.futo.platformplayer.views.pills.RoundButton import com.futo.platformplayer.views.pills.RoundButton
import com.futo.platformplayer.views.pills.RoundButtonGroup import com.futo.platformplayer.views.pills.RoundButtonGroup
@ -97,17 +133,25 @@ import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.platformplayer.views.video.FutoVideoPlayer import com.futo.platformplayer.views.video.FutoVideoPlayer
import com.futo.platformplayer.views.video.FutoVideoPlayerBase import com.futo.platformplayer.views.video.FutoVideoPlayerBase
import com.futo.platformplayer.views.videometa.UpNextView import com.futo.platformplayer.views.videometa.UpNextView
import com.futo.polycentric.core.* import com.futo.polycentric.core.ApiMethods
import com.futo.polycentric.core.ContentType
import com.futo.polycentric.core.Models
import com.futo.polycentric.core.Opinion
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
import com.google.android.exoplayer2.C import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.Format import com.google.android.exoplayer2.Format
import com.google.android.exoplayer2.ui.PlayerControlView import com.google.android.exoplayer2.ui.PlayerControlView
import com.google.android.exoplayer2.ui.TimeBar import com.google.android.exoplayer2.ui.TimeBar
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import userpackage.Protocol import userpackage.Protocol
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.collections.ArrayList
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.roundToLong import kotlin.math.roundToLong
@ -336,7 +380,7 @@ class VideoDetailView : ConstraintLayout {
}; };
_monetization.onSupportTap.subscribe { _monetization.onSupportTap.subscribe {
_container_content_support.setPolycentricProfile(_polycentricProfile?.profile, false); _container_content_support.setPolycentricProfile(_polycentricProfile?.profile);
switchContentView(_container_content_support); switchContentView(_container_content_support);
}; };
@ -484,7 +528,7 @@ class VideoDetailView : ConstraintLayout {
}; };
if (!isInEditMode) { if (!isInEditMode) {
StateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { d, connectionState -> StateCasting.instance.onActiveDeviceConnectionStateChanged.subscribe(this) { _, connectionState ->
if (_onPauseCalled) { if (_onPauseCalled) {
return@subscribe; return@subscribe;
} }
@ -530,7 +574,7 @@ class VideoDetailView : ConstraintLayout {
} }
_playerProgress.player = _player.exoPlayer?.player; _playerProgress.player = _player.exoPlayer?.player;
_playerProgress.setProgressUpdateListener { position, bufferedPosition -> _playerProgress.setProgressUpdateListener { position, _ ->
StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, position); StatePlayer.instance.updateMediaSessionPlaybackState(_player.exoPlayer?.getPlaybackStateCompat() ?: PlaybackStateCompat.STATE_NONE, position);
} }
@ -658,7 +702,7 @@ class VideoDetailView : ConstraintLayout {
_trackingLastVideoSubscription?.let { _trackingLastVideoSubscription?.let {
Logger.i(TAG, "Subscription [${it.channel.name}] watch time delta [${delta}]" + Logger.i(TAG, "Subscription [${it.channel.name}] watch time delta [${delta}]" +
"(${"%.2f".format((_trackingTotalWatched / 1000) / currentVideo.duration.toDouble().coerceAtLeast(1.0))})"); "(${"%.2f".format((_trackingTotalWatched / 1000) / currentVideo.duration.toDouble().coerceAtLeast(1.0))})");
it.updatePlayback(currentVideo, (delta / 1000).toInt()); it.updatePlayback((delta / 1000).toInt());
_trackingTotalWatched += delta; _trackingTotalWatched += delta;
if(!_trackingDidCountView && currentVideo.duration > 0) { if(!_trackingDidCountView && currentVideo.duration > 0) {
val percentage = (_trackingTotalWatched / 1000) / currentVideo.duration.toDouble(); val percentage = (_trackingTotalWatched / 1000) / currentVideo.duration.toDouble();
@ -1048,6 +1092,7 @@ class VideoDetailView : ConstraintLayout {
switchContentView(_container_content_main); switchContentView(_container_content_main);
} }
@OptIn(ExperimentalCoroutinesApi::class)
fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) { fun setVideoDetails(videoDetail: IPlatformVideoDetails, newVideo: Boolean = false) {
Logger.i(TAG, "setVideoDetails (${videoDetail.name})") Logger.i(TAG, "setVideoDetails (${videoDetail.name})")
@ -1064,8 +1109,8 @@ class VideoDetailView : ConstraintLayout {
_player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed()); _player.setPlaybackRate(Settings.instance.playback.getDefaultPlaybackSpeed());
} }
var videoLocal: VideoLocal? = null; val videoLocal: VideoLocal?;
var video: IPlatformVideoDetails? = null; val video: IPlatformVideoDetails?;
if(videoDetail is VideoLocal) { if(videoDetail is VideoLocal) {
videoLocal = videoDetail; videoLocal = videoDetail;
@ -1077,7 +1122,7 @@ class VideoDetailView : ConstraintLayout {
return@invokeOnCompletion; return@invokeOnCompletion;
} }
val result = videoTask.getCompleted(); val result = videoTask.getCompleted();
if(this.video == videoDetail && result != null && result is IPlatformVideoDetails) { if(this.video == videoDetail && result is IPlatformVideoDetails) {
this.video = result; this.video = result;
fragment.lifecycleScope.launch(Dispatchers.Main) { fragment.lifecycleScope.launch(Dispatchers.Main) {
updateQualitySourcesOverlay(result, videoLocal); updateQualitySourcesOverlay(result, videoLocal);
@ -1246,37 +1291,33 @@ class VideoDetailView : ConstraintLayout {
_rating.visibility = View.GONE; _rating.visibility = View.GONE;
} }
if (video.rating != null) { when (video.rating) {
when (video.rating) { is RatingLikeDislikes -> {
is RatingLikeDislikes -> { val r = video.rating as RatingLikeDislikes;
val r = video.rating as RatingLikeDislikes; _layoutRating.visibility = View.VISIBLE;
_layoutRating.visibility = View.VISIBLE;
_textLikes.visibility = View.VISIBLE; _textLikes.visibility = View.VISIBLE;
_imageLikeIcon.visibility = View.VISIBLE; _imageLikeIcon.visibility = View.VISIBLE;
_textLikes.text = r.likes.toHumanNumber(); _textLikes.text = r.likes.toHumanNumber();
_imageDislikeIcon.visibility = View.VISIBLE; _imageDislikeIcon.visibility = View.VISIBLE;
_textDislikes.visibility = View.VISIBLE; _textDislikes.visibility = View.VISIBLE;
_textDislikes.text = r.dislikes.toHumanNumber(); _textDislikes.text = r.dislikes.toHumanNumber();
} }
is RatingLikes -> { is RatingLikes -> {
val r = video.rating as RatingLikes; val r = video.rating as RatingLikes;
_layoutRating.visibility = View.VISIBLE; _layoutRating.visibility = View.VISIBLE;
_textLikes.visibility = View.VISIBLE; _textLikes.visibility = View.VISIBLE;
_imageLikeIcon.visibility = View.VISIBLE; _imageLikeIcon.visibility = View.VISIBLE;
_textLikes.text = r.likes.toHumanNumber(); _textLikes.text = r.likes.toHumanNumber();
_imageDislikeIcon.visibility = View.GONE; _imageDislikeIcon.visibility = View.GONE;
_textDislikes.visibility = View.GONE; _textDislikes.visibility = View.GONE;
} }
else -> { else -> {
_layoutRating.visibility = View.GONE; _layoutRating.visibility = View.GONE;
}
} }
} else {
_layoutRating.visibility = View.GONE;
} }
@ -1636,7 +1677,7 @@ class VideoDetailView : ConstraintLayout {
SlideUpMenuGroup(this.context, context.getString(R.string.offline_video), "video", SlideUpMenuGroup(this.context, context.getString(R.string.offline_video), "video",
*localVideoSources *localVideoSources
.map { .map {
SlideUpMenuItem(this.context, R.drawable.ic_movie, it!!.name, "${it.width}x${it.height}", it, SlideUpMenuItem(this.context, R.drawable.ic_movie, it.name, "${it.width}x${it.height}", it,
{ handleSelectVideoTrack(it) }); { handleSelectVideoTrack(it) });
}.toList().toTypedArray()) }.toList().toTypedArray())
else null, else null,
@ -1660,7 +1701,7 @@ class VideoDetailView : ConstraintLayout {
SlideUpMenuGroup(this.context, context.getString(R.string.stream_video), "video", SlideUpMenuGroup(this.context, context.getString(R.string.stream_video), "video",
*liveStreamVideoFormats *liveStreamVideoFormats
.map { .map {
SlideUpMenuItem(this.context, R.drawable.ic_movie, it?.label ?: it.containerMimeType ?: it.bitrate.toString(), "${it.width}x${it.height}", it, SlideUpMenuItem(this.context, R.drawable.ic_movie, it.label ?: it.containerMimeType ?: it.bitrate.toString(), "${it.width}x${it.height}", it,
{ _player.selectVideoTrack(it.height) }); { _player.selectVideoTrack(it.height) });
}.toList().toTypedArray()) }.toList().toTypedArray())
else null, else null,
@ -1668,7 +1709,7 @@ class VideoDetailView : ConstraintLayout {
SlideUpMenuGroup(this.context, context.getString(R.string.stream_audio), "audio", SlideUpMenuGroup(this.context, context.getString(R.string.stream_audio), "audio",
*liveStreamAudioFormats *liveStreamAudioFormats
.map { .map {
SlideUpMenuItem(this.context, R.drawable.ic_music, "${it?.label ?: it.containerMimeType} ${it.bitrate}", "", it, SlideUpMenuItem(this.context, R.drawable.ic_music, "${it.label ?: it.containerMimeType} ${it.bitrate}", "", it,
{ _player.selectAudioTrack(it.bitrate) }); { _player.selectAudioTrack(it.bitrate) });
}.toList().toTypedArray()) }.toList().toTypedArray())
else null, else null,
@ -2216,7 +2257,7 @@ class VideoDetailView : ConstraintLayout {
_channelName.text = username _channelName.text = username
} }
_monetization.setPolycentricProfile(cachedPolycentricProfile, animate); _monetization.setPolycentricProfile(cachedPolycentricProfile);
} }
fun setProgressBarOverlayed(isOverlayed: Boolean?) { fun setProgressBarOverlayed(isOverlayed: Boolean?) {
@ -2292,13 +2333,13 @@ class VideoDetailView : ConstraintLayout {
StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_NOSOURCES", context.getString(R.string.video_without_source), context.getString(R.string.there_was_a_in_your_queue_videoname_by_authorname_without_the_required_source_being_enabled_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION) StateAnnouncement.instance.registerAnnouncement(video?.id?.value + "_Q_NOSOURCES", context.getString(R.string.video_without_source), context.getString(R.string.there_was_a_in_your_queue_videoname_by_authorname_without_the_required_source_being_enabled_playback_was_skipped).replace("{videoName}", video?.name ?: "").replace("{authorName}", video?.author?.name ?: ""), AnnouncementType.SESSION)
} }
} }
.exception<ScriptLoginRequiredException> { .exception<ScriptLoginRequiredException> { e ->
Logger.w(TAG, "exception<ScriptLoginRequiredException>", it); Logger.w(TAG, "exception<ScriptLoginRequiredException>", e);
UIDialogs.showDialog(context, R.drawable.ic_security, "Authentication", it.message, null, 0, UIDialogs.showDialog(context, R.drawable.ic_security, "Authentication", e.message, null, 0,
UIDialogs.Action("Cancel", {}), UIDialogs.Action("Cancel", {}),
UIDialogs.Action("Login", { UIDialogs.Action("Login", {
val id = it.config?.let { if(it is SourcePluginConfig) it.id else null }; val id = e.config.let { if(it is SourcePluginConfig) it.id else null };
val didLogin = if(id == null) val didLogin = if(id == null)
false false
else StatePlugins.instance.loginPlugin(context, id) { else StatePlugins.instance.loginPlugin(context, id) {

View File

@ -52,7 +52,7 @@ abstract class VideoListEditorView : LinearLayout {
_buttonShare.visibility = View.VISIBLE; _buttonShare.visibility = View.VISIBLE;
} }
else else
_buttonShare?.visibility = View.GONE; _buttonShare.visibility = View.GONE;
buttonPlayAll.setOnClickListener { onPlayAllClick(); }; buttonPlayAll.setOnClickListener { onPlayAllClick(); };
buttonShuffle.setOnClickListener { onShuffleClick(); }; buttonShuffle.setOnClickListener { onShuffleClick(); };
@ -106,11 +106,9 @@ abstract class VideoListEditorView : LinearLayout {
}; };
} else { } else {
_textMetadata.text = "0 " + context.getString(R.string.videos); _textMetadata.text = "0 " + context.getString(R.string.videos);
if(_imagePlaylistThumbnail != null) { Glide.with(_imagePlaylistThumbnail)
Glide.with(_imagePlaylistThumbnail) .load(R.drawable.placeholder_video_thumbnail)
.load(R.drawable.placeholder_video_thumbnail) .into(_imagePlaylistThumbnail)
.into(_imagePlaylistThumbnail);
}
} }
_videoListEditorView.setVideos(videos, canEdit); _videoListEditorView.setVideos(videos, canEdit);

View File

@ -5,10 +5,10 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.states.StatePlaylists
class WatchLaterFragment : MainFragment() { class WatchLaterFragment : MainFragment() {
override val isMainView : Boolean = true; override val isMainView : Boolean = true;
@ -19,7 +19,7 @@ class WatchLaterFragment : MainFragment() {
override fun onShownWithView(parameter: Any?, isBack: Boolean) { override fun onShownWithView(parameter: Any?, isBack: Boolean) {
super.onShownWithView(parameter, isBack); super.onShownWithView(parameter, isBack);
_view?.onShown(parameter, isBack); _view?.onShown();
} }
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -42,7 +42,7 @@ class WatchLaterFragment : MainFragment() {
} }
fun onShown(parameter: Any ?, isBack: Boolean) { fun onShown() {
setName("Watch Later"); setName("Watch Later");
setVideos(StatePlaylists.instance.getWatchLater(), true); setVideos(StatePlaylists.instance.getWatchLater(), true);
} }

View File

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.api.media.IPlatformClient import com.futo.platformplayer.api.media.IPlatformClient
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
@ -73,9 +74,9 @@ class ImportTopBarFragment : TopFragment() {
fun setImportEnabled(enabled: Boolean) { fun setImportEnabled(enabled: Boolean) {
if (enabled) { if (enabled) {
_textImport?.setTextColor(resources.getColor(R.color.colorPrimary)); _textImport?.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
} else { } else {
_textImport?.setTextColor(resources.getColor(R.color.gray_67)); _textImport?.setTextColor(ContextCompat.getColor(requireContext(), R.color.gray_67));
} }
_importEnabled = enabled; _importEnabled = enabled;

View File

@ -1,9 +1,10 @@
@file:Suppress("DEPRECATION")
package com.futo.platformplayer.helpers package com.futo.platformplayer.helpers
import android.net.Uri import android.net.Uri
import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor import com.futo.platformplayer.api.media.models.streams.IVideoSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.streams.sources.HLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource 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.models.streams.sources.IHLSManifestAudioSource
@ -19,6 +20,7 @@ import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.dash.DashMediaSource import com.google.android.exoplayer2.source.dash.DashMediaSource
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser
import com.google.android.exoplayer2.upstream.ResolvingDataSource import com.google.android.exoplayer2.upstream.ResolvingDataSource
import kotlin.math.abs
class VideoHelper { class VideoHelper {
companion object { companion object {
@ -43,19 +45,17 @@ class VideoHelper {
fun selectBestVideoSource(desc: IVideoSourceDescriptor, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? = selectBestVideoSource(desc.videoSources.toList(), desiredPixelCount, prefContainers); fun selectBestVideoSource(desc: IVideoSourceDescriptor, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? = selectBestVideoSource(desc.videoSources.toList(), desiredPixelCount, prefContainers);
fun selectBestVideoSource(sources: Iterable<IVideoSource>, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? { fun selectBestVideoSource(sources: Iterable<IVideoSource>, desiredPixelCount : Int, prefContainers : Array<String>) : IVideoSource? {
val targetVideo = if(desiredPixelCount > 0) val targetVideo = if(desiredPixelCount > 0) {
sources.toList() sources.toList().minByOrNull { x -> abs(x.height * x.width - desiredPixelCount) };
.sortedBy { x -> Math.abs(x.height * x.width - desiredPixelCount) } } else {
.firstOrNull(); sources.toList().lastOrNull();
else }
sources.toList()
.lastOrNull();
val hasPriority = sources.any { it.priority }; val hasPriority = sources.any { it.priority };
val targetPixelCount = if(targetVideo != null) targetVideo.width * targetVideo.height else desiredPixelCount; val targetPixelCount = if(targetVideo != null) targetVideo.width * targetVideo.height else desiredPixelCount;
val altSources = if(hasPriority) { val altSources = if(hasPriority) {
sources.filter { it.priority }.sortedBy { x -> Math.abs(x.height * x.width - targetPixelCount) }; sources.filter { it.priority }.sortedBy { x -> abs(x.height * x.width - targetPixelCount) };
} else { } else {
sources.filter { it.height == (targetVideo?.height ?: 0) }; sources.filter { it.height == (targetVideo?.height ?: 0) };
} }
@ -76,33 +76,42 @@ class VideoHelper {
fun selectBestAudioSource(desc: IVideoSourceDescriptor, prefContainers : Array<String>, prefLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? { fun selectBestAudioSource(desc: IVideoSourceDescriptor, prefContainers : Array<String>, prefLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? {
if(!desc.isUnMuxed) if(!desc.isUnMuxed)
return null; return null;
return selectBestAudioSource((desc as VideoUnMuxedSourceDescriptor).audioSources.toList(), prefContainers, prefLanguage);
return selectBestAudioSource((desc as VideoUnMuxedSourceDescriptor).audioSources.toList(), prefContainers, prefLanguage, targetBitrate);
} }
fun selectBestAudioSource(altSources : Iterable<IAudioSource>, prefContainers : Array<String>, preferredLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? { fun selectBestAudioSource(altSources : Iterable<IAudioSource>, prefContainers : Array<String>, preferredLanguage: String? = null, targetBitrate: Long? = null) : IAudioSource? {
val languageToFilter = if(preferredLanguage != null && altSources.any { it.language == preferredLanguage }) val languageToFilter = if(preferredLanguage != null && altSources.any { it.language == preferredLanguage }) {
preferredLanguage preferredLanguage
else if(preferredLanguage == null) null } else if(preferredLanguage == null) {
else "Unknown"; null
} else {
"Unknown"
}
var usableSources = if(languageToFilter != null && altSources.any { it.language == languageToFilter }) var usableSources = if(languageToFilter != null && altSources.any { it.language == languageToFilter }) {
altSources.filter { it.language == languageToFilter }.sortedBy { it.bitrate }.toList(); altSources.filter { it.language == languageToFilter }.sortedBy { it.bitrate }.toList();
else altSources.sortedBy { it.bitrate }; } else {
altSources.sortedBy { it.bitrate }
}
if(usableSources.any { it.priority }) if(usableSources.any { it.priority }) {
usableSources = usableSources.filter { it.priority }; usableSources = usableSources.filter { it.priority };
}
var bestSource = if(targetBitrate != null) var bestSource = if(targetBitrate != null) {
usableSources.minByOrNull { Math.abs(it.bitrate - targetBitrate) }; usableSources.minByOrNull { abs(it.bitrate - targetBitrate) };
else } else {
usableSources.lastOrNull(); usableSources.lastOrNull();
}
for (prefContainer in prefContainers) { for (prefContainer in prefContainers) {
val betterSources = usableSources.filter { it.container == prefContainer }; val betterSources = usableSources.filter { it.container == prefContainer };
val betterSource = if(targetBitrate != null) val betterSource = if(targetBitrate != null) {
betterSources.minByOrNull { Math.abs(it.bitrate - targetBitrate) }; betterSources.minByOrNull { abs(it.bitrate - targetBitrate) };
else } else {
betterSources.lastOrNull(); betterSources.lastOrNull();
}
if(betterSource != null) { if(betterSource != null) {
bestSource = betterSource; bestSource = betterSource;
@ -112,17 +121,9 @@ class VideoHelper {
return bestSource; return bestSource;
} }
var breakOnce = hashSetOf<String>() @Suppress("DEPRECATION")
fun convertItagSourceToChunkedDashSource(videoSource: JSVideoUrlRangeSource) : MediaSource { fun convertItagSourceToChunkedDashSource(videoSource: JSVideoUrlRangeSource) : MediaSource {
var urlToUse = videoSource.getVideoUrl(); val urlToUse = videoSource.getVideoUrl();
/*
//TODO: REMOVE THIS, PURPOSELY 403s
if(urlToUse.contains("sig=") && !breakOnce.contains(urlToUse)) {
breakOnce.add(urlToUse);
val sigIndex = urlToUse.indexOf("sig=");
urlToUse = urlToUse.substring(0, sigIndex) + "sig=0" + urlToUse.substring(sigIndex + 4);
}*/
val manifestConfig = ProgressiveDashManifestCreator.fromVideoProgressiveStreamingUrl(urlToUse, val manifestConfig = ProgressiveDashManifestCreator.fromVideoProgressiveStreamingUrl(urlToUse,
videoSource.duration * 1000, videoSource.duration * 1000,
videoSource.container, videoSource.container,
@ -143,13 +144,10 @@ class VideoHelper {
return DashMediaSource.Factory(ResolvingDataSource.Factory(videoSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec -> return DashMediaSource.Factory(ResolvingDataSource.Factory(videoSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec ->
Logger.v("PLAYBACK", "Video REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null); Logger.v("PLAYBACK", "Video REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null);
return@Resolver dataSpec; return@Resolver dataSpec;
})) })).createMediaSource(manifest, MediaItem.Builder().setUri(Uri.parse(videoSource.getVideoUrl())).build())
.createMediaSource(manifest,
MediaItem.Builder()
.setUri(Uri.parse(videoSource.getVideoUrl()))
.build())
} }
@Suppress("DEPRECATION")
fun convertItagSourceToChunkedDashSource(audioSource: JSAudioUrlRangeSource) : MediaSource { fun convertItagSourceToChunkedDashSource(audioSource: JSAudioUrlRangeSource) : MediaSource {
val manifestConfig = ProgressiveDashManifestCreator.fromAudioProgressiveStreamingUrl(audioSource.getAudioUrl(), val manifestConfig = ProgressiveDashManifestCreator.fromAudioProgressiveStreamingUrl(audioSource.getAudioUrl(),
audioSource.duration?.times(1000) ?: 0, audioSource.duration?.times(1000) ?: 0,
@ -170,11 +168,7 @@ class VideoHelper {
return DashMediaSource.Factory(ResolvingDataSource.Factory(audioSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec -> return DashMediaSource.Factory(ResolvingDataSource.Factory(audioSource.getHttpDataSourceFactory(), ResolvingDataSource.Resolver { dataSpec ->
Logger.v("PLAYBACK", "Audio REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null); Logger.v("PLAYBACK", "Audio REQ Range [" + dataSpec.position + "-" + (dataSpec.position + dataSpec.length) + "](" + dataSpec.length + ")", null);
return@Resolver dataSpec; return@Resolver dataSpec;
})) })).createMediaSource(manifest, MediaItem.Builder().setUri(Uri.parse(audioSource.getAudioUrl())).build())
.createMediaSource(manifest,
MediaItem.Builder()
.setUri(Uri.parse(audioSource.getAudioUrl()))
.build())
} }
} }
} }

View File

@ -4,7 +4,6 @@ import com.futo.platformplayer.api.media.models.ResultCapabilities
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
import com.futo.platformplayer.api.media.models.channels.SerializedChannel import com.futo.platformplayer.api.media.models.channels.SerializedChannel
import com.futo.platformplayer.api.media.models.contents.IPlatformContent import com.futo.platformplayer.api.media.models.contents.IPlatformContent
import com.futo.platformplayer.api.media.models.contents.IPlatformContentDetails
import com.futo.platformplayer.getNowDiffDays import com.futo.platformplayer.getNowDiffDays
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.serializers.OffsetDateTimeSerializer import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
@ -74,7 +73,7 @@ class Subscription {
this.channel = SerializedChannel.fromChannel(channel); this.channel = SerializedChannel.fromChannel(channel);
} }
fun updatePlayback(content: IPlatformContentDetails, seconds: Int) { fun updatePlayback(seconds: Int) {
playbackSeconds += seconds; playbackSeconds += seconds;
} }
fun addPlaybackView() { fun addPlaybackView() {

View File

@ -3,7 +3,6 @@ package com.futo.platformplayer.others
import android.webkit.* import android.webkit.*
import com.futo.platformplayer.api.media.Serializer import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.api.media.platforms.js.SourceCaptchaData import com.futo.platformplayer.api.media.platforms.js.SourceCaptchaData
import com.futo.platformplayer.api.media.platforms.js.SourcePluginAuthConfig
import com.futo.platformplayer.api.media.platforms.js.SourcePluginCaptchaConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginCaptchaConfig
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
@ -58,7 +57,7 @@ class CaptchaWebViewClient : WebViewClient {
if(request == null) if(request == null)
return super.shouldInterceptRequest(view, request as WebResourceRequest?); return super.shouldInterceptRequest(view, request as WebResourceRequest?);
val extracted = _extractor.handleRequest(view, request); val extracted = _extractor.handleRequest(request);
if(extracted != null && !_didNotify) { if(extracted != null && !_didNotify) {
_didNotify = true; _didNotify = true;
onCaptchaFinished.emit(SourceCaptchaData( onCaptchaFinished.emit(SourceCaptchaData(

View File

@ -1,19 +1,22 @@
package com.futo.platformplayer.others package com.futo.platformplayer.others
import android.net.Uri import android.net.Uri
import android.webkit.* import android.webkit.CookieManager
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import com.futo.platformplayer.BuildConfig import com.futo.platformplayer.BuildConfig
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.Serializer import com.futo.platformplayer.api.media.Serializer
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.api.media.platforms.js.SourceAuth import com.futo.platformplayer.api.media.platforms.js.SourceAuth
import com.futo.platformplayer.api.media.platforms.js.SourcePluginAuthConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginAuthConfig
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event2
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.matchesDomain import com.futo.platformplayer.matchesDomain
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class LoginWebViewClient : WebViewClient { class LoginWebViewClient : WebViewClient {
private val LOG_VERBOSE = false; private val LOG_VERBOSE = false;
@ -30,9 +33,9 @@ class LoginWebViewClient : WebViewClient {
_pluginConfig = config; _pluginConfig = config;
_authConfig = config.authentication!!; _authConfig = config.authentication!!;
Logger.i(TAG, "Login [${config.name}]" + Logger.i(TAG, "Login [${config.name}]" +
"\nRequired Headers: ${config.authentication?.headersToFind?.joinToString(", ")}" + "\nRequired Headers: ${config.authentication.headersToFind?.joinToString(", ")}" +
"\nRequired Domain Headers: ${Serializer.json.encodeToString(config.authentication?.domainHeadersToFind)}" + "\nRequired Domain Headers: ${Serializer.json.encodeToString(config.authentication.domainHeadersToFind)}" +
"\nRequired Cookies: ${Serializer.json.encodeToString(config.authentication?.cookiesToFind)}",); "\nRequired Cookies: ${Serializer.json.encodeToString(config.authentication.cookiesToFind)}",);
} }
constructor(auth: SourcePluginAuthConfig) : super() { constructor(auth: SourcePluginAuthConfig) : super() {
_pluginConfig = null; _pluginConfig = null;

View File

@ -3,8 +3,6 @@ package com.futo.platformplayer.others
import android.net.Uri import android.net.Uri
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebResourceRequest import android.webkit.WebResourceRequest
import android.webkit.WebView
import com.futo.platformplayer.api.media.platforms.js.SourceAuth
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.matchesDomain import com.futo.platformplayer.matchesDomain
@ -33,13 +31,15 @@ class WebViewRequirementExtractor {
} }
fun handleRequest(view: WebView?, request: WebResourceRequest, logVerbose: Boolean = false): ExtractedData? { fun handleRequest(request: WebResourceRequest, logVerbose: Boolean = false): ExtractedData? {
val domain = request.url.host; val domain = request.url.host;
val domainLower = request.url.host?.lowercase(); val domainLower = request.url.host?.lowercase();
if(completionUrl == null) if (completionUrl == null) {
urlFound = true; urlFound = true;
else urlFound = urlFound || request.url == Uri.parse(completionUrl); } else {
urlFound = urlFound || request.url == Uri.parse(completionUrl)
}
//HEADERS //HEADERS
if(domainLower != null) { if(domainLower != null) {

View File

@ -1,22 +1,12 @@
package com.futo.platformplayer.parsers package com.futo.platformplayer.parsers
import android.view.View
import com.futo.platformplayer.R
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantAudioUrlSource import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantAudioUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantSubtitleUrlSource import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantSubtitleUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantVideoUrlSource import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantVideoUrlSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource
import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource
import com.futo.platformplayer.api.media.models.streams.sources.IVideoSource
import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.toYesNo import com.futo.platformplayer.toYesNo
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuGroup
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
import com.futo.platformplayer.yesNoToBoolean import com.futo.platformplayer.yesNoToBoolean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.net.URI import java.net.URI
import java.time.ZonedDateTime import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@ -73,7 +63,7 @@ class HLS {
val segments = mutableListOf<Segment>() val segments = mutableListOf<Segment>()
var currentSegment: MediaSegment? = null var currentSegment: MediaSegment? = null
lines.forEachIndexed { index, line -> lines.forEach { line ->
when { when {
line.startsWith("#EXTINF:") -> { line.startsWith("#EXTINF:") -> {
val duration = line.substringAfter(":").substringBefore(",").toDoubleOrNull() val duration = line.substringAfter(":").substringBefore(",").toDoubleOrNull()

View File

@ -1,7 +1,5 @@
package com.futo.platformplayer.polycentric package com.futo.platformplayer.polycentric
import com.futo.polycentric.core.*
import userpackage.Protocol
import com.futo.platformplayer.api.media.PlatformID import com.futo.platformplayer.api.media.PlatformID
import com.futo.platformplayer.constructs.BatchedTaskHandler import com.futo.platformplayer.constructs.BatchedTaskHandler
import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile import com.futo.platformplayer.fragment.mainactivity.main.PolycentricProfile
@ -12,9 +10,25 @@ import com.futo.platformplayer.serializers.OffsetDateTimeSerializer
import com.futo.platformplayer.states.StatePolycentric import com.futo.platformplayer.states.StatePolycentric
import com.futo.platformplayer.stores.CachedPolycentricProfileStorage import com.futo.platformplayer.stores.CachedPolycentricProfileStorage
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.polycentric.core.ApiMethods
import com.futo.polycentric.core.ContentType
import com.futo.polycentric.core.OwnedClaim
import com.futo.polycentric.core.PublicKey
import com.futo.polycentric.core.SignedEvent
import com.futo.polycentric.core.StorageTypeSystemState
import com.futo.polycentric.core.SystemState
import com.futo.polycentric.core.base64ToByteArray
import com.futo.polycentric.core.base64UrlToByteArray
import com.futo.polycentric.core.getClaimIfValid
import com.futo.polycentric.core.getValidClaims
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import userpackage.Protocol
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.time.OffsetDateTime import java.time.OffsetDateTime
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -251,8 +265,11 @@ class PolycentricCache {
Logger.v(TAG, "getProfileAsync (id: $id) != null (with retrieved valid claims)") Logger.v(TAG, "getProfileAsync (id: $id) != null (with retrieved valid claims)")
return getProfileAsync(claims.ownedClaims.first().system).await() return getProfileAsync(claims.ownedClaims.first().system).await()
} else { } else {
if(urlNullCache != null) synchronized (_cache) {
_profileUrlCache.setAndSave(urlNullCache, PolycentricCache.CachedPolycentricProfile(null)); if (urlNullCache != null) {
_profileUrlCache.setAndSave(urlNullCache, CachedPolycentricProfile(null))
}
}
return null; return null;
} }
} }

View File

@ -4,9 +4,10 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageInstaller import android.content.pm.PackageInstaller
import com.futo.platformplayer.logging.Logger import android.os.Build
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.logging.Logger
class InstallReceiver : BroadcastReceiver() { class InstallReceiver : BroadcastReceiver() {
@ -16,13 +17,19 @@ class InstallReceiver : BroadcastReceiver() {
val onReceiveResult = Event1<String?>(); val onReceiveResult = Event1<String?>();
} }
@Suppress("DEPRECATION")
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1); val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1);
Logger.i(TAG, "Received status $status."); Logger.i(TAG, "Received status $status.");
when (status) { when (status) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> { PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val activityIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT) val activityIntent: Intent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
} else {
intent.getParcelableExtra(Intent.EXTRA_INTENT)
}
if (activityIntent == null) { if (activityIntent == null) {
Logger.w(TAG, "Received STATUS_PENDING_USER_ACTION and activity intent is null.") Logger.w(TAG, "Received STATUS_PENDING_USER_ACTION and activity intent is null.")
return; return;

View File

@ -1,39 +1,37 @@
package com.futo.platformplayer.serializers package com.futo.platformplayer.serializers
import com.futo.platformplayer.api.media.models.ratings.* import com.futo.platformplayer.api.media.models.ratings.IRating
import com.futo.platformplayer.api.media.models.ratings.RatingLikeDislikes
import com.futo.platformplayer.api.media.models.ratings.RatingLikes
import com.futo.platformplayer.api.media.models.ratings.RatingScaler
import com.futo.platformplayer.api.media.models.ratings.RatingType
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.KSerializer import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.PolymorphicSerializer import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.json.int
import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.*
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneOffset
import kotlin.reflect.KClass
class IRatingSerializer() : JsonContentPolymorphicSerializer<IRating>(IRating::class) { class IRatingSerializer() : JsonContentPolymorphicSerializer<IRating>(IRating::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out IRating> { override fun selectDeserializer(element: JsonElement): DeserializationStrategy<IRating> {
val obj = element.jsonObject["type"]; val obj = element.jsonObject["type"];
if(obj?.jsonPrimitive?.isString ?: true) return if(obj?.jsonPrimitive?.isString != false) {
return when(obj?.jsonPrimitive?.contentOrNull) { when (obj?.jsonPrimitive?.contentOrNull) {
"LIKES" -> RatingLikes.serializer(); "LIKES" -> RatingLikes.serializer();
"LIKEDISLIKES" -> RatingLikeDislikes.serializer(); "LIKEDISLIKES" -> RatingLikeDislikes.serializer();
"SCALE" -> RatingScaler.serializer(); "SCALE" -> RatingScaler.serializer();
else -> throw NotImplementedError("Rating Value: ${obj?.jsonPrimitive?.contentOrNull}") else -> throw NotImplementedError("Rating Value: ${obj?.jsonPrimitive?.contentOrNull}")
}; };
else } else {
return when(element.jsonObject["type"]?.jsonPrimitive?.int) { when (element.jsonObject["type"]?.jsonPrimitive?.int) {
RatingType.LIKES.value -> RatingLikes.serializer(); RatingType.LIKES.value -> RatingLikes.serializer();
RatingType.LIKEDISLIKES.value -> RatingLikeDislikes.serializer(); RatingType.LIKEDISLIKES.value -> RatingLikeDislikes.serializer();
RatingType.SCALE.value -> RatingScaler.serializer(); RatingType.SCALE.value -> RatingScaler.serializer();
else -> throw NotImplementedError("Rating Value: ${obj?.jsonPrimitive?.int}") else -> throw NotImplementedError("Rating Value: ${obj.jsonPrimitive.int}")
}; };
}
} }
} }

View File

@ -6,33 +6,40 @@ import com.futo.platformplayer.api.media.models.video.SerializedPlatformNestedCo
import com.futo.platformplayer.api.media.models.video.SerializedPlatformPost import com.futo.platformplayer.api.media.models.video.SerializedPlatformPost
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.* import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
class PlatformContentSerializer() : JsonContentPolymorphicSerializer<SerializedPlatformContent>(SerializedPlatformContent::class) { class PlatformContentSerializer : JsonContentPolymorphicSerializer<SerializedPlatformContent>(SerializedPlatformContent::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out SerializedPlatformContent> { override fun selectDeserializer(element: JsonElement): DeserializationStrategy<SerializedPlatformContent> {
val obj = element.jsonObject["contentType"]; val obj = element.jsonObject["contentType"];
//TODO: Remove this temporary fallback..at some point //TODO: Remove this temporary fallback..at some point
if(obj == null && element.jsonObject["isLive"]?.jsonPrimitive?.booleanOrNull != null) if(obj == null && element.jsonObject["isLive"]?.jsonPrimitive?.booleanOrNull != null)
return SerializedPlatformVideo.serializer(); return SerializedPlatformVideo.serializer();
if(obj?.jsonPrimitive?.isString ?: true) if(obj?.jsonPrimitive?.isString != false) {
return when(obj?.jsonPrimitive?.contentOrNull) { return when (obj?.jsonPrimitive?.contentOrNull) {
"MEDIA" -> SerializedPlatformVideo.serializer(); "MEDIA" -> SerializedPlatformVideo.serializer();
"NESTED_VIDEO" -> SerializedPlatformNestedContent.serializer(); "NESTED_VIDEO" -> SerializedPlatformNestedContent.serializer();
"ARTICLE" -> throw NotImplementedError("Articles not yet implemented"); "ARTICLE" -> throw NotImplementedError("Articles not yet implemented");
"POST" -> SerializedPlatformPost.serializer(); "POST" -> SerializedPlatformPost.serializer();
else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.contentOrNull}") else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.contentOrNull}")
}; };
else } else {
return when(obj?.jsonPrimitive?.int) { return when (obj.jsonPrimitive.int) {
ContentType.MEDIA.value -> SerializedPlatformVideo.serializer(); ContentType.MEDIA.value -> SerializedPlatformVideo.serializer();
ContentType.NESTED_VIDEO.value -> SerializedPlatformNestedContent.serializer(); ContentType.NESTED_VIDEO.value -> SerializedPlatformNestedContent.serializer();
ContentType.ARTICLE.value -> throw NotImplementedError("Articles not yet implemented"); ContentType.ARTICLE.value -> throw NotImplementedError("Articles not yet implemented");
ContentType.POST.value -> SerializedPlatformPost.serializer(); ContentType.POST.value -> SerializedPlatformPost.serializer();
else -> throw NotImplementedError("Unknown Content Type Value: ${obj?.jsonPrimitive?.int}") else -> throw NotImplementedError("Unknown Content Type Value: ${obj.jsonPrimitive.int}")
}; };
}
} }
} }

View File

@ -4,12 +4,16 @@ import com.futo.platformplayer.api.media.models.video.ISerializedVideoSourceDesc
import com.futo.platformplayer.api.media.models.video.SerializedVideoMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.video.SerializedVideoMuxedSourceDescriptor
import com.futo.platformplayer.api.media.models.video.SerializedVideoNonMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.video.SerializedVideoNonMuxedSourceDescriptor
import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.* import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
class VideoDescriptorSerializer() : JsonContentPolymorphicSerializer<ISerializedVideoSourceDescriptor>(ISerializedVideoSourceDescriptor::class) { class VideoDescriptorSerializer() : JsonContentPolymorphicSerializer<ISerializedVideoSourceDescriptor>(ISerializedVideoSourceDescriptor::class) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out ISerializedVideoSourceDescriptor> { override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ISerializedVideoSourceDescriptor> {
return when(element.jsonObject["isUnMuxed"]?.jsonPrimitive?.boolean) { return when(element.jsonObject["isUnMuxed"]?.jsonPrimitive?.boolean) {
false -> SerializedVideoMuxedSourceDescriptor.serializer(); false -> SerializedVideoMuxedSourceDescriptor.serializer();
true -> SerializedVideoNonMuxedSourceDescriptor.serializer(); true -> SerializedVideoNonMuxedSourceDescriptor.serializer();

View File

@ -7,15 +7,16 @@ import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import com.futo.platformplayer.* import com.futo.platformplayer.R
import com.futo.platformplayer.Settings
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.downloads.VideoDownload import com.futo.platformplayer.downloads.VideoDownload
import com.futo.platformplayer.exceptions.DownloadException import com.futo.platformplayer.exceptions.DownloadException
import com.futo.platformplayer.getNowDiffMinutes
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.Announcement import com.futo.platformplayer.states.Announcement
import com.futo.platformplayer.states.AnnouncementType import com.futo.platformplayer.states.AnnouncementType
@ -150,10 +151,16 @@ class DownloadService : Service() {
currentVideo.changeState(VideoDownload.State.ERROR); currentVideo.changeState(VideoDownload.State.ERROR);
ignore.add(currentVideo); ignore.add(currentVideo);
if(ex !is CancellationException) if(ex !is CancellationException) {
StateAnnouncement.instance.registerAnnouncement(currentVideo?.id?.value?:"" + currentVideo?.id?.pluginId?:"" + "_FailDownload", StateAnnouncement.instance.registerAnnouncement(
currentVideo.id.value ?: ("" + currentVideo.id.pluginId),
"Download failed", "Download failed",
"Download for [${currentVideo.name}] failed.\nDownloads are automatically retried.\nReason: ${ex.message}", AnnouncementType.SESSION, null, "download"); "Download for [${currentVideo.name}] failed.\nDownloads are automatically retried.\nReason: ${ex.message}",
AnnouncementType.SESSION,
null,
"download"
);
}
//Give it a sec //Give it a sec
Thread.sleep(500); Thread.sleep(500);
@ -262,7 +269,7 @@ class DownloadService : Service() {
fun closeDownloadSession() { fun closeDownloadSession() {
Logger.i(TAG, "closeDownloadSession"); Logger.i(TAG, "closeDownloadSession");
stopForeground(true); stopForeground(STOP_FOREGROUND_DETACH);
_notificationManager?.cancel(DOWNLOAD_NOTIF_ID); _notificationManager?.cancel(DOWNLOAD_NOTIF_ID);
stopService(); stopService();
_started = false; _started = false;

View File

@ -6,23 +6,26 @@ import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.FileProvider import com.futo.platformplayer.R
import com.futo.platformplayer.*
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.downloads.VideoExport import com.futo.platformplayer.downloads.VideoExport
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.share
import com.futo.platformplayer.states.Announcement import com.futo.platformplayer.states.Announcement
import com.futo.platformplayer.states.AnnouncementType import com.futo.platformplayer.states.AnnouncementType
import com.futo.platformplayer.states.StateAnnouncement import com.futo.platformplayer.states.StateAnnouncement
import com.futo.platformplayer.states.StateDownloads import com.futo.platformplayer.states.StateDownloads
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.UUID import java.util.UUID
@ -184,7 +187,7 @@ class ExportingService : Service() {
fun closeExportSession() { fun closeExportSession() {
Logger.i(TAG, "closeExportSession"); Logger.i(TAG, "closeExportSession");
stopForeground(true); stopForeground(STOP_FOREGROUND_DETACH);
_notificationManager?.cancel(EXPORT_NOTIF_ID); _notificationManager?.cancel(EXPORT_NOTIF_ID);
stopService(); stopService();
_started = false; _started = false;

View File

@ -1,6 +1,10 @@
package com.futo.platformplayer.services package com.futo.platformplayer.services
import android.app.* import android.app.ActivityManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
@ -21,14 +25,14 @@ import androidx.core.app.NotificationCompat
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.request.transition.Transition
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideo
import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.receivers.MediaControlReceiver import com.futo.platformplayer.receivers.MediaControlReceiver
import com.futo.platformplayer.states.StatePlatform
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
class MediaPlaybackService : Service() { class MediaPlaybackService : Service() {
@ -148,7 +152,7 @@ class MediaPlaybackService : Service() {
fun closeMediaSession() { fun closeMediaSession() {
Logger.v(TAG, "closeMediaSession"); Logger.v(TAG, "closeMediaSession");
stopForeground(true); stopForeground(STOP_FOREGROUND_DETACH);
val focusRequest = _focusRequest; val focusRequest = _focusRequest;
if (focusRequest != null) { if (focusRequest != null) {
@ -214,7 +218,7 @@ class MediaPlaybackService : Service() {
else else
notifyMediaSession(video, null); notifyMediaSession(video, null);
} }
private fun generateMediaAction(context: Context, icon: Int, title: String, intent: PendingIntent) : NotificationCompat.Action { private fun generateMediaAction(icon: Int, title: String, intent: PendingIntent) : NotificationCompat.Action {
return NotificationCompat.Action.Builder(icon, title, intent).build(); return NotificationCompat.Action.Builder(icon, title, intent).build();
} }
private fun notifyMediaSession(video: IPlatformVideo?, desiredBitmap: Bitmap?) { private fun notifyMediaSession(video: IPlatformVideo?, desiredBitmap: Bitmap?) {
@ -259,17 +263,37 @@ class MediaPlaybackService : Service() {
val playWhenReady = StatePlayer.instance.isPlaying; val playWhenReady = StatePlayer.instance.isPlaying;
if(hasQueue) if(hasQueue)
builder = builder.addAction(generateMediaAction(this, R.drawable.ic_fast_rewind_notif, "Back", MediaControlReceiver.getPrevIntent(this, 3))) builder = builder.addAction(generateMediaAction(
R.drawable.ic_fast_rewind_notif,
"Back",
MediaControlReceiver.getPrevIntent(this, 3)
))
if(playWhenReady) if(playWhenReady)
builder = builder.addAction(generateMediaAction(this, R.drawable.ic_pause_notif, "Pause", MediaControlReceiver.getPauseIntent(this, 2))); builder = builder.addAction(generateMediaAction(
R.drawable.ic_pause_notif,
"Pause",
MediaControlReceiver.getPauseIntent(this, 2)
));
else else
builder = builder.addAction(generateMediaAction(this, R.drawable.ic_play_notif, "Play", MediaControlReceiver.getPlayIntent(this, 1))); builder = builder.addAction(generateMediaAction(
R.drawable.ic_play_notif,
"Play",
MediaControlReceiver.getPlayIntent(this, 1)
));
if(hasQueue) if(hasQueue)
builder = builder.addAction(generateMediaAction(this, R.drawable.ic_fast_forward_notif, "Forward", MediaControlReceiver.getNextIntent(this, 4))); builder = builder.addAction(generateMediaAction(
R.drawable.ic_fast_forward_notif,
"Forward",
MediaControlReceiver.getNextIntent(this, 4)
));
builder = builder.addAction(generateMediaAction(this, R.drawable.ic_stop_notif, "Stop", MediaControlReceiver.getCloseIntent(this, 5))); builder = builder.addAction(generateMediaAction(
R.drawable.ic_stop_notif,
"Stop",
MediaControlReceiver.getCloseIntent(this, 5)
));
if(bitmap?.isRecycled ?: false) if(bitmap?.isRecycled ?: false)
bitmap = null; bitmap = null;

View File

@ -1,19 +1,15 @@
package com.futo.platformplayer.states package com.futo.platformplayer.states
import android.content.Context
import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.http.ManagedHttpClient
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer import com.futo.platformplayer.serializers.OffsetDateTimeNullableSerializer
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.StringHashSetStorage import com.futo.platformplayer.stores.StringHashSetStorage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.Random import java.util.Random
@ -70,9 +66,7 @@ class StateAnnouncement {
synchronized(_lock) { synchronized(_lock) {
val idActual = id ?: UUID.randomUUID().toString(); val idActual = id ?: UUID.randomUUID().toString();
val announcement = SessionAnnouncement(idActual, title, msg, announceType, time, category, actionButton, idActual); val announcement = SessionAnnouncement(idActual, title, msg, announceType, time, category, actionButton, idActual);
_sessionActions[idActual] = action;
if(action != null)
_sessionActions.put(idActual, action);
registerAnnouncementSession(announcement); registerAnnouncementSession(announcement);
} }
} }
@ -81,20 +75,21 @@ class StateAnnouncement {
val idActual = id ?: UUID.randomUUID().toString(); val idActual = id ?: UUID.randomUUID().toString();
val announcement = SessionAnnouncement(idActual, title, msg, announceType, time, category, actionButton, idActual, cancelButton, if(cancelAction != null) idActual + "_cancel" else null); val announcement = SessionAnnouncement(idActual, title, msg, announceType, time, category, actionButton, idActual, cancelButton, if(cancelAction != null) idActual + "_cancel" else null);
if(action != null) _sessionActions.put(idActual, action);
_sessionActions.put(idActual, action); if(cancelAction != null) {
if(cancelAction != null)
_sessionActions.put(idActual + "_cancel", cancelAction); _sessionActions.put(idActual + "_cancel", cancelAction);
}
registerAnnouncementSession(announcement); registerAnnouncementSession(announcement);
} }
} }
fun registerAnnouncement(id: String?, title: String, msg: String, announceType: AnnouncementType = AnnouncementType.DELETABLE, time: OffsetDateTime? = null, category: String? = null, actionButton: String? = null, actionId: String? = null) { fun registerAnnouncement(id: String?, title: String, msg: String, announceType: AnnouncementType = AnnouncementType.DELETABLE, time: OffsetDateTime? = null, category: String? = null, actionButton: String? = null, actionId: String? = null) {
val newAnnouncement = Announcement(if(id == null) UUID.randomUUID().toString() else id, title, msg, announceType, time, category, actionButton, actionId); val newAnnouncement = Announcement(id ?: UUID.randomUUID().toString(), title, msg, announceType, time, category, actionButton, actionId);
if(announceType == AnnouncementType.SESSION || announceType == AnnouncementType.SESSION_RECURRING) if(announceType == AnnouncementType.SESSION || announceType == AnnouncementType.SESSION_RECURRING) {
registerAnnouncementSession(newAnnouncement); registerAnnouncementSession(newAnnouncement);
else } else {
registerAnnouncement(newAnnouncement); registerAnnouncement(newAnnouncement);
}
} }
fun registerAnnouncementSession(announcement: Announcement) { fun registerAnnouncementSession(announcement: Announcement) {
synchronized(_lock) { synchronized(_lock) {

View File

@ -13,7 +13,6 @@ import android.net.NetworkRequest
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Xml
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -23,33 +22,23 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.activities.CaptchaActivity import com.futo.platformplayer.activities.CaptchaActivity
import com.futo.platformplayer.activities.IWithResultLauncher import com.futo.platformplayer.activities.IWithResultLauncher
import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.MainActivity
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
import com.futo.platformplayer.api.media.platforms.js.DevJSClient import com.futo.platformplayer.api.media.platforms.js.DevJSClient
import com.futo.platformplayer.api.media.platforms.js.JSClient import com.futo.platformplayer.api.media.platforms.js.JSClient
import com.futo.platformplayer.background.BackgroundWorker import com.futo.platformplayer.background.BackgroundWorker
import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.casting.StateCasting
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException import com.futo.platformplayer.engine.exceptions.ScriptCaptchaRequiredException
import com.futo.platformplayer.engine.exceptions.ScriptLoginRequiredException
import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment import com.futo.platformplayer.fragment.mainactivity.main.HomeFragment
import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment import com.futo.platformplayer.fragment.mainactivity.main.SourceDetailFragment
import com.futo.platformplayer.logging.AndroidLogConsumer import com.futo.platformplayer.logging.AndroidLogConsumer
import com.futo.platformplayer.logging.FileLogConsumer import com.futo.platformplayer.logging.FileLogConsumer
import com.futo.platformplayer.logging.LogLevel import com.futo.platformplayer.logging.LogLevel
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.models.HistoryVideo
import com.futo.platformplayer.receivers.AudioNoisyReceiver import com.futo.platformplayer.receivers.AudioNoisyReceiver
import com.futo.platformplayer.serializers.PlatformContentSerializer
import com.futo.platformplayer.services.DownloadService import com.futo.platformplayer.services.DownloadService
import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.stores.db.ManagedDBStore
import com.futo.platformplayer.stores.db.types.DBHistory
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
import com.futo.platformplayer.stores.v2.ManagedStore import com.futo.platformplayer.stores.v2.ManagedStore
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.util.* import java.util.*
@ -393,7 +382,7 @@ class StateApp {
scopeOrNull?.launch(Dispatchers.Main) { scopeOrNull?.launch(Dispatchers.Main) {
try { try {
if (it != null) { if (it != null) {
UIDialogs.toast("Uploaded " + (it ?: "null"), true); UIDialogs.toast("Uploaded $it", true);
} else { } else {
UIDialogs.toast("Failed to upload"); UIDialogs.toast("Failed to upload");
} }
@ -752,9 +741,6 @@ class StateApp {
}) })
} }
} }
fun handleLoginException(client: JSClient, exception: ScriptLoginRequiredException, onSuccess: (client: JSClient)->Unit) {
}
fun getLocaleContext(baseContext: Context?): Context? { fun getLocaleContext(baseContext: Context?): Context? {
val locale = getLocaleSetting(baseContext); val locale = getLocaleSetting(baseContext);

View File

@ -2,7 +2,6 @@ package com.futo.platformplayer.states
import android.content.Context import android.content.Context
import kotlin.streams.asSequence import kotlin.streams.asSequence
import kotlin.streams.toList
/*** /***
* Used to read assets * Used to read assets
@ -33,28 +32,28 @@ class StateAssets {
/** /**
* Does basic asset resolving under certain conditions * Does basic asset resolving under certain conditions
*/ */
fun readAssetRelative(context: Context, base: String, path: String, cache: Boolean = false) : String? { fun readAssetRelative(context: Context, base: String, path: String) : String? {
val finalPath = resolvePath(base, path); val finalPath = resolvePath(base, path);
return readAsset(context, finalPath, cache); return readAsset(context, finalPath);
} }
fun readAssetBinRelative(context: Context, base: String, path: String) : ByteArray? { fun readAssetBinRelative(context: Context, base: String, path: String) : ByteArray? {
val finalPath = resolvePath(base, path); val finalPath = resolvePath(base, path);
return readAssetBin(context, finalPath); return readAssetBin(context, finalPath);
} }
fun readAsset(context: Context, path: String, cache: Boolean = false) : String? { fun readAsset(context: Context, path: String) : String? {
var text: String? = null; var text: String?;
synchronized(_cache) { synchronized(_cache) {
if (!_cache.containsKey(path)) { if (!_cache.containsKey(path)) {
text = context text = context.assets
?.assets
?.open(path) ?.open(path)
?.bufferedReader() ?.bufferedReader()
?.use { it.readText(); }; ?.use { it.readText(); };
_cache.put(path, text); _cache.put(path, text);
} else {
text = _cache[path];
} }
else
text = _cache.get(path);
} }
return text; return text;
} }

Some files were not shown because too many files have changed in this diff Show More