Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Youtube search filters #124

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
635d147
Changes made:
Bluesir9 Oct 19, 2018
d18c68a
Changes made:
Bluesir9 Nov 8, 2018
7bab982
Synced changes from master into youtube_search_filters branch
Bluesir9 Nov 9, 2018
93239eb
Added .java-version file to gitignore and removed it from repo
Bluesir9 Nov 9, 2018
e788a0c
Merge branch 'master' into youtube_search_filters
Bluesir9 Nov 10, 2018
78bb968
Removed usage of DataTypeConverter class and replaced it with newly a…
Bluesir9 Nov 10, 2018
e906c37
Merge branch 'master' into youtube_search_filters
theScrabi Nov 13, 2018
f71b62f
Changes made:
Bluesir9 Dec 8, 2018
75ad8f9
Changes made:
Bluesir9 Dec 8, 2018
98b67a4
Changes made:
Bluesir9 Dec 16, 2018
b02ca4a
Added top level comment explaining modifications made to the Base64 c…
Bluesir9 Dec 16, 2018
6e8515d
Fix bug where index is used incorrectly
Bluesir9 Jan 3, 2019
5464d73
Merge branch 'master' into youtube_search_filters
theScrabi Mar 23, 2019
cd38c1b
Replace Base64 class introduced with Base64 encoding implementation o…
Bluesir9 Mar 23, 2019
56366eb
Merge branch 'dev' into youtube_search_filters
Bluesir9 Mar 23, 2019
7c80354
Create Base64Utils to work around alleged legacy apache commons codec…
Bluesir9 Mar 23, 2019
8152ccf
Add comment explaining purpose of Base64Utils and make bytes array ar…
Bluesir9 Mar 23, 2019
55b126a
Altered test cases to test all filters and sorters individually and u…
Bluesir9 Apr 9, 2019
2b771cb
Merge remote-tracking branch 'upstream/dev' into youtube_search_filters
Bluesir9 Dec 30, 2019
4cfd07b
Undo unnecessary change made to gitignore file
Bluesir9 Dec 30, 2019
68f9547
Change Movie filter name to Film and Show filter name to Programme
Bluesir9 Dec 30, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ gradle-app.setting

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties

.java-version
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory {
///////////////////////////////////

@Override
public abstract String getUrl(String querry, List<String> contentFilter, String sortFilter) throws ParsingException;
public abstract String getUrl(String query, List<String> contentFilter, String sortFilter) throws ParsingException;
public String getSearchString(String url) { return "";}

///////////////////////////////////
Expand All @@ -23,14 +23,14 @@ public abstract class SearchQueryHandlerFactory extends ListLinkHandlerFactory {
public String getId(String url) { return getSearchString(url); }

@Override
public SearchQueryHandler fromQuery(String querry,
public SearchQueryHandler fromQuery(String query,
List<String> contentFilter,
String sortFilter) throws ParsingException {
return new SearchQueryHandler(super.fromQuery(querry, contentFilter, sortFilter));
return new SearchQueryHandler(super.fromQuery(query, contentFilter, sortFilter));
}

public SearchQueryHandler fromQuery(String querry) throws ParsingException {
return fromQuery(querry, new ArrayList<String>(0), "");
public SearchQueryHandler fromQuery(String query) throws ParsingException {
return fromQuery(query, new ArrayList<String>(0), "");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,103 @@

import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory;
import org.schabi.newpipe.extractor.utils.Base64;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class YoutubeSearchQueryHandlerFactory extends SearchQueryHandlerFactory {

public static final String CHARSET_UTF_8 = "UTF-8";

public static final String VIDEOS = "videos";
public static final String CHANNELS = "channels";
public static final String PLAYLISTS = "playlists";
public static final String ALL = "all";
public enum FilterType {
Content(new byte[]{0x10}),
Time(new byte[]{0x08}),
Duration(new byte[]{0x18}),
Feature(new byte[]{});

private final byte[] values;

FilterType(byte[] values) {
this.values = values;
}
}

public enum Filter {
All("All", FilterType.Content, new byte[]{0}),
Video("Video", FilterType.Content, new byte[]{0x01}),
Channel("Channel", FilterType.Content, new byte[]{0x02}),
Playlist("Playlist", FilterType.Content, new byte[]{0x03}),
Movie("Movie", FilterType.Content, new byte[]{0x04}),
Show("Show", FilterType.Content, new byte[]{0x05}),

Hour("Hour", FilterType.Time, new byte[]{0x01}),
Today("Today", FilterType.Time, new byte[]{0x02}),
Week("Week", FilterType.Time, new byte[]{0x03}),
Month("Month", FilterType.Time, new byte[]{0x04}),
Year("Year", FilterType.Time, new byte[]{0x05}),

Short("Short", FilterType.Duration, new byte[]{0x01}),
Long("Long", FilterType.Duration, new byte[]{0x02}),

HD("HD", FilterType.Feature, new byte[]{0x20, 0x01}),
Subtitles("Subtitles", FilterType.Feature, new byte[]{0x28, 0x01}),
CreativeCommons("Creative Commons", FilterType.Feature, new byte[]{0x30, 0x01}),
ThreeDimensional("3D", FilterType.Feature, new byte[]{0x38, 0x01}),
Live("Live", FilterType.Feature, new byte[]{0x40, 0x01}),
Purchased("Purchased", FilterType.Feature, new byte[]{0x48, 0x01}),
FourK("4k", FilterType.Feature, new byte[]{0x70, 0x01}),
ThreeSixty("360", FilterType.Feature, new byte[]{0x78, 0x01}),
Location("Location", FilterType.Feature, new byte[]{(byte) 0xb8, 0x01, 0x01}),
HDR("HDR", FilterType.Feature, new byte[]{(byte) 0xc8, 0x01, 0x01});

private final String title;
private final FilterType type;
private final byte[] values;

Filter(String title, FilterType type, byte[] values) {
this.title = title;
this.type = type;
this.values = values;
}

public String getTitle() {
return title;
}
}

public enum SorterType {
Default((byte) 0x08);

private final byte value;

SorterType(byte value) {
this.value = value;
}
}

public enum Sorter {
Relevance("Relevance", SorterType.Default, (byte) 0x00),
Rating("Rating", SorterType.Default, (byte) 0x01),
Upload_Date("Upload_Date", SorterType.Default, (byte) 0x02),
View_Count("View_Count", SorterType.Default, (byte) 0x03);

private final String title;
private final SorterType type;
private final byte value;

Sorter(String title, SorterType type, byte value) {
this.title = title;
this.type = type;
this.value = value;
}
}

public static YoutubeSearchQueryHandlerFactory getInstance() {
return new YoutubeSearchQueryHandlerFactory();
Expand All @@ -23,31 +107,129 @@ public static YoutubeSearchQueryHandlerFactory getInstance() {
@Override
public String getUrl(String searchString, List<String> contentFilters, String sortFilter) throws ParsingException {
try {
final String url = "https://www.youtube.com/results"
+ "?q=" + URLEncoder.encode(searchString, CHARSET_UTF_8);

if(contentFilters.size() > 0) {
switch (contentFilters.get(0)) {
case VIDEOS: return url + "&sp=EgIQAVAU";
case CHANNELS: return url + "&sp=EgIQAlAU";
case PLAYLISTS: return url + "&sp=EgIQA1AU";
case ALL:
default:
}
String returnURL = getSearchBaseUrl(searchString);
String filterQueryParams = getFilterQueryParams(contentFilters, sortFilter);
if (filterQueryParams != null) {
returnURL = returnURL + "&sp=" + filterQueryParams;
}

return url;
return returnURL;
} catch (UnsupportedEncodingException e) {
throw new ParsingException("Could not encode query", e);
} catch (IllegalArgumentException e) {
throw new ParsingException("Failed to get search results", e);
}
}

@Override
public String[] getAvailableContentFilter() {
return new String[] {
ALL,
VIDEOS,
CHANNELS,
PLAYLISTS};
List<String> contentFiltersList = new ArrayList<>();
for (Filter contentFilter : Filter.values()) {
contentFiltersList.add(contentFilter.title);
}
String[] contentFiltersArray = new String[contentFiltersList.size()];
contentFiltersArray = contentFiltersList.toArray(contentFiltersArray);
return contentFiltersArray;
}

@Override
public String[] getAvailableSortFilter() {
List<String> sortFiltersList = new ArrayList<>();
for (Sorter sortFilter : Sorter.values()) {
sortFiltersList.add(sortFilter.title);
}
String[] sortFiltersArray = new String[sortFiltersList.size()];
sortFiltersArray = sortFiltersList.toArray(sortFiltersArray);
return sortFiltersArray;
}

private String getSearchBaseUrl(String searchQuery)
throws UnsupportedEncodingException {
return "https://www.youtube.com/results" +
"?q=" +
URLEncoder.encode(searchQuery, CHARSET_UTF_8);
}

@Nullable
private String getFilterQueryParams(List<String> contentFilters, String sortFilter)
throws IllegalArgumentException {
List<Byte> returnList = new ArrayList<>();
List<Byte> sortFilterParams = getSortFiltersQueryParam(sortFilter);
if (!sortFilterParams.isEmpty()) {
returnList.addAll(sortFilterParams);
}
List<Byte> contentFilterParams = getContentFiltersQueryParams(contentFilters);
if (!contentFilterParams.isEmpty()) {
returnList.add((byte) 0x12);
returnList.add((byte) contentFilterParams.size());
returnList.addAll(contentFilterParams);
}

if (returnList.isEmpty()) {
return null;
}
return URLEncoder.encode(Base64.encodeToString(convert(returnList), Base64.URL_SAFE));
}

private List<Byte> getContentFiltersQueryParams(List<String> contentFilter) throws IllegalArgumentException {
if (contentFilter == null || contentFilter.isEmpty()) {
return Collections.emptyList();
}
List<Byte> returnList = new ArrayList<>();
for (String filter : contentFilter) {
List<Byte> byteList = getContentFilterQueryParams(filter);
if (!byteList.isEmpty()) {
returnList.addAll(byteList);
}
}
return returnList;
}

private List<Byte> getContentFilterQueryParams(String filter) throws IllegalArgumentException {
Filter contentFilter;
try {
contentFilter = Filter.valueOf(filter);
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
throw new IllegalArgumentException("Unknown content filter type provided = " + filter + ", none will be applied");
}
switch (contentFilter) {
case All:
return Collections.emptyList();
default:
List<Byte> returnList = new ArrayList<>();
returnList.addAll(convert(contentFilter.type.values));
returnList.addAll(convert(contentFilter.values));
return returnList;
}
}

private List<Byte> getSortFiltersQueryParam(String filter) throws IllegalArgumentException {
if (filter == null || filter.isEmpty()) {
return Collections.emptyList();
}
Sorter sorter;
try {
sorter = Sorter.valueOf(filter);
} catch (IllegalArgumentException e) {
e.printStackTrace();
theScrabi marked this conversation as resolved.
Show resolved Hide resolved
throw new IllegalArgumentException("Unknown sort filter = " + filter + " provided, none applied.");
}
return Arrays.asList(sorter.type.value, sorter.value);
}

private byte[] convert(@Nonnull List<Byte> bigByteList) {
byte[] returnArray = new byte[bigByteList.size()];
for (int i = 0; i < bigByteList.size(); i++) {
returnArray[i] = bigByteList.get(i);
}
return returnArray;
}

private List<Byte> convert(@Nonnull byte[] byteArray) {
theScrabi marked this conversation as resolved.
Show resolved Hide resolved
List<Byte> returnList = new ArrayList<>(byteArray.length);
for (int i = 0; i < byteArray.length; i++) {
returnList.add(i, byteArray[0]);
}
return returnList;
}
}
Loading