Skip to content

Commit 3b632db

Browse files
committed
Parallelize YouTubeStreamExtractor with a ExecutorService.
1 parent 6a85836 commit 3b632db

File tree

2 files changed

+92
-14
lines changed

2 files changed

+92
-14
lines changed

extractor/src/main/java/org/schabi/newpipe/extractor/NewPipe.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.schabi.newpipe.extractor.localization.Localization;
2727

2828
import java.util.List;
29+
import java.util.concurrent.ExecutorService;
30+
import java.util.concurrent.Executors;
2931

3032
import javax.annotation.Nonnull;
3133
import javax.annotation.Nullable;
@@ -38,6 +40,8 @@ public final class NewPipe {
3840
private static Localization preferredLocalization;
3941
private static ContentCountry preferredContentCountry;
4042

43+
private static ExecutorService executorService;
44+
4145
private NewPipe() {
4246
}
4347

@@ -51,9 +55,15 @@ public static void init(final Downloader d, final Localization l) {
5155
}
5256

5357
public static void init(final Downloader d, final Localization l, final ContentCountry c) {
58+
init(d, l, c, Executors.newCachedThreadPool());
59+
}
60+
61+
public static void init(final Downloader d, final Localization l, final ContentCountry c,
62+
final ExecutorService e) {
5463
downloader = d;
5564
preferredLocalization = l;
5665
preferredContentCountry = c;
66+
executorService = e;
5767
}
5868

5969
public static Downloader getDownloader() {
@@ -132,4 +142,13 @@ public static ContentCountry getPreferredContentCountry() {
132142
public static void setPreferredContentCountry(final ContentCountry preferredContentCountry) {
133143
NewPipe.preferredContentCountry = preferredContentCountry;
134144
}
145+
146+
@Nonnull
147+
public static ExecutorService getExecutorService() {
148+
return executorService;
149+
}
150+
151+
public static void setExecutorService(final ExecutorService executorService) {
152+
NewPipe.executorService = executorService;
153+
}
135154
}

extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import org.schabi.newpipe.extractor.MediaFormat;
5050
import org.schabi.newpipe.extractor.MetaInfo;
5151
import org.schabi.newpipe.extractor.MultiInfoItemsCollector;
52+
import org.schabi.newpipe.extractor.NewPipe;
5253
import org.schabi.newpipe.extractor.StreamingService;
5354
import org.schabi.newpipe.extractor.downloader.Downloader;
5455
import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
@@ -97,6 +98,8 @@
9798
import java.util.Locale;
9899
import java.util.Map;
99100
import java.util.Objects;
101+
import java.util.concurrent.ExecutionException;
102+
import java.util.concurrent.Future;
100103
import java.util.stream.Collectors;
101104

102105
import javax.annotation.Nonnull;
@@ -773,6 +776,35 @@ public void onFetchPage(@Nonnull final Downloader downloader)
773776
final ContentCountry contentCountry = getExtractorContentCountry();
774777
html5Cpn = generateContentPlaybackNonce();
775778

779+
final Future<Void> nextFuture = NewPipe.getExecutorService().submit(() -> {
780+
try {
781+
final byte[] body = JsonWriter.string(
782+
prepareDesktopJsonBuilder(localization, contentCountry)
783+
.value(VIDEO_ID, videoId)
784+
.value(CONTENT_CHECK_OK, true)
785+
.value(RACY_CHECK_OK, true)
786+
.done())
787+
.getBytes(StandardCharsets.UTF_8);
788+
nextResponse = getJsonPostResponse(NEXT, body, localization);
789+
} catch (final Exception e) {
790+
throw new RuntimeException(e);
791+
}
792+
return null;
793+
});
794+
795+
Future<Void> androidPlayerFuture = null;
796+
797+
if (isAndroidClientFetchForced) {
798+
androidPlayerFuture = getAndroidFetchFuture(contentCountry, localization, videoId);
799+
}
800+
801+
Future<Void> iosPlayerFuture = null;
802+
803+
if (isIosClientFetchForced) {
804+
iosPlayerFuture = getIosFetchFuture(contentCountry, localization, videoId);
805+
}
806+
807+
776808
playerResponse = getJsonPostResponse(PLAYER,
777809
createDesktopPlayerBody(localization, contentCountry, videoId, sts, false,
778810
html5Cpn),
@@ -820,37 +852,64 @@ public void onFetchPage(@Nonnull final Downloader downloader)
820852
playerMicroFormatRenderer = youtubePlayerResponse.getObject("microformat")
821853
.getObject("playerMicroformatRenderer");
822854

823-
final byte[] body = JsonWriter.string(
824-
prepareDesktopJsonBuilder(localization, contentCountry)
825-
.value(VIDEO_ID, videoId)
826-
.value(CONTENT_CHECK_OK, true)
827-
.value(RACY_CHECK_OK, true)
828-
.done())
829-
.getBytes(StandardCharsets.UTF_8);
830-
nextResponse = getJsonPostResponse(NEXT, body, localization);
831-
832855
// streamType can only have LIVE_STREAM, POST_LIVE_STREAM and VIDEO_STREAM values (see
833856
// setStreamType()), so this block will be run only for POST_LIVE_STREAM and VIDEO_STREAM
834857
// values if fetching of the ANDROID client is not forced
835858
if ((!isAgeRestricted && streamType != StreamType.LIVE_STREAM)
836-
|| isAndroidClientFetchForced) {
859+
&& androidPlayerFuture == null) {
860+
androidPlayerFuture = getAndroidFetchFuture(contentCountry, localization, videoId);
861+
}
862+
863+
if ((!isAgeRestricted && streamType == StreamType.LIVE_STREAM)
864+
&& iosPlayerFuture == null) {
865+
iosPlayerFuture = getIosFetchFuture(contentCountry, localization, videoId);
866+
}
867+
868+
try {
869+
nextFuture.get();
870+
871+
if (androidPlayerFuture != null) {
872+
androidPlayerFuture.get();
873+
}
874+
if (iosPlayerFuture != null) {
875+
iosPlayerFuture.get();
876+
}
877+
878+
} catch (final InterruptedException ignored) {
879+
} catch (final ExecutionException e) {
880+
final Throwable cause = e.getCause();
881+
if (cause != null) {
882+
throw new RuntimeException(cause);
883+
}
884+
}
885+
}
886+
887+
private Future<Void> getAndroidFetchFuture(final ContentCountry contentCountry,
888+
final Localization localization,
889+
final String videoId) {
890+
return NewPipe.getExecutorService().submit(() -> {
837891
try {
838892
fetchAndroidMobileJsonPlayer(contentCountry, localization, videoId);
839893
} catch (final Exception ignored) {
840894
// Ignore exceptions related to ANDROID client fetch or parsing, as it is not
841895
// compulsory to play contents
842896
}
843-
}
897+
return null;
898+
});
899+
}
844900

845-
if ((!isAgeRestricted && streamType == StreamType.LIVE_STREAM)
846-
|| isIosClientFetchForced) {
901+
private Future<Void> getIosFetchFuture(final ContentCountry contentCountry,
902+
final Localization localization,
903+
final String videoId) {
904+
return NewPipe.getExecutorService().submit(() -> {
847905
try {
848906
fetchIosMobileJsonPlayer(contentCountry, localization, videoId);
849907
} catch (final Exception ignored) {
850908
// Ignore exceptions related to IOS client fetch or parsing, as it is not
851909
// compulsory to play contents
852910
}
853-
}
911+
return null;
912+
});
854913
}
855914

856915
private void checkPlayabilityStatus(final JsonObject youtubePlayerResponse,

0 commit comments

Comments
 (0)