mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-09-18 15:32:35 +00:00
VOD chat support
This commit is contained in:
parent
ac9a51f105
commit
99fb9b3462
12 changed files with 180 additions and 11 deletions
|
@ -11,6 +11,7 @@ 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.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.platforms.js.models.JSVODEventPager
|
||||||
import com.futo.platformplayer.api.media.structures.IPager
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
import com.futo.platformplayer.constructs.BatchedTaskHandler
|
import com.futo.platformplayer.constructs.BatchedTaskHandler
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
@ -26,12 +27,17 @@ class LiveChatManager {
|
||||||
private val _emojiCache: EmojiCache = EmojiCache();
|
private val _emojiCache: EmojiCache = EmojiCache();
|
||||||
private val _pager: IPager<IPlatformLiveEvent>?;
|
private val _pager: IPager<IPlatformLiveEvent>?;
|
||||||
|
|
||||||
|
private var _position: Long = 0;
|
||||||
|
private var _eventsPosition: Long = 0;
|
||||||
|
|
||||||
private val _history: ArrayList<IPlatformLiveEvent> = arrayListOf();
|
private val _history: ArrayList<IPlatformLiveEvent> = arrayListOf();
|
||||||
|
|
||||||
private var _startCounter = 0;
|
private var _startCounter = 0;
|
||||||
|
|
||||||
private val _followers: HashMap<Any, (List<IPlatformLiveEvent>) -> Unit> = hashMapOf();
|
private val _followers: HashMap<Any, (List<IPlatformLiveEvent>) -> Unit> = hashMapOf();
|
||||||
|
|
||||||
|
val isVOD get() = _pager is JSVODEventPager;
|
||||||
|
|
||||||
var viewCount: Long = 0
|
var viewCount: Long = 0
|
||||||
private set;
|
private set;
|
||||||
|
|
||||||
|
@ -39,7 +45,23 @@ class LiveChatManager {
|
||||||
_scope = scope;
|
_scope = scope;
|
||||||
_pager = pager;
|
_pager = pager;
|
||||||
viewCount = initialViewCount;
|
viewCount = initialViewCount;
|
||||||
|
if(pager is JSVODEventPager)
|
||||||
|
handleEvents(listOf(LiveEventComment("SYSTEM", null, "VOD chat is still under construction. While it is mostly functional, the experience still needs to be improved.\n")));
|
||||||
|
else
|
||||||
handleEvents(listOf(LiveEventComment("SYSTEM", null, "Live chat is still under construction. While it is mostly functional, the experience still needs to be improved.\n")));
|
handleEvents(listOf(LiveEventComment("SYSTEM", null, "Live chat is still under construction. While it is mostly functional, the experience still needs to be improved.\n")));
|
||||||
|
|
||||||
|
if(pager is JSVODEventPager) {
|
||||||
|
var replayResults = pager.getResults().filter { it.time > _eventsPosition || it is LiveEventEmojis };
|
||||||
|
//TODO: Remove this once dripfeed is done properly
|
||||||
|
replayResults = replayResults.filter{ it.time < _eventsPosition + 1500 || it is LiveEventEmojis };
|
||||||
|
if(replayResults.size > 0) {
|
||||||
|
_eventsPosition = replayResults.maxOf { it.time };
|
||||||
|
Logger.i(TAG, "VOD Events last event: " + _eventsPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_eventsPosition = _eventsPosition + 1500;
|
||||||
|
}
|
||||||
|
else
|
||||||
handleEvents(pager.getResults());
|
handleEvents(pager.getResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +74,10 @@ class LiveChatManager {
|
||||||
_startCounter++;
|
_startCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setVideoPosition(ms: Long) {
|
||||||
|
_position = ms;
|
||||||
|
}
|
||||||
|
|
||||||
fun getHistory(): List<IPlatformLiveEvent> {
|
fun getHistory(): List<IPlatformLiveEvent> {
|
||||||
synchronized(_history) {
|
synchronized(_history) {
|
||||||
return _history.toList();
|
return _history.toList();
|
||||||
|
@ -85,13 +111,36 @@ class LiveChatManager {
|
||||||
try {
|
try {
|
||||||
while(_startCounter == counter) {
|
while(_startCounter == counter) {
|
||||||
var nextInterval = 1000L;
|
var nextInterval = 1000L;
|
||||||
|
if(_pager is JSVODEventPager && _eventsPosition > _position) {
|
||||||
|
delay(500);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(_pager == null || !_pager.hasMorePages())
|
if(_pager == null || !_pager.hasMorePages())
|
||||||
return@launch;
|
return@launch;
|
||||||
|
val newEvents = if(_pager is JSVODEventPager) {
|
||||||
|
val requestPosition = _position;
|
||||||
|
_pager.nextPage(requestPosition.toInt());
|
||||||
|
var replayResults = _pager.getResults().filter { it.time > requestPosition || it is LiveEventEmojis };
|
||||||
|
//TODO: Remove this once dripfeed is done properly
|
||||||
|
replayResults = replayResults.filter{ it.time < requestPosition + 1500 || it is LiveEventEmojis };
|
||||||
|
if(replayResults.size > 0) {
|
||||||
|
_eventsPosition = replayResults.maxOf { it.time };
|
||||||
|
Logger.i(TAG, "VOD Events last event: " + _eventsPosition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
_eventsPosition = requestPosition + _pager.nextRequest.coerceAtLeast(800).toLong();
|
||||||
|
replayResults;
|
||||||
|
}
|
||||||
|
else {
|
||||||
_pager.nextPage();
|
_pager.nextPage();
|
||||||
val newEvents = _pager.getResults();
|
_pager.getResults();
|
||||||
|
}
|
||||||
if(_pager is JSLiveEventPager)
|
if(_pager is JSLiveEventPager)
|
||||||
nextInterval = _pager.nextRequest.coerceAtLeast(800).toLong();
|
nextInterval = _pager.nextRequest.coerceAtLeast(800).toLong();
|
||||||
|
else if(_pager is JSVODEventPager)
|
||||||
|
nextInterval = _pager.nextRequest.coerceAtLeast(800).toLong();
|
||||||
|
|
||||||
if(newEvents.size > 0)
|
if(newEvents.size > 0)
|
||||||
Logger.i(TAG, "New Live Events (${newEvents.size}) [${newEvents.map { it.type.name }.joinToString(", ")}]");
|
Logger.i(TAG, "New Live Events (${newEvents.size}) [${newEvents.map { it.type.name }.joinToString(", ")}]");
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.futo.platformplayer.getOrThrow
|
||||||
|
|
||||||
interface IPlatformLiveEvent {
|
interface IPlatformLiveEvent {
|
||||||
val type : LiveEventType;
|
val type : LiveEventType;
|
||||||
|
var time: Long;
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -18,12 +18,15 @@ class LiveEventComment: IPlatformLiveEvent, ILiveEventChatMessage {
|
||||||
val colorName: String?;
|
val colorName: String?;
|
||||||
val badges: List<String>;
|
val badges: List<String>;
|
||||||
|
|
||||||
constructor(name: String, thumbnail: String?, message: String, colorName: String? = null, badges: List<String>? = null) {
|
override var time: Long = -1;
|
||||||
|
|
||||||
|
constructor(name: String, thumbnail: String?, message: String, colorName: String? = null, badges: List<String>? = null, time: Long = -1) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.thumbnail = thumbnail;
|
this.thumbnail = thumbnail;
|
||||||
this.colorName = colorName;
|
this.colorName = colorName;
|
||||||
this.badges = badges ?: listOf();
|
this.badges = badges ?: listOf();
|
||||||
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -39,7 +42,8 @@ class LiveEventComment: IPlatformLiveEvent, ILiveEventChatMessage {
|
||||||
obj.getOrThrow(config, "name", contextName),
|
obj.getOrThrow(config, "name", contextName),
|
||||||
obj.getOrThrow(config, "thumbnail", contextName, true),
|
obj.getOrThrow(config, "thumbnail", contextName, true),
|
||||||
obj.getOrThrow(config, "message", contextName),
|
obj.getOrThrow(config, "message", contextName),
|
||||||
colorName, badges);
|
colorName, badges,
|
||||||
|
obj.getOrDefault(config, "time", contextName, -1) ?: -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,8 @@ class LiveEventDonation: IPlatformLiveEvent, ILiveEventChatMessage {
|
||||||
|
|
||||||
var expire: Int = 6000;
|
var expire: Int = 6000;
|
||||||
|
|
||||||
|
override var time: Long = -1;
|
||||||
|
|
||||||
|
|
||||||
constructor(name: String, thumbnail: String?, message: String, amount: String, expire: Int = 6000, colorDonation: String? = null) {
|
constructor(name: String, thumbnail: String?, message: String, amount: String, expire: Int = 6000, colorDonation: String? = null) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
|
|
@ -10,6 +10,8 @@ class LiveEventEmojis: IPlatformLiveEvent {
|
||||||
|
|
||||||
val emojis: HashMap<String, String>;
|
val emojis: HashMap<String, String>;
|
||||||
|
|
||||||
|
override var time: Long = -1;
|
||||||
|
|
||||||
constructor(emojis: HashMap<String, String>) {
|
constructor(emojis: HashMap<String, String>) {
|
||||||
this.emojis = emojis;
|
this.emojis = emojis;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ class LiveEventRaid: IPlatformLiveEvent {
|
||||||
val targetUrl: String;
|
val targetUrl: String;
|
||||||
val isOutgoing: Boolean;
|
val isOutgoing: Boolean;
|
||||||
|
|
||||||
|
override var time: Long = -1;
|
||||||
|
|
||||||
constructor(name: String, url: String, thumbnail: String, isOutgoing: Boolean) {
|
constructor(name: String, url: String, thumbnail: String, isOutgoing: Boolean) {
|
||||||
this.targetName = name;
|
this.targetName = name;
|
||||||
this.targetUrl = url;
|
this.targetUrl = url;
|
||||||
|
|
|
@ -10,6 +10,8 @@ class LiveEventViewCount: IPlatformLiveEvent {
|
||||||
|
|
||||||
val viewCount: Int;
|
val viewCount: Int;
|
||||||
|
|
||||||
|
override var time: Long = -1;
|
||||||
|
|
||||||
constructor(viewCount: Int) {
|
constructor(viewCount: Int) {
|
||||||
this.viewCount = viewCount;
|
this.viewCount = viewCount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ abstract class JSPager<T> : IPager<T> {
|
||||||
protected var pager: V8ValueObject;
|
protected var pager: V8ValueObject;
|
||||||
|
|
||||||
private var _lastResults: List<T>? = null;
|
private var _lastResults: List<T>? = null;
|
||||||
private var _resultChanged: Boolean = true;
|
protected var _resultChanged: Boolean = true;
|
||||||
private var _hasMorePages: Boolean = false;
|
protected var _hasMorePages: Boolean = false;
|
||||||
//private var _morePagesWasFalse: Boolean = false;
|
//private var _morePagesWasFalse: Boolean = false;
|
||||||
|
|
||||||
val isAvailable get() = plugin.getUnderlyingPlugin()._runtime?.let { !it.isClosed && !it.isDead } ?: false;
|
val isAvailable get() = plugin.getUnderlyingPlugin()._runtime?.let { !it.isClosed && !it.isDead } ?: false;
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.futo.platformplayer.api.media.platforms.js.models
|
||||||
|
|
||||||
|
import com.caoccao.javet.values.V8Value
|
||||||
|
import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
|
import com.futo.platformplayer.api.media.models.live.IPlatformLiveEvent
|
||||||
|
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||||
|
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||||
|
import com.futo.platformplayer.api.media.structures.IPlatformLiveEventPager
|
||||||
|
import com.futo.platformplayer.getOrDefault
|
||||||
|
import com.futo.platformplayer.getOrThrow
|
||||||
|
import com.futo.platformplayer.invokeV8
|
||||||
|
import com.futo.platformplayer.warnIfMainThread
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
class JSVODEventPager : JSPager<IPlatformLiveEvent>, IPlatformLiveEventPager {
|
||||||
|
override var nextRequest: Int;
|
||||||
|
|
||||||
|
constructor(config: SourcePluginConfig, plugin: JSClient, pager: V8ValueObject) : super(config, plugin, pager) {
|
||||||
|
nextRequest = pager.getOrThrow(config, "nextRequest", "LiveEventPager");
|
||||||
|
}
|
||||||
|
|
||||||
|
fun nextPage(ms: Int) = plugin.isBusyWith("JSLiveEventPager.nextPage") {
|
||||||
|
warnIfMainThread("VODEventPager.nextPage");
|
||||||
|
|
||||||
|
val pluginV8 = plugin.getUnderlyingPlugin();
|
||||||
|
pluginV8.busy {
|
||||||
|
val newPager: V8Value = pluginV8.catchScriptErrors("[${plugin.config.name}] JSPager", "pager.nextPage(...)") {
|
||||||
|
pager.invokeV8<V8Value>("nextPage", ms);
|
||||||
|
};
|
||||||
|
if(newPager is V8ValueObject)
|
||||||
|
pager = newPager;
|
||||||
|
_hasMorePages = pager.getOrDefault(config, "hasMore", "Pager", false) ?: false;
|
||||||
|
_resultChanged = true;
|
||||||
|
}
|
||||||
|
nextRequest = pager.getOrThrow(config, "nextRequest", "LiveEventPager");
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nextPage() = nextPage(0);
|
||||||
|
|
||||||
|
override fun convertResult(obj: V8ValueObject): IPlatformLiveEvent {
|
||||||
|
return IPlatformLiveEvent.fromV8(config, obj, "LiveEventPager");
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import com.caoccao.javet.values.reference.V8ValueObject
|
||||||
import com.futo.platformplayer.api.media.IPlatformClient
|
import com.futo.platformplayer.api.media.IPlatformClient
|
||||||
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.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.ratings.IRating
|
import com.futo.platformplayer.api.media.models.ratings.IRating
|
||||||
import com.futo.platformplayer.api.media.models.ratings.RatingLikes
|
import com.futo.platformplayer.api.media.models.ratings.RatingLikes
|
||||||
|
@ -26,12 +27,15 @@ import com.futo.platformplayer.getOrThrow
|
||||||
import com.futo.platformplayer.getOrThrowNullable
|
import com.futo.platformplayer.getOrThrowNullable
|
||||||
import com.futo.platformplayer.invokeV8
|
import com.futo.platformplayer.invokeV8
|
||||||
import com.futo.platformplayer.states.StateDeveloper
|
import com.futo.platformplayer.states.StateDeveloper
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
class JSVideoDetails : JSVideo, IPlatformVideoDetails {
|
class JSVideoDetails : JSVideo, IPlatformVideoDetails {
|
||||||
private val _plugin: JSClient;
|
private val _plugin: JSClient;
|
||||||
private val _hasGetComments: Boolean;
|
private val _hasGetComments: Boolean;
|
||||||
private val _hasGetContentRecommendations: Boolean;
|
private val _hasGetContentRecommendations: Boolean;
|
||||||
private val _hasGetPlaybackTracker: Boolean;
|
private val _hasGetPlaybackTracker: Boolean;
|
||||||
|
private val _hasGetVODEvents: Boolean;
|
||||||
|
|
||||||
//Details
|
//Details
|
||||||
override val description : String;
|
override val description : String;
|
||||||
|
@ -47,7 +51,6 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails {
|
||||||
|
|
||||||
override val subtitles: List<ISubtitleSource>;
|
override val subtitles: List<ISubtitleSource>;
|
||||||
|
|
||||||
|
|
||||||
constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin.config, obj) {
|
constructor(plugin: JSClient, obj: V8ValueObject) : super(plugin.config, obj) {
|
||||||
val contextName = "VideoDetails";
|
val contextName = "VideoDetails";
|
||||||
_plugin = plugin;
|
_plugin = plugin;
|
||||||
|
@ -72,6 +75,7 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails {
|
||||||
_hasGetComments = _content.has("getComments");
|
_hasGetComments = _content.has("getComments");
|
||||||
_hasGetPlaybackTracker = _content.has("getPlaybackTracker");
|
_hasGetPlaybackTracker = _content.has("getPlaybackTracker");
|
||||||
_hasGetContentRecommendations = _content.has("getContentRecommendations");
|
_hasGetContentRecommendations = _content.has("getContentRecommendations");
|
||||||
|
_hasGetVODEvents = _content.has("getVODEvents");
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPlaybackTracker(): IPlaybackTracker? {
|
override fun getPlaybackTracker(): IPlaybackTracker? {
|
||||||
|
@ -138,4 +142,15 @@ class JSVideoDetails : JSVideo, IPlatformVideoDetails {
|
||||||
return@busy JSCommentPager(_pluginConfig, client, commentPager);
|
return@busy JSCommentPager(_pluginConfig, client, commentPager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasVODEvents(): Boolean{
|
||||||
|
return _hasGetVODEvents;
|
||||||
|
}
|
||||||
|
fun getVODEvents(url: String): IPager<IPlatformLiveEvent>? = _plugin.busy {
|
||||||
|
if(!_hasGetVODEvents)
|
||||||
|
return@busy null;
|
||||||
|
|
||||||
|
return@busy JSVODEventPager(_plugin.config, _plugin,
|
||||||
|
_content.invokeV8<V8ValueObject>("getVODEvents", arrayOf<Any>()));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1777,12 +1777,19 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
_liveChat?.stop();
|
_liveChat?.stop();
|
||||||
_liveChat = null;
|
_liveChat = null;
|
||||||
|
var gotLive = false;
|
||||||
if (video.isLive && video.live != null) {
|
if (video.isLive && video.live != null) {
|
||||||
loadLiveChat(video);
|
loadLiveChat(video);
|
||||||
|
gotLive = true;
|
||||||
}
|
}
|
||||||
if (video.isLive && video.live == null && !video.video.videoSources.any())
|
if (video.isLive && video.live == null && !video.video.videoSources.any()) {
|
||||||
startLiveTry(video);
|
startLiveTry(video);
|
||||||
|
gotLive = true;
|
||||||
|
}
|
||||||
|
if(!gotLive && video is JSVideoDetails && video.hasVODEvents()) {
|
||||||
|
Logger.i(TAG, "Loading VOD chat");
|
||||||
|
loadVODChat(video);
|
||||||
|
}
|
||||||
|
|
||||||
_player.updateNextPrevious();
|
_player.updateNextPrevious();
|
||||||
updateMoreButtons();
|
updateMoreButtons();
|
||||||
|
@ -1806,6 +1813,43 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_taskLoadRecommendations.run(videoDetail.url)
|
_taskLoadRecommendations.run(videoDetail.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun loadVODChat(video: IPlatformVideoDetails) {
|
||||||
|
_liveChat?.stop();
|
||||||
|
_container_content_liveChat.cancel();
|
||||||
|
_liveChat = null;
|
||||||
|
if(video !is JSVideoDetails)
|
||||||
|
return;
|
||||||
|
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
var livePager: IPager<IPlatformLiveEvent>?;
|
||||||
|
try {
|
||||||
|
//TODO: Create video.getLiveEvents shortcut/optimalization
|
||||||
|
livePager = video.getVODEvents(video.url);
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to obtain VODEvents pager", ex);
|
||||||
|
livePager = null;
|
||||||
|
}
|
||||||
|
val liveChat = livePager?.let {
|
||||||
|
val liveChatManager = LiveChatManager(fragment.lifecycleScope, livePager, video.viewCount);
|
||||||
|
liveChatManager.start();
|
||||||
|
return@let liveChatManager;
|
||||||
|
}
|
||||||
|
_liveChat = liveChat;
|
||||||
|
|
||||||
|
fragment.lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
try {
|
||||||
|
_container_content_liveChat.load(fragment.lifecycleScope, liveChat, null, if(liveChat != null) video.viewCount else null);
|
||||||
|
switchContentView(_container_content_liveChat);
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to switch content view to vod chat.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ex: Throwable) {
|
||||||
|
Logger.e(TAG, "Failed to load vod chat", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fun loadLiveChat(video: IPlatformVideoDetails) {
|
fun loadLiveChat(video: IPlatformVideoDetails) {
|
||||||
_liveChat?.stop();
|
_liveChat?.stop();
|
||||||
_container_content_liveChat.cancel();
|
_container_content_liveChat.cancel();
|
||||||
|
@ -2962,6 +3006,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
private fun setLastPositionMilliseconds(positionMilliseconds: Long, updateHistory: Boolean) {
|
private fun setLastPositionMilliseconds(positionMilliseconds: Long, updateHistory: Boolean) {
|
||||||
lastPositionMilliseconds = positionMilliseconds;
|
lastPositionMilliseconds = positionMilliseconds;
|
||||||
|
|
||||||
|
_liveChat?.setVideoPosition(lastPositionMilliseconds);
|
||||||
|
|
||||||
val v = video ?: return;
|
val v = video ?: return;
|
||||||
val currentTime = System.currentTimeMillis();
|
val currentTime = System.currentTimeMillis();
|
||||||
if (updateHistory && (_lastPositionSaveTime == -1L || currentTime - _lastPositionSaveTime > 5000)) {
|
if (updateHistory && (_lastPositionSaveTime == -1L || currentTime - _lastPositionSaveTime > 5000)) {
|
||||||
|
|
|
@ -202,6 +202,8 @@ class LiveChatOverlay : LinearLayout {
|
||||||
|
|
||||||
if(viewerCount != null)
|
if(viewerCount != null)
|
||||||
_textViewers.text = viewerCount.toHumanNumber() + " " + context.getString(R.string.viewers);
|
_textViewers.text = viewerCount.toHumanNumber() + " " + context.getString(R.string.viewers);
|
||||||
|
else if(manager != null && manager.isVOD)
|
||||||
|
_textViewers.text = manager.viewCount.toHumanNumber() + " past viewers";
|
||||||
else if(manager != null)
|
else if(manager != null)
|
||||||
_textViewers.text = manager.viewCount.toHumanNumber() + " " + context.getString(R.string.viewers);
|
_textViewers.text = manager.viewCount.toHumanNumber() + " " + context.getString(R.string.viewers);
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue