diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/GenericDownloadAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/GenericDownloadAdapter.kt index bba3c438..ed07ff7a 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/GenericDownloadAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/GenericDownloadAdapter.kt @@ -198,7 +198,7 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity: } override fun areContentsTheSame(oldItem: DownloadItem, newItem: DownloadItem): Boolean { - return oldItem.url == newItem.url && oldItem.format.format_id == newItem.format.format_id + return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt index b2d7a258..895a2c25 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt @@ -43,6 +43,9 @@ interface DownloadDao { @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1") fun getDownloadById(id: Long) : DownloadItem + @Query("SELECT id FROM downloads ORDER BY id DESC LIMIT 1") + fun getLastDownloadId(): Long + @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(item: DownloadItem) : Long 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 683773fa..eaef477b 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 @@ -5,7 +5,7 @@ import com.google.gson.annotations.SerializedName data class Format( @SerializedName(value = "format_id", alternate = ["itag"]) var format_id: String = "", - @SerializedName(value = "ext", alternate = ["container"]) + @SerializedName(value = "ext", alternate = ["container", "format"]) var container: String = "", @SerializedName(value = "vcodec") var vcodec: String = "", @@ -15,7 +15,7 @@ data class Format( var encoding: String = "", @SerializedName(value = "filesize", alternate = ["clen", "filesize_approx"]) var filesize: Long = 0, - @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality"]) + @SerializedName(value = "format_note", alternate = ["resolution", "audioQuality", "quality"]) var format_note: String = "", @SerializedName(value = "fps") var fps: String? = "", diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt index 8c8e3b2b..3661993a 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt @@ -7,5 +7,6 @@ data class VideoPreferences ( var sponsorBlockFilters: ArrayList = arrayListOf(), var writeSubs: Boolean = false, var subsLanguages: String = "en.*,.*-orig", + var audioFormatIDs : ArrayList = arrayListOf(), var removeAudio: Boolean = false ) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt index 01c28485..95265329 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt @@ -72,6 +72,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) { downloadDao.cancelQueued() } + fun getLastDownloadId() : Long { + return downloadDao.getLastDownloadId() + } + fun checkIfReDownloadingErroredOrCancelled(item: DownloadItem) : Long { val converters = Converters() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt index 7af2889c..fdb3b098 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.Context import android.content.SharedPreferences import android.net.ConnectivityManager +import android.util.Log import android.widget.Toast import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData @@ -28,9 +29,12 @@ import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.models.VideoPreferences import com.deniscerri.ytdlnis.database.repository.DownloadRepository +import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.work.DownloadWorker import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.concurrent.TimeUnit @@ -39,6 +43,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel private val repository : DownloadRepository private val sharedPreferences: SharedPreferences private val commandTemplateDao: CommandTemplateDao + private val infoUtil : InfoUtil val allDownloads : LiveData> val queuedDownloads : LiveData> val activeDownloads : LiveData> @@ -62,6 +67,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel repository = DownloadRepository(dao) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) commandTemplateDao = DBManager.getInstance(application).commandTemplateDao + infoUtil = InfoUtil(application) allDownloads = repository.allDownloads.asLiveData() queuedDownloads = repository.queuedDownloads.asLiveData() @@ -74,9 +80,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString() formatIDPreference = sharedPreferences.getString("format_id", "").toString() - val videoFormat = getApplication().resources.getStringArray(R.array.video_formats) + val videoFormat = App.instance.resources.getStringArray(R.array.video_formats) var videoContainer = sharedPreferences.getString("video_format", "Default") - if (videoContainer == "Default") videoContainer = application.getString(R.string.defaultValue) + if (videoContainer == "Default") videoContainer = App.instance.getString(R.string.defaultValue) defaultVideoFormats = mutableListOf() videoFormat.forEach { @@ -95,7 +101,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel bestVideoFormat = defaultVideoFormats.last() var audioContainer = sharedPreferences.getString("audio_format", "mp3") - if (audioContainer == "Default") audioContainer = application.getString(R.string.defaultValue) + if (audioContainer == "Default") audioContainer = App.instance.getString(R.string.defaultValue) bestAudioFormat = Format( getApplication().resources.getString(R.string.best_quality), audioContainer!!, @@ -112,7 +118,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } fun updateDownload(item: DownloadItem) = viewModelScope.launch(Dispatchers.IO){ - repository.update(item); + repository.update(item) } fun getItemByID(id: Long) : DownloadItem { @@ -317,20 +323,20 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } fun getGenericAudioFormats() : MutableList{ - val audioFormats = application.resources.getStringArray(R.array.audio_formats) + val audioFormats = App.instance.resources.getStringArray(R.array.audio_formats) val formats = mutableListOf() var containerPreference = sharedPreferences.getString("audio_format", "Default") - if (containerPreference == "Default") containerPreference = application.getString(R.string.defaultValue) + if (containerPreference == "Default") containerPreference = App.instance.getString(R.string.defaultValue) audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } return formats } fun getGenericVideoFormats() : MutableList{ - val videoFormats = application.resources.getStringArray(R.array.video_formats) + val videoFormats = App.instance.resources.getStringArray(R.array.video_formats) val formats = mutableListOf() var containerPreference = sharedPreferences.getString("video_format", "Default") if (containerPreference == "Default") containerPreference = application.getString(R.string.defaultValue) - videoFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } + videoFormats.forEach { formats.add(Format(it, containerPreference!!,application.getString(R.string.defaultValue),"", "",0, it)) } return formats } @@ -385,14 +391,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel return repository.getActiveAndQueuedDownloads() } - suspend fun queueDownloads(items: List) { - val context = getApplication().applicationContext - val activeAndQueuedDownloads = withContext(Dispatchers.IO){ - repository.getActiveAndQueuedDownloads() - } + suspend fun queueDownloads(items: List) = CoroutineScope(Dispatchers.IO).launch { + val context = App.instance + val activeAndQueuedDownloads = repository.getActiveAndQueuedDownloads() val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true) val queuedItems = mutableListOf() + var lastDownloadId = repository.getLastDownloadId() items.forEach { + lastDownloadId++ it.status = DownloadRepository.Status.Queued.toString() if (activeAndQueuedDownloads.firstOrNull{d -> d.id = 0 @@ -401,12 +407,32 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } != null) { Toast.makeText(context, context.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show() }else{ - it.id = withContext(Dispatchers.IO){ - repository.insert(it) - } + it.id = lastDownloadId + val insert = async {repository.insert(it)} + val id = insert.await() + it.id = id queuedItems.add(it) } } + + //if the download was quick downloaded and are incomplete + if (queuedItems.count() == 1){ + val item = queuedItems.first() + if (item.title.isEmpty() || item.author.isEmpty() || item.thumb.isEmpty()){ + runCatching { + val info = infoUtil.getMissingInfo(item.url) + if (item.title.isEmpty()) item.title = info?.title.toString() + if (item.author.isEmpty()) item.author = info?.author.toString() + item.duration = info?.duration.toString() + item.website = info?.website.toString() + if (item.thumb.isEmpty()) item.thumb = info?.thumb.toString() + repository.update(item) + }.onFailure { err -> + Log.e("error", err.message.toString()) + } + } + } + queuedItems.forEach { val currentTime = System.currentTimeMillis() var delay = if (it.downloadStartTime != 0L){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index 6ac827c3..b5be8878 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -376,13 +376,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene }else{ searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0) } - val suggestions = withContext(Dispatchers.IO){ - if (it!!.isEmpty()) { - resultViewModel.getSearchHistory().map { it.query } - }else{ - infoUtil!!.getSearchSuggestions(it.toString()) + val suggestions = if (sharedPreferences!!.getBoolean("search_suggestions", false)){ + withContext(Dispatchers.IO){ + if (it!!.isEmpty()) { + resultViewModel.getSearchHistory().map { it.query } + }else{ + infoUtil!!.getSearchSuggestions(it.toString()) + } } - } + } else emptyList() if (it!!.isEmpty()){ for (i in suggestions.indices) { @@ -445,8 +447,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene } searchSuggestionsLinearLayout!!.visibility = VISIBLE } - - } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index e2f29ca5..8b053731 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -35,6 +35,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.UiUtil +import com.google.android.material.card.MaterialCardView import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputLayout @@ -141,13 +142,13 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr if (formats.isEmpty()) formats = downloadViewModel.getGenericAudioFormats() - val formatCard = view.findViewById(R.id.format_card_constraintLayout) + val formatCard = view.findViewById(R.id.format_card_constraintLayout) val chosenFormat = downloadItem.format - uiUtil.populateFormatCard(formatCard, chosenFormat) + uiUtil.populateFormatCard(formatCard, chosenFormat, null) val listener = object : OnFormatClickListener { override fun onFormatClick(allFormats: List>, item: List) { downloadItem.format = item.first() - uiUtil.populateFormatCard(formatCard, item.first()) + uiUtil.populateFormatCard(formatCard, item.first(), null) lifecycleScope.launch { withContext(Dispatchers.IO){ resultItem.formats.removeAll(formats.toSet()) @@ -156,6 +157,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr } } formats = allFormats.first().toMutableList() + downloadItem.format.container = container.editText?.text.toString() } } formatCard.setOnClickListener{ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt index d99503aa..d23cbe23 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt @@ -7,6 +7,7 @@ import android.content.Intent import android.os.Bundle 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 @@ -28,6 +29,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.receiver.ShareActivity import com.deniscerri.ytdlnis.util.FileUtil +import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.UiUtil import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -47,6 +49,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val private lateinit var behavior: BottomSheetBehavior private lateinit var commandTemplateDao : CommandTemplateDao private lateinit var uiUtil: UiUtil + private lateinit var infoUtil: InfoUtil private lateinit var downloadAudioFragment: DownloadAudioFragment private lateinit var downloadVideoFragment: DownloadVideoFragment @@ -58,6 +61,7 @@ class DownloadBottomSheetDialog(private val resultItem: ResultItem, private val resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao uiUtil = UiUtil(FileUtil()) + infoUtil = InfoUtil(requireContext()) } @SuppressLint("RestrictedApi") diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index 692e0c94..bf69fabb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -36,6 +36,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.UiUtil +import com.google.android.material.card.MaterialCardView import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.textfield.TextInputLayout @@ -133,8 +134,8 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr )) var formats = mutableListOf() - formats.addAll(resultItem.formats.filter { !it.format_note.contains("audio", ignoreCase = true) }) - if (formats.isEmpty()) formats.addAll(downloadItem.allFormats.filter { !it.format_note.contains("audio", ignoreCase = true) }) + formats.addAll(resultItem.formats) + if (formats.isEmpty()) formats.addAll(downloadItem.allFormats) val containers = requireContext().resources.getStringArray(R.array.video_containers) val container = view.findViewById(R.id.downloadContainer) @@ -145,13 +146,14 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr if (formats.isEmpty()) formats = downloadViewModel.getGenericVideoFormats() - val formatCard = view.findViewById(R.id.format_card_constraintLayout) + val formatCard = view.findViewById(R.id.format_card_constraintLayout) val chosenFormat = downloadItem.format - uiUtil.populateFormatCard(formatCard, chosenFormat) + uiUtil.populateFormatCard(formatCard, chosenFormat, null) val listener = object : OnFormatClickListener { override fun onFormatClick(allFormats: List>, item: List) { downloadItem.format = item.first() + downloadItem.videoPreferences.audioFormatIDs.addAll(item.drop(1).map { it.format_id }) lifecycleScope.launch { withContext(Dispatchers.IO){ resultItem.formats.removeAll(formats.toSet()) @@ -160,7 +162,8 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr } } formats = allFormats.first().toMutableList() - uiUtil.populateFormatCard(formatCard, item.first()) + uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1).map { it.format_note }) + downloadItem.format.container = container.editText?.text.toString() } } formatCard.setOnClickListener{ 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 bc4f490c..fa87c858 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 @@ -8,7 +8,7 @@ import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.widget.* -import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.forEach import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager @@ -22,6 +22,8 @@ import com.deniscerri.ytdlnis.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.google.android.material.card.MaterialCardView +import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -36,6 +38,14 @@ class FormatSelectionBottomSheetDialog(private val items: List, p private lateinit var sharedPreferences: SharedPreferences private lateinit var formatCollection: MutableList> private lateinit var chosenFormats: List + private lateinit var selectedVideo : Format + private lateinit var selectedAudios : MutableList + + private lateinit var videoFormatList : LinearLayout + private lateinit var audioFormatList : LinearLayout + private lateinit var okBtn : Button + private lateinit var videoTitle : TextView + private lateinit var audioTitle : TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -44,6 +54,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p infoUtil = InfoUtil(requireActivity().applicationContext) formatCollection = mutableListOf() chosenFormats = listOf() + selectedAudios = mutableListOf() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) } @@ -61,8 +72,15 @@ class FormatSelectionBottomSheetDialog(private val items: List, p behavior.peekHeight = displayMetrics.heightPixels / 2 } - val linearLayout = view.findViewById(R.id.format_list_linear_layout) + val formatListLinearLayout = view.findViewById(R.id.format_list_linear_layout) val shimmers = view.findViewById(R.id.format_list_shimmer) + + videoFormatList = view.findViewById(R.id.video_linear_layout) + audioFormatList = view.findViewById(R.id.audio_linear_layout) + videoTitle = view.findViewById(R.id.video_title) + audioTitle = view.findViewById(R.id.audio_title) + okBtn = view.findViewById(R.id.format_ok) + shimmers.visibility = View.GONE val hasGenericFormats = when(items.first()!!.type){ Type.audio -> formats.first().size == resources.getStringArray(R.array.audio_formats).size @@ -87,16 +105,15 @@ class FormatSelectionBottomSheetDialog(private val items: List, p }else{ chosenFormats = formats.flatten() } - addFormatsToView(linearLayout) + addFormatsToView() }else{ chosenFormats = formats.flatten() if(!hasGenericFormats){ - 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(items.first()?.type == Type.audio){ + chosenFormats = chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) } } } - addFormatsToView(linearLayout) + addFormatsToView() } val refreshBtn = view.findViewById