Skip to content

Commit 67b6f0a

Browse files
committed
refactor ListHelper Kotlin methods
1 parent 274cb7c commit 67b6f0a

1 file changed

Lines changed: 79 additions & 107 deletions

File tree

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

Lines changed: 79 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ package org.schabi.newpipe.util
33
import android.content.Context
44
import android.content.res.Resources
55
import android.net.ConnectivityManager
6+
import android.util.Log
67
import androidx.annotation.StringRes
78
import androidx.core.content.ContextCompat
89
import androidx.preference.PreferenceManager
9-
import java.util.Collections
1010
import java.util.Locale
11-
import java.util.Objects
12-
import java.util.function.Predicate
13-
import java.util.stream.Collectors
1411
import org.schabi.newpipe.MainActivity
1512
import org.schabi.newpipe.R
1613
import org.schabi.newpipe.extractor.MediaFormat
@@ -22,6 +19,8 @@ import org.schabi.newpipe.extractor.stream.Stream
2219
import org.schabi.newpipe.extractor.stream.VideoStream
2320

2421
object ListHelper {
22+
private const val TAG = "ListHelper"
23+
2524
// Video format in order of quality. 0=lowest quality, n=highest quality
2625
private val VIDEO_FORMAT_QUALITY_RANKING =
2726
listOf(MediaFormat.v3GPP, MediaFormat.WEBM, MediaFormat.MPEG_4)
@@ -70,6 +69,8 @@ object ListHelper {
7069
278, 242, 243, 244, 245, 246, 247, 248, 271, 272, 302, 303, 308, 313, 315
7170
)
7271

72+
private val QUALITY_REGEX = Regex("""^(\d+)p(\d+)?(?:@(\d+)([km])?)?$""", RegexOption.IGNORE_CASE)
73+
7374
/**
7475
* @param context Android app context
7576
* @param videoStreams list of the video streams to check
@@ -197,10 +198,9 @@ object ListHelper {
197198
streamList: List<S>?,
198199
deliveryMethod: DeliveryMethod
199200
): List<S> {
200-
return getFilteredStreamList(
201-
streamList
202-
)
203-
{ stream -> stream.deliveryMethod == deliveryMethod }
201+
return getFilteredStreamList(streamList) { stream ->
202+
stream.deliveryMethod == deliveryMethod
203+
}
204204
}
205205

206206
/**
@@ -214,10 +214,9 @@ object ListHelper {
214214
fun <S : Stream> getUrlAndNonTorrentStreams(
215215
streamList: List<S>?
216216
): List<S> {
217-
return getFilteredStreamList(
218-
streamList
219-
)
220-
{ stream -> stream.isUrl && stream.deliveryMethod != DeliveryMethod.TORRENT }
217+
return getFilteredStreamList(streamList) { stream ->
218+
stream.isUrl && stream.deliveryMethod != DeliveryMethod.TORRENT
219+
}
221220
}
222221

223222
/**
@@ -238,19 +237,15 @@ object ListHelper {
238237
serviceId: Int
239238
): List<S> {
240239
val youtubeServiceId = ServiceList.YouTube.serviceId
241-
return getFilteredStreamList(
242-
streamList
243-
)
244-
{ stream ->
240+
return getFilteredStreamList(streamList) { stream ->
245241
stream.deliveryMethod != DeliveryMethod.TORRENT &&
246242
(
247243
stream.deliveryMethod != DeliveryMethod.HLS ||
248244
stream.format != MediaFormat.OPUS
249245
) &&
250246
(
251247
serviceId != youtubeServiceId ||
252-
stream.itagItem == null ||
253-
SUPPORTED_ITAG_IDS.contains(stream.itagItem!!.id)
248+
stream.itagItem?.id?.let { SUPPORTED_ITAG_IDS.contains(it) } != false
254249
)
255250
}
256251
}
@@ -351,9 +346,9 @@ object ListHelper {
351346
audioStreams: List<AudioStream>?
352347
): List<AudioStream> {
353348
if (audioStreams == null) {
354-
return Collections.emptyList()
349+
return emptyList()
355350
}
356-
val collectedStreams: HashMap<String, AudioStream> = HashMap()
351+
val collectedStreams = mutableMapOf<String, AudioStream>()
357352
val cmp = getAudioFormatComparator(context)
358353
for (stream in audioStreams) {
359354
if (stream.deliveryMethod == DeliveryMethod.TORRENT ||
@@ -364,7 +359,7 @@ object ListHelper {
364359
) {
365360
continue
366361
}
367-
val trackId = Objects.toString(stream.audioTrackId, "")
362+
val trackId = stream.audioTrackId ?: ""
368363
val presentStream = collectedStreams[trackId]
369364
if (presentStream == null || cmp.compare(stream, presentStream) > 0) {
370365
collectedStreams[trackId] = stream
@@ -391,19 +386,11 @@ object ListHelper {
391386
audioStreams: List<AudioStream>?
392387
): List<List<AudioStream>> {
393388
if (audioStreams == null) {
394-
return Collections.emptyList()
395-
}
396-
val collectedStreams: HashMap<String, MutableList<AudioStream>> = HashMap()
397-
for (stream in audioStreams) {
398-
val trackId = Objects.toString(stream.audioTrackId, "")
399-
if (collectedStreams.containsKey(trackId)) {
400-
collectedStreams[trackId]!!.add(stream)
401-
} else {
402-
val list: MutableList<AudioStream> = ArrayList()
403-
list.add(stream)
404-
collectedStreams[trackId] = list
405-
}
389+
return emptyList()
406390
}
391+
val collectedStreams = audioStreams
392+
.groupBy { it.audioTrackId ?: "" }
393+
.toMutableMap()
407394
// Filter unknown audio tracks if there are multiple tracks
408395
if (collectedStreams.size > 1) {
409396
collectedStreams.remove("")
@@ -421,23 +408,21 @@ object ListHelper {
421408
// ////////////////////////////////////////////////////////////////////////
422409

423410
/**
424-
* Get a filtered stream list, by using Java 8 Stream's API and the given predicate.
411+
* Get a filtered stream list using the given predicate.
425412
*
426413
* @param streamList the stream list to filter
427-
* @param streamListPredicate the predicate which will be used to filter streams
414+
* @param predicate the predicate which will be used to filter streams
428415
* @param <S> the item type's class that extends [Stream]
429416
* @return a new stream list filtered using the given predicate
430417
*/
431418
private fun <S : Stream> getFilteredStreamList(
432419
streamList: List<S>?,
433-
streamListPredicate: Predicate<S>
420+
predicate: (S) -> Boolean
434421
): List<S> {
435422
if (streamList == null) {
436-
return Collections.emptyList()
423+
return emptyList()
437424
}
438-
return streamList.stream()
439-
.filter(streamListPredicate)
440-
.collect(Collectors.toList())
425+
return streamList.filter(predicate)
441426
}
442427

443428
private fun computeDefaultResolution(
@@ -569,8 +554,8 @@ object ListHelper {
569554
// preferred. They might have been overridden if allInitialStreams has more than one stream
570555
// for the same resolution key but a none 'defaultFormat' stream was added later.
571556
// See 'qualityKeyOf'.
572-
defaultFormat?.let { defaultFormat ->
573-
allInitialStreams.filter { it.stream.format == defaultFormat }
557+
defaultFormat?.let { fmt ->
558+
allInitialStreams.filter { it.stream.format == fmt }
574559
.forEach { streamsWithDefaultFormatPreferred[qualityKeyOf(it)] = it }
575560
}
576561

@@ -641,7 +626,7 @@ object ListHelper {
641626
* The algorithm iterates over all available streams and assigns each one
642627
* a "priority class". Lower numbers represent a better match.
643628
*
644-
* Matching priority (best worst):
629+
* Matching priority (best -> worst):
645630
*
646631
* 1. Format + resolution + fps + exact bitrate
647632
* 2. Format + resolution + fps
@@ -797,39 +782,32 @@ object ListHelper {
797782
val defaultFormatString = preferences.getString(
798783
context.getString(defaultFormatKey),
799784
defaultFormat
800-
)
801-
return getMediaFormatFromKey(context, defaultFormatString!!)
785+
) ?: defaultFormat
786+
return getMediaFormatFromKey(context, defaultFormatString)
802787
}
803788

804789
private fun getMediaFormatFromKey(
805790
context: Context,
806791
formatKey: String
807792
): MediaFormat? {
808-
var format: MediaFormat? = null
809-
when (formatKey) {
810-
context.getString(R.string.video_webm_key) -> {
811-
format = MediaFormat.WEBM
812-
}
813-
814-
context.getString(R.string.video_mp4_key) -> {
815-
format = MediaFormat.MPEG_4
816-
}
817-
818-
context.getString(R.string.video_3gp_key) -> {
819-
format = MediaFormat.v3GPP
820-
}
821-
822-
context.getString(R.string.audio_webm_key) -> {
823-
format = MediaFormat.WEBMA
824-
}
825-
826-
context.getString(R.string.audio_m4a_key) -> {
827-
format = MediaFormat.M4A
828-
}
793+
return when (formatKey) {
794+
context.getString(R.string.video_webm_key) -> MediaFormat.WEBM
795+
context.getString(R.string.video_mp4_key) -> MediaFormat.MPEG_4
796+
context.getString(R.string.video_3gp_key) -> MediaFormat.v3GPP
797+
context.getString(R.string.audio_webm_key) -> MediaFormat.WEBMA
798+
context.getString(R.string.audio_m4a_key) -> MediaFormat.M4A
799+
else -> null
829800
}
830-
return format
831801
}
832802

803+
/**
804+
* Compares two video stream resolution strings in descending order.
805+
*
806+
* Returns a negative value if r1 has higher quality than r2, zero if equal,
807+
* and a positive value if r1 has lower quality than r2.
808+
* Note: arguments are intentionally compared in reverse order (r2 vs r1)
809+
* to produce descending comparison, matching the original Java behavior.
810+
*/
833811
private fun compareVideoStreamResolution(
834812
r1: String,
835813
r2: String
@@ -855,34 +833,31 @@ object ListHelper {
855833
* @param context App context
856834
* @return maximum resolution allowed or null if there is no maximum
857835
*/
858-
fun getResolutionLimit(context: Context): String? {
859-
var resolutionLimit: String? = null
860-
if (isMeteredNetwork(context)) {
861-
val preferences =
862-
PreferenceManager.getDefaultSharedPreferences(context)
863-
val defValue = context.getString(R.string.limit_data_usage_none_key)
864-
val value = preferences.getString(
865-
context.getString(R.string.limit_mobile_data_usage_key),
866-
defValue
867-
)
868-
resolutionLimit = if (defValue == value) null else value
836+
private fun getResolutionLimit(context: Context): String? {
837+
if (!isMeteredNetwork(context)) {
838+
return null
869839
}
870-
return resolutionLimit
840+
val preferences =
841+
PreferenceManager.getDefaultSharedPreferences(context)
842+
val defValue = context.getString(R.string.limit_data_usage_none_key)
843+
val value = preferences.getString(
844+
context.getString(R.string.limit_mobile_data_usage_key),
845+
defValue
846+
)
847+
return if (defValue == value) null else value
871848
}
872849

873850
/**
874851
* The current network is metered (like mobile data)?
875852
*
876853
* @param context App context
877-
* @return {@code true} if connected to a metered network
854+
* @return `true` if connected to a metered network
878855
*/
879856
@JvmStatic
880857
fun isMeteredNetwork(context: Context): Boolean {
881858
val manager =
882859
ContextCompat.getSystemService(context, ConnectivityManager::class.java)
883-
if (manager == null || manager.activeNetworkInfo == null) {
884-
return false
885-
}
860+
?: return false
886861
return manager.isActiveNetworkMetered
887862
}
888863

@@ -933,7 +908,7 @@ object ListHelper {
933908
{ it.format },
934909
Comparator { o1: MediaFormat?, o2: MediaFormat? ->
935910
if (defaultFormat != null) {
936-
java.lang.Boolean.compare(o1 == defaultFormat, o2 == defaultFormat)
911+
(o1 == defaultFormat).compareTo(o2 == defaultFormat)
937912
} else {
938913
0
939914
}
@@ -1020,10 +995,7 @@ object ListHelper {
1020995
{ it.audioTrackType },
1021996
Comparator { o1: AudioTrackType?, o2: AudioTrackType? ->
1022997
if (preferOriginalAudio) {
1023-
java.lang.Boolean.compare(
1024-
o1 == AudioTrackType.ORIGINAL,
1025-
o2 == AudioTrackType.ORIGINAL
1026-
)
998+
(o1 == AudioTrackType.ORIGINAL).compareTo(o2 == AudioTrackType.ORIGINAL)
1027999
} else {
10281000
0
10291001
}
@@ -1066,20 +1038,6 @@ object ListHelper {
10661038
)
10671039
}
10681040

1069-
// ------Extensions--------
1070-
fun VideoStream.toQuality(): VideoQuality {
1071-
return parseQuality(getResolution(), format)
1072-
}
1073-
1074-
/**
1075-
* Extension on Iterable<VideoStream> that maps each VideoStream
1076-
* to a [VideoStreamWithQuality] using its toQuality() function.
1077-
* This avoids repeatedly parsing the quality string during matching.
1078-
*/
1079-
fun Iterable<VideoStream>.wrapWithQuality(): List<VideoStreamWithQuality> {
1080-
return this.map { stream -> VideoStreamWithQuality(stream, stream.toQuality()) }
1081-
}
1082-
10831041
// --------data classes----------
10841042

10851043
/**
@@ -1097,16 +1055,26 @@ object ListHelper {
10971055
val formatRank: Int
10981056
)
10991057

1100-
// -------- helper ------------
1058+
// -------- private helpers ------------
11011059

1102-
val QUALITY_REGEX = Regex("""^(\d+)p(\d+)?(?:@(\d+)([km])?)?$""", RegexOption.IGNORE_CASE)
1060+
private fun VideoStream.toQuality(): VideoQuality {
1061+
return parseQuality(getResolution(), format)
1062+
}
1063+
1064+
/**
1065+
* Maps each VideoStream to a [VideoStreamWithQuality] using its toQuality() function.
1066+
* This avoids repeatedly parsing the quality string during matching.
1067+
*/
1068+
private fun Iterable<VideoStream>.wrapWithQuality(): List<VideoStreamWithQuality> {
1069+
return map { stream -> VideoStreamWithQuality(stream, stream.toQuality()) }
1070+
}
11031071

11041072
/**
11051073
* Parses a video quality string into a [VideoQuality] object.
11061074
*
11071075
* Supports strings like: `"720p"`, `"720p60"`, `"720p60@1500k"` or `"1080p@2m"`.
11081076
* The components represent resolution, optional fps, and optional bitrate.
1109-
* Bitrate units `k` and `m` are interpreted as ×1000 and ×1_000_000 respectively.
1077+
* Bitrate units `k` and `m` are interpreted as x1000 and x1_000_000 respectively.
11101078
*
11111079
* @param resFpsBitrate string to parse for quality information (e.g. `"720p60@1500k"`), may be null
11121080
* @param format optional media format used to determine the format rank
@@ -1121,7 +1089,9 @@ object ListHelper {
11211089

11221090
val match = QUALITY_REGEX.matchEntire(resFpsBitrateStr)
11231091
?: run {
1124-
if (MainActivity.DEBUG) println("QualityParser" + "Cannot parse: \"$resFpsBitrateStr\"")
1092+
if (MainActivity.DEBUG) {
1093+
Log.d(TAG, "Cannot parse quality: \"$resFpsBitrateStr\"")
1094+
}
11251095
return VideoQuality(0, 0, 0L, -1)
11261096
}
11271097

@@ -1138,7 +1108,9 @@ object ListHelper {
11381108
else -> bitrate
11391109
}
11401110
} catch (e: ArithmeticException) {
1141-
if (MainActivity.DEBUG) println("QualityParser" + "Bitrate overflow in \"$resFpsBitrateStr\"")
1111+
if (MainActivity.DEBUG) {
1112+
Log.d(TAG, "Bitrate overflow in \"$resFpsBitrateStr\"", e)
1113+
}
11421114
bitrate = 0L
11431115
}
11441116
}

0 commit comments

Comments
 (0)