mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-29 04:50:19 +02:00
Reduced amount of warnings.
This commit is contained in:
parent
150a7d5006
commit
fa12f8277c
@ -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"/>
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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?;
|
||||||
|
@ -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
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
};
|
};
|
@ -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();
|
||||||
};
|
};
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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)");
|
||||||
|
@ -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 ->
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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}");
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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) {
|
||||||
|
@ -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");
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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]!!;
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 ->
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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?) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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}")
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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}")
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user