From 24bb71a23f0df82866f7525f5def7e638ae6f6cd Mon Sep 17 00:00:00 2001
From: "Thiago F. G. Albuquerque" <thiagofga@gmail.com>
Date: Mon, 24 Feb 2025 19:22:36 -0300
Subject: [PATCH] [#11930] Making it more efficient: Reverse iteration +
 limit(50) + reverse

---
 .../local/playlist/LocalPlaylistFragment.java | 45 ++++++++++++-------
 .../playlist/LocalPlaylistFragmentTest.java   | 38 +++++++++++-----
 2 files changed, 55 insertions(+), 28 deletions(-)

diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
index 008dc4a9c..e8d8573a1 100644
--- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java
@@ -1,5 +1,7 @@
 package org.schabi.newpipe.local.playlist;
 
+import static com.google.common.collect.Streams.stream;
+import static org.apache.commons.collections4.IterableUtils.reversedIterable;
 import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar;
 import static org.schabi.newpipe.ktx.ViewUtils.animate;
 import static org.schabi.newpipe.local.playlist.PlayListShareMode.JUST_URLS;
@@ -7,6 +9,8 @@ import static org.schabi.newpipe.local.playlist.PlayListShareMode.WITH_TITLES;
 import static org.schabi.newpipe.local.playlist.PlayListShareMode.YOUTUBE_TEMP_PLAYLIST;
 import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout;
 
+import static java.util.Collections.reverse;
+
 import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -30,7 +34,9 @@ import androidx.recyclerview.widget.RecyclerView;
 import androidx.viewbinding.ViewBinding;
 
 import com.evernote.android.state.State;
+import com.google.common.collect.Streams;
 
+import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.queue.CircularFifoQueue;
 import org.reactivestreams.Subscriber;
 import org.reactivestreams.Subscription;
@@ -408,7 +414,7 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
             .flatMapSingle(playlist -> Single.just(export(
 
                 shareMode,
-                playlist.stream().map(PlaylistStreamEntry::getStreamEntity),
+                playlist,
                 context
             )))
             .observeOn(AndroidSchedulers.mainThread())
@@ -430,20 +436,21 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
     }
 
     static String export(final PlayListShareMode shareMode,
-                         final Stream<StreamEntity> entityStream,
+                         final List<PlaylistStreamEntry> playlist,
                          final Context context) {
 
         return switch (shareMode) {
 
-            case WITH_TITLES            -> exportWithTitles(entityStream, context);
-            case JUST_URLS              -> exportJustUrls(entityStream);
-            case YOUTUBE_TEMP_PLAYLIST  -> exportAsYoutubeTempPlaylist(entityStream);
+            case WITH_TITLES            -> exportWithTitles(playlist, context);
+            case JUST_URLS              -> exportJustUrls(playlist);
+            case YOUTUBE_TEMP_PLAYLIST  -> exportAsYoutubeTempPlaylist(playlist);
         };
     }
 
-    static String exportWithTitles(final Stream<StreamEntity> entityStream, final Context context) {
+    static String exportWithTitles(final List<PlaylistStreamEntry> playlist, final Context context) {
 
-        return entityStream
+        return playlist.stream()
+            .map(PlaylistStreamEntry::getStreamEntity)
             .map(entity -> context.getString(R.string.video_details_list_item,
                                              entity.getTitle(),
                                              entity.getUrl()
@@ -452,26 +459,30 @@ public class LocalPlaylistFragment extends BaseLocalListFragment<List<PlaylistSt
             .collect(Collectors.joining("\n"));
     }
 
-    static String exportJustUrls(final Stream<StreamEntity> entityStream) {
+    static String exportJustUrls(final List<PlaylistStreamEntry> playlist) {
 
-        return entityStream
+        return playlist.stream()
+            .map(PlaylistStreamEntry::getStreamEntity)
             .map(StreamEntity::getUrl)
             .collect(Collectors.joining("\n"));
     }
 
-    static String exportAsYoutubeTempPlaylist(final Stream<StreamEntity> entityStream) {
+    static String exportAsYoutubeTempPlaylist(final List<PlaylistStreamEntry> playlist) {
 
-        final CircularFifoQueue<String> last50 = new CircularFifoQueue<>(50);
-
-        entityStream
+        final List<String> videoIDs =
+             stream(reversedIterable(playlist))
+            .map(PlaylistStreamEntry::getStreamEntity)
             .map(entity -> getYouTubeId(entity.getUrl()))
             .filter(Objects::nonNull)
-            .forEachOrdered(last50::add);
+            .limit(50)
+            .collect(Collectors.toList());
 
-        final String videoIDs = last50.stream()
-            .collect(Collectors.joining(","));
+        reverse(videoIDs);
 
-        return "http://www.youtube.com/watch_videos?video_ids=" + videoIDs;
+        final String commaSeparatedVideoIDs = videoIDs.stream()
+                .collect(Collectors.joining(","));
+
+        return "http://www.youtube.com/watch_videos?video_ids=" + commaSeparatedVideoIDs;
     }
 
     /**
diff --git a/app/src/test/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragmentTest.java b/app/src/test/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragmentTest.java
index eca798a24..1d5d4a6a0 100644
--- a/app/src/test/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragmentTest.java
+++ b/app/src/test/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragmentTest.java
@@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
 
 import org.junit.Assert;
 import org.junit.Test;
+import org.schabi.newpipe.database.playlist.PlaylistStreamEntry;
 import org.schabi.newpipe.database.stream.model.StreamEntity;
 import org.schabi.newpipe.extractor.stream.StreamType;
 
@@ -18,7 +19,7 @@ public class LocalPlaylistFragmentTest {
     @Test
     public void exportAsYouTubeTempPlaylist() {
 
-        final Stream<StreamEntity> entityStream = asStreamEntityStream(
+        final List<PlaylistStreamEntry> playlist = asPlaylist(
 
             "https://www.youtube.com/watch?v=1",
             "https://soundcloud.com/cautious-clayofficial/cold-war-2", // non-Youtube URLs should be
@@ -26,7 +27,7 @@ public class LocalPlaylistFragmentTest {
             "https://www.youtube.com/watch?v=3"
         );
 
-        final String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, entityStream, null);
+        final String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, playlist, null);
 
         Assert.assertEquals("http://www.youtube.com/watch_videos?video_ids=1,2,3", url);
     }
@@ -48,11 +49,13 @@ public class LocalPlaylistFragmentTest {
             41, 42, 43, 44, 45, 46, 47, 48, 49, 50
         );
 
-        final Stream<StreamEntity> entityStream = ids.stream()
-            .map(id -> "https://www.youtube.com/watch?v=" + id)
-            .map(LocalPlaylistFragmentTest::newStreamEntity);
+        final List<PlaylistStreamEntry> playlist = asPlaylist(
 
-        final String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, entityStream, null);
+            ids.stream()
+            .map(id -> "https://www.youtube.com/watch?v=" + id)
+        );
+
+        final String url = LocalPlaylistFragment.export(YOUTUBE_TEMP_PLAYLIST, playlist, null);
 
         Assert.assertEquals(
 
@@ -70,14 +73,14 @@ public class LocalPlaylistFragmentTest {
     @Test
     public void exportJustUrls() {
 
-        final Stream<StreamEntity> entityStream = asStreamEntityStream(
+        final List<PlaylistStreamEntry> playlist = asPlaylist(
 
             "https://www.youtube.com/watch?v=1",
             "https://www.youtube.com/watch?v=2",
             "https://www.youtube.com/watch?v=3"
         );
 
-        final String exported = LocalPlaylistFragment.export(JUST_URLS, entityStream, null);
+        final String exported = LocalPlaylistFragment.export(JUST_URLS, playlist, null);
 
         Assert.assertEquals("""
             https://www.youtube.com/watch?v=1
@@ -86,10 +89,23 @@ public class LocalPlaylistFragmentTest {
     }
 
     @NonNull
-    private static Stream<StreamEntity> asStreamEntityStream(final String... urls) {
+    static List<PlaylistStreamEntry> asPlaylist(final String... urls) {
 
-        return Stream.of(urls)
-            .map(LocalPlaylistFragmentTest::newStreamEntity);
+        return asPlaylist(Stream.of(urls));
+    }
+
+    @NonNull
+    static List<PlaylistStreamEntry> asPlaylist(final Stream<String> urls) {
+
+        return urls
+            .map(LocalPlaylistFragmentTest::newPlaylistStreamEntry)
+            .toList();
+    }
+
+    @NonNull
+    private static PlaylistStreamEntry newPlaylistStreamEntry(final String url) {
+
+        return new PlaylistStreamEntry(newStreamEntity(url), 0, 0, 0);
     }
 
     @NonNull