diff --git a/app/build.gradle b/app/build.gradle index 831b758b9..aa7ac8720 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,7 +197,7 @@ dependencies { // name and the commit hash with the commit hash of the (pushed) commit you want to test // This works thanks to JitPack: https://jitpack.io/ implementation 'com.github.TeamNewPipe:nanojson:1d9e1aea9049fc9f85e68b43ba39fe7be1c1f751' - implementation 'com.github.TeamNewPipe:NewPipeExtractor:95a3cc0a173bba28c179f9f9503b1010ec6bff21' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:3be76a6406d59f1fd8eedf5fab6552e6c2a3da76' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ diff --git a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java index 3255489b0..e6177f6a3 100644 --- a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java +++ b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java @@ -75,7 +75,7 @@ public final class QueueItemMenuUtil { return true; case R.id.menu_item_share: shareText(context, item.getTitle(), item.getUrl(), - item.getThumbnailUrl()); + item.getThumbnails()); return true; case R.id.menu_item_download: fetchStreamInfoAndSaveToDatabase(context, item.getServiceId(), item.getUrl(), diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt index d2543ae6d..3b6bc9593 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistStreamEntry.kt @@ -7,6 +7,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamStateEntity import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.util.PicassoHelper data class PlaylistStreamEntry( @Embedded @@ -28,7 +29,7 @@ data class PlaylistStreamEntry( item.duration = streamEntity.duration item.uploaderName = streamEntity.uploader item.uploaderUrl = streamEntity.uploaderUrl - item.thumbnailUrl = streamEntity.thumbnailUrl + item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) return item } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java index 2e9a15d7d..640b6d1fa 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java @@ -11,6 +11,7 @@ import androidx.room.PrimaryKey; import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.PicassoHelper; import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM; import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME; @@ -69,8 +70,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { @Ignore public PlaylistRemoteEntity(final PlaylistInfo info) { this(info.getServiceId(), info.getName(), info.getUrl(), - info.getThumbnailUrl() == null - ? info.getUploaderAvatarUrl() : info.getThumbnailUrl(), + PicassoHelper.choosePreferredImage(info.getThumbnails()), info.getUploaderName(), info.getStreamCount()); } @@ -84,7 +84,8 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { && getStreamCount() == info.getStreamCount() && TextUtils.equals(getName(), info.getName()) && TextUtils.equals(getUrl(), info.getUrl()) - && TextUtils.equals(getThumbnailUrl(), info.getThumbnailUrl()) + && TextUtils.equals(getThumbnailUrl(), + PicassoHelper.choosePreferredImage(info.getThumbnails())) && TextUtils.equals(getUploader(), info.getUploaderName()); } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt index dc0db59d8..a268f8bbf 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamStatisticsEntry.kt @@ -7,6 +7,7 @@ import org.schabi.newpipe.database.history.model.StreamHistoryEntity import org.schabi.newpipe.database.stream.model.StreamEntity import org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS import org.schabi.newpipe.extractor.stream.StreamInfoItem +import org.schabi.newpipe.util.PicassoHelper import java.time.OffsetDateTime class StreamStatisticsEntry( @@ -30,7 +31,7 @@ class StreamStatisticsEntry( item.duration = streamEntity.duration item.uploaderName = streamEntity.uploader item.uploaderUrl = streamEntity.uploaderUrl - item.thumbnailUrl = streamEntity.thumbnailUrl + item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) return item } diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt index c56f91949..8b7639bbd 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamEntity.kt @@ -13,6 +13,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.player.playqueue.PlayQueueItem +import org.schabi.newpipe.util.PicassoHelper import java.io.Serializable import java.time.OffsetDateTime @@ -67,7 +68,7 @@ data class StreamEntity( constructor(item: StreamInfoItem) : this( serviceId = item.serviceId, url = item.url, title = item.name, streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, - uploaderUrl = item.uploaderUrl, thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount, + uploaderUrl = item.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails), viewCount = item.viewCount, textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), isUploadDateApproximation = item.uploadDate?.isApproximation ) @@ -76,7 +77,7 @@ data class StreamEntity( constructor(info: StreamInfo) : this( serviceId = info.serviceId, url = info.url, title = info.name, streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, - uploaderUrl = info.uploaderUrl, thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount, + uploaderUrl = info.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(info.thumbnails), viewCount = info.viewCount, textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), isUploadDateApproximation = info.uploadDate?.isApproximation ) @@ -85,7 +86,8 @@ data class StreamEntity( constructor(item: PlayQueueItem) : this( serviceId = item.serviceId, url = item.url, title = item.title, streamType = item.streamType, duration = item.duration, uploader = item.uploader, - uploaderUrl = item.uploaderUrl, thumbnailUrl = item.thumbnailUrl + uploaderUrl = item.uploaderUrl, + thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails) ) fun toStreamInfoItem(): StreamInfoItem { @@ -93,7 +95,7 @@ data class StreamEntity( item.duration = duration item.uploaderName = uploader item.uploaderUrl = uploaderUrl - item.thumbnailUrl = thumbnailUrl + item.thumbnails = PicassoHelper.urlToImageList(thumbnailUrl) if (viewCount != null) item.viewCount = viewCount as Long item.textualUploadDate = textualUploadDate diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java index 0e4bda490..a455cf258 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java @@ -10,6 +10,7 @@ import androidx.room.PrimaryKey; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.PicassoHelper; import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID; import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; @@ -57,8 +58,8 @@ public class SubscriptionEntity { final SubscriptionEntity result = new SubscriptionEntity(); result.setServiceId(info.getServiceId()); result.setUrl(info.getUrl()); - result.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), - info.getSubscriberCount()); + result.setData(info.getName(), PicassoHelper.choosePreferredImage(info.getAvatars()), + info.getDescription(), info.getSubscriberCount()); return result; } @@ -138,7 +139,7 @@ public class SubscriptionEntity { @Ignore public ChannelInfoItem toChannelInfoItem() { final ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName()); - item.setThumbnailUrl(getAvatarUrl()); + item.setThumbnails(PicassoHelper.urlToImageList(getAvatarUrl())); item.setSubscriberCount(getSubscriberCount()); item.setDescription(getDescription()); return item; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java index 92219883b..e7f257665 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java @@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.util.Localization; import java.util.List; +import org.schabi.newpipe.util.PicassoHelper; import icepick.State; @@ -113,7 +114,7 @@ public class DescriptionFragment extends BaseDescriptionFragment { addMetadataItem(inflater, layout, true, R.string.metadata_host, streamInfo.getHost()); addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url, - streamInfo.getThumbnailUrl()); + PicassoHelper.choosePreferredImage(streamInfo.getThumbnails())); } private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 686e102f1..2e6f493a3 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -71,6 +71,7 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.ReCaptchaActivity; import org.schabi.newpipe.error.UserAction; +import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; @@ -483,7 +484,7 @@ public final class VideoDetailFragment }); binding.detailControlsShare.setOnClickListener(makeOnClickListener(info -> ShareUtils.shareText(requireContext(), info.getName(), info.getUrl(), - info.getThumbnailUrl()))); + info.getThumbnails()))); binding.detailControlsOpenInBrowser.setOnClickListener(makeOnClickListener(info -> ShareUtils.openUrlInBrowser(requireContext(), info.getUrl()))); binding.detailControlsPlayWithKodi.setOnClickListener(makeOnClickListener(info -> @@ -723,7 +724,7 @@ public final class VideoDetailFragment final boolean isPlayerStopped = !isPlayerAvailable() || player.isStopped(); if (playQueueItem != null && isPlayerStopped) { updateOverlayData(playQueueItem.getTitle(), - playQueueItem.getUploader(), playQueueItem.getThumbnailUrl()); + playQueueItem.getUploader(), playQueueItem.getThumbnails()); } } @@ -1536,13 +1537,13 @@ public final class VideoDetailFragment binding.detailSecondaryControlPanel.setVisibility(View.GONE); checkUpdateProgressInfo(info); - PicassoHelper.loadDetailsThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + PicassoHelper.loadDetailsThumbnail(info.getThumbnails()).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.detailThumbnailImageView); showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView, binding.detailMetaInfoSeparator, disposables); if (!isPlayerAvailable() || player.isStopped()) { - updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); + updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnails()); } if (!info.getErrors().isEmpty()) { @@ -1587,7 +1588,7 @@ public final class VideoDetailFragment binding.detailUploaderTextView.setVisibility(View.GONE); } - PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.detailSubChannelThumbnailView); binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); binding.detailUploaderThumbnailView.setVisibility(View.GONE); @@ -1619,10 +1620,10 @@ public final class VideoDetailFragment binding.detailUploaderTextView.setVisibility(View.GONE); } - PicassoHelper.loadAvatar(info.getSubChannelAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + PicassoHelper.loadAvatar(info.getSubChannelAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.detailSubChannelThumbnailView); binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); - PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.detailUploaderThumbnailView); binding.detailUploaderThumbnailView.setVisibility(View.VISIBLE); } @@ -1797,7 +1798,7 @@ public final class VideoDetailFragment return; } - updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); + updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnails()); if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) { return; } @@ -1826,7 +1827,7 @@ public final class VideoDetailFragment if (currentInfo != null) { updateOverlayData(currentInfo.getName(), currentInfo.getUploaderName(), - currentInfo.getThumbnailUrl()); + currentInfo.getThumbnails()); } updateOverlayPlayQueueButtonVisibility(); } @@ -2191,7 +2192,7 @@ public final class VideoDetailFragment playerHolder.stopService(); setInitialData(0, null, "", null); currentInfo = null; - updateOverlayData(null, null, null); + updateOverlayData(null, null, List.of()); } /*////////////////////////////////////////////////////////////////////////// @@ -2373,11 +2374,11 @@ public final class VideoDetailFragment private void updateOverlayData(@Nullable final String overlayTitle, @Nullable final String uploader, - @Nullable final String thumbnailUrl) { + @NonNull final List thumbnails) { binding.overlayTitleTextView.setText(isEmpty(overlayTitle) ? "" : overlayTitle); binding.overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); binding.overlayThumbnail.setImageDrawable(null); - PicassoHelper.loadDetailsThumbnail(thumbnailUrl).tag(PICASSO_VIDEO_DETAILS_TAG) + PicassoHelper.loadDetailsThumbnail(thumbnails).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.overlayThumbnail); } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java index 543fd80f3..eebd12d20 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelAboutFragment.java @@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.fragments.detail.BaseDescriptionFragment; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; import java.util.List; @@ -100,8 +101,8 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { } addMetadataItem(inflater, layout, true, R.string.metadata_avatar_url, - channelInfo.getAvatarUrl()); + PicassoHelper.choosePreferredImage(channelInfo.getAvatars())); addMetadataItem(inflater, layout, true, R.string.metadata_banner_url, - channelInfo.getBannerUrl()); + PicassoHelper.choosePreferredImage(channelInfo.getBanners())); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index c1345180b..6b2c71337 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.fragments.list.channel; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; @@ -234,7 +233,7 @@ public class ChannelFragment extends BaseStateFragment case R.id.menu_item_share: if (currentInfo != null) { ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(), - currentInfo.getAvatarUrl()); + currentInfo.getAvatars()); } break; default: @@ -355,7 +354,7 @@ public class ChannelFragment extends BaseStateFragment channel.setServiceId(info.getServiceId()); channel.setUrl(info.getUrl()); channel.setData(info.getName(), - info.getAvatarUrl(), + PicassoHelper.choosePreferredImage(info.getAvatars()), info.getDescription(), info.getSubscriberCount()); channelSubscription = null; @@ -579,17 +578,17 @@ public class ChannelFragment extends BaseStateFragment currentInfo = result; setInitialData(result.getServiceId(), result.getOriginalUrl(), result.getName()); - if (PicassoHelper.getShouldLoadImages() && !isBlank(result.getBannerUrl())) { - PicassoHelper.loadBanner(result.getBannerUrl()).tag(PICASSO_CHANNEL_TAG) + if (PicassoHelper.getShouldLoadImages() && !result.getBanners().isEmpty()) { + PicassoHelper.loadBanner(result.getBanners()).tag(PICASSO_CHANNEL_TAG) .into(binding.channelBannerImage); } else { // do not waste space for the banner, if the user disabled images or there is not one binding.channelBannerImage.setImageDrawable(null); } - PicassoHelper.loadAvatar(result.getAvatarUrl()).tag(PICASSO_CHANNEL_TAG) + PicassoHelper.loadAvatar(result.getAvatars()).tag(PICASSO_CHANNEL_TAG) .into(binding.channelAvatarView); - PicassoHelper.loadAvatar(result.getParentChannelAvatarUrl()).tag(PICASSO_CHANNEL_TAG) + PicassoHelper.loadAvatar(result.getParentChannelAvatars()).tag(PICASSO_CHANNEL_TAG) .into(binding.subChannelAvatarView); binding.channelTitleView.setText(result.getName()); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 2b7cf9446..9b3f693e1 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -234,7 +234,7 @@ public class PlaylistFragment extends BaseListInfoFragment ShareUtils.shareText(fragment.requireContext(), item.getName(), item.getUrl(), - item.getThumbnailUrl())), + item.getThumbnails())), /** * Opens a {@link DownloadDialog} after fetching some stream info. diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java index 3b375d6eb..d971a6a6d 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java @@ -56,7 +56,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder { itemAdditionalDetailView.setText(getDetailLine(item)); } - PicassoHelper.loadAvatar(item.getThumbnailUrl()).into(itemThumbnailView); + PicassoHelper.loadAvatar(item.getThumbnails()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnChannelSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index 1c8db26d6..d69be077d 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -97,7 +97,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { } final CommentsInfoItem item = (CommentsInfoItem) infoItem; - PicassoHelper.loadAvatar(item.getUploaderAvatarUrl()).into(itemThumbnailView); + PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); if (PicassoHelper.getShouldLoadImages()) { itemThumbnailView.setVisibility(View.VISIBLE); itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java index bf5f57db3..62c7c27eb 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java @@ -46,7 +46,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder { .localizeStreamCountMini(itemStreamCountView.getContext(), item.getStreamCount())); itemUploaderView.setText(item.getUploaderName()); - PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + PicassoHelper.loadPlaylistThumbnail(item.getThumbnails()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnPlaylistSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java index 6dd06e47f..796ea63b3 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java @@ -87,7 +87,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); + PicassoHelper.loadThumbnail(item.getThumbnails()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnStreamSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt index dda4326e9..f428a7c1d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt @@ -58,6 +58,7 @@ import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard import org.schabi.newpipe.streams.io.StoredFileHelper import org.schabi.newpipe.util.NavigationHelper import org.schabi.newpipe.util.OnClickGesture +import org.schabi.newpipe.util.PicassoHelper import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels import org.schabi.newpipe.util.external_communication.ShareUtils @@ -342,7 +343,7 @@ class SubscriptionFragment : BaseStateFragment() { when (i) { 0 -> ShareUtils.shareText( requireContext(), selectedItem.name, selectedItem.url, - selectedItem.thumbnailUrl + PicassoHelper.choosePreferredImage(selectedItem.thumbnails) ) 1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url) 2 -> deleteChannel(selectedItem) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt index 3c11ce152..a74ff305c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionManager.kt @@ -19,6 +19,7 @@ import org.schabi.newpipe.extractor.feed.FeedInfo import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.local.feed.FeedDatabaseManager import org.schabi.newpipe.util.ExtractorHelper +import org.schabi.newpipe.util.PicassoHelper class SubscriptionManager(context: Context) { private val database = NewPipeDatabase.getInstance(context) @@ -71,7 +72,12 @@ class SubscriptionManager(context: Context) { subscriptionTable.getSubscription(info.serviceId, info.url) .flatMapCompletable { Completable.fromRunnable { - it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount) + it.setData( + info.name, + PicassoHelper.choosePreferredImage(info.avatars), + info.description, + info.subscriberCount + ) subscriptionTable.update(it) } } @@ -99,7 +105,7 @@ class SubscriptionManager(context: Context) { } else if (info is ChannelInfo) { subscriptionEntity.setData( info.name, - info.avatarUrl, + PicassoHelper.choosePreferredImage(info.avatars), info.description, info.subscriberCount ) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt index bee2e910a..07a4f11ff 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt @@ -39,7 +39,7 @@ class ChannelItem( itemChannelDescriptionView.text = infoItem.description } - PicassoHelper.loadAvatar(infoItem.thumbnailUrl).into(itemThumbnailView) + PicassoHelper.loadAvatar(infoItem.thumbnails).into(itemThumbnailView) gesturesListener?.run { viewHolder.root.setOnClickListener { selected(infoItem) } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 1a323176c..3ad03891e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -87,6 +87,7 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.AudioStream; +import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -805,10 +806,10 @@ public final class Player implements PlaybackListener, Listener { }; } - private void loadCurrentThumbnail(final String url) { + private void loadCurrentThumbnail(final List thumbnails) { if (DEBUG) { - Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with url = [" - + (url == null ? "null" : url) + "]"); + Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with thumbnails = [" + + thumbnails.size() + "]"); } // first cancel any previous loading @@ -817,12 +818,12 @@ public final class Player implements PlaybackListener, Listener { // Unset currentThumbnail, since it is now outdated. This ensures it is not used in media // session metadata while the new thumbnail is being loaded by Picasso. onThumbnailLoaded(null); - if (isNullOrEmpty(url)) { + if (thumbnails.isEmpty()) { return; } // scale down the notification thumbnail for performance - PicassoHelper.loadScaledDownThumbnail(context, url) + PicassoHelper.loadScaledDownThumbnail(context, thumbnails) .tag(PICASSO_PLAYER_THUMBNAIL_TAG) .into(currentThumbnailTarget); } @@ -1792,7 +1793,7 @@ public final class Player implements PlaybackListener, Listener { maybeAutoQueueNextStream(info); - loadCurrentThumbnail(info.getThumbnailUrl()); + loadCurrentThumbnail(info.getThumbnails()); registerStreamViewed(); notifyMetadataUpdateToListeners(); diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java index ebedf8c71..5a5360b34 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.player.mediaitem; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.player.playqueue.PlayQueueItem; +import org.schabi.newpipe.util.PicassoHelper; import java.util.List; import java.util.Optional; @@ -74,7 +75,7 @@ public final class ExceptionTag implements MediaItemTag { @Override public String getThumbnailUrl() { - return item.getThumbnailUrl(); + return PicassoHelper.choosePreferredImage(item.getThumbnails()); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java index 689f5c72b..eb3abec1f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java @@ -6,6 +6,7 @@ import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.util.PicassoHelper; import java.util.Collections; import java.util.List; @@ -95,7 +96,7 @@ public final class StreamInfoTag implements MediaItemTag { @Override public String getThumbnailUrl() { - return streamInfo.getThumbnailUrl(); + return PicassoHelper.choosePreferredImage(streamInfo.getThumbnails()); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java index 2e54b1129..327c2befe 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.util.Util; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; +import org.schabi.newpipe.util.PicassoHelper; import java.util.ArrayList; import java.util.Collections; @@ -137,7 +138,8 @@ public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, player.getPlayQueue().size()); descBuilder.setExtras(additionalMetadata); - final Uri thumbnailUri = Uri.parse(item.getThumbnailUrl()); + final Uri thumbnailUri = Uri.parse( + PicassoHelper.choosePreferredImage(item.getThumbnails())); if (thumbnailUri != null) { descBuilder.setIconUri(thumbnailUri); } diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java index bf31ea9b1..759c51267 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItem.java @@ -3,12 +3,14 @@ package org.schabi.newpipe.player.playqueue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.util.ExtractorHelper; import java.io.Serializable; +import java.util.List; import io.reactivex.rxjava3.core.Single; import io.reactivex.rxjava3.schedulers.Schedulers; @@ -24,7 +26,7 @@ public class PlayQueueItem implements Serializable { private final int serviceId; private final long duration; @NonNull - private final String thumbnailUrl; + private final List thumbnails; @NonNull private final String uploader; private final String uploaderUrl; @@ -38,7 +40,7 @@ public class PlayQueueItem implements Serializable { PlayQueueItem(@NonNull final StreamInfo info) { this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(), - info.getThumbnailUrl(), info.getUploaderName(), + info.getThumbnails(), info.getUploaderName(), info.getUploaderUrl(), info.getStreamType()); if (info.getStartPosition() > 0) { @@ -48,20 +50,20 @@ public class PlayQueueItem implements Serializable { PlayQueueItem(@NonNull final StreamInfoItem item) { this(item.getName(), item.getUrl(), item.getServiceId(), item.getDuration(), - item.getThumbnailUrl(), item.getUploaderName(), + item.getThumbnails(), item.getUploaderName(), item.getUploaderUrl(), item.getStreamType()); } @SuppressWarnings("ParameterNumber") private PlayQueueItem(@Nullable final String name, @Nullable final String url, final int serviceId, final long duration, - @Nullable final String thumbnailUrl, @Nullable final String uploader, + final List thumbnails, @Nullable final String uploader, final String uploaderUrl, @NonNull final StreamType streamType) { this.title = name != null ? name : EMPTY_STRING; this.url = url != null ? url : EMPTY_STRING; this.serviceId = serviceId; this.duration = duration; - this.thumbnailUrl = thumbnailUrl != null ? thumbnailUrl : EMPTY_STRING; + this.thumbnails = thumbnails; this.uploader = uploader != null ? uploader : EMPTY_STRING; this.uploaderUrl = uploaderUrl; this.streamType = streamType; @@ -88,8 +90,8 @@ public class PlayQueueItem implements Serializable { } @NonNull - public String getThumbnailUrl() { - return thumbnailUrl; + public List getThumbnails() { + return thumbnails; } @NonNull diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java index e7aeb9638..19101a31d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java @@ -33,7 +33,7 @@ public class PlayQueueItemBuilder { holder.itemDurationView.setVisibility(View.GONE); } - PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(holder.itemThumbnailView); + PicassoHelper.loadThumbnail(item.getThumbnails()).into(holder.itemThumbnailView); holder.itemRoot.setOnClickListener(view -> { if (onItemClickListener != null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java index 92e38a6a2..03f90a344 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java @@ -740,7 +740,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh String videoUrl = player.getVideoUrl(); videoUrl += ("&t=" + seconds); ShareUtils.shareText(context, currentItem.getTitle(), - videoUrl, currentItem.getThumbnailUrl()); + videoUrl, currentItem.getThumbnails()); } } }; diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index 119c43b95..b51aaa638 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -226,7 +226,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa final PlayQueueItem currentItem = player.getCurrentItem(); if (currentItem != null) { ShareUtils.shareText(context, currentItem.getTitle(), - player.getVideoUrlAtCurrentTime(), currentItem.getThumbnailUrl()); + player.getVideoUrlAtCurrentTime(), currentItem.getThumbnails()); } })); binding.share.setOnLongClickListener(v -> { diff --git a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java index ece0c7e87..5f3876d78 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java @@ -1,13 +1,14 @@ package org.schabi.newpipe.util; import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.util.Log; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.BitmapCompat; @@ -19,9 +20,13 @@ import com.squareup.picasso.RequestCreator; import com.squareup.picasso.Transformation; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.Image; +import org.schabi.newpipe.extractor.Image.ResolutionLevel; import java.io.File; import java.io.IOException; +import java.util.Comparator; +import java.util.List; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; @@ -42,6 +47,7 @@ public final class PicassoHelper { private static Picasso picassoInstance; private static boolean shouldLoadImages; + private static ResolutionLevel preferredResolutionLevel = ResolutionLevel.HIGH; public static void init(final Context context) { picassoCache = new LruCache(10 * 1024 * 1024); @@ -96,20 +102,33 @@ public final class PicassoHelper { } + public static RequestCreator loadAvatar(final List images) { + return loadImageDefault(images, R.drawable.placeholder_person); + } + public static RequestCreator loadAvatar(final String url) { return loadImageDefault(url, R.drawable.placeholder_person); } + public static RequestCreator loadThumbnail(final List images) { + return loadImageDefault(images, R.drawable.placeholder_thumbnail_video); + } + public static RequestCreator loadThumbnail(final String url) { return loadImageDefault(url, R.drawable.placeholder_thumbnail_video); } - public static RequestCreator loadDetailsThumbnail(final String url) { - return loadImageDefault(url, R.drawable.placeholder_thumbnail_video, false); + public static RequestCreator loadDetailsThumbnail(final List images) { + return loadImageDefault(choosePreferredImage(images), + R.drawable.placeholder_thumbnail_video, false); } - public static RequestCreator loadBanner(final String url) { - return loadImageDefault(url, R.drawable.placeholder_channel_banner); + public static RequestCreator loadBanner(final List images) { + return loadImageDefault(images, R.drawable.placeholder_channel_banner); + } + + public static RequestCreator loadPlaylistThumbnail(final List images) { + return loadImageDefault(images, R.drawable.placeholder_thumbnail_playlist); } public static RequestCreator loadPlaylistThumbnail(final String url) { @@ -125,9 +144,10 @@ public final class PicassoHelper { } - public static RequestCreator loadScaledDownThumbnail(final Context context, final String url) { + public static RequestCreator loadScaledDownThumbnail(final Context context, + final List images) { // scale down the notification thumbnail for performance - return PicassoHelper.loadThumbnail(url) + return PicassoHelper.loadThumbnail(images) .transform(new Transformation() { @Override public Bitmap transform(final Bitmap source) { @@ -180,13 +200,20 @@ public final class PicassoHelper { } - private static RequestCreator loadImageDefault(final String url, final int placeholderResId) { + private static RequestCreator loadImageDefault(final List images, + final int placeholderResId) { + return loadImageDefault(choosePreferredImage(images), placeholderResId); + } + + private static RequestCreator loadImageDefault(final String url, + final int placeholderResId) { return loadImageDefault(url, placeholderResId, true); } - private static RequestCreator loadImageDefault(final String url, final int placeholderResId, + private static RequestCreator loadImageDefault(@Nullable final String url, + final int placeholderResId, final boolean showPlaceholderWhileLoading) { - if (!shouldLoadImages || isBlank(url)) { + if (isNullOrEmpty(url)) { return picassoInstance .load((String) null) .placeholder(placeholderResId) // show placeholder when no image should load @@ -201,4 +228,44 @@ public final class PicassoHelper { return requestCreator; } } + + @Nullable + public static String choosePreferredImage(final List images) { + if (!shouldLoadImages) { + return null; + } + + final Comparator comparator; + switch (preferredResolutionLevel) { + case HIGH: + comparator = Comparator.comparingInt(Image::getHeight).reversed(); + break; + default: + case UNKNOWN: + case MEDIUM: + comparator = Comparator.comparingInt(image -> Math.abs(image.getHeight() - 450)); + break; + case LOW: + comparator = Comparator.comparingInt(Image::getHeight); + break; + } + + return images.stream() + .filter(image -> image.getEstimatedResolutionLevel() != ResolutionLevel.UNKNOWN) + .min(comparator) + .map(Image::getUrl) + .orElseGet(() -> images.stream() + .findAny() + .map(Image::getUrl) + .orElse(null)); + } + + @NonNull + public static List urlToImageList(@Nullable final String url) { + if (url == null) { + return List.of(); + } else { + return List.of(new Image(url, -1, -1, ResolutionLevel.UNKNOWN)); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index 118b77026..f14f22069 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java @@ -23,10 +23,12 @@ import androidx.core.content.FileProvider; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.util.PicassoHelper; import java.io.File; import java.io.FileOutputStream; +import java.util.List; public final class ShareUtils { private static final String TAG = ShareUtils.class.getSimpleName(); @@ -261,6 +263,29 @@ public final class ShareUtils { openAppChooser(context, shareIntent, false); } + /** + * Open the android share sheet to share a content. + * + *

+ * For Android 10+ users, a content preview is shown, which includes the title of the shared + * content and an image preview the content, if its URL is not null or empty and its + * corresponding image is in the image cache. + *

+ * + * @param context the context to use + * @param title the title of the content + * @param content the content to share + * @param images a set of possible {@link Image}s of the subject, among which to choose with + * {@link PicassoHelper#choosePreferredImage(List)} since that's likely to + * provide an image that is in Picasso's cache + */ + public static void shareText(@NonNull final Context context, + @NonNull final String title, + final String content, + final List images) { + shareText(context, title, content, PicassoHelper.choosePreferredImage(images)); + } + /** * Open the android share sheet to share a content. *