package com.deniscerri.ytdl.util import android.animation.Animator import android.animation.ObjectAnimator import android.animation.ValueAnimator import android.annotation.SuppressLint import android.app.Activity import android.app.DownloadManager import android.content.BroadcastReceiver import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.content.DialogInterface import android.content.Intent import android.content.IntentFilter import android.content.SharedPreferences import android.net.Uri import android.os.Build import android.os.Environment import android.text.Editable import android.text.TextWatcher import android.text.format.DateFormat import android.text.method.DigitsKeyListener import android.text.method.LinkMovementMethod import android.util.DisplayMetrics import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver import android.view.Window import android.view.inputmethod.InputMethodManager import android.widget.Button import android.widget.CheckBox import android.widget.EditText import android.widget.LinearLayout import android.widget.PopupMenu import android.widget.RadioButton import android.widget.TextView import android.widget.Toast import androidx.annotation.DimenRes import androidx.annotation.OptIn import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children import androidx.core.view.isVisible import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doOnTextChanged import androidx.fragment.app.FragmentManager import androidx.lifecycle.LifecycleOwner import androidx.preference.PreferenceManager import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.afollestad.materialdialogs.utils.MDUtil.textChanged import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.CommandTemplate import com.deniscerri.ytdl.database.models.DownloadItem import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.models.GithubRelease import com.deniscerri.ytdl.database.models.HistoryItem import com.deniscerri.ytdl.database.models.TemplateShortcut import com.deniscerri.ytdl.database.models.YoutubePlayerClientItem import com.deniscerri.ytdl.database.models.YoutubePoTokenItem import com.deniscerri.ytdl.database.repository.DownloadRepository import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.YTDLPViewModel import com.deniscerri.ytdl.ui.downloadcard.VideoCutListener import com.deniscerri.ytdl.util.Extensions.enableTextHighlight import com.deniscerri.ytdl.util.Extensions.getMediaDuration import com.deniscerri.ytdl.util.Extensions.toStringDuration import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeUtils import com.google.android.material.badge.ExperimentalBadgeUtils import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import com.google.android.material.datepicker.CalendarConstraints import com.google.android.material.datepicker.DateValidatorPointForward import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.materialswitch.MaterialSwitch import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout.END_ICON_NONE import com.google.android.material.timepicker.MaterialTimePicker import com.google.android.material.timepicker.TimeFormat import com.google.gson.Gson import io.noties.markwon.AbstractMarkwonPlugin import io.noties.markwon.Markwon import io.noties.markwon.MarkwonConfiguration import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.io.File import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale object UiUtil { @SuppressLint("SetTextI18n") fun populateFormatCard(context: Context, formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List? = null, showSize: Boolean = true){ var formatNote = chosenFormat.format_note if (formatNote.isEmpty()) formatNote = context.getString(R.string.defaultValue) else if (formatNote == "best") formatNote = context.getString(R.string.best_quality) else if (formatNote == "worst") formatNote = context.getString(R.string.worst_quality) var container = chosenFormat.container if (container == "Default" || container.isBlank()) container = context.getString(R.string.defaultValue) formatCard.findViewById(R.id.container).text = container.uppercase() formatCard.findViewById(R.id.format_note).text = formatNote.uppercase() formatCard.findViewById(R.id.audio_formats).apply { if (!audioFormats.isNullOrEmpty()){ text = "id: " + audioFormats.joinToString("+") { it.format_id } visibility = View.VISIBLE }else if (chosenFormat.vcodec != "none" && chosenFormat.vcodec != "" && chosenFormat.acodec != "none" && chosenFormat.acodec != "") { text = chosenFormat.acodec visibility = View.VISIBLE }else{ visibility = View.GONE } setOnClickListener { formatCard.callOnClick() } } formatCard.findViewById(R.id.format_id).apply { text = "id: ${chosenFormat.format_id}" setOnClickListener { formatCard.callOnClick() } } val codec = if (chosenFormat.encoding != "") { chosenFormat.encoding.uppercase() }else if (chosenFormat.vcodec != "none" && chosenFormat.vcodec != ""){ chosenFormat.vcodec.uppercase() } else { chosenFormat.acodec.uppercase() } formatCard.findViewById(R.id.codec).apply { if (codec == "" || codec == "none"){ visibility = View.GONE }else{ visibility = View.VISIBLE text = codec } setOnClickListener { formatCard.callOnClick() } } var filesize = chosenFormat.filesize if (!audioFormats.isNullOrEmpty() && filesize > 10L) filesize += audioFormats.sumOf { it.filesize } formatCard.findViewById(R.id.file_size).apply { if (showSize) { text = FileUtil.convertFileSize(filesize) }else{ text = "?" } setOnClickListener { formatCard.callOnClick() } } formatCard.findViewById(R.id.bitrate).apply { if (chosenFormat.tbr.isNullOrBlank() || (chosenFormat.vcodec.isNotBlank() && chosenFormat.vcodec != "none")) { isVisible = false }else{ isVisible = true text = chosenFormat.tbr } } } fun populateCommandCard(card: MaterialCardView, item: CommandTemplate){ card.findViewById(R.id.title).text = item.title card.findViewById(R.id.content).text = item.content card.alpha = 1f card.tag = item.id } fun showCommandTemplateCreationOrUpdatingSheet( item: CommandTemplate?, context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel, newTemplate: (newTemplate: CommandTemplate) -> Unit, dismissed: () -> Unit ){ val bottomSheet = BottomSheetDialog(context) bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.setContentView(R.layout.create_command_template) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) val ok : Button = bottomSheet.findViewById(R.id.template_create)!! val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!! val content : TextInputLayout = bottomSheet.findViewById(R.id.content)!! val preferredCommandSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.preferredCommandTemplateSwitch)!! val extraCommandsSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extraCommandsSwitch)!! val extraCommandsAudio : CheckBox = bottomSheet.findViewById(R.id.checkbox_audio)!! val extraCommandsVideo : CheckBox = bottomSheet.findViewById(R.id.checkbox_video)!! val extraCommandsDataFetchingSwitch : MaterialSwitch = bottomSheet.findViewById(R.id.extraCommandDataFetching)!! val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!! val editShortcuts : Button = bottomSheet.findViewById(R.id.edit_shortcuts)!! val urlRegex : TextInputLayout = bottomSheet.findViewById(R.id.url_regex)!! val urlRegexChips : ChipGroup = bottomSheet.findViewById(R.id.urlRegexChipGroup)!! extraCommandsDataFetchingSwitch.isVisible = sharedPreferences.getBoolean("enable_data_fetching_extra_commands", false) if (item != null){ title.editText!!.setText(item.title) content.editText!!.setText(item.content) bottomSheet.findViewById(R.id.bottom_sheet_subtitle)!!.text = content.resources.getString(R.string.update_template) ok.text = content.resources.getString(R.string.update) ok.isEnabled = true content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_all) }else{ ok.isEnabled = false content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_clipboard) } title.editText!!.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun afterTextChanged(p0: Editable?) { ok.isEnabled = title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty() && if (extraCommandsSwitch.isChecked){ ((extraCommandsAudio.isChecked || extraCommandsVideo.isChecked)) }else{ true } } }) content.editText!!.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun afterTextChanged(p0: Editable?) { ok.isEnabled = title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty() && if (extraCommandsSwitch.isChecked){ ((extraCommandsAudio.isChecked || extraCommandsVideo.isChecked)) }else{ true } if (content.editText!!.text.isNotEmpty()){ content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_all) }else{ content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_clipboard) } } }) content.setEndIconOnClickListener { if(content.editText!!.text.isEmpty()){ val clipboard: ClipboardManager = context.getSystemService(AppCompatActivity.CLIPBOARD_SERVICE) as ClipboardManager content.editText!!.setText(clipboard.primaryClip?.getItemAt(0)?.text) }else{ content.editText!!.setText("") } } if (item != null){ preferredCommandSwitch.isChecked = item.preferredCommandTemplate extraCommandsDataFetchingSwitch.isChecked = item.useAsExtraCommandDataFetching && extraCommandsDataFetchingSwitch.isVisible extraCommandsSwitch.isChecked = item.useAsExtraCommand if (item.useAsExtraCommand){ extraCommandsAudio.isVisible = true extraCommandsAudio.isChecked = item.useAsExtraCommandAudio extraCommandsVideo.isVisible = true extraCommandsVideo.isChecked = item.useAsExtraCommandVideo }else{ extraCommandsAudio.isVisible = false extraCommandsAudio.isChecked = false extraCommandsVideo.isVisible = false extraCommandsVideo.isChecked = false } val canUseURLRegex = item.useAsExtraCommand || item.useAsExtraCommandDataFetching || item.preferredCommandTemplate urlRegex.isVisible = canUseURLRegex urlRegexChips.isVisible = canUseURLRegex for(chip in item.urlRegex) { val tmp = context.layoutInflater.inflate(R.layout.input_chip, urlRegexChips, false) as Chip tmp.text = chip tmp.setOnClickListener { urlRegexChips.removeView(tmp) } urlRegexChips.addView(tmp) } } preferredCommandSwitch.setOnCheckedChangeListener { compoundButton, b -> val canUseURLRegex = extraCommandsSwitch.isChecked || extraCommandsDataFetchingSwitch.isChecked || preferredCommandSwitch.isChecked urlRegex.isVisible = canUseURLRegex urlRegexChips.isVisible = canUseURLRegex } extraCommandsSwitch.setOnCheckedChangeListener { compoundButton, b -> extraCommandsAudio.isVisible = extraCommandsSwitch.isChecked extraCommandsAudio.isChecked = true extraCommandsVideo.isVisible = extraCommandsSwitch.isChecked extraCommandsVideo.isChecked = true val canUseURLRegex = extraCommandsSwitch.isChecked || extraCommandsDataFetchingSwitch.isChecked || preferredCommandSwitch.isChecked urlRegex.isVisible = canUseURLRegex urlRegexChips.isVisible = canUseURLRegex } extraCommandsAudio.setOnCheckedChangeListener { compoundButton, b -> ok.isEnabled = (extraCommandsAudio.isChecked || extraCommandsVideo.isChecked) && title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty() } extraCommandsVideo.setOnCheckedChangeListener { compoundButton, b -> ok.isEnabled = (extraCommandsAudio.isChecked || extraCommandsVideo.isChecked) && title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty() } extraCommandsDataFetchingSwitch.setOnCheckedChangeListener { compoundButton, b -> val canUseURLRegex = extraCommandsSwitch.isChecked || extraCommandsDataFetchingSwitch.isChecked || preferredCommandSwitch.isChecked urlRegex.isVisible = canUseURLRegex urlRegexChips.isVisible = canUseURLRegex } urlRegex.isEndIconVisible = false urlRegex.editText!!.doOnTextChanged { text, start, before, count -> urlRegex.isEndIconVisible = urlRegex.editText!!.text.isNotBlank() } urlRegex.setEndIconOnClickListener { val text = urlRegex.editText!!.text urlRegex.editText!!.setText("") val tmp = context.layoutInflater.inflate(R.layout.input_chip, urlRegexChips, false) as Chip tmp.text = text tmp.setOnClickListener { urlRegexChips.removeView(tmp) } urlRegexChips.addView(tmp) } commandTemplateViewModel.shortcuts.observe(lifeCycle){ shortcutsChipGroup.removeAllViews() it.forEach {shortcut -> val chip = context.layoutInflater.inflate(R.layout.suggestion_chip, shortcutsChipGroup, false) as Chip chip.text = shortcut.content chip.setOnClickListener { content.editText!!.text.insert(content.editText!!.selectionStart, shortcut.content + " ") } shortcutsChipGroup.addView(chip) } } editShortcuts.setOnClickListener { showShortcutsSheet(context, lifeCycle, commandTemplateViewModel) } ok.setOnClickListener { val urlRegexes = urlRegexChips.children.map { (it as Chip).text.toString() }.toMutableList() if (item == null){ val t = CommandTemplate( 0, title.editText!!.text.toString(), content.editText!!.text.toString(), extraCommandsSwitch.isChecked, extraCommandsAudio.isChecked, extraCommandsVideo.isChecked, extraCommandsDataFetchingSwitch.isChecked, preferredCommandSwitch.isChecked, urlRegexes ) commandTemplateViewModel.insert(t) newTemplate(t) }else{ item.title = title.editText!!.text.toString() item.content = content.editText!!.text.toString() item.useAsExtraCommand = extraCommandsSwitch.isChecked item.useAsExtraCommandAudio = extraCommandsAudio.isChecked item.useAsExtraCommandVideo = extraCommandsVideo.isChecked item.useAsExtraCommandDataFetching = extraCommandsDataFetchingSwitch.isChecked item.preferredCommandTemplate = preferredCommandSwitch.isChecked item.urlRegex = urlRegexes commandTemplateViewModel.update(item) newTemplate(item) } bottomSheet.cancel() } bottomSheet.setOnDismissListener { dismissed() } bottomSheet.show() bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED bottomSheet.window!!.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) } fun showShortcutsSheet(context: Activity, lifeCycle: LifecycleOwner, commandTemplateViewModel: CommandTemplateViewModel){ val bottomSheet = BottomSheetDialog(context) bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.setContentView(R.layout.template_shortcuts) val title : TextInputLayout = bottomSheet.findViewById(R.id.title)!! val shortcutsChipGroup : ChipGroup = bottomSheet.findViewById(R.id.shortcutsChipGroup)!! title.isEndIconVisible = false title.editText!!.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun afterTextChanged(p0: Editable?) { title.isEndIconVisible = p0!!.isNotEmpty() } }) title.setEndIconOnClickListener { commandTemplateViewModel.insertShortcut(TemplateShortcut(0, title.editText!!.text.toString())) title.editText!!.setText("") title.isEndIconVisible = false } commandTemplateViewModel.shortcuts.observe(lifeCycle){ shortcutsChipGroup.removeAllViews() it.forEach {shortcut -> val chip = context.layoutInflater.inflate(R.layout.input_chip, shortcutsChipGroup, false) as Chip chip.text = shortcut.content chip.setOnClickListener{ commandTemplateViewModel.deleteShortcut(shortcut) } shortcutsChipGroup.addView(chip) } } bottomSheet.show() bottomSheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED bottomSheet.window!!.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) } fun copyLinkToClipBoard(context: Context, url: String) { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clip = ClipData.newPlainText(context.getString(R.string.url), url) clipboard.setPrimaryClip(clip) Toast.makeText(context, context.getString(R.string.link_copied_to_clipboard), Toast.LENGTH_SHORT) .show() } fun openLinkIntent(context: Context, url: String) { val i = Intent(Intent.ACTION_VIEW) i.data = Uri.parse(url) context.startActivity(i) } fun showDatePickerOnly(fragmentManager: FragmentManager , onSubmit : (chosenDate: Calendar) -> Unit ){ val date = Calendar.getInstance() val datePicker = MaterialDatePicker.Builder.datePicker() .setCalendarConstraints( CalendarConstraints.Builder() .setValidator(DateValidatorPointForward.now()) .build() ) .setSelection(MaterialDatePicker.todayInUtcMilliseconds()) .build() datePicker.addOnPositiveButtonClickListener{ date.timeInMillis = it onSubmit(date) } datePicker.show(fragmentManager, "datepicker") } fun showDatePicker(fragmentManager: FragmentManager , preferences: SharedPreferences, onSubmit : (chosenDate: Calendar) -> Unit ){ val currentDate = Calendar.getInstance() currentDate.timeInMillis = (currentDate.timeInMillis - (currentDate.timeInMillis % 1800000)) + 1800000 val date = Calendar.getInstance() val datePicker = MaterialDatePicker.Builder.datePicker() .setCalendarConstraints( CalendarConstraints.Builder() .setValidator(DateValidatorPointForward.now()) .build() ) .setSelection(MaterialDatePicker.todayInUtcMilliseconds()) .build() datePicker.addOnPositiveButtonClickListener{ date.timeInMillis = it showTimePicker(fragmentManager, preferences) { chosenTime -> date[Calendar.HOUR_OF_DAY] = chosenTime[Calendar.HOUR_OF_DAY] date[Calendar.MINUTE] = chosenTime[Calendar.MINUTE] onSubmit(date) } } datePicker.show(fragmentManager, "datepicker") } fun showTimePicker(fragmentManager: FragmentManager , preferences: SharedPreferences, onSubmit : (chosenTime: Calendar) -> Unit ){ val currentDate = Calendar.getInstance() currentDate.timeInMillis = (currentDate.timeInMillis - (currentDate.timeInMillis % 1800000)) + 1800000 val date = Calendar.getInstance() val lastTime = preferences.getString("latest_timepicker_date", "${currentDate.get(Calendar.HOUR_OF_DAY)}:${currentDate.get(Calendar.MINUTE)}")!! val hour = lastTime.split(":")[0].toInt() val minute = lastTime.split(":")[1].toInt() val timepicker = MaterialTimePicker.Builder() .setTimeFormat(TimeFormat.CLOCK_24H) .setHour(hour) .setMinute(minute) .build() timepicker.addOnPositiveButtonClickListener{ date[Calendar.HOUR_OF_DAY] = timepicker.hour date[Calendar.MINUTE] = timepicker.minute preferences.edit().putString("latest_timepicker_date", "${timepicker.hour}:${timepicker.minute}").apply() onSubmit(date) } timepicker.show(fragmentManager, "timepicker") } fun showDownloadItemDetailsCard( item: DownloadItem, context: Activity, status: DownloadRepository.Status, ytdlpViewModel: YTDLPViewModel, preferences: SharedPreferences, removeItem : (DownloadItem, BottomSheetDialog) -> Unit, downloadItem: (DownloadItem) -> Unit, longClickDownloadButton: (DownloadItem) -> Unit, scheduleButtonClick: (DownloadItem) -> Unit? ){ val bottomSheet = BottomSheetDialog(context) bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.setContentView(R.layout.history_item_details_bottom_sheet) bottomSheet.findViewById(R.id.bottom_sheet_title)?.apply { text = item.title.ifEmpty { item.url.ifEmpty { item.playlistTitle.ifEmpty { "`${context.getString(R.string.defaultValue)}`" } } } setOnLongClickListener { showFullTextDialog(context, text.toString(), context.getString(R.string.title)) true } } bottomSheet.findViewById(R.id.bottom_sheet_author)?.apply { text = item.author setOnLongClickListener { showFullTextDialog(context, text.toString(), context.getString(R.string.author)) true } } // BUTTON ---------------------------------- val btn = bottomSheet.findViewById(R.id.download_button_type) val typeImageResource: Int = when (item.type) { DownloadViewModel.Type.audio -> { R.drawable.ic_music } DownloadViewModel.Type.video -> { R.drawable.ic_video } else -> { R.drawable.ic_terminal } } btn?.setImageResource(typeImageResource) val time = bottomSheet.findViewById(R.id.time) val thumbnail = bottomSheet.findViewById(R.id.thumbnail) val formatNote = bottomSheet.findViewById(R.id.format_note) val container = bottomSheet.findViewById(R.id.container_chip) val codec = bottomSheet.findViewById(R.id.codec) val fileSize = bottomSheet.findViewById(R.id.file_size) val command = bottomSheet.findViewById(R.id.command) when(status){ DownloadRepository.Status.Scheduled -> { if (item.downloadStartTime <= System.currentTimeMillis() / 1000) time!!.visibility = View.GONE else { val calendar = Calendar.getInstance() calendar.timeInMillis = item.downloadStartTime time!!.text = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), "ddMMMyyyy - HHmm"), Locale.getDefault()).format(calendar.time) time.setOnClickListener { scheduleButtonClick(item) bottomSheet.dismiss() } } } else -> { time!!.visibility = View.GONE } } thumbnail?.isVisible = item.thumb.isNotBlank() thumbnail?.apply { isVisible = item.thumb.isNotBlank() setOnClickListener { openLinkIntent(context, item.thumb) } } if (item.type != DownloadViewModel.Type.command){ if (item.format.format_note == "?" || item.format.format_note == "") formatNote!!.visibility = View.GONE else formatNote!!.text = item.format.format_note }else{ formatNote?.isVisible = false } if (item.format.container != "") { container!!.text = item.format.container.uppercase() container.setChipIconResource(typeImageResource) }else { container!!.visibility = View.GONE } val codecText = if (item.format.encoding != "") { item.format.encoding.uppercase() }else if (item.format.vcodec != "none" && item.format.vcodec != ""){ item.format.vcodec.uppercase() } else { item.format.acodec.uppercase() } if (codecText == "" || codecText == "none"){ codec!!.visibility = View.GONE }else{ codec!!.visibility = View.VISIBLE codec.text = codecText } val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize) if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE else fileSize!!.text = fileSizeReadable command?.setOnClickListener { showGeneratedCommand(context, preferences, ytdlpViewModel.parseYTDLRequestString(item)) } val link = bottomSheet.findViewById