@@ -41,8 +41,36 @@ object StreamAutoPlaySelector {
4141 preferredBingeGroup : String? = null,
4242 preferBingeGroupInSelection : Boolean = false,
4343 debridEnabled : Boolean = true,
44- ): StreamItem ? {
45- if (streams.isEmpty()) return null
44+ activeResolverProviderId : String? = null,
45+ ): StreamItem ? =
46+ evaluateAutoPlayStream(
47+ streams = streams,
48+ mode = mode,
49+ regexPattern = regexPattern,
50+ source = source,
51+ installedAddonNames = installedAddonNames,
52+ selectedAddons = selectedAddons,
53+ selectedPlugins = selectedPlugins,
54+ preferredBingeGroup = preferredBingeGroup,
55+ preferBingeGroupInSelection = preferBingeGroupInSelection,
56+ debridEnabled = debridEnabled,
57+ activeResolverProviderId = activeResolverProviderId,
58+ ).stream
59+
60+ fun evaluateAutoPlayStream (
61+ streams : List <StreamItem >,
62+ mode : StreamAutoPlayMode ,
63+ regexPattern : String ,
64+ source : StreamAutoPlaySource ,
65+ installedAddonNames : Set <String >,
66+ selectedAddons : Set <String >,
67+ selectedPlugins : Set <String >,
68+ preferredBingeGroup : String? = null,
69+ preferBingeGroupInSelection : Boolean = false,
70+ debridEnabled : Boolean = true,
71+ activeResolverProviderId : String? = null,
72+ ): StreamAutoPlayEvaluation {
73+ if (streams.isEmpty()) return StreamAutoPlayEvaluation ()
4674
4775 val sourceScopedStreams = when (source) {
4876 StreamAutoPlaySource .ALL_SOURCES -> streams
@@ -57,25 +85,26 @@ object StreamAutoPlaySelector {
5785 selectedPlugins.isEmpty() || stream.addonName in selectedPlugins
5886 }
5987 }
60- if (candidateStreams.isEmpty()) return null
61- if (mode == StreamAutoPlayMode .MANUAL ) return null
88+ if (candidateStreams.isEmpty()) return StreamAutoPlayEvaluation ()
89+ if (mode == StreamAutoPlayMode .MANUAL ) return StreamAutoPlayEvaluation ()
6290
6391 val targetBingeGroup = preferredBingeGroup?.trim().orEmpty()
64- if (preferBingeGroupInSelection && targetBingeGroup.isNotEmpty()) {
65- val bingeGroupMatch = candidateStreams.firstOrNull { stream ->
66- stream.behaviorHints.bingeGroup == targetBingeGroup && stream.isAutoPlayable(debridEnabled)
92+ val preferredReadyStream = if (preferBingeGroupInSelection && targetBingeGroup.isNotEmpty()) {
93+ candidateStreams.firstOrNull { stream ->
94+ stream.behaviorHints.bingeGroup == targetBingeGroup &&
95+ stream.isAutoPlayable(debridEnabled, activeResolverProviderId)
6796 }
68- if (bingeGroupMatch != null ) return bingeGroupMatch
97+ } else {
98+ null
6999 }
70-
71- return when (mode) {
72- StreamAutoPlayMode .MANUAL -> null
73- StreamAutoPlayMode .FIRST_STREAM -> candidateStreams.firstOrNull { it.isAutoPlayable(debridEnabled) }
100+ val matchingStreams = when (mode) {
101+ StreamAutoPlayMode .MANUAL -> emptyList()
102+ StreamAutoPlayMode .FIRST_STREAM -> candidateStreams
74103 StreamAutoPlayMode .REGEX_MATCH -> {
75104 val pattern = regexPattern.trim()
76105
77106 val userRegex = runCatching { Regex (pattern, RegexOption .IGNORE_CASE ) }.getOrNull()
78- ? : return null
107+ ? : return StreamAutoPlayEvaluation ()
79108
80109 val exclusionMatches = Regex (" \\ (\\ ?![^)]*?\\ (([^)]+)\\ )" ).findAll(pattern)
81110
@@ -89,8 +118,7 @@ object StreamAutoPlaySelector {
89118 Regex (" \\ b(${exclusionWords.joinToString(" |" )} )\\ b" , RegexOption .IGNORE_CASE )
90119 } else null
91120
92- val matchingStreams = candidateStreams.filter { stream ->
93- if (! stream.isAutoPlayable(debridEnabled)) return @filter false
121+ candidateStreams.filter { stream ->
94122 val url = stream.playableDirectUrl.orEmpty()
95123
96124 val searchableText = buildString {
@@ -109,14 +137,65 @@ object StreamAutoPlaySelector {
109137
110138 true
111139 }
112-
113- if (matchingStreams.isEmpty()) return null
114- matchingStreams.firstOrNull { it.isAutoPlayable(debridEnabled) }
115140 }
116141 }
142+ if (matchingStreams.isEmpty() && preferredReadyStream == null ) return StreamAutoPlayEvaluation ()
143+
144+ val readyStreams = buildList {
145+ preferredReadyStream?.let (::add)
146+ matchingStreams
147+ .filter { it.isAutoPlayable(debridEnabled, activeResolverProviderId) }
148+ .filterNot { it == preferredReadyStream }
149+ .forEach(::add)
150+ }
151+ val selected = readyStreams.firstOrNull()
152+ if (selected != null ) {
153+ return StreamAutoPlayEvaluation (
154+ stream = selected,
155+ readyStreams = readyStreams,
156+ )
157+ }
158+
159+ return StreamAutoPlayEvaluation (
160+ readyStreams = readyStreams,
161+ hasPendingDebridCandidate = matchingStreams.any {
162+ it.isPendingDebridAutoPlay(debridEnabled, activeResolverProviderId)
163+ },
164+ )
117165 }
118166
119- private fun StreamItem.isAutoPlayable (debridEnabled : Boolean ): Boolean =
167+ private fun StreamItem.isAutoPlayable (
168+ debridEnabled : Boolean ,
169+ activeResolverProviderId : String? ,
170+ ): Boolean =
120171 playableDirectUrl != null ||
121- (debridEnabled && isAddonDebridCandidate && (isDirectDebridStream || isCachedDebridTorrentStream))
172+ (debridEnabled && isAddonDebridCandidate && isReadyDebridAutoPlay(activeResolverProviderId))
173+
174+ private fun StreamItem.isReadyDebridAutoPlay (activeResolverProviderId : String? ): Boolean =
175+ when {
176+ isDirectDebridStream -> clientResolve?.service.matchesResolver(activeResolverProviderId)
177+ isCachedDebridTorrentStream -> debridCacheStatus?.providerId.matchesResolver(activeResolverProviderId)
178+ else -> false
179+ }
180+
181+ private fun StreamItem.isPendingDebridAutoPlay (
182+ debridEnabled : Boolean ,
183+ activeResolverProviderId : String? ,
184+ ): Boolean {
185+ if (! debridEnabled || ! isInstalledAddonStream || ! needsLocalDebridResolve) return false
186+ if (! debridCacheStatus?.providerId.matchesResolver(activeResolverProviderId)) return false
187+ val state = debridCacheStatus?.state
188+ return state == null || state == StreamDebridCacheState .CHECKING
189+ }
190+
191+ private fun String?.matchesResolver (activeResolverProviderId : String? ): Boolean {
192+ val active = activeResolverProviderId?.trim().orEmpty()
193+ return active.isBlank() || this == null || equals(active, ignoreCase = true )
194+ }
122195}
196+
197+ data class StreamAutoPlayEvaluation (
198+ val stream : StreamItem ? = null ,
199+ val readyStreams : List <StreamItem > = emptyList(),
200+ val hasPendingDebridCandidate : Boolean = false ,
201+ )
0 commit comments