From 86c404ba19443e92e2abb893a915b4d54673c037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Tue, 3 Sep 2024 15:06:52 +0200 Subject: [PATCH] Migrate Java files to Kotlin (#49) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR migrates the remaining [Java files](https://github.com/search?q=repo%3ASRGSSR%2Fsrgdataprovider-android++language%3AJava&type=code) to Kotlin. Each file is migrated in a dedicated commit. To ensure that this doesn't break usages from either Java or Kotlin clients, I've created the following simple classes. They do not test the behavior, only that the API of each class remained the same. ```kotlin package ch.srg.dataProvider.integrationlayer import ch.srg.dataProvider.integrationlayer.data.remote.Quality import ch.srg.dataProvider.integrationlayer.data.remote.Resource import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod import ch.srg.dataProvider.integrationlayer.request.IlHost import ch.srg.dataProvider.integrationlayer.utils.IlUrn import ch.srg.dataProvider.integrationlayer.utils.StreamComparator class KotlinUsages { fun ilHost() { IlHost.PROD IlHost.TEST IlHost.STAGE IlHost.MMF IlHost.MMF_PUBLIC IlHost.PROD_SAM IlHost.TEST_SAM IlHost.STAGE_SAM val ilHost = IlHost("custom-host", "my.il.host.com") ilHost.value ilHost.name ilHost.hostUri } fun streamComparator() { StreamComparator.SCORE_NOT_SUPPORTED val resource1 = Resource(url = "https://www.rts.ch/", quality = Quality.HD, streamingMethod = StreamingMethod.HLS) val resource2 = Resource(url = "https://www.srgssr.ch/", quality = Quality.SD, streamingMethod = StreamingMethod.DASH) val streamComparator = StreamComparator(emptyList(), emptyList(), emptyList(), false) streamComparator.compare(resource1, resource2) streamComparator.score(resource1) } fun ilUrn() { IlUrn.ASSET_VIDEO IlUrn.ASSET_VIDEO_SET IlUrn.ASSET_AUDIO IlUrn.ASSET_SHOW IlUrn.ASSET_GROUP IlUrn.isUrn("") IlUrn.format("", "", "") IlUrn.isAudio("") IlUrn.isVideo("") IlUrn.getId("") IlUrn.getAssetType("") val ilUrn1 = IlUrn("") val ilUrn2 = IlUrn("", "", "") ilUrn1.bu ilUrn1.assetType ilUrn1.id ilUrn1.isAudio ilUrn1.isVideo ilUrn1.isShow ilUrn1.equalsToString("") } } ``` ```java package ch.srg.dataProvider.integrationlayer; import java.util.Collections; import ch.srg.dataProvider.integrationlayer.data.remote.Resource; import ch.srg.dataProvider.integrationlayer.request.IlHost; import ch.srg.dataProvider.integrationlayer.utils.IlUrn; import ch.srg.dataProvider.integrationlayer.utils.StreamComparator; public class JavaUsages { void ilHost() { var prod = IlHost.PROD; var test = IlHost.TEST; var stage = IlHost.STAGE; var mmf = IlHost.MMF; var mmfPublic = IlHost.MMF_PUBLIC; var prodSam = IlHost.PROD_SAM; var testSam = IlHost.TEST_SAM; var stageSam = IlHost.STAGE_SAM; var ilHost = new IlHost("custom-host", "my.il.host.com"); ilHost.getValue(); ilHost.getName(); ilHost.getHostUri(); } void streamComparator() { var scoreNotSupported = StreamComparator.SCORE_NOT_SUPPORTED; var resource1 = new Resource(); // This doesn't compile, but the goal is just to see if calls on `StreamComparator` are fine var resource2 = new Resource(); // This doesn't compile, but the goal is just to see if calls on `StreamComparator` are fine var streamComparator = new StreamComparator(Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), false); streamComparator.compare(resource1, resource2); streamComparator.score(resource1); } void ilUrn() { var assetVideo = IlUrn.ASSET_VIDEO; var assetVideoSet = IlUrn.ASSET_VIDEO_SET; var assetAudio = IlUrn.ASSET_AUDIO; var assetShow = IlUrn.ASSET_SHOW; var assetGroup = IlUrn.ASSET_GROUP; IlUrn.isUrn(""); IlUrn.format("", "", ""); IlUrn.isAudio(""); IlUrn.isVideo(""); IlUrn.getId(""); IlUrn.getAssetType(""); var ilUrn1 = new IlUrn(""); var ilUrn2 = new IlUrn("", "", ""); ilUrn1.getBu(); ilUrn1.getAssetType(); ilUrn1.getId(); ilUrn1.isAudio(); ilUrn1.isVideo(); ilUrn1.isShow(); ilUrn1.equalsToString(""); } } ``` These should work the same way on the `master` branch and in this PR. --------- Co-authored-by: Joaquim Stähli --- dataprovider-retrofit/build.gradle.kts | 1 - .../integrationlayer/request/IlHost.java | 76 ------ .../integrationlayer/request/IlHost.kt | 52 ++++ .../integrationlayer/utils/IlUrn.java | 186 --------------- .../integrationlayer/utils/IlUrn.kt | 178 ++++++++++++++ .../utils/StreamComparator.java | 72 ------ .../utils/StreamComparator.kt | 58 +++++ .../utils/MediaFetcherInputs.kt | 42 ++++ .../integrationlayer/utils/ResourceBuilder.kt | 31 +++ .../StreamComparatorAllPossibilitiesTest.kt | 224 ++++++++++++++++++ .../utils/StreamComparatorTest.kt | 208 ++++++++++++++++ gradle/libs.versions.toml | 2 - 12 files changed, 793 insertions(+), 337 deletions(-) delete mode 100644 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.java create mode 100644 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.kt delete mode 100755 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.java create mode 100644 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.kt delete mode 100644 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.java create mode 100644 dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.kt create mode 100644 dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/MediaFetcherInputs.kt create mode 100644 dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/ResourceBuilder.kt create mode 100644 dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorAllPossibilitiesTest.kt create mode 100644 dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorTest.kt diff --git a/dataprovider-retrofit/build.gradle.kts b/dataprovider-retrofit/build.gradle.kts index f9c1aa4..5b9137b 100644 --- a/dataprovider-retrofit/build.gradle.kts +++ b/dataprovider-retrofit/build.gradle.kts @@ -53,7 +53,6 @@ android { dependencies { api(project(":data")) api(libs.retrofit) - compileOnly(libs.androidx.annotation) api(platform(libs.retrofit.bom)) implementation(libs.retrofit.converter.kotlinx.serialization) implementation(libs.okhttp.logging.interceptor) diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.java b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.java deleted file mode 100644 index eb62c0f..0000000 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.java +++ /dev/null @@ -1,76 +0,0 @@ -package ch.srg.dataProvider.integrationlayer.request; - -import android.net.Uri; - -import androidx.annotation.NonNull; - -import java.io.Serializable; - -/** - * Copyright (c) SRG SSR. All rights reserved. - *

- * License information is available from the LICENSE file. - */ -public final class IlHost implements Serializable { - public static final IlHost PROD = new IlHost("PROD", "il.srgssr.ch"); - public static final IlHost TEST = new IlHost("TEST", "il-test.srgssr.ch"); - public static final IlHost STAGE = new IlHost("STAGE", "il-stage.srgssr.ch"); - public static final IlHost MMF = new IlHost("MMF", "play-mmf.herokuapp.com/android_26CE9E49-9600"); - public static final IlHost MMF_PUBLIC = new IlHost("MMF", "play-mmf.herokuapp.com"); - public static final IlHost PROD_SAM = new IlHost("PROD_SAM", "il.srgssr.ch/sam"); - public static final IlHost TEST_SAM = new IlHost("TEST_SAM", "il-test.srgssr.ch/sam"); - public static final IlHost STAGE_SAM = new IlHost("STAGE_SAM", "il-stage.srgssr.ch/sam"); - - @NonNull - private final String value; - @NonNull - private final String name; - - /** - * @param name designed name - * @param value hostname of the integration layer url - */ - public IlHost(@NonNull final String name, @NonNull final String value) { - this.name = name; - this.value = value; - } - - @NonNull - public String getValue() { - return value; - } - - @NonNull - public String getName() { - return name; - } - - @NonNull - public Uri getHostUri() { - return Uri.parse("https://" + value); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - IlHost ilHost = (IlHost) o; - - if (!value.equals(ilHost.value)) return false; - return name.equals(ilHost.name); - } - - @Override - public int hashCode() { - int result = value.hashCode(); - result = 31 * result + name.hashCode(); - return result; - } - - @NonNull - @Override - public String toString() { - return name; - } -} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.kt new file mode 100644 index 0000000..d9f1e69 --- /dev/null +++ b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/request/IlHost.kt @@ -0,0 +1,52 @@ +package ch.srg.dataProvider.integrationlayer.request + +import android.net.Uri +import java.io.Serializable + +/** + * Copyright (c) SRG SSR. All rights reserved. + *

+ * License information is available from the LICENSE file. + * + * @property name designed name + * @property value hostname of the integration layer url + */ +data class IlHost( + val name: String, + val value: String, +) : Serializable { + val hostUri: Uri + get() = Uri.parse("https://$value") + + override fun toString(): String { + return name + } + + companion object { + private const val serialVersionUID = 1L + + @JvmField + val PROD = IlHost("PROD", "il.srgssr.ch") + + @JvmField + val TEST = IlHost("TEST", "il-test.srgssr.ch") + + @JvmField + val STAGE = IlHost("STAGE", "il-stage.srgssr.ch") + + @JvmField + val MMF = IlHost("MMF", "play-mmf.herokuapp.com/android_26CE9E49-9600") + + @JvmField + val MMF_PUBLIC = IlHost("MMF", "play-mmf.herokuapp.com") + + @JvmField + val PROD_SAM = IlHost("PROD_SAM", "il.srgssr.ch/sam") + + @JvmField + val TEST_SAM = IlHost("TEST_SAM", "il-test.srgssr.ch/sam") + + @JvmField + val STAGE_SAM = IlHost("STAGE_SAM", "il-stage.srgssr.ch/sam") + } +} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.java b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.java deleted file mode 100755 index e5393d4..0000000 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.java +++ /dev/null @@ -1,186 +0,0 @@ -package ch.srg.dataProvider.integrationlayer.utils; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This very simple class represents a URN provided by the IL. - */ -public class IlUrn { - private final static Pattern PATTERN_BUMAM = Pattern.compile("^urn:(srf|rts|rsi|rtr|swi|default):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):([^:]+)$", Pattern.CASE_INSENSITIVE); - private final static Pattern PATTERN_SWISSTXT = Pattern.compile("^urn:(swisstxt):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):(srf|rts|rsi|rtr|swi):([^:]+)$", Pattern.CASE_INSENSITIVE); - - public static final String ASSET_VIDEO = "video"; - public static final String ASSET_VIDEO_SET = "videoset"; - public static final String ASSET_AUDIO = "audio"; - public static final String ASSET_SHOW = "show"; - public static final String ASSET_GROUP = "assetgroup"; - - private String underlying; - - private String bu; - private String assetType; - private String id; - - /** - * @param urn urn can be any media identifier, valid or not - * @throws IllegalArgumentException if not a valid urn - */ - public IlUrn(@NonNull String urn) throws IllegalArgumentException { - if (!parseBuMam(urn) - && !parseSwissTxt(urn)) { - throw new IllegalArgumentException(String.format("URN '%s' does not match a valid URN pattern.", urn)); - } - } - - private boolean parseBuMam(@NonNull String urn) { - Matcher matcher; - matcher = PATTERN_BUMAM.matcher(urn); - if (matcher.matches()) { - bu = matcher.group(1).toLowerCase(Locale.US); - assetType = matcher.group(2).toLowerCase(Locale.US); - - if (assetType.equals(ASSET_GROUP)) { - // is a synonym of show (used in search request URN) - assetType = ASSET_SHOW; - } - - id = matcher.group(3); // Do not transform ID since it is case-sensitive. - underlying = "urn:" + bu + ":" + assetType + ":" + id; - return true; - } else { - return false; - } - } - - private boolean parseSwissTxt(@NonNull String urn) { - Matcher matcher = PATTERN_SWISSTXT.matcher(urn); - if (matcher.matches()) { - String swisstxt = matcher.group(1).toLowerCase(Locale.US); - bu = matcher.group(3).toLowerCase(Locale.US); - assetType = matcher.group(2).toLowerCase(Locale.US); - if (assetType.equals(ASSET_GROUP)) { - // is a synonym of show (used in search request URN) - assetType = ASSET_SHOW; - } - id = matcher.group(4); - underlying = "urn:" + swisstxt + ":" + assetType + ":" + bu + ":" + id; - return true; - } else { - return false; - } - } - - public IlUrn(@Nullable String bu, @Nullable String assetType, @Nullable String id) { - this.bu = bu == null ? "default" : bu; - this.assetType = assetType; - this.id = id; - underlying = "urn:" + this.bu + ":" + this.assetType + ":" + this.id; - } - - /** - * @param string potential URN - * @return true iff a valid urn (false if string is null) - */ - public static boolean isUrn(@Nullable String string) { - return string != null && (PATTERN_BUMAM.matcher(string).matches() || PATTERN_SWISSTXT.matcher(string).matches()); - } - - - public static String format(String bu, String assetType, String id) { - return String.format("urn:%s:%s:%s", bu, assetType, id); - } - - /** - * Returns Business Unit. - */ - public String getBu() { - return bu; - } - - /** - * Return Asset Type (either audio or video) - */ - public String getAssetType() { - return assetType; - } - - /** - * Return ID - */ - public String getId() { - return id; - } - - /** - * Returns the underlying string representation. - */ - @NonNull - @Override - public String toString() { - return underlying; - } - - public boolean isAudio() { - return TextUtils.equals(ASSET_AUDIO, assetType); - } - - public boolean isVideo() { - return TextUtils.equals(ASSET_VIDEO, assetType) || TextUtils.equals(ASSET_VIDEO_SET, assetType); - } - - /** - * @param urn urn can be any media identifier, valid or not - * @return true if urn is valid and of video type - */ - public static boolean isAudio(@Nullable String urn) { - return isUrn(urn) && new IlUrn(urn).isAudio(); - } - - /** - * @param urn urn can be any media identifier, valid or not - * @return true if urn is valid and of video type - */ - public static boolean isVideo(@Nullable String urn) { - return isUrn(urn) && new IlUrn(urn).isVideo(); - } - - public boolean isShow() { - return TextUtils.equals(ASSET_SHOW, assetType); - } - - - public boolean equalsToString(@NonNull String o) { - try { - return underlying.equals(new IlUrn(o).toString()); - } catch (IllegalArgumentException invalidUrnException) { - return false; - } - } - - /** - * Get Id for given urn string. - * - * @param urn urn can be any media identifier, valid or not - * @return id or full string if unparseable URN - */ - public static String getId(@Nullable String urn) { - return isUrn(urn) ? new IlUrn(urn).getId() : urn; - } - - /** - * Get Asset Type for given urn string. - * - * @param urn urn can be any media identifier, valid or not - * @return assetType or "tv" if unparseable URN - */ - public static String getAssetType(@Nullable String urn) { - return isUrn(urn) ? new IlUrn(urn).getAssetType() : ASSET_VIDEO; - } -} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.kt new file mode 100644 index 0000000..4e9ec12 --- /dev/null +++ b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/IlUrn.kt @@ -0,0 +1,178 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import java.util.regex.Pattern + +/** + * This very simple class represents a URN provided by the IL. + */ +class IlUrn private constructor() { + private var underlying: String? = null + + /** + * Returns Business Unit. + */ + var bu: String? = null + private set + + /** + * Return Asset Type (either audio or video) + */ + var assetType: String? = null + private set + + /** + * Return ID + */ + var id: String? = null + private set + + val isAudio: Boolean + get() = assetType == ASSET_AUDIO + + val isVideo: Boolean + get() = assetType == ASSET_VIDEO || assetType == ASSET_VIDEO_SET + + val isShow: Boolean + get() = assetType == ASSET_SHOW + + /** + * @param urn urn can be any media identifier, valid or not + * @throws IllegalArgumentException if not a valid urn + */ + @Throws(IllegalArgumentException::class) + constructor(urn: String) : this() { + require(parseBuMam(urn) || parseSwissTxt(urn)) { + "URN '$urn' does not match a valid URN pattern." + } + } + + constructor(bu: String?, assetType: String?, id: String?) : this() { + this.bu = bu ?: "default" + this.assetType = assetType + this.id = id + + underlying = "urn:" + this.bu + ":" + this.assetType + ":" + this.id + } + + fun equalsToString(other: String): Boolean { + return try { + underlying == IlUrn(other).toString() + } catch (_: IllegalArgumentException) { + false + } + } + + override fun toString(): String { + return underlying.orEmpty() + } + + @Suppress("MagicNumber") + private fun parseBuMam(urn: String): Boolean { + val matcher = PATTERN_BUMAM.matcher(urn) + if (matcher.matches()) { + bu = matcher.group(1)?.lowercase() + assetType = matcher.group(2)?.lowercase() + id = matcher.group(3) // Do not transform ID since it is case-sensitive. + + if (assetType == ASSET_GROUP) { + // Is a synonym of show (used in search request URN) + assetType = ASSET_SHOW + } + + underlying = "urn:$bu:$assetType:$id" + + return true + } else { + return false + } + } + + @Suppress("MagicNumber") + private fun parseSwissTxt(urn: String): Boolean { + val matcher = PATTERN_SWISSTXT.matcher(urn) + if (matcher.matches()) { + val swissTxt = matcher.group(1)?.lowercase() + bu = matcher.group(3)?.lowercase() + assetType = matcher.group(2)?.lowercase() + id = matcher.group(4) + + if (assetType == ASSET_GROUP) { + // Is a synonym of show (used in search request URN) + assetType = ASSET_SHOW + } + + underlying = "urn:$swissTxt:$assetType:$bu:$id" + + return true + } else { + return false + } + } + + companion object { + const val ASSET_VIDEO = "video" + const val ASSET_VIDEO_SET = "videoset" + const val ASSET_AUDIO = "audio" + const val ASSET_SHOW = "show" + const val ASSET_GROUP = "assetgroup" + + private val PATTERN_BUMAM = + "^urn:(srf|rts|rsi|rtr|swi|default):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):([^:]+)$".toPattern(Pattern.CASE_INSENSITIVE) + private val PATTERN_SWISSTXT = + "^urn:(swisstxt):(?:[^:]+:)?(video|audio|videoset|show|assetgroup):(srf|rts|rsi|rtr|swi):([^:]+)$".toPattern(Pattern.CASE_INSENSITIVE) + + /** + * @param string potential URN + * @return true iff a valid urn (false if string is null) + */ + @JvmStatic + fun isUrn(string: String?): Boolean { + return string != null && (PATTERN_BUMAM.matcher(string).matches() || PATTERN_SWISSTXT.matcher(string).matches()) + } + + @JvmStatic + fun format(bu: String?, assetType: String?, id: String?): String { + return "urn:$bu:$assetType:$id" + } + + /** + * @param urn urn can be any media identifier, valid or not + * @return true if urn is valid and of video type + */ + @JvmStatic + fun isAudio(urn: String?): Boolean { + return urn != null && isUrn(urn) && IlUrn(urn).isAudio + } + + /** + * @param urn urn can be any media identifier, valid or not + * @return true if urn is valid and of video type + */ + @JvmStatic + fun isVideo(urn: String?): Boolean { + return urn != null && isUrn(urn) && IlUrn(urn).isVideo + } + + /** + * Get Id for given urn string. + * + * @param urn urn can be any media identifier, valid or not + * @return id or full string if unparseable URN + */ + @JvmStatic + fun getId(urn: String?): String? { + return if (urn != null && isUrn(urn)) IlUrn(urn).id else urn + } + + /** + * Get Asset Type for given urn string. + * + * @param urn urn can be any media identifier, valid or not + * @return assetType or "tv" if unparseable URN + */ + @JvmStatic + fun getAssetType(urn: String?): String? { + return if (urn != null && isUrn(urn)) IlUrn(urn).assetType else ASSET_VIDEO + } + } +} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.java b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.java deleted file mode 100644 index 2b6c1a9..0000000 --- a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.java +++ /dev/null @@ -1,72 +0,0 @@ -package ch.srg.dataProvider.integrationlayer.utils; - -import androidx.annotation.Nullable; - -import java.util.Comparator; -import java.util.List; - -import ch.srg.dataProvider.integrationlayer.data.remote.Quality; -import ch.srg.dataProvider.integrationlayer.data.remote.Resource; -import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod; - -/** - * Copyright (c) SRG SSR. All rights reserved. - *

- * License information is available from the LICENSE file. - */ -public class StreamComparator implements Comparator { - private static final int HANDICAP_STREAMING = 1000; - private static final int HANDICAP_DVR = 10; - private static final int HANDICAP_QUALITY = 1; - - public static final int SCORE_NOT_SUPPORTED = Integer.MAX_VALUE; - - private final List orderedQualities; - private final List orderedStreaming; - @Nullable - private final List supportedDrms; - private final boolean dvrSupported; - - public StreamComparator(List orderedQualities, List orderedStreaming, @Nullable List supportedDrms, boolean dvrSupported) { - this.orderedQualities = orderedQualities; - this.orderedStreaming = orderedStreaming; - this.supportedDrms = supportedDrms; - this.dvrSupported = dvrSupported; - } - - @Override - public int compare(Resource lhs, Resource rhs) { - int l = score(lhs); - int r = score(rhs); - //noinspection UseCompareMethod - return l == r ? 0 : ((l > r) ? 1 : -1); - } - - public int score(Resource r) { - if (r.getStreamingMethod() == null - || (!dvrSupported && r.getDvr()) - || (!isSupportedDrm(r.getDrmList()))) { - return SCORE_NOT_SUPPORTED; - } - int indexOfStreaming = orderedStreaming.indexOf(r.getStreamingMethod()); // will return -1 if not in the list - int indexOfQuality = orderedQualities.indexOf(r.getQuality()); // will return -1 if not in the list - - return (indexOfStreaming < 0 ? orderedStreaming.size() : indexOfStreaming) * HANDICAP_STREAMING - + (indexOfQuality < 0 ? orderedQualities.size() : indexOfQuality) * HANDICAP_QUALITY - + (r.getDvr() ? 0 : HANDICAP_DVR); - } - - private boolean isSupportedDrm(List drmList) { - if (drmList == null) { - return true; - } - if (supportedDrms != null) { - for (Resource.Drm drm : drmList) { - if (supportedDrms.contains(drm.getType())) { - return true; - } - } - } - return false; - } -} diff --git a/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.kt b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.kt new file mode 100644 index 0000000..3151796 --- /dev/null +++ b/dataprovider-retrofit/src/main/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparator.kt @@ -0,0 +1,58 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import ch.srg.dataProvider.integrationlayer.data.remote.Quality +import ch.srg.dataProvider.integrationlayer.data.remote.Resource +import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod + +/** + * Copyright (c) SRG SSR. All rights reserved. + *

+ * License information is available from the LICENSE file. + */ +class StreamComparator( + private val orderedQualities: List, + private val orderedStreaming: List, + private val supportedDrms: List?, + private val dvrSupported: Boolean, +) : Comparator { + override fun compare(lhs: Resource, rhs: Resource): Int { + val l = score(lhs) + val r = score(rhs) + + return l.compareTo(r) + } + + fun score(resource: Resource): Int { + if ((!dvrSupported && resource.dvr) || (!isSupportedDrm(resource.drmList))) { + return SCORE_NOT_SUPPORTED + } + + val indexOfStreaming = orderedStreaming.indexOf(resource.streamingMethod) // Will return -1 if not in the list + val indexOfQuality = orderedQualities.indexOf(resource.quality) // Will return -1 if not in the list + val scoreStreaming = (if (indexOfStreaming < 0) orderedStreaming.size else indexOfStreaming) * HANDICAP_STREAMING + val scoreQuality = (if (indexOfQuality < 0) orderedQualities.size else indexOfQuality) * HANDICAP_QUALITY + val scoreDvr = if (resource.dvr) 0 else HANDICAP_DVR + + return scoreStreaming + scoreQuality + scoreDvr + } + + private fun isSupportedDrm(drmList: List?): Boolean { + if (drmList == null) { + return true + } + + if (supportedDrms.isNullOrEmpty()) { + return false + } + + return drmList.any { it.type in supportedDrms } + } + + companion object { + const val SCORE_NOT_SUPPORTED = Int.MAX_VALUE + + private const val HANDICAP_STREAMING = 1000 + private const val HANDICAP_DVR = 10 + private const val HANDICAP_QUALITY = 1 + } +} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/MediaFetcherInputs.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/MediaFetcherInputs.kt new file mode 100644 index 0000000..47da78f --- /dev/null +++ b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/MediaFetcherInputs.kt @@ -0,0 +1,42 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import ch.srg.dataProvider.integrationlayer.data.remote.Quality +import ch.srg.dataProvider.integrationlayer.data.remote.Resource +import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod + +/** + * Copyright (c) SRG SSR. All rights reserved. + * + * + * License information is available from the LICENSE file. + */ +object MediaFetcherInputs { + @JvmField + @Transient + val STREAMING_ORDER_DEFAULT: List = listOf( + StreamingMethod.DASH, + StreamingMethod.HLS, + StreamingMethod.PROGRESSIVE + ) + + @Transient + val DRM_TYPES: List = listOf( + Resource.Drm.Type.WIDEVINE, Resource.Drm.Type.PLAYREADY + ) + + @JvmField + @Transient + val QUALITY_ORDER: List = listOf( + Quality.SD, + Quality.HQ, + Quality.HD + ) + + @JvmField + @Transient + val QUALITY_ORDER_HD: List = listOf( + Quality.HD, + Quality.HQ, + Quality.SD + ) +} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/ResourceBuilder.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/ResourceBuilder.kt new file mode 100644 index 0000000..e4126a3 --- /dev/null +++ b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/ResourceBuilder.kt @@ -0,0 +1,31 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import ch.srg.dataProvider.integrationlayer.data.remote.Quality +import ch.srg.dataProvider.integrationlayer.data.remote.Resource +import ch.srg.dataProvider.integrationlayer.data.remote.Resource.Drm +import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod + +/** + * Copyright (c) SRG SSR. All rights reserved. + *

+ * License information is available from the LICENSE file. + */ +object ResourceBuilder { + fun createResourceFrom( + url: String, + quality: Quality, + streaming: StreamingMethod, + live: Boolean, + dvr: Boolean, + vararg drmTypes: Drm.Type + ): Resource { + var drmList: MutableList? = null + if (drmTypes.isNotEmpty()) { + drmList = ArrayList() + for (type in drmTypes) { + drmList.add(Drm(type, "license", null)) + } + } + return Resource(url = url, quality = quality, streamingMethod = streaming, live = live, dvr = dvr, drmList = drmList) + } +} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorAllPossibilitiesTest.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorAllPossibilitiesTest.kt new file mode 100644 index 0000000..16a4648 --- /dev/null +++ b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorAllPossibilitiesTest.kt @@ -0,0 +1,224 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import ch.srg.dataProvider.integrationlayer.data.remote.Quality +import ch.srg.dataProvider.integrationlayer.data.remote.Resource +import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.util.Collections + +/** + * Copyright (c) SRG SSR. All rights reserved. + * + * + * License information is available from the LICENSE file. + */ +@RunWith(Parameterized::class) +class StreamComparatorAllPossibilitiesTest( + private val qualitiesOrder: List, + private val streamingOrder: List, + private val dvrSupport: Boolean, + private val drmSupported: Boolean, + private val expectedResource: ExpectedResult, +) { + + class ExpectedResult(val quality: Quality, val streaming: StreamingMethod, val drmType: Resource.Drm.Type?, val dvr: Boolean) { + constructor(quality: Quality, streaming: StreamingMethod, dvr: Boolean) : this(quality, streaming, null, dvr) + } + + @Test + fun firstDashHdDrmFirstDvr() { + val comparator = StreamComparator(qualitiesOrder, streamingOrder, if (drmSupported) MediaFetcherInputs.DRM_TYPES else null, dvrSupport) + val listResource = listAllPossibilities() + sortAndPrint(comparator, listResource) + val r = listResource[0] + + checkResource(r, expectedResource) + } + + private fun checkResource(r: Resource, expectedResult: ExpectedResult) { + Assert.assertNotNull(r.streamingMethod) + Assert.assertNotNull(r.quality) + Assert.assertEquals(expectedResult.streaming, r.streamingMethod) + Assert.assertEquals(expectedResult.quality, r.quality) + Assert.assertEquals("hasDrm", expectedResult.drmType != null, r.hasDrm()) + r.drmList?.let { + Assert.assertTrue(it.contains(createDrm(Resource.Drm.Type.WIDEVINE))) + } + + Assert.assertEquals("dvr", expectedResult.dvr, r.dvr) + } + + private fun listAllPossibilities(): List { + val resourceList: MutableList = ArrayList() + // StreamComparator keep order in the resource list regarding to DRM situation. + resourceList.add( + ResourceBuilder.createResourceFrom( + "hd_hls_drm_ios", + Quality.HD, + StreamingMethod.HLS, + true, + true, + Resource.Drm.Type.FAIRPLAY + ) + ) + + resourceList.add( + ResourceBuilder.createResourceFrom( + "sd_dash_drm", + Quality.SD, + StreamingMethod.DASH, + true, + false, + Resource.Drm.Type.PLAYREADY, + Resource.Drm.Type.WIDEVINE + ) + ) + resourceList.add( + ResourceBuilder.createResourceFrom( + "sd_dash_drm_dvr", + Quality.SD, + StreamingMethod.DASH, + true, + true, + Resource.Drm.Type.PLAYREADY, + Resource.Drm.Type.WIDEVINE + ) + ) + resourceList.add( + ResourceBuilder.createResourceFrom( + "hd_dash_drm", + Quality.HD, + StreamingMethod.DASH, + true, + false, + Resource.Drm.Type.PLAYREADY, + Resource.Drm.Type.WIDEVINE + ) + ) + resourceList.add( + ResourceBuilder.createResourceFrom( + "hd_dash_drm_dvr", + Quality.HD, + StreamingMethod.DASH, + true, + true, + Resource.Drm.Type.PLAYREADY, + Resource.Drm.Type.WIDEVINE + ) + ) + resourceList.add(ResourceBuilder.createResourceFrom("sd_progressive", Quality.SD, StreamingMethod.PROGRESSIVE, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("sd_progressive_dvr", Quality.SD, StreamingMethod.PROGRESSIVE, true, true)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_progressive", Quality.HD, StreamingMethod.PROGRESSIVE, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_progressive_dvr", Quality.HD, StreamingMethod.PROGRESSIVE, true, true)) + + resourceList.add(ResourceBuilder.createResourceFrom("sd_dash", Quality.SD, StreamingMethod.DASH, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("sd_dash_dvr", Quality.SD, StreamingMethod.DASH, true, true)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_dash", Quality.HD, StreamingMethod.DASH, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_dash_dvr", Quality.HD, StreamingMethod.DASH, true, true)) + + resourceList.add(ResourceBuilder.createResourceFrom("sd_hls", Quality.SD, StreamingMethod.HLS, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("sd_hls_dvr", Quality.SD, StreamingMethod.HLS, true, true)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_hls", Quality.HD, StreamingMethod.HLS, true, false)) + resourceList.add(ResourceBuilder.createResourceFrom("hd_hls_dvr", Quality.HD, StreamingMethod.HLS, true, true)) + + return resourceList + } + + + private fun sortAndPrint(comparator: StreamComparator, resourceList: List) { + Collections.sort(resourceList, comparator) + for (r in resourceList) { + println(r.url + " -> " + comparator.score(r)) + } + } + + companion object { + var WITH_DVR: Boolean = true + var NO_DVR: Boolean = false + var WITH_DRM: Boolean = true + var NO_DRM: Boolean = false + + var HLS_FIRST: List = listOf(StreamingMethod.HLS, StreamingMethod.DASH, StreamingMethod.PROGRESSIVE) + + @JvmStatic + @Parameterized.Parameters + fun parameters(): Iterable> { + return listOf( + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, WITH_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, WITH_DVR, WITH_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, WITH_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, WITH_DVR, WITH_DRM, + ExpectedResult(Quality.SD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, WITH_DVR) + ), + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, WITH_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, WITH_DRM, + ExpectedResult(Quality.SD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, NO_DVR) + ), + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, NO_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, NO_DRM, + ExpectedResult(Quality.SD, StreamingMethod.DASH, NO_DVR) + ), + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, WITH_DVR, NO_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, WITH_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, WITH_DVR, NO_DRM, + ExpectedResult(Quality.SD, StreamingMethod.DASH, WITH_DVR) + ), + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, WITH_DRM, + ExpectedResult(Quality.HD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, NO_DVR, WITH_DRM, + ExpectedResult(Quality.SD, StreamingMethod.DASH, Resource.Drm.Type.WIDEVINE, NO_DVR) + ), + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, HLS_FIRST, NO_DVR, NO_DRM, + ExpectedResult(Quality.HD, StreamingMethod.HLS, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, HLS_FIRST, NO_DVR, NO_DRM, + ExpectedResult(Quality.SD, StreamingMethod.HLS, NO_DVR) + ), // no drm type because no HLS resources with Widevine + + arrayOf( + MediaFetcherInputs.QUALITY_ORDER_HD, HLS_FIRST, NO_DVR, WITH_DRM, + ExpectedResult(Quality.HD, StreamingMethod.HLS, null, NO_DVR) + ), + arrayOf( + MediaFetcherInputs.QUALITY_ORDER, HLS_FIRST, NO_DVR, WITH_DRM, + ExpectedResult(Quality.SD, StreamingMethod.HLS, null, NO_DVR) + ), + ) + } + + private fun createDrm(type: Resource.Drm.Type): Resource.Drm { + return Resource.Drm(type, "license", null) + } + + } +} diff --git a/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorTest.kt b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorTest.kt new file mode 100644 index 0000000..7707e22 --- /dev/null +++ b/dataprovider-retrofit/src/test/java/ch/srg/dataProvider/integrationlayer/utils/StreamComparatorTest.kt @@ -0,0 +1,208 @@ +package ch.srg.dataProvider.integrationlayer.utils + +import ch.srg.dataProvider.integrationlayer.data.remote.Quality +import ch.srg.dataProvider.integrationlayer.data.remote.Resource +import ch.srg.dataProvider.integrationlayer.data.remote.StreamingMethod +import org.junit.Assert +import org.junit.Test +import java.util.Arrays +import java.util.Collections + +/** + * Copyright (c) SRG SSR. All rights reserved. + * + * + * License information is available from the LICENSE file. + */ +class StreamComparatorTest { + @Test + fun streamOrderWithUncompleteStreamingOrderList() { + val listStreamingOrder: List = listOf(StreamingMethod.DASH, StreamingMethod.HLS) + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, listStreamingOrder, null, true) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + sortAndPrint(comparator, resourceList) + Assert.assertEquals(resourceList[0].streamingMethod, StreamingMethod.DASH) + + val resourceList1: MutableList = ArrayList() + resourceList1.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList1.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList1.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + sortAndPrint(comparator, resourceList1) + Assert.assertEquals(resourceList1[0].streamingMethod, StreamingMethod.DASH) + + val resourceList2: MutableList = ArrayList() + resourceList2.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + sortAndPrint(comparator, resourceList2) + Assert.assertEquals(resourceList2[0].streamingMethod, StreamingMethod.DASH) + } + + @Test + fun streamOrderWithUncompleteQualityOrderList() { + val listQualityOrder = Arrays.asList(Quality.HD, Quality.SD) + val comparator = StreamComparator(listQualityOrder, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, true) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HQ, StreamingMethod.DASH, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.SD, StreamingMethod.DASH, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + sortAndPrint(comparator, resourceList) + Assert.assertEquals(resourceList[0].quality, Quality.HD) + + val resourceList1: MutableList = ArrayList() + resourceList1.add(ResourceBuilder.createResourceFrom("2", Quality.SD, StreamingMethod.DASH, false, false)) + resourceList1.add(ResourceBuilder.createResourceFrom("1", Quality.HQ, StreamingMethod.DASH, false, false)) + resourceList1.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + sortAndPrint(comparator, resourceList1) + Assert.assertEquals(resourceList1[0].quality, Quality.HD) + + val resourceList2: MutableList = ArrayList() + resourceList2.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("1", Quality.HQ, StreamingMethod.DASH, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("2", Quality.SD, StreamingMethod.DASH, false, false)) + sortAndPrint(comparator, resourceList2) + Assert.assertEquals(resourceList2[0].quality, Quality.HD) + Assert.assertEquals(resourceList2[1].quality, Quality.SD) + } + + @Test + fun dashFirst() { + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, false) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + + sortAndPrint(comparator, resourceList) + Assert.assertEquals(resourceList[0].streamingMethod, StreamingMethod.DASH) + } + + @Test + fun hlsFirstWhenNoDash() { + val comparator = StreamComparator( + MediaFetcherInputs.QUALITY_ORDER_HD, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, MediaFetcherInputs + .DRM_TYPES, false + ) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.PROGRESSIVE, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + + sortAndPrint(comparator, resourceList) + Assert.assertEquals(resourceList[0].streamingMethod, StreamingMethod.HLS) + } + + @Test + fun unsupportedDvr() { + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, false) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, true)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList) + Assert.assertEquals("3", resourceList[0].url) + + val resourceList2: MutableList = ArrayList() + resourceList2.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, true)) + resourceList2.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList2) + Assert.assertEquals("2", resourceList2[0].url) + } + + @Test + fun unsupportedDvrWithDrmFirst() { + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, false) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, true)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList) + Assert.assertEquals("3", resourceList[0].url) + + val resourceList2: MutableList = ArrayList() + resourceList2.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, true)) + resourceList2.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList2) + Assert.assertEquals("2", resourceList2[0].url) + } + + @Test + fun unsupportedDrm() { + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, false) + + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, false, Resource.Drm.Type.FAIRPLAY)) + resourceList.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("3", Quality.HD, StreamingMethod.DASH, false, false)) + resourceList.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList) + Assert.assertEquals("3", resourceList[0].url) + + val resourceList2: MutableList = ArrayList() + resourceList2.add(ResourceBuilder.createResourceFrom("1", Quality.HD, StreamingMethod.DASH, false, false, Resource.Drm.Type.FAIRPLAY)) + resourceList2.add(ResourceBuilder.createResourceFrom("2", Quality.HD, StreamingMethod.HLS, false, false)) + resourceList2.add(ResourceBuilder.createResourceFrom("4", Quality.SD, StreamingMethod.PROGRESSIVE, false, false)) + sortAndPrint(comparator, resourceList2) + Assert.assertEquals("2", resourceList2[0].url) + } + + @Test + fun nullStreamingType() { + val comparator = StreamComparator(MediaFetcherInputs.QUALITY_ORDER, MediaFetcherInputs.STREAMING_ORDER_DEFAULT, null, true) + + val resourceList = listWithNullStreamingType() + sortAndPrint(comparator, resourceList) + + Assert.assertNotEquals("null", resourceList[0].url) + } + + private fun listWithNullStreamingType(): List { + val resourceList: MutableList = ArrayList() + resourceList.add(ResourceBuilder.createResourceFrom("hsl", Quality.HD, StreamingMethod.HLS, false, true)) + resourceList.add(ResourceBuilder.createResourceFrom("hsl", Quality.HD, StreamingMethod.DASH, false, true)) + resourceList.add(ResourceBuilder.createResourceFrom("drm_fairplay", Quality.HD, StreamingMethod.HLS, false, true, Resource.Drm.Type.FAIRPLAY)) + resourceList.add( + ResourceBuilder.createResourceFrom( + "drm_playready", + Quality.HD, + StreamingMethod.DASH, + false, + true, + Resource.Drm.Type.PLAYREADY + ) + ) + resourceList.add( + ResourceBuilder.createResourceFrom( + "drm_widevine", + Quality.HD, + StreamingMethod.DASH, + false, + true, + Resource.Drm.Type.WIDEVINE + ) + ) + return resourceList + } + + private fun sortAndPrint(comparator: StreamComparator, resourceList: List) { + Collections.sort(resourceList, comparator) + for (r in resourceList) { + println(r.url + " -> " + comparator.score(r)) + } + } + +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 22fc96f..1879561 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,6 @@ [versions] android-gradle-plugin = "8.6.0" androidx-activity = "1.9.1" -androidx-annotation = "1.8.2" androidx-lifecycle = "2.8.4" androidx-paging = "3.3.2" androidx-test-ext-junit = "1.2.1" @@ -15,7 +14,6 @@ robolectric = "4.13" [libraries] androidx-activity = { module = "androidx.activity:activity", version.ref = "androidx-activity" } -androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime", version.ref = "androidx-lifecycle" } androidx-paging-common = { module = "androidx.paging:paging-common", version.ref = "androidx-paging" } androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext-junit" }