Fixed thumbnail to consider max size 1920 and fitcenter and implemented fixes for pagination of library.

This commit is contained in:
Koen J
2025-12-01 14:20:41 +01:00
parent 0abc65a9bd
commit 45f621763a
20 changed files with 242 additions and 129 deletions
@@ -43,6 +43,8 @@ import java.util.concurrent.ThreadLocalRandom
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import androidx.core.graphics.scale
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
private val _allowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz ";
fun getRandomString(sizeOfRandomString: Int): String {
@@ -440,4 +442,17 @@ fun addressScore(addr: InetAddress): Int {
}
}
fun <T> Enumeration<T>.toList(): List<T> = Collections.list(this)
fun <T> Enumeration<T>.toList(): List<T> = Collections.list(this)
fun <T> RequestBuilder<T>.withMaxSizePx(maxSizePx: Int = 1920, useCenterCrop: Boolean = false): RequestBuilder<T> {
var builder = this
.downsample(DownsampleStrategy.AT_MOST)
.override(maxSizePx, maxSizePx)
builder = if (useCenterCrop) {
builder.centerCrop()
} else {
builder.fitCenter()
}
return builder
}
@@ -55,7 +55,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
protected val _toolbarContentView: LinearLayout;
protected val _bottomContentView: LinearLayout;
private var _loading: Boolean = true;
private var _loading: Boolean = false;
private val _pagerLock = Object();
private var _cache: ItemCache<TResult>? = null;
@@ -180,10 +180,9 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
val visibleItemCount = _recyclerResults.childCount;
val firstVisibleItem = recyclerData.layoutManager.findFirstVisibleItemPosition()
//Logger.i(TAG, "onScrolled loadNextPage visibleItemCount=$visibleItemCount firstVisibleItem=$visibleItemCount")
//Logger.i(TAG, "onScrolled loadNextPage(): firstVisibleItem=$firstVisibleItem visibleItemCount=$visibleItemCount visibleThreshold=$visibleThreshold recyclerData.results.size=${recyclerData.results.size}")
if (!_loading && firstVisibleItem + visibleItemCount + visibleThreshold >= recyclerData.results.size && firstVisibleItem > 0) {
//Logger.i(TAG, "onScrolled loadNextPage(): firstVisibleItem=$firstVisibleItem visibleItemCount=$visibleItemCount visibleThreshold=$visibleThreshold recyclerData.results.size=${recyclerData.results.size}")
if (!_loading && firstVisibleItem + visibleItemCount + visibleThreshold >= recyclerData.results.size) {
loadNextPage();
}
}
@@ -197,57 +196,44 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
}
private fun ensureEnoughContentVisible(filteredResults: List<TConverted>) {
val canScroll = if (recyclerData.results.isEmpty()) false else {
val height = resources.displayMetrics.heightPixels;
_recyclerResults.post {
val canScroll = _recyclerResults.canScrollVertically(1)
Logger.i(
TAG,
"ensureEnoughContentVisible loadNextPage canScroll=$canScroll _automaticNextPageCounter=$_automaticNextPageCounter"
)
if (!canScroll || filteredResults.isEmpty()) {
_automaticNextPageCounter++
if (_automaticNextPageCounter < _automaticBackoff.size) {
if (_automaticNextPageCounter > 0) {
val automaticNextPageCounterSaved = _automaticNextPageCounter;
fragment.lifecycleScope.launch(Dispatchers.Default) {
val backoff = _automaticBackoff[Math.min(
_automaticBackoff.size - 1,
_automaticNextPageCounter
)];
val layoutManager = recyclerData.layoutManager
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
val firstVisibleItemView = if(firstVisibleItemPosition != RecyclerView.NO_POSITION) layoutManager.findViewByPosition(firstVisibleItemPosition) else null;
val lastVisibleItemPosition = layoutManager.findLastCompletelyVisibleItemPosition();
val lastVisibleItemView = if(lastVisibleItemPosition != RecyclerView.NO_POSITION) layoutManager.findViewByPosition(lastVisibleItemPosition) else null;
val rows = if(recyclerData.layoutManager is GridLayoutManager) Math.max(1, recyclerData.results.size / recyclerData.layoutManager.spanCount) else 1;
val rowsHeight = (firstVisibleItemView?.height ?: 0) * rows;
if(lastVisibleItemView != null && lastVisibleItemPosition == (recyclerData.results.size - 1)) {
false;
}
else if (firstVisibleItemView != null && height != null && rowsHeight < height) {
false;
} else {
true;
}
}
Logger.i(TAG, "ensureEnoughContentVisible loadNextPage canScroll=$canScroll _automaticNextPageCounter=$_automaticNextPageCounter")
if (!canScroll || filteredResults.isEmpty()) {
_automaticNextPageCounter++
if(_automaticNextPageCounter < _automaticBackoff.size) {
if(_automaticNextPageCounter > 0) {
val automaticNextPageCounterSaved = _automaticNextPageCounter;
fragment.lifecycleScope.launch(Dispatchers.Default) {
val backoff = _automaticBackoff[Math.min(_automaticBackoff.size - 1, _automaticNextPageCounter)];
withContext(Dispatchers.Main) {
setLoading(true);
}
delay(backoff.toLong());
if(automaticNextPageCounterSaved == _automaticNextPageCounter) {
withContext(Dispatchers.Main) {
loadNextPage();
setLoading(true);
}
delay(backoff.toLong());
if (automaticNextPageCounterSaved == _automaticNextPageCounter) {
withContext(Dispatchers.Main) {
loadNextPage();
}
} else {
withContext(Dispatchers.Main) {
setLoading(false);
}
}
}
else {
withContext(Dispatchers.Main) {
setLoading(false);
}
}
}
} else
loadNextPage();
}
else
loadNextPage();
} else {
Logger.i(TAG, "ensureEnoughContentVisible automaticNextPageCounter reset");
_automaticNextPageCounter = 0;
}
} else {
Logger.i(TAG, "ensureEnoughContentVisible automaticNextPageCounter reset");
_automaticNextPageCounter = 0;
}
}
fun resetAutomaticNextPageCounter(){
@@ -484,6 +470,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
recyclerData.resultsUnfiltered.addAll(toAdd);
recyclerData.adapter.notifyDataSetChanged();
recyclerData.loadedFeedStyle = feedStyle;
setLoading(false)
if(pager.hasMorePages())
ensureEnoughContentVisible(filteredResults)
}
@@ -519,7 +506,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
synchronized(_pagerLock) {
val pager: TPager = recyclerData.pager ?: return;
val hasMorePages = pager.hasMorePages();
Logger.i(TAG, "loadNextPage() hasMorePages=$hasMorePages, page size=${pager.getResults().size}");
//Logger.i(TAG, "loadNextPage() hasMorePages=$hasMorePages, page size=${pager.getResults().size}");
//loadCachedPage();
if (pager.hasMorePages()) {
@@ -96,7 +96,6 @@ class LibraryVideosFragment : MainFragment() {
fun onShown() {
val initialAlbums = StateLibrary.instance.getAlbums();
Logger.i(TAG, "Initial album count: " + initialAlbums.size);
val buckets = StateLibrary.instance.getVideoBucketNames();
setPager(StateLibrary.instance.getVideos(fragment._toggleBuckets));
}
@@ -364,7 +364,7 @@ class RemotePlaylistFragment : MainFragment() {
_imagePlaylistThumbnail.let {
Glide.with(it)
.load(video.thumbnails.getHQThumbnail())
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.placeholder(R.drawable.placeholder_video_thumbnail)
.crossfade()
.into(it);
@@ -78,6 +78,7 @@ import com.futo.platformplayer.views.video.FutoShortPlayer
import com.futo.platformplayer.views.video.FutoVideoPlayerBase
import com.futo.platformplayer.views.video.FutoVideoPlayerBase.Companion.PREFERED_AUDIO_CONTAINERS
import com.futo.platformplayer.views.video.FutoVideoPlayerBase.Companion.PREFERED_VIDEO_CONTAINERS
import com.futo.platformplayer.withMaxSizePx
import com.futo.polycentric.core.ApiMethods
import com.futo.polycentric.core.ContentType
import com.futo.polycentric.core.Models
@@ -859,7 +860,7 @@ class ShortView : FrameLayout {
val thumbnail = videoDetails.thumbnails.getHQThumbnail()
if (videoSource == null && !thumbnail.isNullOrBlank()) Glide.with(context).asBitmap()
.load(thumbnail).downsample(DownsampleStrategy.AT_MOST).override(1080, 1080).into(object : CustomTarget<Bitmap>() {
.load(thumbnail).withMaxSizePx().into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
player.setArtwork(resource.toDrawable(resources))
}
@@ -162,6 +162,7 @@ import com.futo.platformplayer.views.subscriptions.SubscribeButton
import com.futo.platformplayer.views.video.FutoVideoPlayer
import com.futo.platformplayer.views.video.FutoVideoPlayerBase
import com.futo.platformplayer.views.videometa.UpNextView
import com.futo.platformplayer.withMaxSizePx
import com.futo.polycentric.core.ApiMethods
import com.futo.polycentric.core.ContentType
import com.futo.polycentric.core.Models
@@ -2050,7 +2051,7 @@ class VideoDetailView : ConstraintLayout {
} else {
val thumbnail = video.thumbnails.getHQThumbnail();
if ((videoSource == null) && !thumbnail.isNullOrBlank()) // || _player.isAudioMode
Glide.with(context).asBitmap().load(thumbnail).downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
Glide.with(context).asBitmap().load(thumbnail).withMaxSizePx()
.into(object: CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
_player.setArtwork(BitmapDrawable(resources, resource));
@@ -29,6 +29,7 @@ import com.futo.platformplayer.toHumanDuration
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.SearchView
import com.futo.platformplayer.views.lists.VideoListEditorView
import com.futo.platformplayer.withMaxSizePx
abstract class VideoListEditorView : LinearLayout {
private var _videoListEditorView: VideoListEditorView;
@@ -212,7 +213,7 @@ abstract class VideoListEditorView : LinearLayout {
_imagePlaylistThumbnail.let {
Glide.with(it)
.load(video.thumbnails.getHQThumbnail())
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.placeholder(R.drawable.placeholder_video_thumbnail)
.crossfade()
.into(it);
@@ -7,6 +7,7 @@ import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.futo.platformplayer.api.media.models.Thumbnails
import com.futo.platformplayer.withMaxSizePx
class GlideHelper {
@@ -15,7 +16,7 @@ class GlideHelper {
fun ImageView.loadThumbnails(thumbnails: Thumbnails, isHQ: Boolean = true, continuation: ((RequestBuilder<Drawable>) -> Unit)? = null) {
val url = if(isHQ) thumbnails.getHQThumbnail() ?: thumbnails.getLQThumbnail() else thumbnails.getLQThumbnail();
val req = Glide.with(this).load(url).downsample(DownsampleStrategy.AT_MOST).override(1080, 1080);
val req = Glide.with(this).load(url).withMaxSizePx()
if (thumbnails.hasMultiple() && false) { //TODO: Resolve issue where fallback triggered on second loads?
val fallbackUrl = if (isHQ) thumbnails.getLQThumbnail() else thumbnails.getHQThumbnail();
@@ -39,6 +39,7 @@ 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.withMaxSizePx
class MediaPlaybackService : Service() {
private val TAG = "MediaPlaybackService";
@@ -225,7 +226,7 @@ class MediaPlaybackService : Service() {
val tag = video;
Glide.with(this).asBitmap()
.load(thumbnail)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.into(object: CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap,transition: Transition<in Bitmap>?) {
if (tag != _notif_last_video) return
@@ -1,10 +1,12 @@
package com.futo.platformplayer.states
import android.content.ContentResolver
import android.content.ContentUris
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Artists
import android.webkit.MimeTypeMap
@@ -154,34 +156,101 @@ class StateLibrary {
fun getArtist(id: Long): Artist? {
return Artist.getArtist(id);
}
fun getVideos(
buckets: List<String>? = null,
pageSize: Int = 20
): IPager<IPlatformContent> {
val resolver = StateApp.instance.contextOrNull?.contentResolver ?: return EmptyPager()
val selection: String?
val selectionArgs: Array<String>?
fun getVideos(buckets: List<String>? = null): IPager<IPlatformContent> {
var query = if(buckets != null) "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} IN " + "(" + buckets.map { "'${it}'" }.joinToString(",") + ")" else null;
val cursor = StateApp.instance.contextOrNull?.contentResolver?.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, PROJECTION_VIDEO,
query,
null,
MediaStore.Video.Media.DATE_ADDED + " DESC") ?: return EmptyPager();
if (!buckets.isNullOrEmpty()) {
val placeholders = buckets.joinToString(",") { "?" }
selection = "${MediaStore.Video.Media.BUCKET_DISPLAY_NAME} IN ($placeholders)"
selectionArgs = buckets.toTypedArray()
} else {
selection = null
selectionArgs = null
}
//Ongoing usage of cursor..todo disposal
//return cursor.use {
cursor.moveToFirst();
val list = mutableListOf<IPlatformVideo>()
while(!cursor.isAfterLast && list.size < 10) {
list.add(videoFromCursor(cursor));
cursor.moveToNext();
val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
} else {
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
}
var nextPageIndex = 0
fun loadPage(pageIndex: Int): List<IPlatformContent> {
Logger.i(TAG, "loadPage $pageIndex")
val offset = pageIndex * pageSize
val queryArgs = Bundle().apply {
selection?.let {
putString(ContentResolver.QUERY_ARG_SQL_SELECTION, it)
}
selectionArgs?.let {
putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, it)
}
putStringArray(
ContentResolver.QUERY_ARG_SORT_COLUMNS,
arrayOf(
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media._ID
)
)
putInt(
ContentResolver.QUERY_ARG_SORT_DIRECTION,
ContentResolver.QUERY_SORT_DIRECTION_DESCENDING
)
putInt(ContentResolver.QUERY_ARG_LIMIT, pageSize)
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
}
return AdhocPager<IPlatformContent>({
val list = mutableListOf<IPlatformContent>()
while(!cursor.isAfterLast && list.size < 10) {
list.add(videoFromCursor(cursor));
cursor.moveToNext();
val cursor = resolver.query(
collectionUri,
PROJECTION_VIDEO,
queryArgs,
null
)
if (cursor == null) {
Logger.i(TAG, "loadPage $pageIndex null, returning empty list")
return emptyList()
}
cursor.use { c ->
if (!c.moveToFirst()) {
Logger.i(TAG, "loadPage $pageIndex moveToFirst failed, returning empty list")
return emptyList()
}
Logger.i(TAG, "Videos nextPage: ${list.size}")
return@AdhocPager list;
}, list);
//}
val list = ArrayList<IPlatformContent>(pageSize)
do {
list.add(videoFromCursor(c))
} while (c.moveToNext() && list.size < pageSize)
Logger.i(TAG, "loadPage $pageIndex found ${list.size} items")
return list
}
}
val firstPage = loadPage(0)
if (firstPage.isEmpty()) {
return EmptyPager()
}
nextPageIndex = 1
return AdhocPager<IPlatformContent>({
val page = loadPage(nextPageIndex)
nextPageIndex++
Logger.i(TAG, "loadPage nextPage: ${page.size}")
page
}, firstPage)
}
fun getRecentVideos(buckets: List<String>? = null, count: Int = 20): List<IPlatformVideo> {
val videoPager = getVideos(buckets);
val items = mutableListOf<IPlatformVideo>();
@@ -193,48 +262,80 @@ class StateLibrary {
return items;
}
private var _cacheBucketNames: List<Bucket>? = null;
fun getVideoBucketNames(): List<Bucket> {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
return listOf();
if(_cacheBucketNames != null)
return _cacheBucketNames ?: listOf();
try {
val cur: Cursor = StateApp.instance.contextOrNull?.contentResolver?.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, arrayOf(
MediaStore.Video.Media.BUCKET_ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
), null, null, null
) ?: return listOf();
@Volatile
private var _cachedVideoBuckets: List<Bucket>? = null
private val _bucketCacheLock = Any()
return cur.use {
val buckets = mutableListOf<Bucket>();
val list = HashSet<Long>();
if (cur.moveToFirst()) {
var id: Long;
var bucket: String
do {
try {
id = cur.getLong(0);
bucket = cur.getStringOrNull(1) ?: continue;
if (!list.contains(id)) {
list.add(id);
buckets.add(Bucket(id, bucket));
}
} catch (ex: Throwable) {
Logger.e(TAG, "Failed to parse bucket due to ${ex.message}", ex);
}
} while (cur.moveToNext())
fun getVideoBucketNames(forceRefresh: Boolean = false): List<Bucket> {
if (!forceRefresh) {
_cachedVideoBuckets?.let { return it }
}
val resolver = StateApp.instance.contextOrNull?.contentResolver
?: return emptyList()
val projection = arrayOf(
MediaStore.Video.VideoColumns.BUCKET_ID,
MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME
)
val sortOrder = "${MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME} COLLATE NOCASE ASC"
val loadedBuckets: List<Bucket> = try {
resolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
sortOrder
)?.use { cursor ->
if (!cursor.moveToFirst()) {
return@use emptyList<Bucket>()
}
_cacheBucketNames = buckets.toList()
return@use _cacheBucketNames ?: listOf();
val idxId = cursor.getColumnIndexOrThrow(MediaStore.Video.VideoColumns.BUCKET_ID)
val idxName = cursor.getColumnIndexOrThrow(MediaStore.Video.VideoColumns.BUCKET_DISPLAY_NAME)
val seenIds = HashSet<Long>()
val buckets = ArrayList<Bucket>()
do {
try {
val id = cursor.getLong(idxId)
if (!seenIds.add(id)) {
continue
}
val name = cursor.getStringOrNull(idxName) ?: continue
buckets.add(Bucket(id, name))
} catch (e: Exception) {
Logger.e(TAG, "Failed to parse video bucket row: ${e.message}", e)
}
} while (cursor.moveToNext())
buckets
} ?: emptyList()
} catch (e: Exception) {
Logger.e(TAG, "Buckets loading failed, returning empty: ${e.message}", e)
emptyList()
}
if (loadedBuckets.isEmpty()) {
if (!forceRefresh) {
_cachedVideoBuckets?.let { return it }
}
return emptyList()
}
catch(ex: Throwable) {
Logger.e(TAG, "Buckets loading failed, returning empty");
return listOf();
synchronized(_bucketCacheLock) {
if (!forceRefresh) {
_cachedVideoBuckets?.let { return it }
}
_cachedVideoBuckets = loadedBuckets
return loadedBuckets
}
}
fun invalidateVideoBucketNamesCache() {
_cachedVideoBuckets = null
}
companion object {
@@ -23,6 +23,7 @@ import com.futo.platformplayer.serializers.PlatformContentSerializer
import com.futo.platformplayer.stores.FragmentedStorage
import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanNowDiffStringMinDay
import com.futo.platformplayer.withMaxSizePx
import java.time.OffsetDateTime
class StateNotifications {
@@ -97,7 +98,7 @@ class StateNotifications {
if(thumbnail != null)
Glide.with(context).asBitmap()
.load(thumbnail)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.into(object: CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
notifyNewContent(context, manager, notificationChannel, id, content, resource);
@@ -12,6 +12,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
import com.futo.platformplayer.models.Playlist
import com.futo.platformplayer.withMaxSizePx
class PlaylistsViewHolder : ViewHolder {
private val _root: ConstraintLayout;
@@ -45,7 +46,7 @@ class PlaylistsViewHolder : ViewHolder {
if (p.videos.isNotEmpty()) {
Glide.with(_imageThumbnail)
.load(p.videos[0].thumbnails.getMinimumThumbnail(380))
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.placeholder(R.drawable.placeholder_video_thumbnail)
.crossfade()
.into(_imageThumbnail);
@@ -24,6 +24,7 @@ import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.others.ProgressBar
import com.futo.platformplayer.views.platform.PlatformIndicator
import com.futo.platformplayer.withMaxSizePx
class VideoListEditorViewHolder : ViewHolder {
private val _root: ConstraintLayout;
@@ -90,7 +91,7 @@ class VideoListEditorViewHolder : ViewHolder {
fun bind(v: IPlatformVideo, canEdit: Boolean) {
Glide.with(_imageThumbnail)
.load(v.thumbnails.getHQThumbnail())
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.placeholder(R.drawable.placeholder_video_thumbnail)
.crossfade()
.into(_imageThumbnail);
@@ -17,6 +17,7 @@ import com.futo.platformplayer.states.Artist
import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.views.adapters.AnyAdapter
import com.futo.platformplayer.withMaxSizePx
import com.google.android.material.imageview.ShapeableImageView
@@ -50,7 +51,7 @@ class LocalVideoTileViewHolder(val _viewGroup: ViewGroup) : AnyAdapter.AnyViewHo
Glide.with(it)
.load(content.thumbnails.getHQThumbnail())
.placeholder(R.drawable.unknown_music)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.into(it)
else
Glide.with(it).load(R.drawable.unknown_music).into(it);
@@ -33,6 +33,7 @@ import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.views.TargetTapLoaderView
import com.futo.platformplayer.views.behavior.GestureControlView
import com.futo.platformplayer.withMaxSizePx
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -307,7 +308,7 @@ class CastView : ConstraintLayout {
Glide.with(_thumbnail)
.load(video.thumbnails.getHQThumbnail())
.placeholder(R.drawable.placeholder_video_thumbnail)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.into(_thumbnail);
_textPosition.text = (position * 1000).formatDuration();
_textDuration.text = (video.duration * 1000).formatDuration();
@@ -61,8 +61,8 @@ class ActiveDownloadItem: LinearLayout {
}
Glide.with(_videoImage)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.load(download.thumbnail)
.withMaxSizePx()
.crossfade()
.into(_videoImage);
@@ -9,6 +9,7 @@ import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
import com.futo.platformplayer.R
import com.futo.platformplayer.images.GlideHelper.Companion.crossfade
import com.futo.platformplayer.models.PlaylistDownloaded
import com.futo.platformplayer.withMaxSizePx
class PlaylistDownloadItem(context: Context, playlistName: String, playlistThumbnail: String?, val obj: Any): LinearLayout(context) {
init { inflate(context, R.layout.list_downloaded_playlist, this) }
@@ -20,7 +21,7 @@ class PlaylistDownloadItem(context: Context, playlistName: String, playlistThumb
imageText.text = playlistName;
Glide.with(imageView)
.load(playlistThumbnail)
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.crossfade()
.into(imageView);
}
@@ -26,6 +26,7 @@ import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails
import com.futo.platformplayer.helpers.VideoHelper
import com.futo.platformplayer.toHumanTime
import com.futo.platformplayer.video.PlayerManager
import com.futo.platformplayer.withMaxSizePx
class FutoThumbnailPlayer : FutoVideoPlayerBase {
@@ -136,7 +137,7 @@ class FutoThumbnailPlayer : FutoVideoPlayerBase {
if (videoSource == null && audioSource != null) {
val thumbnail = video.thumbnails.getHQThumbnail();
if (!thumbnail.isNullOrBlank()) {
Glide.with(videoView).asBitmap().load(thumbnail).downsample(DownsampleStrategy.AT_MOST).override(1080, 1080).into(_loadArtwork);
Glide.with(videoView).asBitmap().load(thumbnail).withMaxSizePx().into(_loadArtwork);
} else {
Glide.with(videoView).clear(_loadArtwork);
setArtwork(null);
@@ -54,6 +54,7 @@ import com.futo.platformplayer.states.StatePlayer
import com.futo.platformplayer.views.TargetTapLoaderView
import com.futo.platformplayer.views.behavior.GestureControlView
import com.futo.platformplayer.views.others.ProgressBar
import com.futo.platformplayer.withMaxSizePx
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@@ -928,11 +929,9 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
override fun switchToAudioMode(video: IPlatformVideoDetails?) {
super.switchToAudioMode(video)
//This causes issues, and is in general confusing, needs improvements
/*
val thumbnail = video?.thumbnails?.getHQThumbnail()
if (!thumbnail.isNullOrBlank()) {
Glide.with(context).asBitmap().load(thumbnail)
Glide.with(context).asBitmap().load(thumbnail).withMaxSizePx()
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(
resource: Bitmap,
@@ -946,6 +945,5 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
}
})
}
*/
}
}
@@ -17,6 +17,7 @@ import com.futo.platformplayer.R
import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.toHumanNowDiffString
import com.futo.platformplayer.toHumanNumber
import com.futo.platformplayer.withMaxSizePx
class UpNextView : LinearLayout {
private val _layoutContainer: LinearLayout;
@@ -161,7 +162,7 @@ class UpNextView : LinearLayout {
_textChannelName.text = nextItem.author.name;
Glide.with(_imageThumbnail)
.load(nextItem.thumbnails.getHQThumbnail())
.downsample(DownsampleStrategy.AT_MOST).override(1080, 1080)
.withMaxSizePx()
.placeholder(R.drawable.placeholder_video_thumbnail)
.into(_imageThumbnail);
Glide.with(_imageChannelThumbnail)