Skip to content

Commit

Permalink
Separate imageListToDbUrl from choosePreferredImage
Browse files Browse the repository at this point in the history
imageListToDbUrl should be used if the URL is going to be saved to the database, to avoid saving nothing in case at the moment of saving the user preference is to not show images.
  • Loading branch information
Stypox committed Sep 19, 2023
1 parent 35b1f9f commit fd83ec9
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ data class PlaylistStreamEntry(
item.duration = streamEntity.duration
item.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl
item.thumbnails = ImageStrategy.urlToImageList(streamEntity.thumbnailUrl)
item.thumbnails = ImageStrategy.dbUrlToImageList(streamEntity.thumbnailUrl)

return item
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public PlaylistRemoteEntity(final int serviceId, final String name, final String
public PlaylistRemoteEntity(final PlaylistInfo info) {
this(info.getServiceId(), info.getName(), info.getUrl(),
// use uploader avatar when no thumbnail is available
ImageStrategy.choosePreferredImage(info.getThumbnails().isEmpty()
ImageStrategy.imageListToDbUrl(info.getThumbnails().isEmpty()
? info.getUploaderAvatars() : info.getThumbnails()),
info.getUploaderName(), info.getStreamCount());
}
Expand All @@ -89,7 +89,7 @@ && getStreamCount() == info.getStreamCount()
// we want to update the local playlist data even when either the remote thumbnail
// URL changes, or the preferred image quality setting is changed by the user
&& TextUtils.equals(getThumbnailUrl(),
ImageStrategy.choosePreferredImage(info.getThumbnails()))
ImageStrategy.imageListToDbUrl(info.getThumbnails()))
&& TextUtils.equals(getUploader(), info.getUploaderName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class StreamStatisticsEntry(
item.duration = streamEntity.duration
item.uploaderName = streamEntity.uploader
item.uploaderUrl = streamEntity.uploaderUrl
item.thumbnails = ImageStrategy.urlToImageList(streamEntity.thumbnailUrl)
item.thumbnails = ImageStrategy.dbUrlToImageList(streamEntity.thumbnailUrl)

return item
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ 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 = ImageStrategy.choosePreferredImage(item.thumbnails), viewCount = item.viewCount,
uploaderUrl = item.uploaderUrl,
thumbnailUrl = ImageStrategy.imageListToDbUrl(item.thumbnails), viewCount = item.viewCount,
textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(),
isUploadDateApproximation = item.uploadDate?.isApproximation
)
Expand All @@ -77,7 +78,8 @@ 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 = ImageStrategy.choosePreferredImage(info.thumbnails), viewCount = info.viewCount,
uploaderUrl = info.uploaderUrl,
thumbnailUrl = ImageStrategy.imageListToDbUrl(info.thumbnails), viewCount = info.viewCount,
textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(),
isUploadDateApproximation = info.uploadDate?.isApproximation
)
Expand All @@ -87,15 +89,15 @@ data class StreamEntity(
serviceId = item.serviceId, url = item.url, title = item.title,
streamType = item.streamType, duration = item.duration, uploader = item.uploader,
uploaderUrl = item.uploaderUrl,
thumbnailUrl = ImageStrategy.choosePreferredImage(item.thumbnails)
thumbnailUrl = ImageStrategy.imageListToDbUrl(item.thumbnails)
)

fun toStreamInfoItem(): StreamInfoItem {
val item = StreamInfoItem(serviceId, url, title, streamType)
item.duration = duration
item.uploaderName = uploader
item.uploaderUrl = uploaderUrl
item.thumbnails = ImageStrategy.urlToImageList(thumbnailUrl)
item.thumbnails = ImageStrategy.dbUrlToImageList(thumbnailUrl)

if (viewCount != null) item.viewCount = viewCount as Long
item.textualUploadDate = textualUploadDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public static SubscriptionEntity from(@NonNull final ChannelInfo info) {
final SubscriptionEntity result = new SubscriptionEntity();
result.setServiceId(info.getServiceId());
result.setUrl(info.getUrl());
result.setData(info.getName(), ImageStrategy.choosePreferredImage(info.getAvatars()),
result.setData(info.getName(), ImageStrategy.imageListToDbUrl(info.getAvatars()),
info.getDescription(), info.getSubscriberCount());
return result;
}
Expand Down Expand Up @@ -139,7 +139,7 @@ public void setData(final String n, final String au, final String d, final Long
@Ignore
public ChannelInfoItem toChannelInfoItem() {
final ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName());
item.setThumbnails(ImageStrategy.urlToImageList(getAvatarUrl()));
item.setThumbnails(ImageStrategy.dbUrlToImageList(getAvatarUrl()));
item.setSubscriberCount(getSubscriberCount());
item.setDescription(getDescription());
return item;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ private Consumer<List<SubscriptionEntity>> getSubscribeUpdateMonitor(final Chann
channel.setServiceId(info.getServiceId());
channel.setUrl(info.getUrl());
channel.setData(info.getName(),
ImageStrategy.choosePreferredImage(info.getAvatars()),
ImageStrategy.imageListToDbUrl(info.getAvatars()),
info.getDescription(),
info.getSubscriberCount());
channelSubscription = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ import org.schabi.newpipe.util.OnClickGesture
import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels
import org.schabi.newpipe.util.external_communication.ShareUtils
import org.schabi.newpipe.util.image.ImageStrategy
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
Expand Down Expand Up @@ -342,8 +341,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() {
val actions = DialogInterface.OnClickListener { _, i ->
when (i) {
0 -> ShareUtils.shareText(
requireContext(), selectedItem.name, selectedItem.url,
ImageStrategy.choosePreferredImage(selectedItem.thumbnails)
requireContext(), selectedItem.name, selectedItem.url, selectedItem.thumbnails
)
1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url)
2 -> deleteChannel(selectedItem)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class SubscriptionManager(context: Context) {
Completable.fromRunnable {
it.setData(
info.name,
ImageStrategy.choosePreferredImage(info.avatars),
ImageStrategy.imageListToDbUrl(info.avatars),
info.description,
info.subscriberCount
)
Expand Down Expand Up @@ -105,7 +105,7 @@ class SubscriptionManager(context: Context) {
} else if (info is ChannelInfo) {
subscriptionEntity.setData(
info.name,
ImageStrategy.choosePreferredImage(info.avatars),
ImageStrategy.imageListToDbUrl(info.avatars),
info.description,
info.subscriberCount
)
Expand Down
104 changes: 80 additions & 24 deletions app/src/main/java/org/schabi/newpipe/util/image/ImageStrategy.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,30 +49,16 @@ static double estimatePixelCount(final Image image, final double widthOverHeight
}

/**
* Chooses an image amongst the provided list based on the user preference previously set with
* {@link #setPreferredImageQuality(PreferredImageQuality)}. {@code null} will be returned in
* case the list is empty or the user preference is to not show images.
* <br>
* These properties will be preferred, from most to least important:
* <ol>
* <li>The image's {@link Image#getEstimatedResolutionLevel()} is not unknown and is close
* to {@link #preferredImageQuality}</li>
* <li>At least one of the image's width or height are known</li>
* <li>The highest resolution image is finally chosen if the user's preference is {@link
* PreferredImageQuality#HIGH}, otherwise the chosen image is the one that has the closest
* height to {@link #BEST_LOW_H} or {@link #BEST_MEDIUM_H}</li>
* </ol>
* {@link #choosePreferredImage(List)} contains the description for this function's logic.
*
* @param images the images from which to choose
* @return the chosen preferred image, or {@link null} if the list is empty or the user disabled
* images
* @param images the images from which to choose
* @param nonNoneQuality the preferred quality (must NOT be {@link PreferredImageQuality#NONE})
* @return the chosen preferred image, or {@link null} if the list is empty
* @see #choosePreferredImage(List)
*/
@Nullable
public static String choosePreferredImage(@NonNull final List<Image> images) {
if (preferredImageQuality == PreferredImageQuality.NONE) {
return null; // do not load images
}

static String choosePreferredImage(@NonNull final List<Image> images,
final PreferredImageQuality nonNoneQuality) {
// this will be used to estimate the pixel count for images where only one of height or
// width are known
final double widthOverHeight = images.stream()
Expand All @@ -82,7 +68,7 @@ public static String choosePreferredImage(@NonNull final List<Image> images) {
.findFirst()
.orElse(1.0);

final Image.ResolutionLevel preferredLevel = preferredImageQuality.toResolutionLevel();
final Image.ResolutionLevel preferredLevel = nonNoneQuality.toResolutionLevel();
final Comparator<Image> initialComparator = Comparator
// the first step splits the images into groups of resolution levels
.<Image>comparingInt(i -> {
Expand All @@ -105,7 +91,7 @@ public static String choosePreferredImage(@NonNull final List<Image> images) {
// on how close its size is to BEST_LOW_H or BEST_MEDIUM_H (with proper units). Subgroups
// without known image size will be left untouched since estimatePixelCount always returns
// the same number for those.
final Comparator<Image> finalComparator = switch (preferredImageQuality) {
final Comparator<Image> finalComparator = switch (nonNoneQuality) {
case NONE -> initialComparator; // unreachable
case LOW -> initialComparator.thenComparingDouble(image -> {
final double pixelCount = estimatePixelCount(image, widthOverHeight);
Expand All @@ -128,8 +114,78 @@ public static String choosePreferredImage(@NonNull final List<Image> images) {
.orElse(null);
}

/**
* Chooses an image amongst the provided list based on the user preference previously set with
* {@link #setPreferredImageQuality(PreferredImageQuality)}. {@code null} will be returned in
* case the list is empty or the user preference is to not show images.
* <br>
* These properties will be preferred, from most to least important:
* <ol>
* <li>The image's {@link Image#getEstimatedResolutionLevel()} is not unknown and is close
* to {@link #preferredImageQuality}</li>
* <li>At least one of the image's width or height are known</li>
* <li>The highest resolution image is finally chosen if the user's preference is {@link
* PreferredImageQuality#HIGH}, otherwise the chosen image is the one that has the height
* closest to {@link #BEST_LOW_H} or {@link #BEST_MEDIUM_H}</li>
* </ol>
* <br>
* Use {@link #imageListToDbUrl(List)} if the URL is going to be saved to the database, to avoid
* saving nothing in case at the moment of saving the user preference is to not show images.
*
* @param images the images from which to choose
* @return the chosen preferred image, or {@link null} if the list is empty or the user disabled
* images
* @see #imageListToDbUrl(List)
*/
@Nullable
public static String choosePreferredImage(@NonNull final List<Image> images) {
if (preferredImageQuality == PreferredImageQuality.NONE) {
return null; // do not load images
}

return choosePreferredImage(images, preferredImageQuality);
}

/**
* Like {@link #choosePreferredImage(List)}, except that if {@link #preferredImageQuality} is
* {@link PreferredImageQuality#NONE} an image will be chosen anyway (with preferred quality
* {@link PreferredImageQuality#MEDIUM}.
* <br>
* To go back to a list of images (obviously with just the one chosen image) from a URL saved in
* the database use {@link #dbUrlToImageList(String)}.
*
* @param images the images from which to choose
* @return the chosen preferred image, or {@link null} if the list is empty
* @see #choosePreferredImage(List)
* @see #dbUrlToImageList(String)
*/
@Nullable
public static String imageListToDbUrl(@NonNull final List<Image> images) {
final PreferredImageQuality quality;
if (preferredImageQuality == PreferredImageQuality.NONE) {
quality = PreferredImageQuality.MEDIUM;
} else {
quality = preferredImageQuality;
}

return choosePreferredImage(images, quality);
}

/**
* Wraps the URL (coming from the database) in a {@code List<Image>} so that it is usable
* seamlessly in all of the places where the extractor would return a list of images, including
* allowing to build info objects based on database objects.
* <br>
* To obtain a url to save to the database from a list of images use {@link
* #imageListToDbUrl(List)}.
*
* @param url the URL to wrap coming from the database, or {@code null} to get an empty list
* @return a list containing just one {@link Image} wrapping the provided URL, with unknown
* image size fields, or an empty list if the URL is {@code null}
* @see #imageListToDbUrl(List)
*/
@NonNull
public static List<Image> urlToImageList(@Nullable final String url) {
public static List<Image> dbUrlToImageList(@Nullable final String url) {
if (url == null) {
return List.of();
} else {
Expand Down

0 comments on commit fd83ec9

Please sign in to comment.