Thread addon tracker URLs from sources field through to magnet URI construction

This commit is contained in:
CrissZollo 2026-04-06 18:24:26 +02:00
parent 1eb82495c9
commit 65e7abe251
No known key found for this signature in database
6 changed files with 36 additions and 11 deletions

View file

@ -18,7 +18,8 @@ fun StreamDto.toDomain(addonName: String, addonLogo: String?): Stream = Stream(
externalUrl = externalUrl,
behaviorHints = behaviorHints?.toDomain(),
addonName = addonName,
addonLogo = addonLogo
addonLogo = addonLogo,
sources = sources
)
fun BehaviorHintsDto.toDomain(): StreamBehaviorHints = StreamBehaviorHints(

View file

@ -17,7 +17,8 @@ data class Stream(
val externalUrl: String?,
val behaviorHints: StreamBehaviorHints?,
val addonName: String,
val addonLogo: String?
val addonLogo: String?,
val sources: List<String>? = null
) {
/**
* Returns the primary stream source URL

View file

@ -479,7 +479,8 @@ fun NuvioNavHost(
addonLogo = playbackInfo.addonLogo,
streamDescription = playbackInfo.streamDescription,
infoHash = playbackInfo.infoHash,
fileIdx = playbackInfo.fileIdx
fileIdx = playbackInfo.fileIdx,
sources = playbackInfo.sources
)
)
}
@ -517,7 +518,8 @@ fun NuvioNavHost(
addonLogo = playbackInfo.addonLogo,
streamDescription = playbackInfo.streamDescription,
infoHash = playbackInfo.infoHash,
fileIdx = playbackInfo.fileIdx
fileIdx = playbackInfo.fileIdx,
sources = playbackInfo.sources
)
) {
popUpTo(Screen.Stream.route) { inclusive = true }

View file

@ -62,7 +62,7 @@ sealed class Screen(val route: String) {
return "stream/$encodedVideoId/$encodedContentTypePath/$encodedTitle?poster=$encodedPoster&backdrop=$encodedBackdrop&logo=$encodedLogo&season=${season ?: ""}&episode=${episode ?: ""}&episodeName=$encodedEpisodeName&genres=$encodedGenres&year=$encodedYear&contentId=$encodedContentId&contentName=$encodedContentName&runtime=${runtime ?: ""}&manualSelection=$manualSelection&returnToDetailOnBack=$returnToDetailOnBack&returnToHomeOnBack=$returnToHomeOnBack&startFromBeginning=$startFromBeginning"
}
}
data object Player : Screen("player/{streamUrl}/{title}?streamName={streamName}&year={year}&headers={headers}&contentId={contentId}&contentType={contentType}&contentName={contentName}&poster={poster}&backdrop={backdrop}&logo={logo}&videoId={videoId}&season={season}&episode={episode}&episodeTitle={episodeTitle}&bingeGroup={bingeGroup}&autoPlayNav={autoPlayNav}&returnToDetailOnBack={returnToDetailOnBack}&returnToHomeOnBack={returnToHomeOnBack}&filename={filename}&videoHash={videoHash}&videoSize={videoSize}&startFromBeginning={startFromBeginning}&addonName={addonName}&addonLogo={addonLogo}&streamDescription={streamDescription}&infoHash={infoHash}&fileIdx={fileIdx}") {
data object Player : Screen("player/{streamUrl}/{title}?streamName={streamName}&year={year}&headers={headers}&contentId={contentId}&contentType={contentType}&contentName={contentName}&poster={poster}&backdrop={backdrop}&logo={logo}&videoId={videoId}&season={season}&episode={episode}&episodeTitle={episodeTitle}&bingeGroup={bingeGroup}&autoPlayNav={autoPlayNav}&returnToDetailOnBack={returnToDetailOnBack}&returnToHomeOnBack={returnToHomeOnBack}&filename={filename}&videoHash={videoHash}&videoSize={videoSize}&startFromBeginning={startFromBeginning}&addonName={addonName}&addonLogo={addonLogo}&streamDescription={streamDescription}&infoHash={infoHash}&fileIdx={fileIdx}&sources={sources}") {
private fun encode(value: String): String =
URLEncoder.encode(value, "UTF-8").replace("+", "%20")
@ -94,7 +94,8 @@ sealed class Screen(val route: String) {
addonLogo: String? = null,
streamDescription: String? = null,
infoHash: String? = null,
fileIdx: Int? = null
fileIdx: Int? = null,
sources: List<String>? = null
): String {
val encodedUrl = encode(streamUrl)
val encodedTitle = encode(title)
@ -118,7 +119,8 @@ sealed class Screen(val route: String) {
val encodedAddonLogo = addonLogo?.let { encode(it) } ?: ""
val encodedStreamDescription = streamDescription?.let { encode(it) } ?: ""
val encodedInfoHash = infoHash ?: ""
return "player/$encodedUrl/$encodedTitle?streamName=$encodedStreamName&year=$encodedYear&headers=$encodedHeaders&contentId=$encodedContentId&contentType=$encodedContentType&contentName=$encodedContentName&poster=$encodedPoster&backdrop=$encodedBackdrop&logo=$encodedLogo&videoId=$encodedVideoId&season=${season ?: ""}&episode=${episode ?: ""}&episodeTitle=$encodedEpisodeTitle&bingeGroup=$encodedBingeGroup&autoPlayNav=$autoPlayNav&returnToDetailOnBack=$returnToDetailOnBack&returnToHomeOnBack=$returnToHomeOnBack&filename=$encodedFilename&videoHash=$encodedVideoHash&videoSize=${videoSize ?: ""}&startFromBeginning=$startFromBeginning&addonName=$encodedAddonName&addonLogo=$encodedAddonLogo&streamDescription=$encodedStreamDescription&infoHash=$encodedInfoHash&fileIdx=${fileIdx ?: ""}"
val encodedSources = sources?.let { encode(org.json.JSONArray(it).toString()) } ?: ""
return "player/$encodedUrl/$encodedTitle?streamName=$encodedStreamName&year=$encodedYear&headers=$encodedHeaders&contentId=$encodedContentId&contentType=$encodedContentType&contentName=$encodedContentName&poster=$encodedPoster&backdrop=$encodedBackdrop&logo=$encodedLogo&videoId=$encodedVideoId&season=${season ?: ""}&episode=${episode ?: ""}&episodeTitle=$encodedEpisodeTitle&bingeGroup=$encodedBingeGroup&autoPlayNav=$autoPlayNav&returnToDetailOnBack=$returnToDetailOnBack&returnToHomeOnBack=$returnToHomeOnBack&filename=$encodedFilename&videoHash=$encodedVideoHash&videoSize=${videoSize ?: ""}&startFromBeginning=$startFromBeginning&addonName=$encodedAddonName&addonLogo=$encodedAddonLogo&streamDescription=$encodedStreamDescription&infoHash=$encodedInfoHash&fileIdx=${fileIdx ?: ""}&sources=$encodedSources"
}
}
data object Search : Screen("search")

View file

@ -1,6 +1,7 @@
package com.nuvio.tv.ui.screens.player
import androidx.lifecycle.SavedStateHandle
import org.json.JSONArray
import java.net.URLDecoder
internal data class PlayerNavigationArgs(
@ -28,8 +29,23 @@ internal data class PlayerNavigationArgs(
val addonLogo: String?,
val streamDescription: String?,
val infoHash: String?,
val fileIdx: Int?
val fileIdx: Int?,
val sourcesJson: String?
) {
val torrentTrackers: List<String>
get() {
val json = sourcesJson ?: return emptyList()
return try {
val arr = JSONArray(json)
(0 until arr.length())
.mapNotNull { arr.optString(it) }
.filter { it.startsWith("tracker:") }
.map { it.removePrefix("tracker:") }
} catch (_: Exception) {
emptyList()
}
}
companion object {
fun from(savedStateHandle: SavedStateHandle): PlayerNavigationArgs {
fun decodedOrNull(key: String): String? {
@ -63,7 +79,8 @@ internal data class PlayerNavigationArgs(
addonLogo = decodedOrNull("addonLogo"),
streamDescription = decodedOrNull("streamDescription"),
infoHash = savedStateHandle.get<String>("infoHash")?.takeIf { it.isNotEmpty() },
fileIdx = savedStateHandle.get<String>("fileIdx")?.toIntOrNull()
fileIdx = savedStateHandle.get<String>("fileIdx")?.toIntOrNull(),
sourcesJson = decodedOrNull("sources")
)
}
}

View file

@ -667,7 +667,8 @@ class StreamScreenViewModel @Inject constructor(
addonName = stream.addonName,
addonLogo = stream.addonLogo,
streamDescription = stream.description,
fileIdx = stream.fileIdx
fileIdx = stream.fileIdx,
sources = stream.sources
)
val url = playbackInfo.url
@ -724,5 +725,6 @@ data class StreamPlaybackInfo(
val addonName: String? = null,
val addonLogo: String? = null,
val streamDescription: String? = null,
val fileIdx: Int? = null
val fileIdx: Int? = null,
val sources: List<String>? = null
)