From 92dfbddc3eda989c857633c5c3bdef411201869d Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Fri, 25 Apr 2025 19:26:52 +0200 Subject: [PATCH] add ffmpeg output in active downloads & terminal --- app/build.gradle | 2 +- .../ytdl/util/extractors/YTDLPUtil.kt | 250 +++++++++--------- .../deniscerri/ytdl/work/DownloadWorker.kt | 2 +- .../ytdl/work/TerminalDownloadWorker.kt | 2 +- 4 files changed, 127 insertions(+), 129 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 619b10e8..c90da14d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,7 @@ dependencies { // implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer" - implementation "io.github.junkfood02.youtubedl-android:library:0.17.2" + implementation "io.github.junkfood02.youtubedl-android:library:0.17.4" implementation "io.github.junkfood02.youtubedl-android:ffmpeg:0.17.2" implementation "io.github.junkfood02.youtubedl-android:aria2c:0.17.2" diff --git a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt index 9d0371d3..21ca526b 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt @@ -40,7 +40,6 @@ import org.json.JSONArray import org.json.JSONObject import java.io.File import java.lang.reflect.Type -import java.util.ArrayList import java.util.Locale import java.util.StringJoiner import java.util.UUID @@ -174,8 +173,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co if (Patterns.WEB_URL.matcher(query).matches() && playlistURL?.isEmpty() == true){ query }else{ - jsonObject.getStringByAny("webpage_url", "original_url", "url", ) - jsonObject.getString("webpage_url") + jsonObject.getStringByAny("webpage_url", "original_url", "url") } } @@ -580,7 +578,129 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co this.add(it) } } - @OptIn(ExperimentalStdlibApi::class) + + fun getVersion(context: Context, channel: String) : String { + if (listOf("stable", "nightly", "master").contains(channel)) { + return YoutubeDL.version(context) ?: "" + } + + val req = YoutubeDLRequest(emptyList()) + req.addOption("--version") + return YoutubeDL.getInstance().execute(req).out.trim() + } + + private fun YoutubeDLRequest.setYoutubeExtractorArgs(url: String?) { + val extractorArgs = mutableListOf() + val playerClients = mutableSetOf() + val poTokens = mutableListOf() + + val configuredPlayerClientsRaw = sharedPreferences.getString("youtube_player_clients", "[]")!!.ifEmpty { "[]" } + kotlin.runCatching { + val configuredPlayerClients = Gson().fromJson(configuredPlayerClientsRaw, Array::class.java).toMutableList() + + for (value in configuredPlayerClients) { + if (value.enabled) { + if (!value.useOnlyPoToken) { + playerClients.add(value.playerClient) + } + + var canUsePoToken = true + if (value.urlRegex.isNotEmpty() && url != null) { + canUsePoToken = value.urlRegex.any { url.matches(it.toRegex()) } + } + + if (canUsePoToken) { + value.poTokens.forEach { pt -> + poTokens.add("${value.playerClient}.${pt.context}+${pt.token}") + } + } + } + } + } + + val dataSyncID = sharedPreferences.getString("youtube_data_sync_id", "")!! + if (dataSyncID.isNotBlank()) { + extractorArgs.add("player_skip=webpage,configs") + extractorArgs.add("data_sync_id=${dataSyncID}") + } + + val generatedPoTokensRaw = sharedPreferences.getString("youtube_generated_po_tokens", "[]")!!.ifEmpty { "[]" } + kotlin.runCatching { + val generatedPoTokens = Gson().fromJson(generatedPoTokensRaw,Array::class.java).toMutableList() + if (generatedPoTokens.isNotEmpty()) { + for (value in generatedPoTokens) { + if (value.enabled) { + for (cl in value.clients) { + playerClients.add(cl) + for (pt in value.poTokens) { + if (pt.token.isNotBlank()) { + poTokens.add("${cl}.${pt.context}+${pt.token}") + } + } + } + + if (dataSyncID.isBlank() && value.useVisitorData) { + extractorArgs.add("player_skip=webpage,configs") + extractorArgs.add("visitor_data=${value.visitorData}") + } + + } + } + } + } + + if (playerClients.isNotEmpty()){ + extractorArgs.add("player_client=${playerClients.joinToString(",")}") + } + + if (poTokens.isNotEmpty()) { + extractorArgs.add("po_token=${poTokens.joinToString(",")}") + } + + val useLanguageForMetadata = sharedPreferences.getBoolean("use_app_language_for_metadata", true) + if (useLanguageForMetadata) { + val lang = Locale.getDefault().language + val langTag = Locale.getDefault().toLanguageTag() + if (context.getStringArray(R.array.subtitle_langs).contains(lang)) { + extractorArgs.add("lang=$lang") + }else if (context.getStringArray(R.array.subtitle_langs).contains(langTag)) { + extractorArgs.add("lang=$langTag") + } + } + + val otherArgs = sharedPreferences.getString("youtube_other_extractor_args", "")!! + if (otherArgs.isNotBlank()) { + extractorArgs.add(otherArgs) + } + + val extArgs = extractorArgs.joinToString(";") + if (extractorArgs.isNotEmpty()) { + this.addOption("--extractor-args", "youtube:${extArgs}") + } + } + + private fun YoutubeDLRequest.addConfig(commandString: String) { + this.addOption( + "--config-locations", + File(context.cacheDir.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt").apply { + writeText(commandString) + }.absolutePath + ) + } + + private fun StringJoiner.addOption(vararg elements: Any) { + this.add(elements.first().toString()) + if (elements.size > 1) { + for (el in elements.drop(1)) { + val arg = el.toString() + //.replace("\\", "\\\\") + .replace("\"", "\\\"") + + this.add("\"$arg\"") + } + } + } + @SuppressLint("RestrictedApi") fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest { val useItemURL = sharedPreferences.getBoolean("use_itemurl_instead_playlisturl", false) @@ -1272,126 +1392,4 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co ytDlRequest.addCommands(tmp) return ytDlRequest } - - fun getVersion(context: Context, channel: String) : String { - if (listOf("stable", "nightly", "master").contains(channel)) { - return YoutubeDL.version(context) ?: "" - } - - val req = YoutubeDLRequest(emptyList()) - req.addOption("--version") - return YoutubeDL.getInstance().execute(req).out.trim() - } - - private fun YoutubeDLRequest.setYoutubeExtractorArgs(url: String?) { - val extractorArgs = mutableListOf() - val playerClients = mutableSetOf() - val poTokens = mutableListOf() - - val configuredPlayerClientsRaw = sharedPreferences.getString("youtube_player_clients", "[]")!!.ifEmpty { "[]" } - kotlin.runCatching { - val configuredPlayerClients = Gson().fromJson(configuredPlayerClientsRaw, Array::class.java).toMutableList() - - for (value in configuredPlayerClients) { - if (value.enabled) { - if (!value.useOnlyPoToken) { - playerClients.add(value.playerClient) - } - - var canUsePoToken = true - if (value.urlRegex.isNotEmpty() && url != null) { - canUsePoToken = value.urlRegex.any { url.matches(it.toRegex()) } - } - - if (canUsePoToken) { - value.poTokens.forEach { pt -> - poTokens.add("${value.playerClient}.${pt.context}+${pt.token}") - } - } - } - } - } - - val dataSyncID = sharedPreferences.getString("youtube_data_sync_id", "")!! - if (dataSyncID.isNotBlank()) { - extractorArgs.add("player_skip=webpage,configs") - extractorArgs.add("data_sync_id=${dataSyncID}") - } - - val generatedPoTokensRaw = sharedPreferences.getString("youtube_generated_po_tokens", "[]")!!.ifEmpty { "[]" } - kotlin.runCatching { - val generatedPoTokens = Gson().fromJson(generatedPoTokensRaw,Array::class.java).toMutableList() - if (generatedPoTokens.isNotEmpty()) { - for (value in generatedPoTokens) { - if (value.enabled) { - for (cl in value.clients) { - playerClients.add(cl) - for (pt in value.poTokens) { - if (pt.token.isNotBlank()) { - poTokens.add("${cl}.${pt.context}+${pt.token}") - } - } - } - - if (dataSyncID.isBlank() && value.useVisitorData) { - extractorArgs.add("player_skip=webpage,configs") - extractorArgs.add("visitor_data=${value.visitorData}") - } - - } - } - } - } - - if (playerClients.isNotEmpty()){ - extractorArgs.add("player_client=${playerClients.joinToString(",")}") - } - - if (poTokens.isNotEmpty()) { - extractorArgs.add("po_token=${poTokens.joinToString(",")}") - } - - val useLanguageForMetadata = sharedPreferences.getBoolean("use_app_language_for_metadata", true) - if (useLanguageForMetadata) { - val lang = Locale.getDefault().language - val langTag = Locale.getDefault().toLanguageTag() - if (context.getStringArray(R.array.subtitle_langs).contains(lang)) { - extractorArgs.add("lang=$lang") - }else if (context.getStringArray(R.array.subtitle_langs).contains(langTag)) { - extractorArgs.add("lang=$langTag") - } - } - - val otherArgs = sharedPreferences.getString("youtube_other_extractor_args", "")!! - if (otherArgs.isNotBlank()) { - extractorArgs.add(otherArgs) - } - - val extArgs = extractorArgs.joinToString(";") - if (extractorArgs.isNotEmpty()) { - this.addOption("--extractor-args", "youtube:${extArgs}") - } - } - - private fun YoutubeDLRequest.addConfig(commandString: String) { - this.addOption( - "--config-locations", - File(context.cacheDir.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt").apply { - writeText(commandString) - }.absolutePath - ) - } - - private fun StringJoiner.addOption(vararg elements: Any) { - this.add(elements.first().toString()) - if (elements.size > 1) { - for (el in elements.drop(1)) { - val arg = el.toString() - //.replace("\\", "\\\\") - .replace("\"", "\\\"") - - this.add("\"$arg\"") - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt index 4922361b..a4402b53 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt @@ -221,7 +221,7 @@ class DownloadWorker( runCatching { YoutubeDL.getInstance().destroyProcessById(downloadItem.id.toString()) - YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line -> + YoutubeDL.getInstance().execute(request, downloadItem.id.toString(), true){ progress, _, line -> eventBus.post(WorkerProgress(progress.toInt(), line, downloadItem.id, downloadItem.logID)) val title: String = downloadItem.title.ifEmpty { downloadItem.url } notificationUtil.updateDownloadNotification( diff --git a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt index 1fe1b8a2..dc4583b2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/TerminalDownloadWorker.kt @@ -120,7 +120,7 @@ class TerminalDownloadWorker( } } - YoutubeDL.getInstance().execute(request, itemId.toString()){ progress, _, line -> + YoutubeDL.getInstance().execute(request, itemId.toString(), true){ progress, _, line -> runBlocking { eventBus.post(DownloadWorker.WorkerProgress(progress.toInt(), line, itemId.toLong(), logItem.id)) }