diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt index ac637ae6..ef5a61ca 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt @@ -329,7 +329,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { val itemsToQueue = mutableListOf() itemsToQueue.add(item) - getAlsoAudioDownloadItem{ audioDownloadItem -> + getAlsoAudioDownloadItem(finished = { audioDownloadItem -> audioDownloadItem.downloadStartTime = it.timeInMillis audioDownloadItem.status = DownloadRepository.Status.Scheduled.toString() itemsToQueue.add(audioDownloadItem) @@ -347,7 +347,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { handleDuplicatesAndDismiss(result.duplicateDownloadIDs) } } - } + }) }else{ lifecycleScope.launch { val result = withContext(Dispatchers.IO){ @@ -377,16 +377,18 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { val itemsToQueue = mutableListOf() itemsToQueue.add(item) - getAlsoAudioDownloadItem { + getAlsoAudioDownloadItem(finished = { itemsToQueue.add(it) - runBlocking { - val result = downloadViewModel.queueDownloads(itemsToQueue) + lifecycleScope.launch { + val result = withContext(Dispatchers.IO) { + downloadViewModel.queueDownloads(itemsToQueue) + } withContext(Dispatchers.Main){ handleDuplicatesAndDismiss(result.duplicateDownloadIDs) } } - } + }) }else{ val result = withContext(Dispatchers.IO) { downloadViewModel.queueDownloads(listOf(item)) @@ -686,7 +688,11 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { private fun getAlsoAudioDownloadItem(finished: (it: DownloadItem) -> Unit) { try { val ff = fragmentAdapter.fragments[0] as DownloadAudioFragment - ff.updateSelectedAudioFormat(getDownloadItem(1).videoPreferences.audioFormatIDs.first()) + getDownloadItem(1).videoPreferences.audioFormatIDs.apply { + if (this.isNotEmpty()) { + ff.updateSelectedAudioFormat(this.first()) + } + } finished(ff.downloadItem) }catch (e: Exception){ val fragmentLifecycleCallback = object: diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt index d4407bb5..1accbad4 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt @@ -253,10 +253,18 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap downloadMusic.setOnClickListener { onButtonClick(DownloadViewModel.Type.audio) } + downloadMusic.setOnLongClickListener { + onButtonClick(DownloadViewModel.Type.audio) + true + } downloadVideo.setOnClickListener { onButtonClick(DownloadViewModel.Type.video) } + downloadVideo.setOnLongClickListener { + onButtonClick(DownloadViewModel.Type.video) + true + } videoView = view.findViewById(R.id.video_view) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/UpdateSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/UpdateSettingsFragment.kt index c8c4f693..7e0b82d4 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/UpdateSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/UpdateSettingsFragment.kt @@ -141,7 +141,7 @@ class UpdateSettingsFragment : BaseSettingsFragment() { lifecycleScope.launch { ytdlVersion!!.summary = getString(R.string.loading) val version = withContext(Dispatchers.IO){ - ytdlpUtil.getVersion() + ytdlpUtil.getVersion(requireContext(), preferences.getString("ytdlp_source", "stable")!!) } preferences.edit().apply { putString("ytdl-version", version) diff --git a/app/src/main/java/com/deniscerri/ytdl/util/UpdateUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/UpdateUtil.kt index 6ab2cff3..ecefd718 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/UpdateUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/UpdateUtil.kt @@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat.startActivity import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import androidx.work.WorkManager.UpdateResult import com.deniscerri.ytdl.BuildConfig import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.GithubRelease @@ -48,6 +49,12 @@ class UpdateUtil(var context: Context) { private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) private val downloadManager: DownloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + private val channelMap = mapOf( + Pair("stable", YoutubeDL.UpdateChannel.STABLE), + Pair("nightly", YoutubeDL.UpdateChannel.NIGHTLY), + Pair("master", YoutubeDL.UpdateChannel.MASTER) + ) + @SuppressLint("UnspecifiedRegisterReceiverFlag") fun updateApp(result: (result: String) -> Unit) { try { @@ -229,15 +236,33 @@ class UpdateUtil(var context: Context) { updatingYTDL = true val channel = if (c.isNullOrBlank()) sharedPreferences.getString("ytdlp_source", "stable") else c - val request = YoutubeDLRequest(emptyList()) - request.addOption("--update-to", "${channel}@latest") - val res = YoutubeDL.getInstance().execute(request) - val out = res.out.split(System.getProperty("line.separator")).filter { it.isNotBlank() }.last() - if (out.contains("ERROR")) YTDLPUpdateResponse(YTDLPUpdateStatus.ERROR, out) - if (out.contains("yt-dlp is up to date")) YTDLPUpdateResponse(YTDLPUpdateStatus.ALREADY_UP_TO_DATE, out) - else YTDLPUpdateResponse(YTDLPUpdateStatus.DONE, out) + + when(channel) { + "stable", "nightly", "master" -> { + val res = YoutubeDL.updateYoutubeDL(context, channelMap[channel]!!) + if (res != YoutubeDL.UpdateStatus.DONE) { + YTDLPUpdateResponse(YTDLPUpdateStatus.ALREADY_UP_TO_DATE) + }else { + val version = YoutubeDL.version(context) + YTDLPUpdateResponse(YTDLPUpdateStatus.DONE, "Updated yt-dlp to ${channel}@${version}") + } + } + else -> { + val request = YoutubeDLRequest(emptyList()) + request.addOption("--update-to", "${channel}@latest") + + val res = YoutubeDL.getInstance().execute(request) + val out = res.out.split(System.getProperty("line.separator")).last { it.isNotBlank() } + + if (out.contains("ERROR")) YTDLPUpdateResponse(YTDLPUpdateStatus.ERROR, out) + if (out.contains("yt-dlp is up to date")) YTDLPUpdateResponse(YTDLPUpdateStatus.ALREADY_UP_TO_DATE, out) + else YTDLPUpdateResponse(YTDLPUpdateStatus.DONE, out) + } + } + + } companion object { 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 c6b709a2..0b603b47 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 @@ -39,6 +39,7 @@ 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 class YTDLPUtil(private val context: Context) { @@ -571,9 +572,11 @@ class YTDLPUtil(private val context: Context) { val useItemURL = sharedPreferences.getBoolean("use_itemurl_instead_playlisturl", false) var isPlaylistItem = false - val request = if (downloadItem.url.endsWith(".txt")) { + val request = StringJoiner(" ") + + val ytDlRequest = if (downloadItem.url.endsWith(".txt")) { YoutubeDLRequest(listOf()).apply { - addOption("-a", downloadItem.url) + request.addOption("-a", downloadItem.url) } }else if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || useItemURL){ YoutubeDLRequest(downloadItem.url) @@ -582,17 +585,17 @@ class YTDLPUtil(private val context: Context) { YoutubeDLRequest(downloadItem.playlistURL!!).apply { if(downloadItem.playlistIndex == null){ val matchPortion = downloadItem.url.split("/").last().split("=").last().split("&").first() - addOption("--match-filter", "id~='${matchPortion}'") + request.addOption("--match-filter", "id~='${matchPortion}'") }else{ - addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}") + request.addOption("-I", "${downloadItem.playlistIndex!!}:${downloadItem.playlistIndex}") } - addOption("-i") + request.addOption("-i") } } request.addOption("--newline") - val metadataCommands = mutableListOf() + val metadataCommands = StringJoiner(" ") if (downloadItem.playlistIndex != null && useItemURL) { metadataCommands.addOption("--parse-metadata", " ${downloadItem.playlistIndex}: %(playlist_index)s") @@ -681,7 +684,7 @@ class YTDLPUtil(private val context: Context) { request.addOption("--no-part") } - request.addOption("--trim-filenames", 254/* - downDir.absolutePath.length*/) + request.addOption("--trim-filenames", 254 - downDir.absolutePath.length) if (downloadItem.SaveThumb) { request.addOption("--write-thumbnail") @@ -730,7 +733,7 @@ class YTDLPUtil(private val context: Context) { if (it.isBlank()) return@forEach request.addOption("--download-sections", "*${it.split(" ")[0]}") - if (sharedPreferences.getBoolean("force_keyframes", false) && !request.hasOption("--force-keyframes-at-cuts")){ + if (sharedPreferences.getBoolean("force_keyframes", false) && !request.toString().contains("--force-keyframes-at-cuts")){ request.addOption("--force-keyframes-at-cuts") } } @@ -749,7 +752,7 @@ class YTDLPUtil(private val context: Context) { } if (downloadItem.url.isYoutubeURL()) { - request.setYoutubeExtractorArgs() + ytDlRequest.setYoutubeExtractorArgs() } //TODO REVIEW TO ADD THIS AGAIN LATER? @@ -903,7 +906,7 @@ class YTDLPUtil(private val context: Context) { val cropThumb = downloadItem.audioPreferences.cropThumb ?: sharedPreferences.getBoolean("crop_thumbnail", true) if (downloadItem.audioPreferences.embedThumb){ metadataCommands.addOption("--embed-thumbnail") - if (!request.hasOption("--convert-thumbnails")) metadataCommands.addOption("--convert-thumbnails", thumbnailFormat!!) + if (!request.toString().contains("--convert-thumbnails")) metadataCommands.addOption("--convert-thumbnails", thumbnailFormat!!) val thumbnailConfig = StringBuilder("") val cropConfig = """-vf crop=\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\""""" @@ -917,11 +920,7 @@ class YTDLPUtil(private val context: Context) { } if (thumbnailConfig.isNotBlank()){ - runCatching { - val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##ffmpegCrop.txt") - config.writeText(thumbnailConfig.toString()) - metadataCommands.addOption("--config", config.absolutePath) - } + request.addOption(thumbnailConfig.toString()) } } @@ -969,7 +968,7 @@ class YTDLPUtil(private val context: Context) { val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) if (embedThumb) { metadataCommands.addOption("--embed-thumbnail") - if (!request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", thumbnailFormat!!) + if (!request.toString().contains("--convert-thumbnails")) request.addOption("--convert-thumbnails", thumbnailFormat!!) } } } @@ -1021,7 +1020,11 @@ class YTDLPUtil(private val context: Context) { if (altAudioF.isNotBlank()){ f.append("$videoF+$altAudioF/") } - f.append("$videoF+ba/$videoF/b") + if (!f.contains("$videoF+ba")) { + f.append("$videoF+ba/") + } + f.append("$videoF/b") + if (audioF.count("+") > 0){ request.addOption("--audio-multistreams") @@ -1206,39 +1209,34 @@ class YTDLPUtil(private val context: Context) { request.addOption("-P", downDir.absolutePath) } - request.addOption( - "--config-locations", - File(context.cacheDir.absolutePath + "/config[${downloadItem.id}].txt").apply { - writeText(downloadItem.format.format_note) - }.absolutePath - ) - + request.addOption(downloadItem.format.format_note) } else -> {} } - if (metadataCommands.isNotEmpty()){ - request.addCommands(metadataCommands) - } + request.merge(metadataCommands) if (downloadItem.extraCommands.isNotBlank() && downloadItem.type != DownloadViewModel.Type.command){ - //request.addCommands(downloadItem.extraCommands.replace("\"", "").split(" ", "\t", "\n")) - - val cache = File(FileUtil.getCachePath(context)) - cache.mkdirs() - val conf = File(cache.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt") - conf.createNewFile() - conf.writeText(downloadItem.extraCommands) - val tmp = mutableListOf() - tmp.addOption("--config-locations", conf.absolutePath) - request.addCommands(tmp) + request.addOption(downloadItem.extraCommands) } - return request + val cache = File(FileUtil.getCachePath(context)) + cache.mkdirs() + val conf = File(cache.absolutePath + "/${System.currentTimeMillis()}${UUID.randomUUID()}.txt") + conf.createNewFile() + conf.writeText(request.toString()) + val tmp = mutableListOf() + tmp.addOption("--config-locations", conf.absolutePath) + ytDlRequest.addCommands(tmp) + return ytDlRequest } - fun getVersion() : String { + 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() @@ -1302,4 +1300,13 @@ class YTDLPUtil(private val context: Context) { }.absolutePath ) } + + private fun StringJoiner.addOption(vararg elements: Any) { + this.add(elements.first().toString()) + if (elements.size > 1) { + for (el in elements.drop(1)) { + this.add(""""$el"""") + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30456620..cea49d6c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,7 +9,7 @@ Settings Home Folders - Music Folder + Audio Folder Video Folder Updating Useful if you have problems downloading