mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-04-20 03:24:50 +00:00
Deleting playlist video deletes local files, Post links are now clickable, going back to channel page from post now shows channel page correctly, search capabilities correct for channel content search, Fix loader not disappearing in certain cases on post details
This commit is contained in:
parent
b370af9d91
commit
66c7741c38
13 changed files with 103 additions and 17 deletions
|
@ -788,8 +788,8 @@ class UISlideOverlays {
|
|||
return SlideUpMenuOverlay(container.context, container, container.context.getString(R.string.add_to), null, true, items).apply { show() };
|
||||
}
|
||||
|
||||
fun showFiltersOverlay(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>): SlideUpMenuFilters {
|
||||
val overlay = SlideUpMenuFilters(lifecycleScope, container, enabledClientsIds, filterValues);
|
||||
fun showFiltersOverlay(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>, isChannelSearch: Boolean = false): SlideUpMenuFilters {
|
||||
val overlay = SlideUpMenuFilters(lifecycleScope, container, enabledClientsIds, filterValues, isChannelSearch);
|
||||
overlay.show();
|
||||
return overlay;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ data class PlatformClientCapabilities(
|
|||
val hasGetChannelUrlByClaim: Boolean = false,
|
||||
val hasGetChannelTemplateByClaimMap: Boolean = false,
|
||||
val hasGetSearchCapabilities: Boolean = false,
|
||||
val hasGetSearchChannelContentsCapabilities: Boolean = false,
|
||||
val hasGetChannelCapabilities: Boolean = false,
|
||||
val hasGetLiveEvents: Boolean = false,
|
||||
val hasGetLiveChatWindow: Boolean = false,
|
||||
|
|
|
@ -216,6 +216,7 @@ open class JSClient : IPlatformClient {
|
|||
hasGetChannelTemplateByClaimMap = plugin.executeBoolean("!!source.getChannelTemplateByClaimMap") ?: false,
|
||||
hasGetSearchCapabilities = plugin.executeBoolean("!!source.getSearchCapabilities") ?: false,
|
||||
hasGetChannelCapabilities = plugin.executeBoolean("!!source.getChannelCapabilities") ?: false,
|
||||
hasGetSearchChannelContentsCapabilities = plugin.executeBoolean("!!source.getSearchChannelContentsCapabilities") ?: false,
|
||||
hasGetLiveEvents = plugin.executeBoolean("!!source.getLiveEvents") ?: false,
|
||||
hasGetLiveChatWindow = plugin.executeBoolean("!!source.getLiveChatWindow") ?: false,
|
||||
hasGetContentChapters = plugin.executeBoolean("!!source.getContentChapters") ?: false,
|
||||
|
@ -308,6 +309,9 @@ open class JSClient : IPlatformClient {
|
|||
|
||||
@JSDocs(4, "source.getSearchChannelContentsCapabilities()", "Gets capabilities this plugin has for search videos")
|
||||
override fun getSearchChannelContentsCapabilities(): ResultCapabilities {
|
||||
if(!capabilities.hasGetSearchChannelContentsCapabilities)
|
||||
return ResultCapabilities(listOf(ResultCapabilities.TYPE_MIXED));
|
||||
|
||||
ensureEnabled();
|
||||
if (_searchChannelContentsCapabilities != null)
|
||||
return _searchChannelContentsCapabilities!!;
|
||||
|
|
|
@ -34,6 +34,20 @@ class PackageUtilities : V8Package {
|
|||
fun md5(arr: ByteArray): ByteArray {
|
||||
return MessageDigest.getInstance("MD5").digest(arr);
|
||||
}
|
||||
@V8Function
|
||||
fun md5String(str: String): String {
|
||||
return md5(str.toByteArray(Charsets.UTF_8)).fold("") { str, it -> str + "%02x".format(it) };
|
||||
}
|
||||
|
||||
|
||||
@V8Function
|
||||
fun sha256(arr: ByteArray): ByteArray {
|
||||
return MessageDigest.getInstance("SHA-256").digest(arr);
|
||||
}
|
||||
@V8Function
|
||||
fun sha256String(str: String): String {
|
||||
return sha256(str.toByteArray(Charsets.UTF_8)).fold("") { str, it -> str + "%02x".format(it) };
|
||||
}
|
||||
|
||||
@V8Function
|
||||
fun randomUUID(): String {
|
||||
|
|
|
@ -271,7 +271,7 @@ class ChannelFragment : MainFragment() {
|
|||
_taskLoadPolycentricProfile.cancel();
|
||||
_selectedTabIndex = -1;
|
||||
|
||||
if (!isBack) {
|
||||
if (!isBack || _url == null) {
|
||||
_imageBanner.setImageDrawable(null);
|
||||
|
||||
if (parameter is String) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.futo.platformplayer.fragment.mainactivity.main
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.Browser
|
||||
import android.view.LayoutInflater
|
||||
|
@ -220,8 +221,10 @@ class CommentsFragment : MainFragment() {
|
|||
|
||||
Logger.i(TAG, "onAuthorClick: " + c.author.id.value);
|
||||
if(c.author.id.value?.startsWith("polycentric://") ?: false) {
|
||||
val navUrl = "https://harbor.social/" + c.author.id.value?.substring("polycentric://".length);
|
||||
_fragment.navigate<BrowserFragment>(navUrl);
|
||||
//val navUrl = "https://harbor.social/" + c.author.id.value?.substring("polycentric://".length);
|
||||
val navUrl = "https://polycentric.io/user/" + c.author.id.value?.substring("polycentric://".length);
|
||||
_fragment.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(navUrl)))
|
||||
//_fragment.navigate<BrowserFragment>(navUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ class ContentSearchResultsFragment : MainFragment() {
|
|||
onFilterClick.subscribe(this) {
|
||||
_overlayContainer.let {
|
||||
val filterValuesCopy = HashMap(_filterValues);
|
||||
val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy);
|
||||
val filtersOverlay = UISlideOverlays.showFiltersOverlay(lifecycleScope, it, _enabledClientIds!!, filterValuesCopy, _channelUrl != null);
|
||||
filtersOverlay.onOK.subscribe { enabledClientIds, changed ->
|
||||
if (changed) {
|
||||
setFilterValues(filtersOverlay.commonCapabilities, filterValuesCopy);
|
||||
|
@ -170,7 +170,11 @@ class ContentSearchResultsFragment : MainFragment() {
|
|||
|
||||
fragment.lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val commonCapabilities = StatePlatform.instance.getCommonSearchCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
||||
val commonCapabilities =
|
||||
if(_channelUrl == null)
|
||||
StatePlatform.instance.getCommonSearchCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
||||
else
|
||||
StatePlatform.instance.getCommonSearchChannelContentsCapabilities(StatePlatform.instance.getEnabledClients().map { it.id });
|
||||
val sorts = commonCapabilities?.sorts ?: listOf();
|
||||
if (sorts.size > 1) {
|
||||
withContext(Dispatchers.Main) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import com.futo.platformplayer.fullyBackfillServersAnnounceExceptions
|
|||
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.polycentric.PolycentricCache
|
||||
import com.futo.platformplayer.setPlatformPlayerLinkMovementMethod
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
|
@ -211,6 +212,8 @@ class PostDetailFragment : MainFragment {
|
|||
|
||||
_repliesOverlay = findViewById(R.id.replies_overlay);
|
||||
|
||||
_textContent.setPlatformPlayerLinkMovementMethod(context);
|
||||
|
||||
_buttonSubscribe.onSubscribed.subscribe {
|
||||
//TODO: add overlay to layout
|
||||
//UISlideOverlays.showSubscriptionOptionsOverlay(it, _overlayContainer);
|
||||
|
|
|
@ -656,11 +656,11 @@ class VideoDetailView : ConstraintLayout {
|
|||
|
||||
Logger.i(TAG, "onAuthorClick: " + c.author.id.value);
|
||||
if(c.author.id.value?.startsWith("polycentric://") ?: false) {
|
||||
val navUrl = "https://harbor.social/" + c.author.id.value?.substring("polycentric://".length);
|
||||
//fragment.navigate<BrowserFragment>(navUrl);
|
||||
//fragment.minimizeVideoDetail();
|
||||
_container_content_browser.goto(navUrl);
|
||||
switchContentView(_container_content_browser);
|
||||
//val navUrl = "https://harbor.social/" + c.author.id.value?.substring("polycentric://".length);
|
||||
val navUrl = "https://polycentric.io/user/" + c.author.id.value?.substring("polycentric://".length);
|
||||
fragment.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(navUrl)))
|
||||
//_container_content_browser.goto(navUrl);
|
||||
//switchContentView(_container_content_browser);
|
||||
}
|
||||
};
|
||||
_commentsList.onRepliesClick.subscribe { c ->
|
||||
|
|
|
@ -144,10 +144,15 @@ class StateDownloads {
|
|||
fun getDownloadedVideos(): List<VideoLocal> {
|
||||
return _downloaded.getItems();
|
||||
}
|
||||
fun getDownloadedVideosPlaylist(str: String): List<VideoLocal> {
|
||||
val videos = _downloaded.findItems { it.groupID == str };
|
||||
return videos;
|
||||
}
|
||||
|
||||
fun getDownloadPlaylists(): List<PlaylistDownloadDescriptor> {
|
||||
return _downloadPlaylists.getItems();
|
||||
}
|
||||
|
||||
fun isPlaylistCached(id: String): Boolean {
|
||||
return getDownloadPlaylists().any{it.id == id};
|
||||
}
|
||||
|
@ -188,6 +193,21 @@ class StateDownloads {
|
|||
DownloadService.getOrCreateService(it);
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForOutdatedPlaylistVideos(playlistId: String) {
|
||||
val playlistVideos = if(playlistId == VideoDownload.GROUP_WATCHLATER)
|
||||
(if(getWatchLaterDescriptor() != null) StatePlaylists.instance.getWatchLater() else listOf())
|
||||
else
|
||||
getCachedPlaylist(playlistId)?.playlist?.videos ?: return;
|
||||
val playlistVideosDownloaded = getDownloadedVideosPlaylist(playlistId);
|
||||
val urls = playlistVideos.map { it.url }.toHashSet();
|
||||
for(item in playlistVideosDownloaded) {
|
||||
if(!urls.contains(item.url)) {
|
||||
Logger.i(TAG, "Playlist [${playlistId}] deleting removed video [${item.name}]");
|
||||
deleteCachedVideo(item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
fun checkForOutdatedPlaylists(): Boolean {
|
||||
var hasChanged = false;
|
||||
val playlistsDownloaded = getCachedPlaylists();
|
||||
|
@ -230,7 +250,7 @@ class StateDownloads {
|
|||
else {
|
||||
Logger.i(TAG, "New watchlater video ${item.name}");
|
||||
download(VideoDownload(item, playlistDownload.targetPxCount, playlistDownload.targetBitrate)
|
||||
.withGroup(VideoDownload.GROUP_PLAYLIST, VideoDownload.GROUP_WATCHLATER), false);
|
||||
.withGroup(VideoDownload.GROUP_WATCHLATER, VideoDownload.GROUP_WATCHLATER), false);
|
||||
hasNew = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,12 +529,23 @@ class StatePlatform {
|
|||
}
|
||||
|
||||
fun getCommonSearchCapabilities(clientIds: List<String>): ResultCapabilities? {
|
||||
return getCommonSearchCapabilitiesType(clientIds){
|
||||
it.getSearchCapabilities()
|
||||
};
|
||||
}
|
||||
fun getCommonSearchChannelContentsCapabilities(clientIds: List<String>): ResultCapabilities? {
|
||||
return getCommonSearchCapabilitiesType(clientIds){
|
||||
it.getSearchChannelContentsCapabilities()
|
||||
};
|
||||
}
|
||||
|
||||
fun getCommonSearchCapabilitiesType(clientIds: List<String>, capabilitiesGetter: (client: IPlatformClient)-> ResultCapabilities): ResultCapabilities? {
|
||||
try {
|
||||
Logger.i(TAG, "Platform - getCommonSearchCapabilities");
|
||||
|
||||
val clients = getEnabledClients().filter { clientIds.contains(it.id) };
|
||||
val c = clients.firstOrNull() ?: return null;
|
||||
val cap = c.getSearchCapabilities();
|
||||
val cap = capabilitiesGetter(c)//c.getSearchCapabilities();
|
||||
|
||||
//var types = arrayListOf<String>();
|
||||
var sorts = cap.sorts.toMutableList();
|
||||
|
@ -544,7 +555,7 @@ class StatePlatform {
|
|||
val filtersToRemove = arrayListOf<Int>();
|
||||
|
||||
for (i in 1 until clients.size) {
|
||||
val clientSearchCapabilities = clients[i].getSearchCapabilities();
|
||||
val clientSearchCapabilities = capabilitiesGetter(clients[i]);//.getSearchCapabilities();
|
||||
|
||||
for (j in 0 until sorts.size) {
|
||||
if (!clientSearchCapabilities.sorts.contains(sorts[j])) {
|
||||
|
|
|
@ -11,6 +11,7 @@ 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.SerializedPlatformVideo
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.downloads.VideoDownload
|
||||
import com.futo.platformplayer.engine.exceptions.ScriptUnavailableException
|
||||
import com.futo.platformplayer.exceptions.ReconstructionException
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
|
@ -66,6 +67,10 @@ class StatePlaylists {
|
|||
_watchlistOrderStore.save();
|
||||
}
|
||||
onWatchLaterChanged.emit();
|
||||
|
||||
if(StateDownloads.instance.getWatchLaterDescriptor() != null) {
|
||||
StateDownloads.instance.checkForOutdatedPlaylistVideos(VideoDownload.GROUP_WATCHLATER);
|
||||
}
|
||||
}
|
||||
fun removeFromWatchLater(video: SerializedPlatformVideo) {
|
||||
synchronized(_watchlistStore) {
|
||||
|
@ -74,6 +79,10 @@ class StatePlaylists {
|
|||
_watchlistOrderStore.save();
|
||||
}
|
||||
onWatchLaterChanged.emit();
|
||||
|
||||
if(StateDownloads.instance.getWatchLaterDescriptor() != null) {
|
||||
StateDownloads.instance.checkForOutdatedPlaylistVideos(VideoDownload.GROUP_WATCHLATER);
|
||||
}
|
||||
}
|
||||
fun addToWatchLater(video: SerializedPlatformVideo) {
|
||||
synchronized(_watchlistStore) {
|
||||
|
@ -82,6 +91,8 @@ class StatePlaylists {
|
|||
_watchlistOrderStore.save();
|
||||
}
|
||||
onWatchLaterChanged.emit();
|
||||
|
||||
StateDownloads.instance.checkForOutdatedPlaylists();
|
||||
}
|
||||
|
||||
fun getLastPlayedPlaylist() : Playlist? {
|
||||
|
@ -131,6 +142,11 @@ class StatePlaylists {
|
|||
fun createOrUpdatePlaylist(playlist: Playlist) {
|
||||
playlist.dateUpdate = OffsetDateTime.now();
|
||||
playlistStore.saveAsync(playlist, true);
|
||||
if(playlist.id.isNotEmpty()) {
|
||||
if (StateDownloads.instance.isPlaylistCached(playlist.id)) {
|
||||
StateDownloads.instance.checkForOutdatedPlaylistVideos(playlist.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
fun addToPlaylist(id: String, video: IPlatformVideo) {
|
||||
synchronized(playlistStore) {
|
||||
|
@ -143,6 +159,9 @@ class StatePlaylists {
|
|||
|
||||
fun removePlaylist(playlist: Playlist) {
|
||||
playlistStore.delete(playlist);
|
||||
if(StateDownloads.instance.isPlaylistCached(playlist.id)) {
|
||||
StateDownloads.instance.deleteCachedPlaylist(playlist.id);
|
||||
}
|
||||
}
|
||||
|
||||
fun createPlaylistShareUri(context: Context, playlist: Playlist): Uri {
|
||||
|
|
|
@ -28,13 +28,17 @@ class SlideUpMenuFilters {
|
|||
private var _changed: Boolean = false;
|
||||
private val _lifecycleScope: CoroutineScope;
|
||||
|
||||
private var _isChannelSearch = false;
|
||||
|
||||
var commonCapabilities: ResultCapabilities? = null;
|
||||
|
||||
constructor(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>) {
|
||||
|
||||
constructor(lifecycleScope: CoroutineScope, container: ViewGroup, enabledClientsIds: List<String>, filterValues: HashMap<String, List<String>>, isChannelSearch: Boolean = false) {
|
||||
_lifecycleScope = lifecycleScope;
|
||||
_container = container;
|
||||
_enabledClientsIds = enabledClientsIds;
|
||||
_filterValues = filterValues;
|
||||
_isChannelSearch = isChannelSearch;
|
||||
_slideUpMenuOverlay = SlideUpMenuOverlay(_container.context, _container, container.context.getString(R.string.filters), container.context.getString(R.string.done), true, listOf());
|
||||
_slideUpMenuOverlay.onOK.subscribe {
|
||||
onOK.emit(_enabledClientsIds, _changed);
|
||||
|
@ -47,7 +51,10 @@ class SlideUpMenuFilters {
|
|||
private fun updateCommonCapabilities() {
|
||||
_lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val caps = StatePlatform.instance.getCommonSearchCapabilities(_enabledClientsIds);
|
||||
val caps = if(!_isChannelSearch)
|
||||
StatePlatform.instance.getCommonSearchCapabilities(_enabledClientsIds);
|
||||
else
|
||||
StatePlatform.instance.getCommonSearchChannelContentsCapabilities(_enabledClientsIds);
|
||||
synchronized(_filterValues) {
|
||||
if (caps != null) {
|
||||
val keysToRemove = arrayListOf<String>();
|
||||
|
|
Loading…
Add table
Reference in a new issue