add --lazy-playlist support & subs po token generated

This commit is contained in:
deniscerri 2025-09-20 09:14:41 +02:00
parent b4960e8308
commit 47716a3334
No known key found for this signature in database
GPG key ID: 95C43D517D830350
4 changed files with 93 additions and 49 deletions

View file

@ -18,8 +18,11 @@ import com.deniscerri.ytdl.util.extractors.GoogleApiUtil
import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil
import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.deniscerri.ytdl.util.extractors.YTDLPUtil
import com.deniscerri.ytdl.util.extractors.YoutubeApiUtil import com.deniscerri.ytdl.util.extractors.YoutubeApiUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) { class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) {
@ -68,7 +71,7 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
"custom" -> { "custom" -> {
val customURL = sharedPreferences.getString("custom_home_recommendation_url", "") val customURL = sharedPreferences.getString("custom_home_recommendation_url", "")
if (customURL.isNullOrBlank()) arrayListOf() if (customURL.isNullOrBlank()) arrayListOf()
else ytdlpUtil.getFromYTDL(customURL) else ytdlpUtil.getFromYTDL(customURL, resultsGenerated = {})
} }
else -> arrayListOf() else -> arrayListOf()
} }
@ -96,7 +99,7 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
.getOrDefault(Pair(listOf(""), null)) .getOrDefault(Pair(listOf(""), null))
} }
suspend fun search(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : ArrayList<ResultItem>{ suspend fun search(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : List<ResultItem>{
if (resetResults) deleteAll() if (resetResults) deleteAll()
val res = when(sharedPreferences.getString("search_engine", "ytsearch")) { val res = when(sharedPreferences.getString("search_engine", "ytsearch")) {
"ytsearch" -> newPipeUtil.search(inputQuery) "ytsearch" -> newPipeUtil.search(inputQuery)
@ -108,7 +111,7 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
res.getOrNull()!! res.getOrNull()!!
}else{ }else{
//fallback to yt-dlp //fallback to yt-dlp
ytdlpUtil.getFromYTDL(inputQuery) ytdlpUtil.getFromYTDL(inputQuery, resultsGenerated = {})
} }
itemCount.value = items.size itemCount.value = items.size
@ -145,7 +148,7 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
val response = if (newpipeExtractorResult.isSuccess){ val response = if (newpipeExtractorResult.isSuccess){
newpipeExtractorResult.getOrElse { items } newpipeExtractorResult.getOrElse { items }
}else{ }else{
val res = ytdlpUtil.getFromYTDL(inputQuery) val res = ytdlpUtil.getFromYTDL(inputQuery, resultsGenerated = {})
if (addToResults) { if (addToResults) {
val ids = resultDao.insertMultiple(res) val ids = resultDao.insertMultiple(res)
ids.forEachIndexed { index, id -> ids.forEachIndexed { index, id ->
@ -170,7 +173,7 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
val res = if (newpipeExtractorResult.isSuccess) { val res = if (newpipeExtractorResult.isSuccess) {
newpipeExtractorResult.getOrNull()!! newpipeExtractorResult.getOrNull()!!
}else{ }else{
ytdlpUtil.getFromYTDL(inputQuery) ytdlpUtil.getFromYTDL(inputQuery, resultsGenerated = {})
} }
if (resetResults) { if (resetResults) {
@ -209,22 +212,30 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
Result.failure(Throwable()) Result.failure(Throwable())
} }
val result = if (ytExtractorResult.isSuccess){ val finalResults = mutableListOf<ResultItem>()
ytExtractorResult.getOrElse { items } if (ytExtractorResult.isSuccess) {
}else{ ytExtractorResult.getOrElse { items }.apply {
val res = ytdlpUtil.getFromYTDL(inputQuery) finalResults.addAll(this)
if (addToResults) { itemCount.value = this.size
val ids = resultDao.insertMultiple(res) }
ids.forEachIndexed { index, id -> }else {
res[index].id = id var itemCounts = 0
} ytdlpUtil.getFromYTDL(inputQuery) {
if (addToResults) {
runBlocking {
val ids = resultDao.insertMultiple(it)
ids.forEachIndexed { index, id ->
it[index].id = id
}
}
}
finalResults.addAll(it)
itemCounts+=it.size
itemCount.value = itemCounts
} }
res
} }
itemCount.value = result.size return finalResults
return result
} }
private suspend fun getYoutubeChannel(url: String, resetResults: Boolean, addToResults: Boolean) : List<ResultItem>{ private suspend fun getYoutubeChannel(url: String, resetResults: Boolean, addToResults: Boolean) : List<ResultItem>{
@ -246,38 +257,58 @@ class ResultRepository(private val resultDao: ResultDao, private val commandTemp
Result.failure(Throwable()) Result.failure(Throwable())
} }
val response = if (ytExtractorResult.isSuccess){ val finalResults = mutableListOf<ResultItem>()
ytExtractorResult.getOrElse { items } if (ytExtractorResult.isSuccess) {
}else{ ytExtractorResult.getOrElse { items }.apply {
val res = ytdlpUtil.getFromYTDL(url) finalResults.addAll(this)
val ids = resultDao.insertMultiple(res) itemCount.value = this.size
ids.forEachIndexed { index, id -> }
res[index].id = id }else {
var itemCounts = 0
ytdlpUtil.getFromYTDL(url) {
if (addToResults) {
runBlocking {
val ids = resultDao.insertMultiple(it)
ids.forEachIndexed { index, id ->
it[index].id = id
}
}
}
finalResults.addAll(it)
itemCounts+=it.size
itemCount.value = itemCounts
} }
res
} }
itemCount.value = response.size return finalResults
return response
} }
private suspend fun getFromYTDLP(inputQuery: String, resetResults: Boolean, addToResults: Boolean, singleItem: Boolean = false) : ArrayList<ResultItem> { private suspend fun getFromYTDLP(inputQuery: String, resetResults: Boolean, addToResults: Boolean, singleItem: Boolean = false) : List<ResultItem> {
val items = ytdlpUtil.getFromYTDL(inputQuery, singleItem)
if (resetResults) { if (resetResults) {
deleteAll() deleteAll()
itemCount.value = items.size
}else{
items.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH }
} }
if (addToResults){ var itemsCount = 0
val ids = resultDao.insertMultiple(items.toList()) val itemsToReturn = mutableListOf<ResultItem>()
ids.forEachIndexed { index, id ->
items[index].id = id ytdlpUtil.getFromYTDL(inputQuery, singleItem) { results ->
if (resetResults) {
itemsCount += results.size
itemCount.value = itemsCount
} }
results.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH }
if (addToResults) {
runBlocking {
val ids = resultDao.insertMultiple(results)
ids.forEachIndexed { index, id ->
results[index].id = id
}
}
}
itemsToReturn.addAll(results)
} }
return items return itemsToReturn
} }
fun getFormats(url: String, source : String? = null) : List<Format> { fun getFormats(url: String, source : String? = null) : List<Format> {

View file

@ -147,6 +147,7 @@ class GenerateYoutubePoTokensFragment : Fragment() {
if (result.resultCode == Activity.RESULT_OK) { if (result.resultCode == Activity.RESULT_OK) {
val streamingDataPoTokenResult = result.data!!.getStringExtra("streaming_potoken") ?: "" val streamingDataPoTokenResult = result.data!!.getStringExtra("streaming_potoken") ?: ""
val playerRequestPoTokenResult = result.data!!.getStringExtra("player_potoken") ?: "" val playerRequestPoTokenResult = result.data!!.getStringExtra("player_potoken") ?: ""
val subsRequestPoTokenResult = result.data!!.getStringExtra("subs_potoken") ?: ""
val visitorDataResult = result.data!!.getStringExtra("visitor_data") ?: "" val visitorDataResult = result.data!!.getStringExtra("visitor_data") ?: ""
@ -154,6 +155,7 @@ class GenerateYoutubePoTokensFragment : Fragment() {
conf.poTokens.clear() conf.poTokens.clear()
conf.poTokens.add(YoutubePoTokenItem("gvs", streamingDataPoTokenResult)) conf.poTokens.add(YoutubePoTokenItem("gvs", streamingDataPoTokenResult))
conf.poTokens.add(YoutubePoTokenItem("player", playerRequestPoTokenResult)) conf.poTokens.add(YoutubePoTokenItem("player", playerRequestPoTokenResult))
conf.poTokens.add(YoutubePoTokenItem("subs", subsRequestPoTokenResult))
conf.visitorData = visitorDataResult conf.visitorData = visitorDataResult
configuration.add(conf) configuration.add(conf)

View file

@ -120,6 +120,7 @@ class PoTokenWebViewLoginActivity : BaseActivity() {
val intent = Intent() val intent = Intent()
intent.putExtra("visitor_data", res.visitorData) intent.putExtra("visitor_data", res.visitorData)
intent.putExtra("player_potoken", res.playerRequestPoToken) intent.putExtra("player_potoken", res.playerRequestPoToken)
intent.putExtra("subs_potoken", res.playerRequestPoToken)
intent.putExtra("streaming_potoken", res.streamingDataPoToken) intent.putExtra("streaming_potoken", res.streamingDataPoToken)
setResult(RESULT_OK, intent) setResult(RESULT_OK, intent)
} }

View file

@ -104,7 +104,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun getFromYTDL(query: String, singleItem: Boolean = false): ArrayList<ResultItem> { fun getFromYTDL(query: String, singleItem: Boolean = false, resultsGenerated: (results: List<ResultItem>) -> Unit): List<ResultItem> {
val searchEngine = sharedPreferences.getString("search_engine", "ytsearch") val searchEngine = sharedPreferences.getString("search_engine", "ytsearch")
val request : YoutubeDLRequest val request : YoutubeDLRequest
@ -134,21 +134,31 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
request.addOption("--flat-playlist") request.addOption("--flat-playlist")
request.addOption("--lazy-playlist")
request.addOption(if (singleItem) "-J" else "-j") request.addOption(if (singleItem) "-J" else "-j")
request.applyDefaultOptionsForFetchingData(if (query.isURL()) query else null) request.applyDefaultOptionsForFetchingData(if (query.isURL()) query else null)
println(parseYTDLRequestString(request)) val finalResults = mutableListOf<ResultItem>()
val youtubeDLResponse = YoutubeDL.getInstance().execute(request) var postedProgress = false
val results: List<String?> = try { YoutubeDL.getInstance().execute(request) { progress, _, line ->
val lineSeparator = System.getProperty("line.separator") runCatching {
youtubeDLResponse.out.split(lineSeparator!!) val generatedResults = parseYTDLPListResults(listOf(line))
} catch (e: Exception) { if (generatedResults.isNotEmpty()) {
listOf(youtubeDLResponse.out) finalResults.addAll(generatedResults)
}.filter { it.isNotBlank() }.apply { if (!postedProgress) {
if (this.isEmpty()) throw Exception("Command Used: \n yt-dlp ${parseYTDLRequestString(request)}") if (finalResults.size > 1) {
resultsGenerated(finalResults)
postedProgress = true
}
}else{
resultsGenerated(generatedResults)
}
}
}
} }
return parseYTDLPListResults(results, query) if (!postedProgress) resultsGenerated(finalResults)
return finalResults
} }
private fun parseYTDLPListResults(results: List<String?>, query: String = "") : ArrayList<ResultItem> { private fun parseYTDLPListResults(results: List<String?>, query: String = "") : ArrayList<ResultItem> {