NewPipe/app/src/main/java/org/schabi/newpipe/util/ListHelper.java

270 lines
12 KiB
Java

package org.schabi.newpipe.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.StringRes;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.VideoStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@SuppressWarnings("WeakerAccess")
public final class ListHelper {
private static final List<String> HIGH_RESOLUTION_LIST = Arrays.asList("1440p", "2160p", "1440p60", "2160p60");
/**
* Return the index of the default stream in the list, based on the parameters
* defaultResolution and defaultFormat
*
* @return index of the default resolution&format
*/
public static int getDefaultResolutionIndex(String defaultResolution, String bestResolutionKey, MediaFormat defaultFormat, List<VideoStream> videoStreams) {
if (videoStreams == null || videoStreams.isEmpty()) return -1;
sortStreamList(videoStreams, false);
if (defaultResolution.equals(bestResolutionKey)) {
return 0;
}
int defaultStreamIndex = getDefaultStreamIndex(defaultResolution, defaultFormat, videoStreams);
if (defaultStreamIndex == -1 && defaultResolution.contains("p60")) {
defaultStreamIndex = getDefaultStreamIndex(defaultResolution.replace("p60", "p"), defaultFormat, videoStreams);
}
// this is actually an error,
// but maybe there is really no stream fitting to the default value.
if (defaultStreamIndex == -1) return 0;
return defaultStreamIndex;
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getDefaultResolutionIndex(Context context, List<VideoStream> videoStreams) {
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_resolution_key), context.getString(R.string.default_resolution_value));
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
/**
* @see #getDefaultResolutionIndex(String, String, MediaFormat, List)
*/
public static int getPopupDefaultResolutionIndex(Context context, List<VideoStream> videoStreams) {
SharedPreferences defaultPreferences = PreferenceManager.getDefaultSharedPreferences(context);
if (defaultPreferences == null) return 0;
String defaultResolution = defaultPreferences.getString(context.getString(R.string.default_popup_resolution_key), context.getString(R.string.default_popup_resolution_value));
return getDefaultResolutionWithDefaultFormat(context, defaultResolution, videoStreams);
}
public static int getDefaultAudioFormat(Context context, List<AudioStream> audioStreams) {
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_audio_format_key, R.string.default_audio_format_value);
return getHighestQualityAudioIndex(defaultFormat, audioStreams);
}
public static int getHighestQualityAudioIndex(List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty()) return -1;
int highestQualityIndex = 0;
if (audioStreams.size() > 1) for (int i = 1; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
if (audioStream.average_bitrate >= audioStreams.get(highestQualityIndex).average_bitrate) highestQualityIndex = i;
}
return highestQualityIndex;
}
/**
* Get the audio from the list with the highest bitrate
*
* @param audioStreams list the audio streams
* @return audio with highest average bitrate
*/
public static AudioStream getHighestQualityAudio(List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty()) return null;
return audioStreams.get(getHighestQualityAudioIndex(audioStreams));
}
/**
* Get the audio from the list with the highest bitrate
*
* @param audioStreams list the audio streams
* @return index of the audio with the highest average bitrate of the default format
*/
public static int getHighestQualityAudioIndex(MediaFormat defaultFormat, List<AudioStream> audioStreams) {
if (audioStreams == null || audioStreams.isEmpty() || defaultFormat == null) return -1;
int highestQualityIndex = -1;
for (int i = 0; i < audioStreams.size(); i++) {
AudioStream audioStream = audioStreams.get(i);
if (highestQualityIndex == -1 && audioStream.format == defaultFormat.id) highestQualityIndex = i;
if (highestQualityIndex != -1 && audioStream.format == defaultFormat.id
&& audioStream.average_bitrate > audioStreams.get(highestQualityIndex).average_bitrate) {
highestQualityIndex = i;
}
}
if (highestQualityIndex == -1) highestQualityIndex = getHighestQualityAudioIndex(audioStreams);
return highestQualityIndex;
}
/**
* Join the two lists of video streams (video_only and normal videos), and sort them according with default format
* chosen by the user
*
* @param context context to search for the format to give preference
* @param videoStreams normal videos list
* @param videoOnlyStreams video only stream list
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
* @return the sorted list
*/
public static List<VideoStream> getSortedStreamVideosList(Context context, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean showHigherResolutions = preferences.getBoolean(context.getString(R.string.show_higher_resolutions_key), false);
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_video_format_key, R.string.default_video_format_value);
return getSortedStreamVideosList(defaultFormat, showHigherResolutions, videoStreams, videoOnlyStreams, ascendingOrder);
}
/**
* Join the two lists of video streams (video_only and normal videos), and sort them according with default format
* chosen by the user
*
* @param defaultFormat format to give preference
* @param showHigherResolutions show >1080p resolutions
* @param videoStreams normal videos list
* @param videoOnlyStreams video only stream list
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest @return the sorted list
* @return the sorted list
*/
public static List<VideoStream> getSortedStreamVideosList(MediaFormat defaultFormat, boolean showHigherResolutions, List<VideoStream> videoStreams, List<VideoStream> videoOnlyStreams, boolean ascendingOrder) {
ArrayList<VideoStream> retList = new ArrayList<>();
HashMap<String, VideoStream> hashMap = new HashMap<>();
if (videoOnlyStreams != null) {
for (VideoStream stream : videoOnlyStreams) {
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
retList.add(stream);
}
}
if (videoStreams != null) {
for (VideoStream stream : videoStreams) {
if (!showHigherResolutions && HIGH_RESOLUTION_LIST.contains(stream.resolution)) continue;
retList.add(stream);
}
}
// Add all to the hashmap
for (VideoStream videoStream : retList) hashMap.put(videoStream.resolution, videoStream);
// Override the values when the key == resolution, with the defaultFormat
for (VideoStream videoStream : retList) {
if (videoStream.format == defaultFormat.id) hashMap.put(videoStream.resolution, videoStream);
}
retList.clear();
retList.addAll(hashMap.values());
sortStreamList(retList, ascendingOrder);
return retList;
}
/**
* Sort the streams list depending on the parameter ascendingOrder;
* <p>
* It works like that:<br>
* - Take a string resolution, remove the letters, replace "0p60" (for 60fps videos) with "1"
* and sort by the greatest:<br>
* <blockquote><pre>
* 720p -> 720
* 720p60 -> 721
* 360p -> 360
* 1080p -> 1080
* 1080p60 -> 1081
* <br>
* ascendingOrder ? 360 < 720 < 721 < 1080 < 1081
* !ascendingOrder ? 1081 < 1080 < 721 < 720 < 360</pre></blockquote>
*
* @param videoStreams list that the sorting will be applied
* @param ascendingOrder true -> smallest to greatest | false -> greatest to smallest
*/
public static void sortStreamList(List<VideoStream> videoStreams, final boolean ascendingOrder) {
Collections.sort(videoStreams, new Comparator<VideoStream>() {
@Override
public int compare(VideoStream o1, VideoStream o2) {
int res1 = Integer.parseInt(o1.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
int res2 = Integer.parseInt(o2.resolution.replace("0p60", "1").replaceAll("[^\\d.]", ""));
return ascendingOrder ? res1 - res2 : res2 - res1;
}
});
}
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
private static int getDefaultStreamIndex(String defaultResolution, MediaFormat defaultFormat, List<VideoStream> videoStreams) {
int defaultStreamIndex = -1;
for (int i = 0; i < videoStreams.size(); i++) {
VideoStream stream = videoStreams.get(i);
if (defaultStreamIndex == -1 && stream.resolution.equals(defaultResolution)) defaultStreamIndex = i;
if (stream.format == defaultFormat.id && stream.resolution.equals(defaultResolution)) {
return i;
}
}
return defaultStreamIndex;
}
private static int getDefaultResolutionWithDefaultFormat(Context context, String defaultResolution, List<VideoStream> videoStreams) {
MediaFormat defaultFormat = getDefaultFormat(context, R.string.default_video_format_key, R.string.default_video_format_value);
return getDefaultResolutionIndex(defaultResolution, context.getString(R.string.best_resolution_key), defaultFormat, videoStreams);
}
private static MediaFormat getDefaultFormat(Context context, @StringRes int defaultFormatKey, @StringRes int defaultFormatValueKey) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String defaultFormat = context.getString(defaultFormatValueKey);
String defaultFormatString = preferences.getString(context.getString(defaultFormatKey), defaultFormat);
MediaFormat defaultMediaFormat = getMediaFormatFromKey(context, defaultFormatString);
if (defaultMediaFormat == null) {
preferences.edit().putString(context.getString(defaultFormatKey), defaultFormat).apply();
defaultMediaFormat = getMediaFormatFromKey(context, defaultFormat);
}
return defaultMediaFormat;
}
private static MediaFormat getMediaFormatFromKey(Context context, String formatKey) {
MediaFormat format = null;
if (formatKey.equals(context.getString(R.string.video_webm_key))) {
format = MediaFormat.WEBM;
} else if (formatKey.equals(context.getString(R.string.video_mp4_key))) {
format = MediaFormat.MPEG_4;
} else if (formatKey.equals(context.getString(R.string.video_3gp_key))) {
format = MediaFormat.v3GPP;
} else if (formatKey.equals(context.getString(R.string.audio_webm_key))) {
format = MediaFormat.WEBMA;
} else if (formatKey.equals(context.getString(R.string.audio_m4a_key))) {
format = MediaFormat.M4A;
}
return format;
}
}