Implement better image selection strategy

This commit is contained in:
Stypox 2023-05-02 12:30:27 +02:00
parent 0a8f28b1c6
commit 35073c780d
No known key found for this signature in database
GPG key ID: 4BDF1B40A49FDD23
19 changed files with 161 additions and 89 deletions

View file

@ -20,6 +20,7 @@ import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ServiceHelper;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
@ -100,7 +101,7 @@ public class App extends Application {
// Initialize image loader // Initialize image loader
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
PicassoHelper.init(this); PicassoHelper.init(this);
PicassoHelper.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this, ImageStrategy.setPreferredImageQuality(PreferredImageQuality.fromPreferenceKey(this,
prefs.getString(getString(R.string.image_quality_key), prefs.getString(getString(R.string.image_quality_key),
getString(R.string.image_quality_default)))); getString(R.string.image_quality_default))));
PicassoHelper.setIndicatorsEnabled(MainActivity.DEBUG PicassoHelper.setIndicatorsEnabled(MainActivity.DEBUG

View file

@ -7,7 +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.StreamEntity
import org.schabi.newpipe.database.stream.model.StreamStateEntity import org.schabi.newpipe.database.stream.model.StreamStateEntity
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.image.PicassoHelper import org.schabi.newpipe.util.image.ImageStrategy
data class PlaylistStreamEntry( data class PlaylistStreamEntry(
@Embedded @Embedded
@ -29,7 +29,7 @@ data class PlaylistStreamEntry(
item.duration = streamEntity.duration item.duration = streamEntity.duration
item.uploaderName = streamEntity.uploader item.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl item.uploaderUrl = streamEntity.uploaderUrl
item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) item.thumbnails = ImageStrategy.urlToImageList(streamEntity.thumbnailUrl)
return item return item
} }

View file

@ -11,7 +11,7 @@ import androidx.room.PrimaryKey;
import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.database.playlist.PlaylistLocalItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM; import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM;
import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME; import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME;
@ -70,7 +70,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
@Ignore @Ignore
public PlaylistRemoteEntity(final PlaylistInfo info) { public PlaylistRemoteEntity(final PlaylistInfo info) {
this(info.getServiceId(), info.getName(), info.getUrl(), this(info.getServiceId(), info.getName(), info.getUrl(),
PicassoHelper.choosePreferredImage(info.getThumbnails()), ImageStrategy.choosePreferredImage(info.getThumbnails()),
info.getUploaderName(), info.getStreamCount()); info.getUploaderName(), info.getStreamCount());
} }
@ -85,7 +85,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem {
&& TextUtils.equals(getName(), info.getName()) && TextUtils.equals(getName(), info.getName())
&& TextUtils.equals(getUrl(), info.getUrl()) && TextUtils.equals(getUrl(), info.getUrl())
&& TextUtils.equals(getThumbnailUrl(), && TextUtils.equals(getThumbnailUrl(),
PicassoHelper.choosePreferredImage(info.getThumbnails())) ImageStrategy.choosePreferredImage(info.getThumbnails()))
&& TextUtils.equals(getUploader(), info.getUploaderName()); && TextUtils.equals(getUploader(), info.getUploaderName());
} }

View file

@ -7,7 +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.StreamEntity
import org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS import org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.util.image.PicassoHelper import org.schabi.newpipe.util.image.ImageStrategy
import java.time.OffsetDateTime import java.time.OffsetDateTime
class StreamStatisticsEntry( class StreamStatisticsEntry(
@ -31,7 +31,7 @@ class StreamStatisticsEntry(
item.duration = streamEntity.duration item.duration = streamEntity.duration
item.uploaderName = streamEntity.uploader item.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl item.uploaderUrl = streamEntity.uploaderUrl
item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) item.thumbnails = ImageStrategy.urlToImageList(streamEntity.thumbnailUrl)
return item return item
} }

View file

@ -13,7 +13,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.stream.StreamType import org.schabi.newpipe.extractor.stream.StreamType
import org.schabi.newpipe.player.playqueue.PlayQueueItem import org.schabi.newpipe.player.playqueue.PlayQueueItem
import org.schabi.newpipe.util.image.PicassoHelper import org.schabi.newpipe.util.image.ImageStrategy
import java.io.Serializable import java.io.Serializable
import java.time.OffsetDateTime import java.time.OffsetDateTime
@ -68,7 +68,7 @@ data class StreamEntity(
constructor(item: StreamInfoItem) : this( constructor(item: StreamInfoItem) : this(
serviceId = item.serviceId, url = item.url, title = item.name, serviceId = item.serviceId, url = item.url, title = item.name,
streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, streamType = item.streamType, duration = item.duration, uploader = item.uploaderName,
uploaderUrl = item.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails), viewCount = item.viewCount, uploaderUrl = item.uploaderUrl, thumbnailUrl = ImageStrategy.choosePreferredImage(item.thumbnails), viewCount = item.viewCount,
textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(),
isUploadDateApproximation = item.uploadDate?.isApproximation isUploadDateApproximation = item.uploadDate?.isApproximation
) )
@ -77,7 +77,7 @@ data class StreamEntity(
constructor(info: StreamInfo) : this( constructor(info: StreamInfo) : this(
serviceId = info.serviceId, url = info.url, title = info.name, serviceId = info.serviceId, url = info.url, title = info.name,
streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, streamType = info.streamType, duration = info.duration, uploader = info.uploaderName,
uploaderUrl = info.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(info.thumbnails), viewCount = info.viewCount, uploaderUrl = info.uploaderUrl, thumbnailUrl = ImageStrategy.choosePreferredImage(info.thumbnails), viewCount = info.viewCount,
textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(),
isUploadDateApproximation = info.uploadDate?.isApproximation isUploadDateApproximation = info.uploadDate?.isApproximation
) )
@ -87,7 +87,7 @@ data class StreamEntity(
serviceId = item.serviceId, url = item.url, title = item.title, serviceId = item.serviceId, url = item.url, title = item.title,
streamType = item.streamType, duration = item.duration, uploader = item.uploader, streamType = item.streamType, duration = item.duration, uploader = item.uploader,
uploaderUrl = item.uploaderUrl, uploaderUrl = item.uploaderUrl,
thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails) thumbnailUrl = ImageStrategy.choosePreferredImage(item.thumbnails)
) )
fun toStreamInfoItem(): StreamInfoItem { fun toStreamInfoItem(): StreamInfoItem {
@ -95,7 +95,7 @@ data class StreamEntity(
item.duration = duration item.duration = duration
item.uploaderName = uploader item.uploaderName = uploader
item.uploaderUrl = uploaderUrl item.uploaderUrl = uploaderUrl
item.thumbnails = PicassoHelper.urlToImageList(thumbnailUrl) item.thumbnails = ImageStrategy.urlToImageList(thumbnailUrl)
if (viewCount != null) item.viewCount = viewCount as Long if (viewCount != null) item.viewCount = viewCount as Long
item.textualUploadDate = textualUploadDate item.textualUploadDate = textualUploadDate

View file

@ -10,7 +10,7 @@ import androidx.room.PrimaryKey;
import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID; import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID;
import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE;
@ -58,7 +58,7 @@ public class SubscriptionEntity {
final SubscriptionEntity result = new SubscriptionEntity(); final SubscriptionEntity result = new SubscriptionEntity();
result.setServiceId(info.getServiceId()); result.setServiceId(info.getServiceId());
result.setUrl(info.getUrl()); result.setUrl(info.getUrl());
result.setData(info.getName(), PicassoHelper.choosePreferredImage(info.getAvatars()), result.setData(info.getName(), ImageStrategy.choosePreferredImage(info.getAvatars()),
info.getDescription(), info.getSubscriberCount()); info.getDescription(), info.getSubscriberCount());
return result; return result;
} }
@ -139,7 +139,7 @@ public class SubscriptionEntity {
@Ignore @Ignore
public ChannelInfoItem toChannelInfoItem() { public ChannelInfoItem toChannelInfoItem() {
final ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName()); final ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName());
item.setThumbnails(PicassoHelper.urlToImageList(getAvatarUrl())); item.setThumbnails(ImageStrategy.urlToImageList(getAvatarUrl()));
item.setSubscriberCount(getSubscriberCount()); item.setSubscriberCount(getSubscriberCount());
item.setDescription(getDescription()); item.setDescription(getDescription());
return item; return item;

View file

@ -17,7 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import java.util.List; import java.util.List;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import icepick.State; import icepick.State;
@ -114,7 +114,7 @@ public class DescriptionFragment extends BaseDescriptionFragment {
addMetadataItem(inflater, layout, true, R.string.metadata_host, addMetadataItem(inflater, layout, true, R.string.metadata_host,
streamInfo.getHost()); streamInfo.getHost());
addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url, addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url,
PicassoHelper.choosePreferredImage(streamInfo.getThumbnails())); ImageStrategy.choosePreferredImage(streamInfo.getThumbnails()));
} }
private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) {

View file

@ -17,7 +17,7 @@ import org.schabi.newpipe.extractor.stream.Description;
import org.schabi.newpipe.fragments.detail.BaseDescriptionFragment; import org.schabi.newpipe.fragments.detail.BaseDescriptionFragment;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import java.util.List; import java.util.List;
@ -101,8 +101,8 @@ public class ChannelAboutFragment extends BaseDescriptionFragment {
} }
addMetadataItem(inflater, layout, true, R.string.metadata_avatar_url, addMetadataItem(inflater, layout, true, R.string.metadata_avatar_url,
PicassoHelper.choosePreferredImage(channelInfo.getAvatars())); ImageStrategy.choosePreferredImage(channelInfo.getAvatars()));
addMetadataItem(inflater, layout, true, R.string.metadata_banner_url, addMetadataItem(inflater, layout, true, R.string.metadata_banner_url,
PicassoHelper.choosePreferredImage(channelInfo.getBanners())); ImageStrategy.choosePreferredImage(channelInfo.getBanners()));
} }
} }

View file

@ -49,6 +49,7 @@ import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.StateSaver;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.external_communication.ShareUtils;
@ -147,7 +148,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
setTitle(name); setTitle(name);
binding.channelTitleView.setText(name); binding.channelTitleView.setText(name);
if (!PicassoHelper.shouldLoadImages()) { if (!ImageStrategy.shouldLoadImages()) {
// do not waste space for the banner if it is not going to be loaded // do not waste space for the banner if it is not going to be loaded
binding.channelBannerImage.setImageDrawable(null); binding.channelBannerImage.setImageDrawable(null);
} }
@ -354,7 +355,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
channel.setServiceId(info.getServiceId()); channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl()); channel.setUrl(info.getUrl());
channel.setData(info.getName(), channel.setData(info.getName(),
PicassoHelper.choosePreferredImage(info.getAvatars()), ImageStrategy.choosePreferredImage(info.getAvatars()),
info.getDescription(), info.getDescription(),
info.getSubscriberCount()); info.getSubscriberCount());
channelSubscription = null; channelSubscription = null;
@ -578,7 +579,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo>
currentInfo = result; currentInfo = result;
setInitialData(result.getServiceId(), result.getOriginalUrl(), result.getName()); setInitialData(result.getServiceId(), result.getOriginalUrl(), result.getName());
if (PicassoHelper.shouldLoadImages() && !result.getBanners().isEmpty()) { if (ImageStrategy.shouldLoadImages() && !result.getBanners().isEmpty()) {
PicassoHelper.loadBanner(result.getBanners()).tag(PICASSO_CHANNEL_TAG) PicassoHelper.loadBanner(result.getBanners()).tag(PICASSO_CHANNEL_TAG)
.into(binding.channelBannerImage); .into(binding.channelBannerImage);
} else { } else {

View file

@ -31,6 +31,7 @@ import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.external_communication.ShareUtils;
import org.schabi.newpipe.util.text.CommentTextOnTouchListener; import org.schabi.newpipe.util.text.CommentTextOnTouchListener;
@ -98,7 +99,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder {
final CommentsInfoItem item = (CommentsInfoItem) infoItem; final CommentsInfoItem item = (CommentsInfoItem) infoItem;
PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView);
if (PicassoHelper.shouldLoadImages()) { if (ImageStrategy.shouldLoadImages()) {
itemThumbnailView.setVisibility(View.VISIBLE); itemThumbnailView.setVisibility(View.VISIBLE);
itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding,
commentVerticalPadding, commentVerticalPadding); commentVerticalPadding, commentVerticalPadding);

View file

@ -61,7 +61,7 @@ import org.schabi.newpipe.util.OnClickGesture
import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels
import org.schabi.newpipe.util.external_communication.ShareUtils import org.schabi.newpipe.util.external_communication.ShareUtils
import org.schabi.newpipe.util.image.PicassoHelper import org.schabi.newpipe.util.image.ImageStrategy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -343,7 +343,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
when (i) { when (i) {
0 -> ShareUtils.shareText( 0 -> ShareUtils.shareText(
requireContext(), selectedItem.name, selectedItem.url, requireContext(), selectedItem.name, selectedItem.url,
PicassoHelper.choosePreferredImage(selectedItem.thumbnails) ImageStrategy.choosePreferredImage(selectedItem.thumbnails)
) )
1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url) 1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url)
2 -> deleteChannel(selectedItem) 2 -> deleteChannel(selectedItem)

View file

@ -19,7 +19,7 @@ import org.schabi.newpipe.extractor.feed.FeedInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.local.feed.FeedDatabaseManager import org.schabi.newpipe.local.feed.FeedDatabaseManager
import org.schabi.newpipe.util.ExtractorHelper import org.schabi.newpipe.util.ExtractorHelper
import org.schabi.newpipe.util.image.PicassoHelper import org.schabi.newpipe.util.image.ImageStrategy
class SubscriptionManager(context: Context) { class SubscriptionManager(context: Context) {
private val database = NewPipeDatabase.getInstance(context) private val database = NewPipeDatabase.getInstance(context)
@ -74,7 +74,7 @@ class SubscriptionManager(context: Context) {
Completable.fromRunnable { Completable.fromRunnable {
it.setData( it.setData(
info.name, info.name,
PicassoHelper.choosePreferredImage(info.avatars), ImageStrategy.choosePreferredImage(info.avatars),
info.description, info.description,
info.subscriberCount info.subscriberCount
) )
@ -105,7 +105,7 @@ class SubscriptionManager(context: Context) {
} else if (info is ChannelInfo) { } else if (info is ChannelInfo) {
subscriptionEntity.setData( subscriptionEntity.setData(
info.name, info.name,
PicassoHelper.choosePreferredImage(info.avatars), ImageStrategy.choosePreferredImage(info.avatars),
info.description, info.description,
info.subscriberCount info.subscriberCount
) )

View file

@ -3,7 +3,7 @@ package org.schabi.newpipe.player.mediaitem;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -75,7 +75,7 @@ public final class ExceptionTag implements MediaItemTag {
@Override @Override
public String getThumbnailUrl() { public String getThumbnailUrl() {
return PicassoHelper.choosePreferredImage(item.getThumbnails()); return ImageStrategy.choosePreferredImage(item.getThumbnails());
} }
@Override @Override

View file

@ -6,7 +6,7 @@ import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.extractor.stream.VideoStream; import org.schabi.newpipe.extractor.stream.VideoStream;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -96,7 +96,7 @@ public final class StreamInfoTag implements MediaItemTag {
@Override @Override
public String getThumbnailUrl() { public String getThumbnailUrl() {
return PicassoHelper.choosePreferredImage(streamInfo.getThumbnails()); return ImageStrategy.choosePreferredImage(streamInfo.getThumbnails());
} }
@Override @Override

View file

@ -20,7 +20,7 @@ import com.google.android.exoplayer2.util.Util;
import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.Player;
import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue;
import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.PlayQueueItem;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.ImageStrategy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -139,7 +139,7 @@ public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator
descBuilder.setExtras(additionalMetadata); descBuilder.setExtras(additionalMetadata);
final Uri thumbnailUri = Uri.parse( final Uri thumbnailUri = Uri.parse(
PicassoHelper.choosePreferredImage(item.getThumbnails())); ImageStrategy.choosePreferredImage(item.getThumbnails()));
if (thumbnailUri != null) { if (thumbnailUri != null) {
descBuilder.setIconUri(thumbnailUri); descBuilder.setIconUri(thumbnailUri);
} }

View file

@ -31,6 +31,7 @@ import org.schabi.newpipe.extractor.localization.Localization;
import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard;
import org.schabi.newpipe.streams.io.StoredFileHelper; import org.schabi.newpipe.streams.io.StoredFileHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
import org.schabi.newpipe.util.ZipHelper; import org.schabi.newpipe.util.ZipHelper;
import org.schabi.newpipe.util.image.PreferredImageQuality; import org.schabi.newpipe.util.image.PreferredImageQuality;
@ -109,7 +110,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment {
final Preference imageQualityPreference = requirePreference(R.string.image_quality_key); final Preference imageQualityPreference = requirePreference(R.string.image_quality_key);
imageQualityPreference.setOnPreferenceChangeListener( imageQualityPreference.setOnPreferenceChangeListener(
(preference, newValue) -> { (preference, newValue) -> {
PicassoHelper.setPreferredImageQuality(PreferredImageQuality ImageStrategy.setPreferredImageQuality(PreferredImageQuality
.fromPreferenceKey(requireContext(), (String) newValue)); .fromPreferenceKey(requireContext(), (String) newValue));
try { try {
PicassoHelper.clearCache(preference.getContext()); PicassoHelper.clearCache(preference.getContext());

View file

@ -24,6 +24,7 @@ import androidx.core.content.FileProvider;
import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.util.image.ImageStrategy;
import org.schabi.newpipe.util.image.PicassoHelper; import org.schabi.newpipe.util.image.PicassoHelper;
import java.io.File; import java.io.File;
@ -251,7 +252,7 @@ public final class ShareUtils {
// If loading of images has been disabled, don't try to generate a content preview // If loading of images has been disabled, don't try to generate a content preview
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& !TextUtils.isEmpty(imagePreviewUrl) && !TextUtils.isEmpty(imagePreviewUrl)
&& PicassoHelper.shouldLoadImages()) { && ImageStrategy.shouldLoadImages()) {
final ClipData clipData = generateClipDataForImagePreview(context, imagePreviewUrl); final ClipData clipData = generateClipDataForImagePreview(context, imagePreviewUrl);
if (clipData != null) { if (clipData != null) {
@ -276,14 +277,14 @@ public final class ShareUtils {
* @param title the title of the content * @param title the title of the content
* @param content the content to share * @param content the content to share
* @param images a set of possible {@link Image}s of the subject, among which to choose with * @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 * {@link ImageStrategy#choosePreferredImage(List)} since that's likely to
* provide an image that is in Picasso's cache * provide an image that is in Picasso's cache
*/ */
public static void shareText(@NonNull final Context context, public static void shareText(@NonNull final Context context,
@NonNull final String title, @NonNull final String title,
final String content, final String content,
final List<Image> images) { final List<Image> images) {
shareText(context, title, content, PicassoHelper.choosePreferredImage(images)); shareText(context, title, content, ImageStrategy.choosePreferredImage(images));
} }
/** /**

View file

@ -0,0 +1,115 @@
package org.schabi.newpipe.util.image;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.schabi.newpipe.extractor.Image;
import java.util.Comparator;
import java.util.List;
public final class ImageStrategy {
// the height thresholds also used by the extractor (TODO move them to the extractor)
private static final int LOW_MEDIUM = 175;
private static final int MEDIUM_HIGH = 720;
private static PreferredImageQuality preferredImageQuality = PreferredImageQuality.MEDIUM;
private ImageStrategy() {
}
public static void setPreferredImageQuality(final PreferredImageQuality preferredImageQuality) {
ImageStrategy.preferredImageQuality = preferredImageQuality;
}
public static boolean shouldLoadImages() {
return preferredImageQuality != PreferredImageQuality.NONE;
}
private static double estimatePixelCount(final Image image,
final double widthOverHeight,
final boolean unknownsLast) {
if (image.getHeight() == Image.HEIGHT_UNKNOWN) {
if (image.getWidth() == Image.WIDTH_UNKNOWN) {
switch (image.getEstimatedResolutionLevel()) {
case LOW:
return unknownsLast
? (LOW_MEDIUM - 1) * (LOW_MEDIUM - 1) * widthOverHeight
: 0;
case MEDIUM:
return unknownsLast
? (MEDIUM_HIGH - 1) * (MEDIUM_HIGH - 1) * widthOverHeight
: LOW_MEDIUM * LOW_MEDIUM * widthOverHeight;
case HIGH:
return unknownsLast
? 1e20 // less than 1e21 to prefer over fully unknown image sizes
: MEDIUM_HIGH * MEDIUM_HIGH * widthOverHeight;
default:
case UNKNOWN:
// images whose size is completely unknown will be avoided when possible
return unknownsLast ? 1e21 : -1;
}
} else {
return image.getWidth() * image.getWidth() / widthOverHeight;
}
} else if (image.getWidth() == Image.WIDTH_UNKNOWN) {
return image.getHeight() * image.getHeight() * widthOverHeight;
} else {
return image.getHeight() * image.getWidth();
}
}
@Nullable
public static String choosePreferredImage(@NonNull final List<Image> images) {
if (preferredImageQuality == PreferredImageQuality.NONE) {
return null; // do not load images
}
final double widthOverHeight = images.stream()
.filter(image -> image.getHeight() != Image.HEIGHT_UNKNOWN
&& image.getWidth() != Image.WIDTH_UNKNOWN)
.mapToDouble(image -> ((double) image.getWidth()) / image.getHeight())
.findFirst()
.orElse(1.0);
final Comparator<Image> comparator;
switch (preferredImageQuality) {
case LOW:
comparator = Comparator.comparingDouble(
image -> estimatePixelCount(image, widthOverHeight, true));
break;
default:
case MEDIUM:
comparator = Comparator.comparingDouble(image -> {
final double pixelCount = estimatePixelCount(image, widthOverHeight, true);
final double mediumHeight = (LOW_MEDIUM + MEDIUM_HIGH) / 2.0;
return Math.abs(pixelCount - mediumHeight * mediumHeight * widthOverHeight);
});
break;
case HIGH:
comparator = Comparator.<Image>comparingDouble(
image -> estimatePixelCount(image, widthOverHeight, false))
.reversed();
break;
}
return images.stream()
.min(comparator)
.map(Image::getUrl)
.orElse(null);
}
@NonNull
public static List<Image> urlToImageList(@Nullable final String url) {
if (url == null) {
return List.of();
} else {
return List.of(new Image(url, -1, -1, Image.ResolutionLevel.UNKNOWN));
}
}
}

View file

@ -2,13 +2,13 @@ package org.schabi.newpipe.util.image;
import static org.schabi.newpipe.MainActivity.DEBUG; import static org.schabi.newpipe.MainActivity.DEBUG;
import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
import static org.schabi.newpipe.util.image.ImageStrategy.choosePreferredImage;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.graphics.BitmapCompat; import androidx.core.graphics.BitmapCompat;
@ -21,11 +21,9 @@ import com.squareup.picasso.Transformation;
import org.schabi.newpipe.R; import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.Image; import org.schabi.newpipe.extractor.Image;
import org.schabi.newpipe.extractor.Image.ResolutionLevel;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -46,7 +44,6 @@ public final class PicassoHelper {
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
private static Picasso picassoInstance; private static Picasso picassoInstance;
private static PreferredImageQuality preferredImageQuality = PreferredImageQuality.MEDIUM;
public static void init(final Context context) { public static void init(final Context context) {
picassoCache = new LruCache(10 * 1024 * 1024); picassoCache = new LruCache(10 * 1024 * 1024);
@ -92,14 +89,6 @@ public final class PicassoHelper {
picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging
} }
public static void setPreferredImageQuality(final PreferredImageQuality preferredImageQuality) {
PicassoHelper.preferredImageQuality = preferredImageQuality;
}
public static boolean shouldLoadImages() {
return preferredImageQuality != PreferredImageQuality.NONE;
}
public static RequestCreator loadAvatar(final List<Image> images) { public static RequestCreator loadAvatar(final List<Image> images) {
return loadImageDefault(images, R.drawable.placeholder_person); return loadImageDefault(images, R.drawable.placeholder_person);
@ -227,41 +216,4 @@ public final class PicassoHelper {
return requestCreator; return requestCreator;
} }
} }
@Nullable
public static String choosePreferredImage(final List<Image> images) {
final Comparator<Image> comparator;
switch (preferredImageQuality) {
case NONE:
return null;
case HIGH:
comparator = Comparator.comparingInt(Image::getHeight).reversed();
break;
default:
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<Image> urlToImageList(@Nullable final String url) {
if (url == null) {
return List.of();
} else {
return List.of(new Image(url, -1, -1, ResolutionLevel.UNKNOWN));
}
}
} }