From 4b433f66b91a3b0240a5754f2e6d6e0f4b106017 Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Fri, 21 Apr 2023 00:10:30 +0200 Subject: [PATCH] added format details sheet on long press --- .../ConfigureMultipleDownloadsAdapter.kt | 6 +- .../ytdlnis/database/models/Format.kt | 7 +- .../downloadcard/CutVideoBottomSheetDialog.kt | 58 ++--- .../DownloadMultipleBottomSheetDialog.kt | 59 ++++- .../FormatSelectionBottomSheetDialog.kt | 110 ++++++++- .../res/drawable/baseline_music_off_24.xml | 5 + app/src/main/res/layout/cut_video_sheet.xml | 28 +++ .../main/res/layout/format_details_sheet.xml | 217 ++++++++++++++++++ app/src/main/res/values/strings.xml | 4 + 9 files changed, 450 insertions(+), 44 deletions(-) create mode 100644 app/src/main/res/drawable/baseline_music_off_24.xml create mode 100644 app/src/main/res/layout/format_details_sheet.xml diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt index 80d62d74..a94433fb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt @@ -107,6 +107,10 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener val btn = card.findViewById(R.id.action_button) if (btn.hasOnClickListeners()) btn.setOnClickListener(null) + btn.setOnClickListener { + onItemClickListener.onButtonClick(item.url) + } + when(item.type) { DownloadViewModel.Type.audio -> { btn.setIconResource(R.drawable.ic_music) @@ -125,7 +129,7 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener } interface OnItemClickListener { - fun onButtonClick(videoURL: String, type: String?) + fun onButtonClick(itemURL: String) fun onCardClick(itemURL: String) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt index 8c34348e..683773fa 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt @@ -16,5 +16,10 @@ data class Format( @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"]) var filesize: Long = 0, @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"]) - var format_note: String = "" + var format_note: String = "", + @SerializedName(value = "fps") + var fps: String? = "", + @SerializedName(value = "asr", alternate = ["audioSampleRate"]) + var asr: String? = "", + ) \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt index 49f3e5de..98a716be 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt @@ -27,23 +27,17 @@ import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_ import com.google.android.exoplayer2.ExoPlayer import com.google.android.exoplayer2.MediaItem.fromUri import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.database.DatabaseProvider -import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.mediacodec.MediaCodecInfo import com.google.android.exoplayer2.mediacodec.MediaCodecSelector +import com.google.android.exoplayer2.source.DefaultMediaSourceFactory import com.google.android.exoplayer2.source.MediaSource import com.google.android.exoplayer2.source.MergingMediaSource -import com.google.android.exoplayer2.source.ProgressiveMediaSource import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.ui.PlayerView -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory -import com.google.android.exoplayer2.upstream.cache.CacheDataSource -import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor -import com.google.android.exoplayer2.upstream.cache.SimpleCache import com.google.android.exoplayer2.util.MimeTypes -import com.google.android.exoplayer2.util.Util import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment +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 @@ -59,7 +53,6 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.File import java.lang.reflect.Type import java.util.* import kotlin.properties.Delegates @@ -75,6 +68,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list private lateinit var cutSection : ConstraintLayout private lateinit var durationText: TextView private lateinit var progress : ProgressBar + private lateinit var pauseBtn : MaterialButton + private lateinit var muteBtn : MaterialButton private lateinit var rangeSlider : RangeSlider private lateinit var fromTextInput : TextInputLayout private lateinit var toTextInput : TextInputLayout @@ -92,8 +87,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list private lateinit var chapters: List private lateinit var selectedCuts: MutableList - private lateinit var cache: SimpleCache - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -138,26 +131,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list val trackSelector = DefaultTrackSelector() val loadControl = DefaultLoadControl() -// Specify cache folder, my cache folder named media which is inside getCacheDir. - val cacheFolder = File(requireContext().cacheDir, "media") - -// Specify cache size and removing policies - val cacheEvictor = LeastRecentlyUsedCacheEvictor(1 * 1024 * 1024) // My cache size will be 1MB and it will automatically remove least recently used files if the size is reached out. - -// Build cache - val databaseProvider: DatabaseProvider = StandaloneDatabaseProvider(requireContext()) - cache = SimpleCache(cacheFolder, cacheEvictor, databaseProvider) - -// Build data source factory with cache enabled, if data is available in cache it will return immediately, otherwise it will open a new connection to get the data. - val defaultDataSourceFactory = DefaultDataSourceFactory( - requireContext(), - Util.getUserAgent(requireContext(), requireContext().getString(R.string.app_name)) - ) - - val cacheDataSourceFactory = CacheDataSource.Factory() - .setCache(cache) - .setUpstreamDataSourceFactory(defaultDataSourceFactory) - player = ExoPlayer.Builder(requireContext(), renderersFactory) .setTrackSelector(trackSelector) .setLoadControl(loadControl) @@ -173,6 +146,8 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list durationText = view.findViewById(R.id.durationText) durationText.text = "" progress = view.findViewById(R.id.progress) + pauseBtn = view.findViewById(R.id.pause) + muteBtn = view.findViewById(R.id.mute) rangeSlider = view.findViewById(R.id.rangeSlider) fromTextInput = view.findViewById(R.id.from_textinput) toTextInput = view.findViewById(R.id.to_textinput) @@ -218,10 +193,10 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list if (data.isEmpty()) throw Exception("No Streaming URL found!") if (data.size == 2){ val audioSource : MediaSource = - ProgressiveMediaSource.Factory(cacheDataSourceFactory) + DefaultMediaSourceFactory(requireContext()) .createMediaSource(fromUri(Uri.parse(data[0]))) val videoSource: MediaSource = - ProgressiveMediaSource.Factory(cacheDataSourceFactory) + DefaultMediaSourceFactory(requireContext()) .createMediaSource(fromUri(Uri.parse(data[1]))) (player as ExoPlayer).setMediaSource(MergingMediaSource(videoSource, audioSource)) }else{ @@ -256,9 +231,23 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list } videoView.setOnClickListener { - if (player.isPlaying) player.pause() + if (player.isPlaying){ + player.pause() + pauseBtn.visibility = View.VISIBLE + } else { player.play() + pauseBtn.visibility = View.GONE + } + } + + muteBtn.setOnClickListener { + if (player.volume > 0F) { + muteBtn.setIconResource(R.drawable.baseline_music_off_24) + player.volume = 0F + }else { + muteBtn.setIconResource(R.drawable.ic_music) + player.volume = 1F } } @@ -572,7 +561,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list private fun cleanUp(){ kotlin.runCatching { player.stop() - cache.release() parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("cutVideoSheet")!!).commit() } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index 9a321e22..d56cc89e 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -308,8 +308,63 @@ class DownloadMultipleBottomSheetDialog(private var results: List, } } - override fun onButtonClick(videoURL: String, type: String?) { - TODO("Not yet implemented") + override fun onButtonClick(itemURL: String) { + lifecycleScope.launch { + val item = items.find { it.url == itemURL } ?: return@launch + val position = items.indexOf(item) + + val bottomSheet = BottomSheetDialog(requireContext()) + bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) + bottomSheet.setContentView(R.layout.download_type_sheet) + + // BUTTON ---------------------------------- + val audio = bottomSheet.findViewById(R.id.audio) + val video = bottomSheet.findViewById(R.id.video) + val command = bottomSheet.findViewById(R.id.command) + + withContext(Dispatchers.IO){ + val nr = commandTemplateViewModel.getTotalNumber() + if(nr == 0){ + command!!.visibility = View.GONE + }else{ + command!!.visibility = View.VISIBLE + } + } + + audio!!.setOnClickListener { + items[position] = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.audio).first() + listAdapter.submitList(items.toList()) + listAdapter.notifyItemChanged(position) + bottomSheet.cancel() + } + + video!!.setOnClickListener { + items[position] = downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.video).first() + listAdapter.submitList(items.toList()) + listAdapter.notifyItemChanged(position) + bottomSheet.cancel() + } + + command!!.setOnClickListener { + lifecycleScope.launch { + items[position] = withContext(Dispatchers.IO){ + downloadViewModel.switchDownloadType(listOf(item), DownloadViewModel.Type.command).first() + } + listAdapter.submitList(items.toList()) + listAdapter.notifyItemChanged(position) + bottomSheet.cancel() + } + } + + bottomSheet.show() + val displayMetrics = DisplayMetrics() + requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) + bottomSheet.behavior.peekHeight = displayMetrics.heightPixels + bottomSheet.window!!.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } } override fun onCardClick(itemURL: String) { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt index 5f04c772..e45a1748 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt @@ -6,27 +6,36 @@ import android.app.Dialog import android.content.* import android.os.Bundle import android.os.Looper +import android.text.format.DateFormat import android.util.DisplayMetrics import android.util.Log import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup +import android.view.Window import android.widget.* import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.Format +import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.google.android.material.button.MaterialButton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import java.text.SimpleDateFormat import java.util.* import kotlin.collections.ArrayList @@ -193,17 +202,108 @@ class FormatSelectionBottomSheetDialog(private val items: List, p dismiss() } formatItem.setOnLongClickListener { - val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText("format_id", format.format_id) - clipboard.setPrimaryClip(clip) - Toast.makeText(context, requireContext().getString(R.string.formatid_copied_to_clipboard), Toast.LENGTH_SHORT) - .show() + val bottomSheet = BottomSheetDialog(requireContext()) + bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) + bottomSheet.setContentView(R.layout.format_details_sheet) + + val formatIdParent = bottomSheet.findViewById(R.id.format_id_parent) + val containerParent = bottomSheet.findViewById(R.id.container_parent) + val codecParent = bottomSheet.findViewById(R.id.codec_parent) + val filesizeParent = bottomSheet.findViewById(R.id.filesize_parent) + val formatnoteParent = bottomSheet.findViewById(R.id.format_note_parent) + val fpsParent = bottomSheet.findViewById(R.id.fps_parent) + val asrParent = bottomSheet.findViewById(R.id.asr_parent) + + if (format.format_id.isBlank()) formatIdParent?.visibility = View.GONE + else { + formatIdParent?.findViewById(R.id.format_id_value)?.text = format.format_id + formatIdParent?.setOnClickListener { + copyToClipboard(format.format_note) + } + } + + + if (format.container.isBlank()) containerParent?.visibility = View.GONE + else { + containerParent?.findViewById(R.id.container_value)?.text = format.container + containerParent?.setOnClickListener { + copyToClipboard(format.container) + } + } + + val codecField = + if (format.encoding != "") { + format.encoding.uppercase() + }else if (format.vcodec != "none" && format.vcodec != ""){ + format.vcodec.uppercase() + } else { + format.acodec.uppercase() + } + + if (codecField.isBlank()) codecParent?.visibility = View.GONE + else { + codecParent?.findViewById(R.id.codec_value)?.text = codecField + codecParent?.setOnClickListener { + copyToClipboard(codecField) + } + } + + if (format.filesize != 0L) filesizeParent?.visibility = View.GONE + else { + filesizeParent?.findViewById(R.id.filesize_value)?.text = fileUtil.convertFileSize(format.filesize) + filesizeParent?.setOnClickListener { + copyToClipboard(fileUtil.convertFileSize(format.filesize)) + } + } + + if (format.format_note.isBlank()) formatnoteParent?.visibility = View.GONE + else { + formatnoteParent?.findViewById(R.id.format_note_value)?.text = format.format_note + formatnoteParent?.setOnClickListener { + copyToClipboard(format.format_note) + } + } + + if (format.fps.isNullOrBlank()) fpsParent?.visibility = View.GONE + else { + fpsParent?.findViewById(R.id.fps_value)?.text = format.fps + fpsParent?.setOnClickListener { + copyToClipboard(format.fps!!) + } + } + + if (format.asr.isNullOrBlank()) asrParent?.visibility = View.GONE + else { + asrParent?.findViewById(R.id.asr_value)?.text = format.asr + asrParent?.setOnClickListener { + copyToClipboard(format.asr!!) + } + } + + + + bottomSheet.show() + val displayMetrics = DisplayMetrics() + requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics) + bottomSheet.behavior.peekHeight = displayMetrics.heightPixels + bottomSheet.window!!.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + true } linearLayout.addView(formatItem) } } + private fun copyToClipboard(text: String){ + val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText(text, text) + clipboard.setPrimaryClip(clip) + Toast.makeText(context, requireContext().getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT) + .show() + } override fun onCancel(dialog: DialogInterface) { diff --git a/app/src/main/res/drawable/baseline_music_off_24.xml b/app/src/main/res/drawable/baseline_music_off_24.xml new file mode 100644 index 00000000..8f9f7609 --- /dev/null +++ b/app/src/main/res/drawable/baseline_music_off_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/cut_video_sheet.xml b/app/src/main/res/layout/cut_video_sheet.xml index e9eba763..278c2798 100644 --- a/app/src/main/res/layout/cut_video_sheet.xml +++ b/app/src/main/res/layout/cut_video_sheet.xml @@ -37,6 +37,34 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index caca95a6..219a71ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -219,4 +219,8 @@ All items must be of the same type to use this option Preferred Home Screen Undo + Format ID + Codec + Audio Sample Rate + Copied To Clipboard \ No newline at end of file