|
49 | 49 | import org.schabi.newpipe.extractor.MediaFormat; |
50 | 50 | import org.schabi.newpipe.extractor.MetaInfo; |
51 | 51 | import org.schabi.newpipe.extractor.MultiInfoItemsCollector; |
| 52 | +import org.schabi.newpipe.extractor.NewPipe; |
52 | 53 | import org.schabi.newpipe.extractor.StreamingService; |
53 | 54 | import org.schabi.newpipe.extractor.downloader.Downloader; |
54 | 55 | import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException; |
|
97 | 98 | import java.util.Locale; |
98 | 99 | import java.util.Map; |
99 | 100 | import java.util.Objects; |
| 101 | +import java.util.concurrent.ExecutionException; |
| 102 | +import java.util.concurrent.Future; |
100 | 103 | import java.util.stream.Collectors; |
101 | 104 |
|
102 | 105 | import javax.annotation.Nonnull; |
@@ -773,6 +776,35 @@ public void onFetchPage(@Nonnull final Downloader downloader) |
773 | 776 | final ContentCountry contentCountry = getExtractorContentCountry(); |
774 | 777 | html5Cpn = generateContentPlaybackNonce(); |
775 | 778 |
|
| 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 | + |
776 | 808 | playerResponse = getJsonPostResponse(PLAYER, |
777 | 809 | createDesktopPlayerBody(localization, contentCountry, videoId, sts, false, |
778 | 810 | html5Cpn), |
@@ -820,37 +852,64 @@ public void onFetchPage(@Nonnull final Downloader downloader) |
820 | 852 | playerMicroFormatRenderer = youtubePlayerResponse.getObject("microformat") |
821 | 853 | .getObject("playerMicroformatRenderer"); |
822 | 854 |
|
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 | | - |
832 | 855 | // streamType can only have LIVE_STREAM, POST_LIVE_STREAM and VIDEO_STREAM values (see |
833 | 856 | // setStreamType()), so this block will be run only for POST_LIVE_STREAM and VIDEO_STREAM |
834 | 857 | // values if fetching of the ANDROID client is not forced |
835 | 858 | 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(() -> { |
837 | 891 | try { |
838 | 892 | fetchAndroidMobileJsonPlayer(contentCountry, localization, videoId); |
839 | 893 | } catch (final Exception ignored) { |
840 | 894 | // Ignore exceptions related to ANDROID client fetch or parsing, as it is not |
841 | 895 | // compulsory to play contents |
842 | 896 | } |
843 | | - } |
| 897 | + return null; |
| 898 | + }); |
| 899 | + } |
844 | 900 |
|
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(() -> { |
847 | 905 | try { |
848 | 906 | fetchIosMobileJsonPlayer(contentCountry, localization, videoId); |
849 | 907 | } catch (final Exception ignored) { |
850 | 908 | // Ignore exceptions related to IOS client fetch or parsing, as it is not |
851 | 909 | // compulsory to play contents |
852 | 910 | } |
853 | | - } |
| 911 | + return null; |
| 912 | + }); |
854 | 913 | } |
855 | 914 |
|
856 | 915 | private void checkPlayabilityStatus(final JsonObject youtubePlayerResponse, |
|
0 commit comments