1.7.9
This commit is contained in:
parent
6ba8d65afd
commit
d57ab39e82
26 changed files with 378 additions and 199 deletions
|
|
@ -10,7 +10,7 @@ plugins {
|
|||
def properties = new Properties()
|
||||
def versionMajor = 1
|
||||
def versionMinor = 7
|
||||
def versionPatch = 8
|
||||
def versionPatch = 9
|
||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||
def isBeta = false
|
||||
def versionExt = isBeta ? ".${versionBuild}-beta" : ""
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ interface DownloadDao {
|
|||
""")
|
||||
fun getProcessingDownloadTypes() : List<String>
|
||||
|
||||
@Query("""
|
||||
SELECT DISTINCT container from downloads where status = 'Processing'
|
||||
""")
|
||||
fun getProcessingDownloadContainers() : List<String>
|
||||
|
||||
|
||||
@Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)")
|
||||
suspend fun updateItemsToProcessing(ids: List<Long>)
|
||||
|
|
@ -60,6 +65,9 @@ interface DownloadDao {
|
|||
@Query("UPDATE downloads set downloadPath=:path WHERE status ='Processing'")
|
||||
suspend fun updateProcessingDownloadPath(path: String)
|
||||
|
||||
@Query("UPDATE downloads set container=:cont WHERE status ='Processing'")
|
||||
suspend fun updateProcessingContainer(cont: String)
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||
fun getActiveDownloadsList() : List<DownloadItem>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import androidx.room.Query
|
|||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import com.deniscerri.ytdl.database.models.LogItem
|
||||
import com.deniscerri.ytdl.util.Extensions.appendLineToLog
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
|
|
@ -41,10 +40,15 @@ interface LogDao {
|
|||
suspend fun updateLogContent(l: String, id: Long)
|
||||
|
||||
@Transaction
|
||||
suspend fun updateLog(line: String, id: Long) {
|
||||
suspend fun updateLog(line: String, id: Long, resetLog: Boolean = false) {
|
||||
val l = getByID(id) ?: return
|
||||
val log = l.content ?: ""
|
||||
updateLogContent(log.appendLineToLog(line), id)
|
||||
var log = l.content ?: ""
|
||||
if (resetLog) {
|
||||
log = line.lines().joinToString("\n")
|
||||
}else{
|
||||
log += "\n${line}"
|
||||
}
|
||||
updateLogContent(log, id)
|
||||
}
|
||||
|
||||
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.room.Insert
|
|||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import com.deniscerri.ytdl.database.models.TerminalItem
|
||||
import com.deniscerri.ytdl.util.Extensions.appendLineToLog
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
|
|
@ -29,10 +28,15 @@ interface TerminalDao {
|
|||
@Query("SELECT * FROM terminalDownloads WHERE id=:id LIMIT 1")
|
||||
fun getTerminalById(id: Long) : TerminalItem?
|
||||
@Transaction
|
||||
suspend fun updateLog(line: String, id: Long){
|
||||
suspend fun updateLog(line: String, id: Long, resetLog : Boolean = false){
|
||||
val t = getTerminalById(id) ?: return
|
||||
val log = t.log ?: ""
|
||||
updateTerminalLog(log.appendLineToLog(line), id)
|
||||
var log = t.log ?: ""
|
||||
if (resetLog) {
|
||||
log = line.lines().joinToString("\n")
|
||||
}else{
|
||||
log += "\n${line}"
|
||||
}
|
||||
updateTerminalLog(log, id)
|
||||
}
|
||||
|
||||
@Insert
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ data class DownloadItemConfigureMultiple(
|
|||
var title: String,
|
||||
var thumb: String,
|
||||
var duration: String,
|
||||
var container: String,
|
||||
var format: Format,
|
||||
var allFormats: MutableList<Format>,
|
||||
var audioPreferences: AudioPreferences,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.deniscerri.ytdl.database.repository
|
|||
|
||||
import com.deniscerri.ytdl.database.dao.LogDao
|
||||
import com.deniscerri.ytdl.database.models.LogItem
|
||||
import com.deniscerri.ytdl.util.Extensions.appendLineToLog
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class LogRepository(private val logDao: LogDao) {
|
||||
|
|
@ -38,9 +37,9 @@ class LogRepository(private val logDao: LogDao) {
|
|||
return logDao.getByID(id)
|
||||
}
|
||||
|
||||
suspend fun update(line: String, id: Long){
|
||||
suspend fun update(line: String, id: Long, resetLog: Boolean = false){
|
||||
kotlin.runCatching {
|
||||
logDao.updateLog(line, id)
|
||||
logDao.updateLog(line, id, resetLog)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1015,6 +1015,14 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateProcessingContainer(cont: String) {
|
||||
var container = ""
|
||||
if (cont != resources.getString(R.string.defaultValue)) {
|
||||
container = cont
|
||||
}
|
||||
dao.updateProcessingContainer(container)
|
||||
}
|
||||
|
||||
suspend fun updateProcessingDownloadPath(path: String){
|
||||
dao.updateProcessingDownloadPath(path)
|
||||
}
|
||||
|
|
@ -1111,6 +1119,11 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
|||
return Pair(types.size == 1, Type.valueOf(types.first()))
|
||||
}
|
||||
|
||||
fun checkIfAllProcessingItemsHaveSameContainer() : Pair<Boolean, String> {
|
||||
val containers = dao.getProcessingDownloadContainers()
|
||||
return Pair(containers.size == 1, containers.first())
|
||||
}
|
||||
|
||||
|
||||
suspend fun updateItemsWithIdsToProcessingStatus(ids: List<Long>) {
|
||||
repository.deleteProcessing()
|
||||
|
|
|
|||
|
|
@ -175,6 +175,14 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
|
|||
return repository.getItemByURL(url)
|
||||
}
|
||||
|
||||
fun getAllByURL(url: String) : List<ResultItem> {
|
||||
return repository.getAllByURL(url)
|
||||
}
|
||||
|
||||
fun getByID(id: Long) : ResultItem? {
|
||||
return repository.getItemByID(id)
|
||||
}
|
||||
|
||||
fun addSearchQueryToHistory(query: String) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val allQueries = searchHistoryRepository.getAll()
|
||||
if (allQueries.none { it.query == query }){
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.models.ResultItem
|
||||
import com.deniscerri.ytdl.database.viewmodel.CookieViewModel
|
||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||
import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel
|
||||
|
|
@ -185,13 +186,18 @@ class ShareActivity : BaseActivity() {
|
|||
val command = intent.getStringExtra("COMMAND") ?: ""
|
||||
|
||||
lifecycleScope.launch {
|
||||
var result = withContext(Dispatchers.IO){
|
||||
resultViewModel.getItemByURL(inputQuery)
|
||||
val result: ResultItem
|
||||
val existingResults = withContext(Dispatchers.IO){
|
||||
resultViewModel.getAllByURL(inputQuery)
|
||||
}
|
||||
if (result == null) {
|
||||
|
||||
if (existingResults.isEmpty() || existingResults.size > 1) {
|
||||
resultViewModel.deleteAll()
|
||||
result = downloadViewModel.createEmptyResultItem(inputQuery)
|
||||
}else{
|
||||
result = existingResults.first()
|
||||
}
|
||||
|
||||
val downloadType = DownloadViewModel.Type.valueOf(type ?: downloadViewModel.getDownloadType(url = result.url).toString())
|
||||
if (sharedPreferences.getBoolean("download_card", true) && !background){
|
||||
val bundle = Bundle()
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import com.deniscerri.ytdl.ui.adapter.HomeAdapter
|
|||
import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter
|
||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdl.util.InfoUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.deniscerri.ytdl.util.ThemeUtil
|
||||
import com.deniscerri.ytdl.util.UiUtil
|
||||
import com.facebook.shimmer.ShimmerFrameLayout
|
||||
|
|
@ -84,6 +85,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
private var clipboardFab: ExtendedFloatingActionButton? = null
|
||||
private var homeFabs: LinearLayout? = null
|
||||
private var infoUtil: InfoUtil? = null
|
||||
private var notificationUtil: NotificationUtil? = null
|
||||
private var downloadQueue: ArrayList<ResultItem>? = null
|
||||
|
||||
private lateinit var resultViewModel : ResultViewModel
|
||||
|
|
@ -121,6 +123,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
mainActivity = activity as MainActivity?
|
||||
quickLaunchSheet = false
|
||||
infoUtil = InfoUtil(requireContext())
|
||||
notificationUtil = NotificationUtil(requireContext())
|
||||
selectedObjects = arrayListOf()
|
||||
return fragmentView
|
||||
}
|
||||
|
|
@ -306,6 +309,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
|||
}
|
||||
|
||||
if (arguments?.getBoolean("showDownloadsWithUpdatedFormats") == true){
|
||||
notificationUtil?.cancelDownloadNotification(NotificationUtil.FORMAT_UPDATING_FINISHED_NOTIFICATION_ID)
|
||||
arguments?.remove("showDownloadsWithUpdatedFormats")
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val ids = arguments?.getLongArray("downloadIds") ?: return@launch
|
||||
|
|
|
|||
|
|
@ -112,6 +112,10 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
codec.text = codecText
|
||||
}
|
||||
|
||||
val container = card.findViewById<TextView>(R.id.container)
|
||||
container.isVisible = item.container.isNotBlank()
|
||||
container.text = item.container.uppercase()
|
||||
|
||||
val fileSize = card.findViewById<TextView>(R.id.file_size)
|
||||
val fileSizeReadable = if(item.type != DownloadViewModel.Type.video){
|
||||
FileUtil.convertFileSize(item.format.filesize)
|
||||
|
|
@ -185,6 +189,7 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
override fun areContentsTheSame(oldItem: DownloadItemConfigureMultiple, newItem: DownloadItemConfigureMultiple): Boolean {
|
||||
return oldItem.title == newItem.title &&
|
||||
oldItem.type == newItem.type &&
|
||||
oldItem.container == newItem.container &&
|
||||
oldItem.videoPreferences == newItem.videoPreferences &&
|
||||
oldItem.format == newItem.format &&
|
||||
oldItem.incognito == newItem.incognito
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import java.util.*
|
|||
class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<ResultItem?, PlaylistAdapter.ViewHolder>(AsyncDifferConfig.Builder(
|
||||
DIFF_CALLBACK
|
||||
).build()) {
|
||||
private val checkedItems: ArrayList<String>
|
||||
private val checkedItems: ArrayList<Long>
|
||||
private val onItemClickListener: OnItemClickListener
|
||||
private val activity: Activity
|
||||
private val sharedPreferences: SharedPreferences
|
||||
|
|
@ -70,9 +70,9 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
|
||||
// CHECKBOX ----------------------------------
|
||||
val check = card.findViewById<CheckBox>(R.id.checkBox)
|
||||
check.isChecked = checkedItems.contains(item.url)
|
||||
check.isChecked = checkedItems.contains(item.id)
|
||||
check.setOnClickListener {
|
||||
checkCard(check.isChecked, item.url)
|
||||
checkCard(check.isChecked, item.id)
|
||||
}
|
||||
|
||||
card.setOnClickListener {
|
||||
|
|
@ -80,25 +80,25 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkCard(isChecked: Boolean, itemURL: String) {
|
||||
private fun checkCard(isChecked: Boolean, id: Long) {
|
||||
if (isChecked) {
|
||||
checkedItems.add(itemURL)
|
||||
checkedItems.add(id)
|
||||
} else {
|
||||
checkedItems.remove(itemURL)
|
||||
checkedItems.remove(id)
|
||||
}
|
||||
onItemClickListener.onCardSelect(itemURL, isChecked, checkedItems)
|
||||
onItemClickListener.onCardSelect(id, isChecked, checkedItems)
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List<String>)
|
||||
fun onCardSelect(itemID: Long, isChecked: Boolean, checkedItems: List<Long>)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun clearCheckeditems() {
|
||||
for (i in 0 until itemCount){
|
||||
val item = getItem(i)
|
||||
if (checkedItems.find { it == item?.url } != null){
|
||||
checkedItems.remove(item?.url)
|
||||
if (checkedItems.find { it == item?.id } != null){
|
||||
checkedItems.remove(item?.id)
|
||||
notifyItemChanged(i)
|
||||
}
|
||||
}
|
||||
|
|
@ -109,15 +109,15 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
checkedItems.clear()
|
||||
for (i in 0 until itemCount){
|
||||
val item = getItem(i)
|
||||
checkedItems.add(item!!.url)
|
||||
checkedItems.add(item!!.id)
|
||||
notifyItemChanged(i)
|
||||
}
|
||||
}
|
||||
|
||||
fun invertSelected(items: List<ResultItem?>?){
|
||||
val invertedList = mutableListOf<String>()
|
||||
val invertedList = mutableListOf<Long>()
|
||||
items?.forEach {
|
||||
if (!checkedItems.contains(it!!.url)) invertedList.add(it.url)
|
||||
if (!checkedItems.contains(it!!.id)) invertedList.add(it.id)
|
||||
}
|
||||
checkedItems.clear()
|
||||
checkedItems.addAll(invertedList)
|
||||
|
|
@ -128,19 +128,19 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
|||
checkedItems.clear()
|
||||
if (start == end ){
|
||||
val item = getItem(start)
|
||||
checkedItems.add(item!!.url)
|
||||
checkedItems.add(item!!.id)
|
||||
notifyItemChanged(start)
|
||||
}else{
|
||||
for (i in start..end){
|
||||
val item = getItem(i)
|
||||
checkedItems.add(item!!.url)
|
||||
checkedItems.add(item!!.id)
|
||||
notifyItemChanged(i)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getCheckedItems() : List<String>{
|
||||
fun getCheckedItems() : List<Long>{
|
||||
return checkedItems
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,12 +16,12 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.Window
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -90,6 +90,9 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
private lateinit var currentDownloadIDs: List<Long>
|
||||
private var processingItemsCount : Int = 0
|
||||
|
||||
private lateinit var containerBtn : MenuItem
|
||||
private lateinit var containerTextView: TextView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||
|
|
@ -145,10 +148,16 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
itemTouchHelper.attachToRecyclerView(recyclerView)
|
||||
}
|
||||
|
||||
scheduleBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_schedule_button)
|
||||
downloadBtn = view.findViewById<MaterialButton>(R.id.bottomsheet_download_button)
|
||||
scheduleBtn = view.findViewById(R.id.bottomsheet_schedule_button)
|
||||
downloadBtn = view.findViewById(R.id.bottomsheet_download_button)
|
||||
bottomAppBar = view.findViewById(R.id.bottomAppBar)
|
||||
|
||||
val preferredDownloadType = bottomAppBar.menu.findItem(R.id.preferred_download_type)
|
||||
val formatBtn = bottomAppBar.menu.findItem(R.id.format)
|
||||
val moreBtn = bottomAppBar.menu.findItem(R.id.more)
|
||||
containerBtn = bottomAppBar.menu.findItem(R.id.container)
|
||||
containerTextView = containerBtn.actionView as TextView
|
||||
val incognitoBtn = bottomAppBar.menu.findItem(R.id.incognito)
|
||||
|
||||
filesize = view.findViewById(R.id.filesize)
|
||||
count = view.findViewById(R.id.count)
|
||||
|
|
@ -167,20 +176,28 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
lifecycleScope.launch {
|
||||
downloadViewModel.processingDownloads.collectLatest { items ->
|
||||
processingItemsCount = items.size
|
||||
count.text = "${processingItemsCount} ${getString(R.string.selected)}"
|
||||
count.text = "$processingItemsCount ${getString(R.string.selected)}"
|
||||
listAdapter.submitList(items)
|
||||
|
||||
updateFileSize(items)
|
||||
|
||||
if (items.isNotEmpty()){
|
||||
if (items.all { it2 -> it2.type == items[0].type }) {
|
||||
bottomAppBar.menu[1].icon?.alpha = 255
|
||||
formatBtn.icon?.alpha = 255
|
||||
if (items[0].type != DownloadViewModel.Type.command) {
|
||||
bottomAppBar.menu[4].icon?.alpha = 255
|
||||
moreBtn.icon?.alpha = 255
|
||||
}
|
||||
|
||||
containerBtn.isVisible = items.first().type != DownloadViewModel.Type.command
|
||||
} else {
|
||||
bottomAppBar.menu[1].icon?.alpha = 30
|
||||
bottomAppBar.menu[4].icon?.alpha = 30
|
||||
formatBtn.icon?.alpha = 30
|
||||
moreBtn.icon?.alpha = 30
|
||||
}
|
||||
|
||||
if (items.all { it.container == items[0].container }) {
|
||||
setContainerText(items[0].container)
|
||||
}else{
|
||||
setContainerText("")
|
||||
}
|
||||
|
||||
val type = items.first().type
|
||||
|
|
@ -191,10 +208,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
DownloadViewModel.Type.video -> {
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
||||
|
||||
}
|
||||
DownloadViewModel.Type.command -> {
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
||||
setContainerText("")
|
||||
}
|
||||
|
||||
else -> {}
|
||||
|
|
@ -303,7 +320,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
downloadViewModel.areAllProcessingIncognito()
|
||||
}
|
||||
|
||||
bottomAppBar.menu.children.first { it.itemId == R.id.incognito }.icon!!.apply {
|
||||
incognitoBtn.icon!!.apply {
|
||||
alpha = if (allIncognito){
|
||||
255
|
||||
}else{
|
||||
|
|
@ -336,7 +353,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
R.id.incognito -> {
|
||||
lifecycleScope.launch {
|
||||
if (m.icon!!.alpha == 255) {
|
||||
bottomAppBar.menu[3].isEnabled = false
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(false)
|
||||
withContext(Dispatchers.Main){
|
||||
|
|
@ -344,18 +361,18 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
m.isEnabled = true
|
||||
}
|
||||
}
|
||||
bottomAppBar.menu[3].icon?.alpha = 30
|
||||
bottomAppBar.menu[3].isEnabled = true
|
||||
incognitoBtn.icon?.alpha = 30
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.disabled)}", Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
bottomAppBar.menu[3].isEnabled = false
|
||||
incognitoBtn.isEnabled = false
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateProcessingIncognito(true)
|
||||
withContext(Dispatchers.Main){
|
||||
}
|
||||
}
|
||||
bottomAppBar.menu[3].icon?.alpha = 255
|
||||
bottomAppBar.menu[3].isEnabled = true
|
||||
incognitoBtn.icon?.alpha = 255
|
||||
incognitoBtn.isEnabled = true
|
||||
Toast.makeText(requireContext(), "${getString(R.string.incognito)}: ${getString(R.string.ok)}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
|
@ -393,10 +410,20 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.audio)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_audio_file_24)
|
||||
bottomAppBar.menu[1].icon?.alpha = 255
|
||||
bottomAppBar.menu[4].icon?.alpha = 255
|
||||
formatBtn.icon?.alpha = 255
|
||||
moreBtn.icon?.alpha = 255
|
||||
bottomSheet.cancel()
|
||||
}
|
||||
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!res.first) {
|
||||
setContainerText("")
|
||||
}else{
|
||||
setContainerText(res.second)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -405,10 +432,19 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.video)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_video_file_24)
|
||||
bottomAppBar.menu[1].icon?.alpha = 255
|
||||
bottomAppBar.menu[4].icon?.alpha = 255
|
||||
formatBtn.icon?.alpha = 255
|
||||
moreBtn.icon?.alpha = 255
|
||||
bottomSheet.cancel()
|
||||
}
|
||||
|
||||
val res = downloadViewModel.checkIfAllProcessingItemsHaveSameContainer()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!res.first) {
|
||||
setContainerText("")
|
||||
}else{
|
||||
setContainerText(res.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -417,10 +453,12 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
downloadViewModel.updateProcessingType(DownloadViewModel.Type.command)
|
||||
withContext(Dispatchers.Main){
|
||||
preferredDownloadType.setIcon(R.drawable.baseline_insert_drive_file_24)
|
||||
bottomAppBar.menu[1].icon?.alpha = 255
|
||||
bottomAppBar.menu[4].icon?.alpha = 30
|
||||
formatBtn.icon?.alpha = 255
|
||||
moreBtn.icon?.alpha = 30
|
||||
bottomSheet.cancel()
|
||||
containerBtn.isVisible = false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -676,6 +714,41 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
true
|
||||
}
|
||||
|
||||
containerTextView.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
val res = withContext(Dispatchers.IO){
|
||||
downloadViewModel.checkIfAllProcessingItemsHaveSameType()
|
||||
}
|
||||
if (!res.first){
|
||||
Toast.makeText(requireContext(), getString(R.string.format_filtering_hint), Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
val popup = PopupMenu(activity, containerTextView)
|
||||
when(res.second) {
|
||||
DownloadViewModel.Type.audio -> resources.getStringArray(R.array.audio_containers)
|
||||
//video
|
||||
else -> resources.getStringArray(R.array.video_containers)
|
||||
}.forEach {
|
||||
popup.menu.add(it)
|
||||
}
|
||||
|
||||
popup.setOnMenuItemClickListener { mm ->
|
||||
val container = mm.title
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
downloadViewModel.updateProcessingContainer(container.toString())
|
||||
}
|
||||
}
|
||||
setContainerText(container.toString())
|
||||
true
|
||||
}
|
||||
|
||||
popup.show()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun toggleLoading(loading: Boolean){
|
||||
|
|
@ -964,5 +1037,21 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun setContainerText(cont: String?) {
|
||||
if (cont == null) {
|
||||
containerBtn.isVisible = false
|
||||
return
|
||||
}
|
||||
|
||||
if (cont == getString(R.string.defaultValue) || cont.isEmpty()) {
|
||||
containerTextView.text = ".ext"
|
||||
return
|
||||
}else{
|
||||
containerTextView.text = cont
|
||||
}
|
||||
containerBtn.isVisible = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
private lateinit var selectBetween: MenuItem
|
||||
|
||||
private lateinit var resultItemIDs: List<Long>
|
||||
private lateinit var itemURLs: List<String>
|
||||
private var items = listOf<ResultItem>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
|
@ -179,9 +178,9 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
ok.isEnabled = false
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val checkedItems = listAdapter.getCheckedItems()
|
||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item.url) }
|
||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item.id) }
|
||||
if (checkedResultItems.size == 1){
|
||||
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0].url)!!
|
||||
val resultItem = resultViewModel.getByID(checkedResultItems[0].id)!!
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf(
|
||||
Pair("result", resultItem),
|
||||
|
|
@ -223,8 +222,8 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
if(selectedItems.size != 2){
|
||||
m.isVisible = false
|
||||
}else{
|
||||
val item2 = itemURLs.indexOf(selectedItems.last())
|
||||
val item1 = itemURLs.indexOf(selectedItems.first())
|
||||
val item2 = resultItemIDs.indexOf(selectedItems.last())
|
||||
val item1 = resultItemIDs.indexOf(selectedItems.first())
|
||||
if(item1 > item2) listAdapter.checkRange(item2, item1)
|
||||
else listAdapter.checkRange(item1, item2)
|
||||
count.text = "${listAdapter.getCheckedItems().size} ${resources.getString(R.string.selected)}"
|
||||
|
|
@ -249,7 +248,7 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
toTextInput.isEnabled = !isLoading
|
||||
|
||||
items = it
|
||||
itemURLs = items.map { itm -> itm.url }
|
||||
resultItemIDs = items.map { itm -> itm.id }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +283,7 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
}
|
||||
|
||||
|
||||
override fun onCardSelect(itemURL: String, isChecked: Boolean, checkedItems: List<String>) {
|
||||
override fun onCardSelect(itemID: Long, isChecked: Boolean, checkedItems: List<Long>) {
|
||||
if (checkedItems.size == items.size){
|
||||
count.text = "(${listAdapter.getCheckedItems().size}) ${resources.getString(R.string.all_items_selected)} "
|
||||
}else{
|
||||
|
|
@ -302,8 +301,8 @@ class SelectPlaylistItemsDialog : BottomSheetDialogFragment(), PlaylistAdapter.O
|
|||
val size = checkedItems.size
|
||||
if(size != 2) false
|
||||
else {
|
||||
val item1 = itemURLs.indexOf(checkedItems.first())
|
||||
val item2 = itemURLs.indexOf(checkedItems.last())
|
||||
val item1 = resultItemIDs.indexOf(checkedItems.first())
|
||||
val item2 = resultItemIDs.indexOf(checkedItems.last())
|
||||
|
||||
(item1-item2).absoluteValue > 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,12 +167,17 @@ class ActiveDownloadsFragment : Fragment(), ActiveDownloadAdapter.OnItemClickLis
|
|||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
val activeQueuedDownloadsCount = withContext(Dispatchers.IO) {
|
||||
downloadViewModel.getActiveQueuedDownloadsCount()
|
||||
val activeCount = withContext(Dispatchers.IO) {
|
||||
downloadViewModel.getActiveDownloadsCount()
|
||||
}
|
||||
|
||||
val queuedCount = withContext(Dispatchers.IO) {
|
||||
downloadViewModel.getQueuedDownloadsCount()
|
||||
}
|
||||
|
||||
val pausedDownloads = preferences.getBoolean("paused_downloads", false)
|
||||
pause.isVisible = activeQueuedDownloadsCount > 0 && !pausedDownloads
|
||||
resume.isVisible = activeQueuedDownloadsCount > 0 && pausedDownloads
|
||||
pause.isVisible = (queuedCount > 0 || activeCount > 0) && !pausedDownloads
|
||||
resume.isVisible = (queuedCount > 0 || activeCount > 0) && pausedDownloads
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.work.WorkManager
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||
|
|
@ -22,11 +23,14 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
|
||||
import com.deniscerri.ytdl.ui.more.terminal.TerminalActivity
|
||||
import com.deniscerri.ytdl.util.NavbarUtil
|
||||
import com.deniscerri.ytdl.util.NotificationUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.yausername.youtubedl_android.YoutubeDL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class MoreFragment : Fragment() {
|
||||
|
|
@ -125,28 +129,40 @@ class MoreFragment : Fragment() {
|
|||
doNotShowAgain = compoundButton.isChecked
|
||||
}
|
||||
|
||||
val workManager = WorkManager.getInstance(requireContext())
|
||||
val notificationUtil = NotificationUtil(requireContext())
|
||||
|
||||
terminateDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
terminateDialog.setPositiveButton(getString(R.string.ok)) { diag: DialogInterface?, _: Int ->
|
||||
runBlocking {
|
||||
val job : Job = lifecycleScope.launch(Dispatchers.IO) {
|
||||
val activeDownloads = downloadViewModel.getActiveDownloads().toMutableList()
|
||||
activeDownloads.map { it.status = DownloadRepository.Status.Queued.toString() }
|
||||
activeDownloads.forEach {
|
||||
downloadViewModel.updateDownload(it)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
val activeDownloads = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getActiveDownloadsCount()
|
||||
}
|
||||
runBlocking {
|
||||
job.join()
|
||||
if (doNotShowAgain){
|
||||
mainSharedPreferencesEditor.putBoolean("ask_terminate_app", false)
|
||||
mainSharedPreferencesEditor.commit()
|
||||
if (activeDownloads > 0) {
|
||||
workManager.cancelAllWorkByTag("download")
|
||||
val activeDownloadsList = withContext(Dispatchers.IO){
|
||||
downloadViewModel.getActiveDownloads()
|
||||
}
|
||||
mainActivity.finishAndRemoveTask()
|
||||
mainActivity.finishAffinity()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
|
||||
activeDownloadsList.forEach {
|
||||
it.status = DownloadRepository.Status.Queued.toString()
|
||||
YoutubeDL.getInstance().destroyProcessById(it.id.toString())
|
||||
notificationUtil.cancelDownloadNotification(it.id.toInt())
|
||||
withContext(Dispatchers.IO) {
|
||||
downloadViewModel.updateDownload(it)
|
||||
}
|
||||
}
|
||||
mainSharedPreferencesEditor.putBoolean("paused_downloads", true).apply()
|
||||
}
|
||||
|
||||
if (doNotShowAgain){
|
||||
mainSharedPreferencesEditor.putBoolean("ask_terminate_app", false).apply()
|
||||
}
|
||||
mainSharedPreferencesEditor.commit()
|
||||
mainActivity.finishAndRemoveTask()
|
||||
mainActivity.finishAffinity()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
||||
terminateDialog.show()
|
||||
}else{
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@ import androidx.core.view.get
|
|||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.deniscerri.ytdl.MainActivity
|
||||
import com.deniscerri.ytdl.R
|
||||
import com.deniscerri.ytdl.database.viewmodel.LogViewModel
|
||||
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
|
||||
import com.deniscerri.ytdl.util.Extensions.enableTextHighlight
|
||||
import com.deniscerri.ytdl.util.Extensions.setCustomTextSize
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
|
|
@ -104,8 +106,14 @@ class DownloadLogFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
content.isFocusable = true
|
||||
content.enableTextHighlight()
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
content.isFocusable = true
|
||||
content.enableTextHighlight()
|
||||
}
|
||||
|
||||
contentScrollView.enableFastScroll()
|
||||
|
||||
|
||||
val slider = view.findViewById<Slider>(R.id.textsize_seekbar)
|
||||
bottomAppBar?.setOnMenuItemClickListener { m: MenuItem ->
|
||||
when(m.itemId){
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import android.view.ViewOutlineProvider
|
|||
import android.view.animation.Interpolator
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.ScrollView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.Px
|
||||
import androidx.core.content.ContextCompat
|
||||
|
|
@ -139,6 +140,15 @@ object Extensions {
|
|||
.build()
|
||||
}
|
||||
|
||||
fun ScrollView.enableFastScroll() {
|
||||
val drawable = ShapeDrawable(OvalShape())
|
||||
drawable.paint.color = context.getColor(android.R.color.transparent)
|
||||
|
||||
FastScrollerBuilder(this)
|
||||
.useMd2Style()
|
||||
.setTrackDrawable(drawable)
|
||||
.build()
|
||||
}
|
||||
fun File.getMediaDuration(context: Context): Int {
|
||||
return kotlin.runCatching {
|
||||
if (!exists()) return 0
|
||||
|
|
@ -251,20 +261,20 @@ object Extensions {
|
|||
}
|
||||
}
|
||||
|
||||
fun String.appendLineToLog(line: String): String {
|
||||
val lines = this.split("\n")
|
||||
if (!lines.takeLast(3).contains(line)){
|
||||
//clean dublicate progress + add newline
|
||||
var newLine = line
|
||||
if (newLine.contains("[download")) {
|
||||
newLine = "[download]" + line.split("[download]").last()
|
||||
}
|
||||
|
||||
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
// fun String.appendLineToLog(line: String): String {
|
||||
// val lines = this.split("\n")
|
||||
// if (!lines.takeLast(3).contains(line)){
|
||||
// //clean dublicate progress + add newline
|
||||
// var newLine = line
|
||||
// if (newLine.contains("[download")) {
|
||||
// newLine = "[download]" + line.split("[download]").last()
|
||||
// }
|
||||
//
|
||||
// return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||
// }
|
||||
//
|
||||
// return this
|
||||
// }
|
||||
|
||||
fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){
|
||||
if(!hideThumb){
|
||||
|
|
|
|||
|
|
@ -97,11 +97,15 @@ class DownloadWorker(
|
|||
|
||||
val running = ArrayList(runningYTDLInstances)
|
||||
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
||||
if (items.isEmpty() && running.isEmpty()) WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
||||
if (items.isEmpty() && running.isEmpty()) {
|
||||
WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
||||
return@collect
|
||||
}
|
||||
|
||||
if (useScheduler){
|
||||
if (items.none{it.downloadStartTime > 0L} && running.isEmpty() && !alarmScheduler.isDuringTheScheduledTime()) {
|
||||
WorkManager.getInstance(context).cancelWorkById(this@DownloadWorker.id)
|
||||
return@collect
|
||||
}
|
||||
}
|
||||
val concurrentDownloads = sharedPreferences.getInt("concurrent_downloads", 1) - running.size
|
||||
|
|
@ -138,15 +142,16 @@ class DownloadWorker(
|
|||
|
||||
|
||||
val commandString = infoUtil.parseYTDLRequestString(request)
|
||||
val logString = StringBuilder("\n ${commandString}\n\n")
|
||||
val initialLogDetails = "Downloading:\n" +
|
||||
"Title: ${downloadItem.title}\n" +
|
||||
"URL: ${downloadItem.url}\n" +
|
||||
"Type: ${downloadItem.type}\n" +
|
||||
"Command: \n $commandString \n\n"
|
||||
val logString = StringBuilder(initialLogDetails)
|
||||
val logItem = LogItem(
|
||||
0,
|
||||
downloadItem.title.ifBlank { downloadItem.url },
|
||||
"Downloading:\n" +
|
||||
"Title: ${downloadItem.title}\n" +
|
||||
"URL: ${downloadItem.url}\n" +
|
||||
"Type: ${downloadItem.type}\n" +
|
||||
"Command: $logString",
|
||||
logString.toString(),
|
||||
downloadItem.format,
|
||||
downloadItem.type,
|
||||
System.currentTimeMillis(),
|
||||
|
|
@ -182,7 +187,7 @@ class DownloadWorker(
|
|||
resultRepo.updateDownloadItem(downloadItem)?.apply {
|
||||
dao.updateWithoutUpsert(this)
|
||||
}
|
||||
val wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||
//val wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||
runBlocking {
|
||||
var finalPaths : MutableList<String>?
|
||||
|
||||
|
|
@ -304,7 +309,7 @@ class DownloadWorker(
|
|||
dao.delete(downloadItem.id)
|
||||
|
||||
if (logDownloads){
|
||||
logRepo.update(it.out, logItem.id)
|
||||
logRepo.update(initialLogDetails + "\n" + it.out, logItem.id, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,11 +88,12 @@ class TerminalDownloadWorker(
|
|||
|
||||
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
|
||||
|
||||
val initialLogDetails = "Terminal Task\n" +
|
||||
"Command: \n ${command}\n\n"
|
||||
val logItem = LogItem(
|
||||
0,
|
||||
"Terminal Task",
|
||||
"Terminal Task\n" +
|
||||
"Command: \n ${command}\n\n",
|
||||
initialLogDetails,
|
||||
Format(),
|
||||
DownloadViewModel.Type.command,
|
||||
System.currentTimeMillis(),
|
||||
|
|
@ -139,8 +140,7 @@ class TerminalDownloadWorker(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logDownloads) logRepo.update(it.out, logItem.id)
|
||||
if (logDownloads) logRepo.update(initialLogDetails + "\n" + it.out, logItem.id, true)
|
||||
dao.updateLog(it.out, itemId.toLong())
|
||||
notificationUtil.cancelDownloadNotification(itemId)
|
||||
delay(1000)
|
||||
|
|
|
|||
12
app/src/main/res/layout/bottom_app_bar_text.xml
Normal file
12
app/src/main/res/layout/bottom_app_bar_text.xml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Button xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:id="@+id/bottom_app_bar_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAllCaps="true"
|
||||
android:minHeight="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:textColor="?android:colorAccent"
|
||||
android:textStyle="bold"
|
||||
android:textSize="15sp"/>
|
||||
|
|
@ -187,6 +187,27 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/container"
|
||||
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:background="@drawable/rounded_corner"
|
||||
android:backgroundTint="?attr/colorSecondary"
|
||||
android:clickable="false"
|
||||
android:gravity="center"
|
||||
android:minWidth="30dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/codec"
|
||||
style="@style/Widget.Material3.FloatingActionButton.Large.Secondary"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
<me.zhanghai.android.fastscroll.FastScrollScrollView
|
||||
android:id="@+id/content_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
</me.zhanghai.android.fastscroll.FastScrollScrollView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@
|
|||
android:icon="@drawable/baseline_folder_24"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/container"
|
||||
android:title="@string/container"
|
||||
app:actionLayout="@layout/bottom_app_bar_text"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/incognito"
|
||||
android:title="@string/incognito"
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@
|
|||
android:defaultValue=""
|
||||
android:entries="@array/cleanup_leftover_downloads"
|
||||
android:entryValues="@array/cleanup_leftover_downloads_values"
|
||||
android:icon="@drawable/baseline_delete_24"
|
||||
android:icon="@drawable/ic_folder_delete"
|
||||
app:key="cleanup_leftover_downloads"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/cleanup_leftover_downloads" />
|
||||
|
|
|
|||
|
|
@ -1,87 +1,43 @@
|
|||
TEMPORARY just to save changelog
|
||||
# What's Changed
|
||||
|
||||
List is empty.
|
||||
|
||||
kotlin.collections.CollectionsKt.first(SourceFile:2)
|
||||
com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.checkIfAllProcessingItemsHaveSameType(Unknown Source:21)
|
||||
com.deniscerri.ytdl.ui.downloadcard.DownloadMultipleBottomSheetDialog$setupDialog$9$3$res$1.invokeSuspend(SourceFile:18)
|
||||
kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:9)
|
||||
kotlinx.coroutines.DispatchedTask.run(Unknown Source:96)
|
||||
androidx.work.Worker$2.run(SourceFile:39)
|
||||
kotlinx.coroutines.scheduling.TaskImpl.run(Unknown Source:2)
|
||||
kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:96)
|
||||
|
||||
|
||||
add option in download settings to use alarm manager instead of workmanager for scheduled tasks
|
||||
|
||||
fix audio format importance
|
||||
|
||||
When updating formats for multiple items, now the app remembers what items dont need new formats and skips them
|
||||
|
||||
Move format source from the bottom of the format list to the filter bottom sheet
|
||||
|
||||
Added ability for the app to update the result items formats when you update the download item formats if there are any results with the same url
|
||||
|
||||
moved queue screen back in its separate tab due to performance issues
|
||||
|
||||
format updating in the multiple download card now applies updates immediately on each item
|
||||
|
||||
Update formats in background review. Also prevent from showing up every time it updates. Also delete saved after opening items n the multiple download card lol wtf. Also the notification. Change the title of notification to
|
||||
|
||||
Can that other one under the Copy log be fixed, too?snackbar
|
||||
|
||||
Bulk download shows size without audio fixed
|
||||
|
||||
When selecting audio format add generic last
|
||||
|
||||
Update ui crashes app from share activity
|
||||
|
||||
add option to use video url instead of playlist url to avoid piped private playlist issue
|
||||
|
||||
add write uri permission when opening files
|
||||
|
||||
fix app still updating formats even though the user closed the format bottom sheet
|
||||
|
||||
[BUG] Video title doesn't change in the download card #526
|
||||
|
||||
fixed app fetching player urls and chapters from yt-dlp
|
||||
|
||||
added ability to change theme without restarting the app. If the icon changes then the app will restart
|
||||
|
||||
prevent avi and flv containers from embedding thumbnails
|
||||
|
||||
Fixed Subs language preference isn't saved in the download card
|
||||
|
||||
add player_client=default,mediaconnect,android extractor args when data fetching and downloading to show more formats
|
||||
|
||||
Turned some metadata into digits
|
||||
- Fix App crashing with exception "com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.checkIfAllProcessingItemsHaveSameType(Unknown Source:21)..."
|
||||
- Added option in download settings to use AlarmManager instead of WorkManager for scheduled downloads to improve accuracy
|
||||
- When updating formats for multiple items, now the app remembers what items don't need new formats and skips them to save time and data
|
||||
- Moved format source from the bottom of the format list to the filter bottom sheet
|
||||
- Added ability for the app to update the result items formats when you update the download item formats if there are any results with the same url
|
||||
- Moved "In Queue" screen back in its separate tab due to recyclerview performance issues
|
||||
- Format updating in multiple download card now applies updates immediately on each item
|
||||
- Deleted saved downloads after a format update in the background after the user decided to open for review
|
||||
- App now shows the actual predicted filesize along with the audio size when downloading videos in the multiple download card. Also in single download mode when the audio size is known but the video size is unknown the app wont show the audio size only, it wont show anything
|
||||
- Made snackbar show up above the copy log button
|
||||
- Fixed App crashing when updating fragments in the download card while in the Share Activity
|
||||
- Added a setting to use the video url instead of the playlist url to download. The app used playlist url and then indexes to get the video to take advantage of playlist metadata. Some users have been using libretube playlists that cant be recognised properly by yt-dlp. Turn this option if this affects you
|
||||
- Added Write URI Permission when opening files
|
||||
- Fixed App still updating formats even though the user closed the format bottom sheet
|
||||
- Fixed App keeping old video titles in the download card #526
|
||||
- Refactored code on how to fetch player url and chapters from yt-dlp in the cut bottom sheet
|
||||
- Added ability to change the theme accent without restarting the app. If you change light/dark mode the app is supposed to restart because the icon activity changes aswell
|
||||
- Prevented AVI and FLV containers from embedding thumbnails
|
||||
- Fixed Subs Language preference not being saved in the download card
|
||||
- Added player_client=default,mediaconnect,android extractor args when data fetching and downloading to use more formats
|
||||
- Turned some metadata into digits
|
||||
-> --parse-metadata "%(track_number,playlist_index)d:(?P<track_number>\d+)" --parse-metadata "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P<meta_date>\d+)"
|
||||
|
||||
Removed NA from meta album artist --parse-metadata "%(artist,uploader|)s:^(?P<meta_album_artist>[^,]*)"
|
||||
|
||||
Add --recode-video toggle in the settings to use instead of --merge-output-format
|
||||
|
||||
adjusted the "Adjust video" section in the download card and put all subtitle items together
|
||||
|
||||
Fixed app not selecting an audio format to show in the format view in the download card after updating formats
|
||||
|
||||
download_rescheduled_to shows up twice, one in original language and one in english
|
||||
|
||||
It's very difficult to tap on video icon for the video at the bottom
|
||||
|
||||
#529
|
||||
|
||||
https://www.instagram.com/reel/C9zTRlONU6G/?igsh=MWJ1djBhYjdscG5wMw== chooses smallest format
|
||||
|
||||
Limit audio id length
|
||||
|
||||
Remember scroll position in history tab
|
||||
|
||||
Make piped not fetch mix playlists
|
||||
|
||||
turn incognito toast to snackbar
|
||||
|
||||
duplicate dialog crash app when in share activity because the activity closes too early
|
||||
|
||||
4. In videos with multiple languages, previously, when I entered the suggested tab, all audios in my preferred language were displayed at the top, with the lowest quality. However, this no longer happens, and now I need to search among multiple languages (again, I'm not entirely sure about this, but it's challenging to compare with a previous version since I no longer have access to older app versions)
|
||||
|
||||
- Removed NA from meta album artist --parse-metadata "%(artist,uploader|)s:^(?P<meta_album_artist>[^,]*)"
|
||||
- Added --recode-video toggle in the settings to use instead of --merge-output-format
|
||||
- Also added that preference as a button in the adjust video in the download card
|
||||
- Adjusted the "Adjust video" section in the download card and bundled some common items together to save space
|
||||
- Fixed app not selecting an audio format to show in the format view in the download card after updating formats
|
||||
- Fixed app showing when the download will start up when you queue up a scheduled download
|
||||
- Made home recyclerview bottom padding bigger to avoid the FABs
|
||||
- Fixed Issue #529
|
||||
- App now remembers the scroll position in the download history tab
|
||||
- Made piped api not fetch data when fed mix playlists
|
||||
- Turned the incognito toast to a snackbar in the download card
|
||||
- Fixed duplicate dialog crashing app when in share activity
|
||||
- Fixed issue of app not showing audio formats of preferred language at the top when selecting the suggested filter
|
||||
- Added a feature to delete cancelled downloads, errored downloads and download cache older than a certain period. You can select daily, weekly, monthly.
|
||||
- Added ability to change the container in the multiple download card
|
||||
- Fixed App issues with download logs
|
||||
- Added preferred language in the format sorter in yt-dlp
|
||||
- More other small changes i forgor what i fixed
|
||||
|
|
|
|||
Loading…
Reference in a new issue