implemented fetching common formats for a playlist (WIP)

This commit is contained in:
deniscerri 2023-04-18 23:12:30 +02:00
parent a91110d23e
commit 81a47c3407
No known key found for this signature in database
GPG key ID: 95C43D517D830350
18 changed files with 320 additions and 175 deletions

View file

@ -4,6 +4,7 @@ import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources
import android.net.ConnectivityManager
import android.widget.Toast
import androidx.lifecycle.AndroidViewModel
@ -24,7 +25,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit
class DownloadViewModel(application: Application) : AndroidViewModel(application) {
class DownloadViewModel(private val application: Application) : AndroidViewModel(application) {
private val repository : DownloadRepository
private val sharedPreferences: SharedPreferences
private val commandTemplateDao: CommandTemplateDao
@ -213,19 +214,39 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
else -> {
val c = commandTemplateDao.getFirst()
return Format(
c.title,
"",
"",
"",
"",
0,
c.content.replace("\n", " ")
)
return generateCommandFormat(c)
}
}
}
fun generateCommandFormat(c: CommandTemplate) : Format {
return Format(
c.title,
"",
"",
"",
"",
0,
c.content.replace("\n", " ")
)
}
fun getGenericAudioFormats() : MutableList<Format>{
val audioFormats = application.resources.getStringArray(R.array.audio_formats)
val formats = mutableListOf<Format>()
val containerPreference = sharedPreferences.getString("audio_format", "Default")
audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
return formats
}
fun getGenericVideoFormats() : MutableList<Format>{
val videoFormats = application.resources.getStringArray(R.array.video_formats)
val formats = mutableListOf<Format>()
val containerPreference = sharedPreferences.getString("video_format", "Default")
videoFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
return formats
}
fun getLatestCommandTemplateAsFormat() : Format {
val t = commandTemplateDao.getFirst()
return Format(t.title, "", "", "", "", 0, t.content)

View file

@ -45,7 +45,6 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
private lateinit var onDownloadItemUpdateListener: OnDownloadItemUpdateListener
private lateinit var uiUtil: UiUtil
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
@ -71,7 +70,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
behavior.peekHeight = displayMetrics.heightPixels - 700
behavior.peekHeight = displayMetrics.heightPixels - 400
}
tabLayout = view.findViewById(R.id.download_tablayout)
@ -82,7 +81,7 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
overScrollMode = View.OVER_SCROLL_NEVER
}
val fragments = mutableListOf<Fragment>(DownloadAudioFragment(resultItem, downloadItem), DownloadVideoFragment(resultItem, downloadItem))
val fragments = mutableListOf(DownloadAudioFragment(resultItem, downloadItem), DownloadVideoFragment(resultItem, downloadItem))
var commandTemplateNr = 0
lifecycleScope.launch{
withContext(Dispatchers.IO){
@ -211,10 +210,12 @@ class ConfigureDownloadBottomSheetDialog(private val resultItem: ResultItem, pri
// }
private fun cleanUp(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet")!!).commit()
for (i in 0 until viewPager2.adapter?.itemCount!!){
if (parentFragmentManager.findFragmentByTag("f${i}") != null){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit()
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet")!!).commit()
for (i in 0 until viewPager2.adapter?.itemCount!!){
if (parentFragmentManager.findFragmentByTag("f${i}") != null){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit()
}
}
}
}

View file

@ -570,9 +570,11 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val list
private fun cleanUp(){
player.stop()
cache.release()
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("cutVideoSheet")!!).commit()
kotlin.runCatching {
player.stop()
cache.release()
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("cutVideoSheet")!!).commit()
}
}
}

View file

@ -123,42 +123,40 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
var formats = mutableListOf<Format>()
formats.addAll(resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) })
val audioFormats = resources.getStringArray(R.array.audio_formats)
if (formats.isEmpty()) formats.addAll(downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) })
val containers = requireContext().resources.getStringArray(R.array.audio_containers)
val containerPreference = sharedPreferences.getString("audio_format", getString(R.string.defaultValue))
val containerPreference = sharedPreferences.getString("audio_format", "Default")
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.container_textview)
if (formats.isEmpty()) {
audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
}
if (formats.isEmpty()) formats = downloadViewModel.getGenericAudioFormats()
val formatCard = view.findViewById<ConstraintLayout>(R.id.format_card_constraintLayout)
val chosenFormat = downloadItem.format
uiUtil.populateFormatCard(formatCard, chosenFormat)
val listener = object : OnFormatClickListener {
override fun onFormatClick(allFormats: List<Format>, item: Format) {
downloadItem.format = item
uiUtil.populateFormatCard(formatCard, item)
if (containers.contains(item.container)){
downloadItem.format.container = item.container
containerAutoCompleteTextView.setText(item.container, false)
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
downloadItem.format = item.first()
uiUtil.populateFormatCard(formatCard, item.first())
if (containers.contains(item.first().container)){
downloadItem.format.container = item.first().container
containerAutoCompleteTextView.setText(item.first().container, false)
}
lifecycleScope.launch {
withContext(Dispatchers.IO){
resultItem.formats.removeAll(formats.toSet())
resultItem.formats.addAll(allFormats)
resultItem.formats.addAll(allFormats.first())
resultViewModel.update(resultItem)
}
}
formats = allFormats.toMutableList()
formats = allFormats.first().toMutableList()
}
}
formatCard.setOnClickListener{_ ->
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener)
val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats), listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
}
}

View file

@ -15,6 +15,7 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
@ -30,6 +31,8 @@ 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.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.TabLayoutOnPageChangeListener
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@ -64,7 +67,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
behavior = BottomSheetBehavior.from(view.parent as View)
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
behavior.peekHeight = displayMetrics.heightPixels - 700
behavior.peekHeight = displayMetrics.heightPixels - 400
}
@ -158,7 +161,6 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
dismiss()
}
}
val download = view.findViewById<Button>(R.id.bottomsheet_download_button)
download!!.setOnClickListener {
val item: DownloadItem = getDownloadItem();
@ -208,14 +210,16 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val
private fun cleanUp(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadSingleSheet")!!).commit()
for (i in 0 until viewPager2.adapter?.itemCount!!){
if (parentFragmentManager.findFragmentByTag("f${i}") != null){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit()
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadSingleSheet")!!).commit()
for (i in 0 until viewPager2.adapter?.itemCount!!){
if (parentFragmentManager.findFragmentByTag("f${i}") != null){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("f$i")!!).commit()
}
}
if (activity is ShareActivity){
(activity as ShareActivity).finish()
}
}
if (activity is ShareActivity){
(activity as ShareActivity).finish()
}
}
}

View file

@ -8,12 +8,12 @@ import android.content.Intent
import android.os.Bundle
import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.get
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
@ -21,6 +21,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.ConfigureMultipleDownloadsAdapter
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@ -34,7 +35,6 @@ 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.flow.asFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@ -51,6 +51,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var uiUtil: UiUtil
private lateinit var bottomAppBar: BottomAppBar
override fun onCreate(savedInstanceState: Bundle?) {
@ -68,7 +69,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
@SuppressLint("RestrictedApi")
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
override fun setupDialog(dialog: Dialog, style: Int) {
super.setupDialog(dialog, style)
val view = LayoutInflater.from(context).inflate(R.layout.download_multiple_bottom_sheet, null)
@ -116,7 +117,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
dismiss()
}
val bottomAppBar = view.findViewById<BottomAppBar>(R.id.bottomAppBar)
bottomAppBar = view.findViewById(R.id.bottomAppBar)
val preferredDownloadType = bottomAppBar.menu.findItem(R.id.preferred_download_type)
when(items.first().type){
DownloadViewModel.Type.audio -> {
@ -131,7 +132,19 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
}
}
bottomAppBar!!.setOnMenuItemClickListener { m: MenuItem ->
val formatListener = object : OnFormatClickListener {
override fun onFormatClick(allFormats: List<List<Format>>, selectedFormats: List<Format>) {
items.forEachIndexed { index, it ->
it.allFormats.clear()
it.allFormats.addAll(allFormats[index])
it.format = selectedFormats[index]
}
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
}
}
bottomAppBar.setOnMenuItemClickListener { m: MenuItem ->
when (m.itemId) {
R.id.folder -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
@ -166,6 +179,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
bottomAppBar.menu[2].icon?.alpha = 255
bottomSheet.cancel()
}
@ -174,6 +188,7 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
bottomAppBar.menu[2].icon?.alpha = 255
bottomSheet.cancel()
}
@ -185,11 +200,15 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
bottomAppBar.menu[2].icon?.alpha = 255
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
@ -198,47 +217,48 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
}
R.id.format -> {
if (results.first()!!.formats.isEmpty()){
if (! items.all { it.type == items[0].type }){
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
}else{
if (items.first().type == DownloadViewModel.Type.command){
lifecycleScope.launch {
uiUtil.showCommandTemplates(requireActivity(), commandTemplateViewModel) {
val format = downloadViewModel.generateCommandFormat(it)
items.forEach { f ->
f.format = format
}
listAdapter.submitList(items.toList())
listAdapter.notifyDataSetChanged()
}
}
}else{
lifecycleScope.launch {
val flatFormatCollection = items.map { it.allFormats }.flatten()
val commonFormats = withContext(Dispatchers.IO){
flatFormatCollection.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flatFormatCollection.first { f -> f.format_id == it.key } }.map { it.value }
}
val formats = if (commonFormats.isNotEmpty() && items.none{it.allFormats.isEmpty()}) {
items.map { it.allFormats }
}else{
when(items.first().type){
DownloadViewModel.Type.audio -> listOf<List<Format>>(downloadViewModel.getGenericAudioFormats())
else -> listOf<List<Format>>(downloadViewModel.getGenericVideoFormats())
}
}
val bottomSheet = FormatSelectionBottomSheetDialog(items, formats, formatListener)
bottomSheet.show(parentFragmentManager, "formatSheet")
}
}
}
// val formatListCollection = items.map { it.format }
// val commonFormats = getCommonFormats(formatListCollection)
//
// val listener = object : OnFormatClickListener {
// override fun onFormatClick(allFormats: List<Format>, item: Format) {
//
// }
// }
// if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
// val bottomSheet = FormatSelectionBottomSheetDialog(null, commonFormats.toList(), listener)
// bottomSheet.show(parentFragmentManager, "formatSheet")
// }
}
}
true
}
val itemsLiveData = items.asFlow()
}
// private fun getCommonFormats(vararg lists: MutableList<List<Format>>): Set<Format> {
// val commons: MutableList<Format> = mutableListOf()
// commons.addAll(lists[1])
// {
// lists[1].forEach {
// commons.retainAll(it)
// }
// }
//
// for (numbers in list) {
// result = Sets.intersection(result, Sets.newHashSet(numbers))
// }
// return result
//
//
// }
private var pathResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
@ -268,9 +288,11 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
}
private fun cleanup(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadMultipleSheet")!!).commit()
if (parentFragmentManager.fragments.size == 1){
(activity as ShareActivity).finish()
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadMultipleSheet")!!).commit()
if (parentFragmentManager.fragments.size == 1){
(activity as ShareActivity).finish()
}
}
}
@ -298,7 +320,11 @@ class DownloadMultipleBottomSheetDialog(private var results: List<ResultItem?>,
override fun onDownloadItemUpdate(resultItemID: Long, item: DownloadItem) {
val i = items.indexOf(items.find { it.url == item.url })
items[i] = item
Log.e("aa", items.toList().toString())
if (! items.all { it.type == items[0].type }){
bottomAppBar.menu[2].icon?.alpha = 30
}else{
bottomAppBar.menu[2].icon?.alpha = 255
}
listAdapter.submitList(items.toList())
}

View file

@ -129,44 +129,42 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
var formats = mutableListOf<Format>()
formats.addAll(resultItem.formats.filter { !it.format_note.contains("audio", ignoreCase = true) })
val videoFormats = resources.getStringArray(R.array.video_formats)
if (formats.isEmpty()) formats.addAll(downloadItem.allFormats.filter { !it.format_note.contains("audio", ignoreCase = true) })
val containers = requireContext().resources.getStringArray(R.array.video_containers)
val container = view.findViewById<TextInputLayout>(R.id.downloadContainer)
val containerAutoCompleteTextView =
view.findViewById<AutoCompleteTextView>(R.id.container_textview)
val containerPreference = sharedPreferences.getString("video_format", getString(R.string.defaultValue))
val containerPreference = sharedPreferences.getString("video_format", "Default")
if (formats.isEmpty()) {
videoFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) }
}
if (formats.isEmpty()) formats = downloadViewModel.getGenericVideoFormats()
val formatCard = view.findViewById<ConstraintLayout>(R.id.format_card_constraintLayout)
val chosenFormat = downloadItem.format
uiUtil.populateFormatCard(formatCard, chosenFormat)
val listener = object : OnFormatClickListener {
override fun onFormatClick(allFormats: List<Format>, item: Format) {
downloadItem.format = item
override fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>) {
downloadItem.format = item.first()
if (containers.contains(item.container)){
downloadItem.format.container = item.container
containerAutoCompleteTextView.setText(item.container, false)
if (containers.contains(item.first().container)){
downloadItem.format.container = item.first().container
containerAutoCompleteTextView.setText(item.first().container, false)
}
lifecycleScope.launch {
withContext(Dispatchers.IO){
resultItem.formats.removeAll(formats.toSet())
resultItem.formats.addAll(allFormats)
resultItem.formats.addAll(allFormats.first())
resultViewModel.update(resultItem)
}
}
formats = allFormats.toMutableList()
uiUtil.populateFormatCard(formatCard, item)
formats = allFormats.first().toMutableList()
uiUtil.populateFormatCard(formatCard, item.first())
}
}
formatCard.setOnClickListener{
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
val bottomSheet = FormatSelectionBottomSheetDialog(downloadItem, formats, listener)
val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats), listener)
bottomSheet.show(parentFragmentManager, "formatSheet")
}
}

View file

@ -5,6 +5,7 @@ import android.app.Activity
import android.app.Dialog
import android.content.*
import android.os.Bundle
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.LayoutInflater
@ -27,20 +28,25 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import kotlin.collections.ArrayList
class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private var formats: List<Format>, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() {
class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, private var formats: List<List<Format>>, private val listener: OnFormatClickListener) : BottomSheetDialogFragment() {
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var fileUtil: FileUtil
private lateinit var infoUtil: InfoUtil
private lateinit var uiUtil: UiUtil
private lateinit var sharedPreferences: SharedPreferences
private lateinit var formatCollection: MutableList<List<Format>>
private lateinit var chosenFormats: List<Format>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
fileUtil = FileUtil()
uiUtil = UiUtil(fileUtil)
infoUtil = InfoUtil(requireActivity().applicationContext)
formatCollection = mutableListOf()
chosenFormats = listOf()
sharedPreferences = requireContext().getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
}
@ -61,10 +67,35 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private
val linearLayout = view.findViewById<LinearLayout>(R.id.format_list_linear_layout)
val shimmers = view.findViewById<ShimmerFrameLayout>(R.id.format_list_shimmer)
shimmers.visibility = View.GONE
addFormatsToView(linearLayout)
if (items.size > 1){
val hasGenericFormats = when(items.first()!!.type){
Type.audio -> formats.flatten().size == resources.getStringArray(R.array.audio_formats).size
else -> formats.flatten().size == resources.getStringArray(R.array.video_formats).size
}
if (!hasGenericFormats){
formatCollection.addAll(formats)
val flattenFormats = formats.flatten()
val commonFormats = flattenFormats.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flattenFormats.first { f -> f.format_id == it.key } }.map { it.value }
commonFormats.forEach {
it.filesize =
flattenFormats.filter { f -> f.format_id == it.format_id }
.sumOf { itt -> itt.filesize }
}
chosenFormats = commonFormats
}else{
chosenFormats = formats.flatten()
}
addFormatsToView(linearLayout)
}else{
chosenFormats = formats.flatten()
addFormatsToView(linearLayout)
}
val refreshBtn = view.findViewById<Button>(R.id.format_refresh)
if (formats.none { it.filesize == 0L } || item == null) refreshBtn.visibility = View.GONE
if (formats.flatten().none { it.filesize == 0L } || items.isEmpty()) refreshBtn.visibility = View.GONE
refreshBtn.setOnClickListener {
lifecycleScope.launch {
try {
@ -73,15 +104,45 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private
shimmers.visibility = View.VISIBLE
shimmers.startShimmer()
val res = withContext(Dispatchers.IO){
infoUtil.getFormats(item!!.url)
//simple download
if (items.size == 1){
val res = withContext(Dispatchers.IO){
infoUtil.getFormats(items.first()!!.url)
}
chosenFormats = res.formats.filter { it.filesize != 0L }
chosenFormats = when(items.first()?.type){
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> chosenFormats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
if (chosenFormats.isEmpty()) throw Exception()
//playlist format filtering
}else{
var progress = "0/${items.size}"
refreshBtn.text = progress
withContext(Dispatchers.IO){
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
lifecycleScope.launch(Dispatchers.Main){
progress = "${formatCollection.size}/${items.size}"
refreshBtn.text = progress
}
formatCollection.add(it)
}
}
val flatFormatCollection = formatCollection.flatten()
val commonFormats = flatFormatCollection.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flatFormatCollection.first { f -> f.format_id == it.key } }.map { it.value }
chosenFormats = commonFormats.filter { it.filesize != 0L }.mapTo(mutableListOf()) {it.copy()}
chosenFormats = when(items.first()?.type){
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> chosenFormats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
if (chosenFormats.isEmpty()) throw Exception()
chosenFormats.forEach {
it.filesize =
flatFormatCollection.filter { f -> f.format_id == it.format_id }
.sumOf { itt -> itt.filesize }
}
}
formats = res.formats.filter { it.filesize != 0L }
formats = when(item?.type){
Type.audio -> formats.filter { it.format_note.contains("audio", ignoreCase = true) }
else -> formats.filter { !it.format_note.contains("audio", ignoreCase = true) }
}
if (formats.isEmpty()) throw Exception()
addFormatsToView(linearLayout)
refreshBtn.visibility = View.GONE
@ -90,6 +151,7 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private
shimmers.stopShimmer()
}catch (e: Exception){
refreshBtn.isEnabled = true
refreshBtn.text = getString(R.string.update_formats)
linearLayout.visibility = View.VISIBLE
shimmers.visibility = View.GONE
shimmers.stopShimmer()
@ -99,20 +161,27 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private
}
}
}
if (sharedPreferences.getBoolean("update_formats", false) && refreshBtn.isVisible){
if (sharedPreferences.getBoolean("update_formats", false) && refreshBtn.isVisible && items.size == 1){
refreshBtn.performClick()
}
}
private fun addFormatsToView(linearLayout: LinearLayout){
linearLayout.removeAllViews()
Log.e("aa", formats.toString())
for (i in formats.lastIndex downTo 0){
val format = formats[i]
Log.e("aa", chosenFormats.toString())
for (i in chosenFormats.lastIndex downTo 0){
val format = chosenFormats[i]
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
uiUtil.populateFormatCard(formatItem as ConstraintLayout, format)
formatItem.setOnClickListener{_ ->
listener.onFormatClick(formats, format)
if (items.size == 1){
listener.onFormatClick(List(items.size){chosenFormats}, listOf(format))
}else{
val selectedFormats = mutableListOf<Format>()
formatCollection.forEach {
selectedFormats.add(it.first{ f -> f.format_id == format.format_id})
}
listener.onFormatClick(formatCollection, selectedFormats)
}
dismiss()
}
formatItem.setOnLongClickListener {
@ -141,10 +210,12 @@ class FormatSelectionBottomSheetDialog(private val item: DownloadItem?, private
private fun cleanUp(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
}
}
}
interface OnFormatClickListener{
fun onFormatClick(allFormats: List<Format>, item: Format)
fun onFormatClick(allFormats: List<List<Format>>, item: List<Format>)
}

View file

@ -179,9 +179,11 @@ class SelectPlaylistItemsBottomSheetDialog(private val items: List<ResultItem?>,
}
private fun cleanup(){
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadPlaylistSheet")!!).commit()
if (parentFragmentManager.fragments.size == 1){
(activity as ShareActivity).finish()
kotlin.runCatching {
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("downloadPlaylistSheet")!!).commit()
if (parentFragmentManager.fragments.size == 1){
(activity as ShareActivity).finish()
}
}
}

View file

@ -184,6 +184,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
openFile.visibility = View.GONE
bottomSheet.show()
bottomSheet.behavior.peekHeight = 512
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT

View file

@ -183,6 +183,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
openFile.visibility = View.GONE
bottomSheet.show()
bottomSheet.behavior.peekHeight = 512
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT

View file

@ -483,6 +483,7 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
else redownload.visibility = GONE
bottomSheet!!.show()
bottomSheet!!.behavior.peekHeight = 512
bottomSheet!!.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT

View file

@ -202,6 +202,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
redownload!!.visibility = View.GONE
bottomSheet.show()
bottomSheet.behavior.peekHeight = 512
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT

View file

@ -25,7 +25,9 @@ import androidx.lifecycle.lifecycleScope
import androidx.work.*
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.work.TerminalDownloadWorker
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomappbar.BottomAppBar
@ -55,6 +57,7 @@ class TerminalActivity : AppCompatActivity() {
private lateinit var downloadFile : File
private lateinit var observer: FileObserver
private lateinit var imm : InputMethodManager
private lateinit var uiUtil: UiUtil
var context: Context? = null
public override fun onCreate(savedInstanceState: Bundle?) {
@ -65,6 +68,7 @@ class TerminalActivity : AppCompatActivity() {
downloadFile = File(cacheDir.absolutePath + "/$downloadID.txt")
if (! downloadFile.exists()) downloadFile.createNewFile()
imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
uiUtil = UiUtil(FileUtil())
context = baseContext
scrollView = findViewById(R.id.custom_command_scrollview)
@ -89,7 +93,17 @@ class TerminalActivity : AppCompatActivity() {
}
bottomAppBar.setOnMenuItemClickListener {
when(it.itemId){
R.id.command_templates -> showCommandTemplates()
R.id.command_templates -> {
lifecycleScope.launch {
uiUtil.showCommandTemplates(this@TerminalActivity, commandTemplateViewModel){ template ->
input!!.text.insert(input!!.selectionStart, template.content + " ")
input!!.postDelayed({
input!!.requestFocus()
imm.showSoftInput(input!!, 0)
}, 200)
}
}
}
R.id.shortcuts -> showShortcuts()
else -> {}
}
@ -210,41 +224,6 @@ class TerminalActivity : AppCompatActivity() {
fab!!.setIconResource(R.drawable.ic_cancel)
}
private fun showCommandTemplates(){
lifecycleScope.launch {
val bottomSheet = BottomSheetDialog(this@TerminalActivity)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.command_template_list)
val linearLayout = bottomSheet.findViewById<LinearLayout>(R.id.command_list_linear_layout)
val list = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll()
}
linearLayout!!.removeAllViews()
list.forEach {template ->
val item = layoutInflater.inflate(R.layout.command_template_item, linearLayout, false) as ConstraintLayout
item.findViewById<TextView>(R.id.title).text = template.title
item.findViewById<TextView>(R.id.content).text = template.content
item.setOnClickListener {
input!!.text.insert(input!!.selectionStart, template.content + " ")
bottomSheet.cancel()
input!!.postDelayed({
input!!.requestFocus()
imm.showSoftInput(input!!, 0)
}, 200)
}
linearLayout.addView(item)
}
bottomSheet.show()
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}
private fun showShortcuts() {
lifecycleScope.launch {
val bottomSheet = BottomSheetDialog(this@TerminalActivity)

View file

@ -504,7 +504,7 @@ class InfoUtil(private val context: Context) {
}
}
fun getFormatsTest(urls: List<String>, progress: (progress: List<Format>) -> Unit){
fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){
init()
val urlsFile = File(context.cacheDir, "urls.txt")
urlsFile.delete()
@ -524,26 +524,30 @@ class InfoUtil(private val context: Context) {
request.addOption("--cookies", cookiesFile.absolutePath)
}
val youtubeDLResponse = YoutubeDL.getInstance().execute(request){ progress, _, line ->
val formats = mutableListOf<Format>()
val listOfStrings = JSONArray(line)
for (f in 0 until listOfStrings.length()){
val format = listOfStrings.get(f) as JSONObject
try{
if (format.getString("filesize") == "None") continue
}catch (e: Exception) { continue }
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.filesize > 0){
if ( !formatProper.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
YoutubeDL.getInstance().execute(request){ progress, _, line ->
try{
val formats = mutableListOf<Format>()
val listOfStrings = JSONArray(line)
for (f in 0 until listOfStrings.length()){
val format = listOfStrings.get(f) as JSONObject
try{
if (format.getString("filesize") == "None") continue
}catch (e: Exception) { continue }
val formatProper = Gson().fromJson(format.toString(), Format::class.java)
if (formatProper.filesize > 0){
if ( !formatProper.format_note.contains("audio only", true)) {
formatProper.format_note = format.getString("format_note")
}else{
formatProper.format_note = "${format.getString("format_note")} audio"
}
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
formatProper.container = format.getString("ext")
formats.add(formatProper)
}
progress(formats)
}catch (e: Exception){
progress(emptyList())
}
progress(formats)
}
} catch (e: Exception) {
Looper.prepare().run {

View file

@ -13,6 +13,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
@ -21,6 +22,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.FileProvider
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CommandTemplate
import com.deniscerri.ytdlnis.database.models.Format
@ -35,6 +37,9 @@ import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.textfield.TextInputLayout
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.*
@ -282,4 +287,33 @@ class UiUtil(private val fileUtil: FileUtil) {
}
datePicker.show(fragmentManager, "datepicker")
}
suspend fun showCommandTemplates(activity: Activity, commandTemplateViewModel: CommandTemplateViewModel, itemSelected: (itemSelected: CommandTemplate) -> Unit) {
val bottomSheet = BottomSheetDialog(activity)
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
bottomSheet.setContentView(R.layout.command_template_list)
val linearLayout = bottomSheet.findViewById<LinearLayout>(R.id.command_list_linear_layout)
val list = withContext(Dispatchers.IO){
commandTemplateViewModel.getAll()
}
linearLayout!!.removeAllViews()
list.forEach {template ->
val item = activity.layoutInflater.inflate(R.layout.command_template_item, linearLayout, false) as ConstraintLayout
item.findViewById<TextView>(R.id.title).text = template.title
item.findViewById<TextView>(R.id.content).text = template.content
item.setOnClickListener {
itemSelected(template)
bottomSheet.cancel()
}
linearLayout.addView(item)
}
bottomSheet.show()
bottomSheet.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
}

View file

@ -145,7 +145,7 @@ class DownloadWorker(
val ext = downloadItem.format.container
if (audioQualityId.isBlank() || ext == "Default") request.addOption("-x")
request.addOption("-f", audioQualityId)
else request.addOption("-f", audioQualityId)
if(ext != "webm"){
val codec = when(downloadItem.format.container){

View file

@ -216,4 +216,5 @@
<string name="video_recommendations_summary">Get Recommended Youtube Videos on the Home Screen from Invidious</string>
<string name="preferred_search_engine">Preferred Search Engine</string>
<string name="preferred_search_engine_summary">The search engine to use for in-app searches</string>
<string name="format_filtering_hint">All items must be of the same type to use this option</string>
</resources>