diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeSearchExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeSearchExtractor.java index a0be72cc8f..41607b8acb 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeSearchExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeSearchExtractor.java @@ -7,6 +7,7 @@ import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.prepareDesktopJsonBuilder; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.ALL; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.CHANNELS; +import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.EXACT; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.PLAYLISTS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.VIDEOS; import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory.getSearchParameter; @@ -64,6 +65,7 @@ public class YoutubeSearchExtractor extends SearchExtractor { @Nullable private final String searchType; + public final boolean isExactSearch; private final boolean extractVideoResults; private final boolean extractChannelResults; private final boolean extractPlaylistResults; @@ -75,15 +77,20 @@ public YoutubeSearchExtractor(final StreamingService service, super(service, linkHandler); final List contentFilters = linkHandler.getContentFilters(); searchType = isNullOrEmpty(contentFilters) ? null : contentFilters.get(0); + // Save whether we should extract video, channel and playlist results depending on the // requested search type, as YouTube returns sometimes videos inside channel search results - // If no search type is provided or ALL filter is requested, extract everything + // If no search type is provided or ALL/EXACT (without another search type) filter + // is requested, extract everything extractVideoResults = searchType == null || ALL.equals(searchType) - || VIDEOS.equals(searchType); + || VIDEOS.equals(searchType) || EXACT.equals(searchType); extractChannelResults = searchType == null || ALL.equals(searchType) - || CHANNELS.equals(searchType); + || CHANNELS.equals(searchType) || EXACT.equals(searchType); extractPlaylistResults = searchType == null || ALL.equals(searchType) - || PLAYLISTS.equals(searchType); + || PLAYLISTS.equals(searchType) || EXACT.equals(searchType); + + // If EXACT is NOT the search type this is needed to filter for a content filter + EXACT + isExactSearch = !isNullOrEmpty(contentFilters) && contentFilters.contains(EXACT); } @Override @@ -91,8 +98,7 @@ public void onFetchPage(@Nonnull final Downloader downloader) throws IOException ExtractionException { final String query = super.getSearchString(); final Localization localization = getExtractorLocalization(); - final String params = getSearchParameter(searchType); - + final String params = getSearchParameter(searchType, isExactSearch); final JsonBuilder jsonBody = prepareDesktopJsonBuilder(localization, getExtractorContentCountry()) .value("query", query); diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/linkHandler/YoutubeSearchQueryHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/linkHandler/YoutubeSearchQueryHandlerFactory.java index e2d60c6a34..a961b6524d 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/linkHandler/YoutubeSearchQueryHandlerFactory.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/linkHandler/YoutubeSearchQueryHandlerFactory.java @@ -16,6 +16,7 @@ public final class YoutubeSearchQueryHandlerFactory extends SearchQueryHandlerFa new YoutubeSearchQueryHandlerFactory(); public static final String ALL = "all"; + public static final String EXACT = "exact"; public static final String VIDEOS = "videos"; public static final String CHANNELS = "channels"; public static final String PLAYLISTS = "playlists"; @@ -33,20 +34,23 @@ public final class YoutubeSearchQueryHandlerFactory extends SearchQueryHandlerFa public static YoutubeSearchQueryHandlerFactory getInstance() { return INSTANCE; } - @Override public String getUrl(final String searchString, @Nonnull final List contentFilters, final String sortFilter) throws ParsingException, UnsupportedOperationException { final String contentFilter = !contentFilters.isEmpty() ? contentFilters.get(0) : ""; + final boolean isExactSearch = !contentFilters.isEmpty() && contentFilter.contains(EXACT); + switch (contentFilter) { + case EXACT: + return SEARCH_URL + encodeUrlUtf8(searchString) + "&sp=QgIIAQ%253D%253D"; case VIDEOS: - return SEARCH_URL + encodeUrlUtf8(searchString) + "&sp=EgIQAfABAQ%253D%253D"; + return SEARCH_URL + encodeUrlUtf8(searchString) + (isExactSearch ? "&sp=EgIQAUICCAE%253D" : "&sp=EgIQAfABAQ%253D%253D"); case CHANNELS: - return SEARCH_URL + encodeUrlUtf8(searchString) + "&sp=EgIQAvABAQ%253D%253D"; + return SEARCH_URL + encodeUrlUtf8(searchString) + (isExactSearch ? "&sp=EgIQAkICCAE%253D" : "&sp=EgIQAvABAQ%253D%253D"); case PLAYLISTS: - return SEARCH_URL + encodeUrlUtf8(searchString) + "&sp=EgIQA_ABAQ%253D%253D"; + return SEARCH_URL + encodeUrlUtf8(searchString) + (isExactSearch ? "&sp=EgIQA0ICCAE%253D" : "&sp=EgIQA_ABAQ%253D%253D"); case MUSIC_SONGS: case MUSIC_VIDEOS: case MUSIC_ALBUMS: @@ -62,6 +66,7 @@ public String getUrl(final String searchString, public String[] getAvailableContentFilter() { return new String[]{ ALL, + //EXACT, Not a separate content filter (yet) VIDEOS, CHANNELS, PLAYLISTS, @@ -74,26 +79,28 @@ public String[] getAvailableContentFilter() { } @Nonnull - public static String getSearchParameter(final String contentFilter) { + public static String getSearchParameter(final String contentFilter, final boolean isExactSearch) { if (isNullOrEmpty(contentFilter)) { return "8AEB"; } switch (contentFilter) { - case VIDEOS: - return "EgIQAfABAQ%3D%3D"; - case CHANNELS: - return "EgIQAvABAQ%3D%3D"; - case PLAYLISTS: - return "EgIQA_ABAQ%3D%3D"; - case MUSIC_SONGS: - case MUSIC_VIDEOS: - case MUSIC_ALBUMS: - case MUSIC_PLAYLISTS: - case MUSIC_ARTISTS: - return ""; - default: - return "8AEB"; + case EXACT: + return "QgIIAQ%3D%3D"; + case VIDEOS: + return isExactSearch ? "EgIQAUICCAE%3D" : "EgIQAfABAQ%3D%3D"; + case CHANNELS: + return isExactSearch ? "EgIQAkICCAE%3D" : "EgIQAvABAQ%3D%3D"; + case PLAYLISTS: + return isExactSearch ? "EgIQA0ICCAE%3D" : "EgIQA_ABAQ%3D%3D"; + case MUSIC_SONGS: + case MUSIC_VIDEOS: + case MUSIC_ALBUMS: + case MUSIC_PLAYLISTS: + case MUSIC_ARTISTS: + return ""; + default: + return "8AEB"; } } }