diff --git a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt index 8f7002c8..ac180500 100644 --- a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt @@ -273,9 +273,14 @@ class MainActivity : BaseActivity() { if(DBManager.getInstance(this@MainActivity).downloadDao.getDownloadsCountByStatus(listOf("Active", "Queued")) == 0){ if (UpdateUtil(this@MainActivity).updateYoutubeDL() == YoutubeDL.UpdateStatus.DONE) { val version = YoutubeDL.getInstance().version(context) - Snackbar.make(findViewById(R.id.frame_layout), + val snack = Snackbar.make(findViewById(R.id.frame_layout), this@MainActivity.getString(R.string.ytld_update_success) + " [${version}]", - Snackbar.LENGTH_LONG).show() + Snackbar.LENGTH_LONG) + + if (navigationView is BottomNavigationView) { + snack.setAnchorView(navigationView) + } + snack.show() } } } diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt index 14c528fa..515257c1 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt @@ -11,6 +11,7 @@ import androidx.room.Transaction import androidx.room.Update import androidx.room.Upsert import com.deniscerri.ytdl.database.models.DownloadItem +import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple import com.deniscerri.ytdl.database.models.DownloadItemSimple import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.repository.DownloadRepository @@ -25,8 +26,9 @@ interface DownloadDao { @Query("SELECT * FROM downloads WHERE status='Active'") fun getActiveDownloads() : Flow> + @RewriteQueriesToDropUnusedColumns @Query("SELECT * FROM downloads WHERE status = 'Processing'") - fun getProcessingDownloads() : Flow> + fun getProcessingDownloads() : Flow> @Query("SELECT COUNT(*) FROM downloads WHERE status in (:statuses)") fun getDownloadsCountFlow(statuses: List) : Flow @@ -181,13 +183,17 @@ interface DownloadDao { suspend fun deleteSingleProcessing(id: Long) @Upsert - suspend fun update(item: DownloadItem) + suspend fun update(item: DownloadItem) : Long @Transaction - suspend fun updateAll(list: List){ + suspend fun updateAll(list: List) : List { + val toReturn = mutableListOf() list.forEach { - update(it) + it.id = update(it) + toReturn.add(it) } + + return toReturn } @Query("UPDATE downloads set status=:status where id=:id") diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt index 19939da4..aa2915cd 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt @@ -7,6 +7,7 @@ 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 @@ -36,6 +37,16 @@ interface LogDao { } } + @Query("UPDATE logs set content=:l where id=:id") + suspend fun updateLogContent(l: String, id: Long) + + @Transaction + suspend fun updateLog(line: String, id: Long) { + val l = getByID(id) ?: return + val log = l.content ?: "" + updateLogContent(log.appendLineToLog(line), id) + } + @Update(onConflict = OnConflictStrategy.REPLACE) suspend fun update(item: LogItem) diff --git a/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt b/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt new file mode 100644 index 00000000..b7c13454 --- /dev/null +++ b/app/src/main/java/com/deniscerri/ytdl/database/models/DownloadItemConfigureMultiple.kt @@ -0,0 +1,24 @@ +package com.deniscerri.ytdl.database.models + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel + +@Entity(tableName = "downloads") +data class DownloadItemConfigureMultiple( + @PrimaryKey(autoGenerate = true) + var id: Long, + var url: String, + var title: String, + var thumb: String, + var duration: String, + var format: Format, + var allFormats: MutableList, + var audioPreferences: AudioPreferences, + var videoPreferences: VideoPreferences, + @ColumnInfo(defaultValue = "Queued") + var status: String, + var type: DownloadViewModel.Type, + var incognito: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/database/models/VideoPreferences.kt b/app/src/main/java/com/deniscerri/ytdl/database/models/VideoPreferences.kt index 8941a875..c4d14bbf 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/models/VideoPreferences.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/models/VideoPreferences.kt @@ -14,5 +14,6 @@ data class VideoPreferences ( var subsLanguages: String = "en.*,.*-orig", var audioFormatIDs : ArrayList = arrayListOf(), var removeAudio: Boolean = false, - var alsoDownloadAsAudio: Boolean = false + var alsoDownloadAsAudio: Boolean = false, + var recodeVideo: Boolean = false ) : Parcelable diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt index dc67c8f6..37bc0d4d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt @@ -23,6 +23,7 @@ import com.deniscerri.ytdl.App import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.dao.DownloadDao import com.deniscerri.ytdl.database.models.DownloadItem +import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple import com.deniscerri.ytdl.database.models.DownloadItemSimple import com.deniscerri.ytdl.util.Extensions.toListString import com.deniscerri.ytdl.util.FileUtil @@ -43,7 +44,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) { pagingSourceFactory = {downloadDao.getAllDownloads()} ) val activeDownloads : Flow> = downloadDao.getActiveDownloads().distinctUntilChanged() - val processingDownloads : Flow> = downloadDao.getProcessingDownloads().distinctUntilChanged() + val processingDownloads : Flow> = downloadDao.getProcessingDownloads().distinctUntilChanged() val queuedDownloads : Pager = Pager( config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1), pagingSourceFactory = {downloadDao.getQueuedDownloads()} @@ -74,7 +75,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) { val scheduledDownloadsCount : Flow = downloadDao.getDownloadsCountByStatusFlow(listOf(Status.Scheduled).toListString()) enum class Status { - Active, Queued, Error, Cancelled, Saved, Processing, Scheduled + Active, Queued, Error, Cancelled, Saved, Processing, Scheduled, Duplicate } suspend fun insert(item: DownloadItem) : Long { @@ -102,8 +103,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) { downloadDao.update(item) } - suspend fun updateAll(list: List) { - downloadDao.updateAll(list) + suspend fun updateAll(list: List) : List { + return downloadDao.updateAll(list) } suspend fun updateWithoutUpsert(item: DownloadItem){ diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt index 7344ec9b..4e5beb75 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt @@ -39,12 +39,8 @@ class LogRepository(private val logDao: LogDao) { } suspend fun update(line: String, id: Long){ - runCatching { - val item = getItem(id) ?: return - val log = item.content ?: "" - item.content = log.appendLineToLog(line) - //item.content += "\n$line" - logDao.update(item) + kotlin.runCatching { + logDao.updateLog(line, id) } } diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index 93b70210..a1cf5e72 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -27,6 +27,7 @@ import com.deniscerri.ytdl.database.dao.DownloadDao import com.deniscerri.ytdl.database.models.AudioPreferences import com.deniscerri.ytdl.database.models.CommandTemplate import com.deniscerri.ytdl.database.models.DownloadItem +import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple import com.deniscerri.ytdl.database.models.DownloadItemSimple import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.models.HistoryItem @@ -68,7 +69,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val allDownloads : Flow> val queuedDownloads : Flow> val activeDownloads : Flow> - val processingDownloads : Flow> + val processingDownloads : Flow> val cancelledDownloads : Flow> val erroredDownloads : Flow> val savedDownloads : Flow> @@ -211,6 +212,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false) val saveSubs = sharedPreferences.getBoolean("write_subtitles", false) val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false) + val recodeVideo = sharedPreferences.getBoolean("recode_video", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) @@ -250,7 +252,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel ArrayList(sponsorblock), saveSubs, saveAutoSubs, - audioFormatIDs = preferredAudioFormats + audioFormatIDs = preferredAudioFormats, + recodeVideo = recodeVideo ) val extraCommands = when(type){ @@ -383,6 +386,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false) val saveSubs = sharedPreferences.getBoolean("write_subtitles", false) val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false) + val recodeVideo = sharedPreferences.getBoolean("recode_video", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) @@ -416,7 +420,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!)) - val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs) + val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs, recodeVideo = recodeVideo) var path = defaultPath historyItem.downloadPath.first().apply { File(this).parent?.apply { @@ -789,43 +793,36 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } //CHECK DUPLICATES - var alreadyExists = false + var isDuplicate = false if (checkDuplicate.isNotEmpty() && !ignoreDuplicates){ when(checkDuplicate){ "download_archive" -> { if (downloadArchive.any { d -> it.url.contains(d) }){ - alreadyExists = true - if (it.id == 0L) { - it.status = DownloadRepository.Status.Processing.toString() + isDuplicate = true + if (it.id == 0L){ val id = runBlocking { repository.insert(it) } it.id = id } - existingItemIDs.add( - AlreadyExistsIDs( - it.id, - null - ) - ) + it.status = DownloadRepository.Status.Duplicate.toString() + repository.update(it) + existingItemIDs.add(AlreadyExistsIDs(it.id,null)) } } "url_type" -> { val existingDownload = activeAndQueuedDownloads.firstOrNull { a -> a.type == it.type && a.url == it.url } if (existingDownload != null){ - it.status = DownloadRepository.Status.Processing.toString() - val id = runBlocking { - repository.insert(it) + isDuplicate = true + if (it.id == 0L){ + val id = runBlocking { + repository.insert(it) + } + it.id = id } - it.id = id - - alreadyExists = true - existingItemIDs.add( - AlreadyExistsIDs( - it.id, - null - ) - ) + it.status = DownloadRepository.Status.Duplicate.toString() + repository.update(it) + existingItemIDs.add(AlreadyExistsIDs(it.id,null)) }else{ //check if downloaded and file exists val history = withContext(Dispatchers.IO){ @@ -837,17 +834,16 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } if (existingHistoryItem != null){ - alreadyExists = true - it.status = DownloadRepository.Status.Processing.toString() - val id = runBlocking { - repository.insert(it) + isDuplicate = true + if (it.id == 0L){ + val id = runBlocking { + repository.insert(it) + } + it.id = id } - existingItemIDs.add( - AlreadyExistsIDs( - id, - existingHistoryItem.id - ) - ) + it.status = DownloadRepository.Status.Duplicate.toString() + repository.update(it) + existingItemIDs.add(AlreadyExistsIDs(it.id,existingHistoryItem.id)) } } } @@ -863,12 +859,16 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } if (existingDownload != null){ - it.status = DownloadRepository.Status.Processing.toString() - val id = runBlocking { - repository.insert(it) + isDuplicate = true + if (it.id == 0L){ + val id = runBlocking { + repository.insert(it) + } + it.id = id } - alreadyExists = true - existingItemIDs.add(AlreadyExistsIDs(id, null)) + it.status = DownloadRepository.Status.Duplicate.toString() + repository.update(it) + existingItemIDs.add(AlreadyExistsIDs(it.id, null)) }else{ //check if downloaded and file exists val history = withContext(Dispatchers.IO){ @@ -880,31 +880,30 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } if (existingHistoryItem != null){ - alreadyExists = true - it.status = DownloadRepository.Status.Processing.toString() - val id = runBlocking { - repository.insert(it) + isDuplicate = true + if (it.id == 0L){ + val id = runBlocking { + repository.insert(it) + } + it.id = id } - existingItemIDs.add( - AlreadyExistsIDs( - id, - existingHistoryItem.id - ) - ) + it.status = DownloadRepository.Status.Duplicate.toString() + repository.update(it) + existingItemIDs.add(AlreadyExistsIDs(it.id, existingHistoryItem.id)) } } } } } - if (!alreadyExists){ + if (!isDuplicate){ queuedItems.add(it) } } - repository.updateAll(queuedItems) + val queued = repository.updateAll(queuedItems) //if scheduler is on val useScheduler = sharedPreferences.getBoolean("use_scheduler", false) @@ -919,12 +918,12 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } }else{ if (!sharedPreferences.getBoolean("paused_downloads", false)) { - repository.startDownloadWorker(queuedItems, context) + repository.startDownloadWorker(queued, context) } if(!useScheduler){ CoroutineScope(Dispatchers.IO).launch { - queuedItems.filter { it.downloadStartTime != 0L && (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach { + queued.filter { it.downloadStartTime != 0L && (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach { kotlin.runCatching { resultRepository.updateDownloadItem(it)?.apply { repository.updateWithoutUpsert(this) @@ -934,7 +933,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } }else{ CoroutineScope(Dispatchers.IO).launch { - queuedItems.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach { + queued.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach { kotlin.runCatching { resultRepository.updateDownloadItem(it)?.apply { repository.updateWithoutUpsert(this) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt index da27ba7d..b08a3db0 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt @@ -60,6 +60,7 @@ import com.google.android.material.floatingactionbutton.ExtendedFloatingActionBu import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.search.SearchBar import com.google.android.material.search.SearchView +import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt index c6009b71..9131d708 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt @@ -18,15 +18,17 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.deniscerri.ytdl.R -import com.deniscerri.ytdl.database.models.DownloadItemSimple +import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple +import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.util.Extensions.loadThumbnail import com.deniscerri.ytdl.util.Extensions.popup import com.deniscerri.ytdl.util.FileUtil +import com.deniscerri.ytdl.util.InfoUtil import com.google.android.material.button.MaterialButton import java.util.Locale -class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter( +class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter( AsyncDifferConfig.Builder( DIFF_CALLBACK ).build()) { @@ -111,7 +113,25 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener } val fileSize = card.findViewById(R.id.file_size) - val fileSizeReadable = FileUtil.convertFileSize(item.format.filesize) + val fileSizeReadable = if(item.type != DownloadViewModel.Type.video){ + FileUtil.convertFileSize(item.format.filesize) + }else{ + if (item.format.filesize < 10L) { + FileUtil.convertFileSize(0) + }else{ + val preferredAudioFormatIDs = item.videoPreferences.audioFormatIDs + val audioFilesize = if (item.videoPreferences.removeAudio) { + 0 + }else{ + item.allFormats + .filter { preferredAudioFormatIDs.contains(it.format_id) } + .sumOf { it.filesize } + } + + FileUtil.convertFileSize(item.format.filesize + audioFilesize) + } + + } if (fileSizeReadable == "?") fileSize.visibility = View.GONE else { fileSize.text = fileSizeReadable @@ -157,15 +177,15 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener } companion object { - private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean { + private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DownloadItemConfigureMultiple, newItem: DownloadItemConfigureMultiple): Boolean { return oldItem.url == newItem.url } - override fun areContentsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean { + override fun areContentsTheSame(oldItem: DownloadItemConfigureMultiple, newItem: DownloadItemConfigureMultiple): Boolean { return oldItem.title == newItem.title && - oldItem.author == newItem.author && oldItem.type == newItem.type && + oldItem.videoPreferences == newItem.videoPreferences && oldItem.format == newItem.format && oldItem.incognito == newItem.incognito } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt index bb9f41f7..08d7a142 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt @@ -16,6 +16,7 @@ import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.LinearLayout import android.widget.TextView +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.ViewCompat @@ -359,7 +360,8 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private @SuppressLint("RestrictedApi") fun updateSelectedAudioFormat(formatID: String){ - resultItem?.formats?.find { it.format_id == formatID }?.apply { + val formats = (resultItem?.formats ?: listOf()) + genericAudioFormats + formats.find { it.format_id == formatID }?.apply { downloadItem.format = this val formatCard = requireView().findViewById(R.id.format_card_constraintLayout) UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, listOf()) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index 1a91a1c4..d5c0ac41 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -31,6 +31,7 @@ import androidx.recyclerview.widget.RecyclerView import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.DownloadItem +import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel @@ -161,7 +162,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure processingItemsCount = items.size count.text = "${processingItemsCount} ${getString(R.string.selected)}" listAdapter.submitList(items) - updateFileSize(items.map { it2 -> it2.format.filesize }) + + updateFileSize(items) if (items.isNotEmpty()){ if (items.all { it2 -> it2.type == items[0].type }) { @@ -445,7 +447,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.setContentView(R.layout.adjust_audio) val sheetView = bottomSheet.findViewById(android.R.id.content)!! - sheetView.findViewById(R.id.adjust).setPadding(padding,padding,padding,padding) + sheetView.findViewById(R.id.adjust).setPadding(10,padding,10,padding) val items = withContext(Dispatchers.IO){ downloadViewModel.getProcessingDownloads() @@ -526,7 +528,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE) bottomSheet.setContentView(R.layout.adjust_video) val sheetView = bottomSheet.findViewById(android.R.id.content)!! - sheetView.findViewById(R.id.adjust).setPadding(padding,padding,padding,padding) + sheetView.findViewById(R.id.adjust).setPadding(10,padding,10,padding) val items = withContext(Dispatchers.IO){ downloadViewModel.getProcessingDownloads() @@ -586,6 +588,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure items.forEach { it.videoPreferences.removeAudio = checked } CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } }, + recodeVideoClicked = {checked -> + items.forEach { it.videoPreferences.recodeVideo = checked } + CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } + }, alsoDownloadAsAudioClicked = {}, extraCommandsClicked = { val callback = object : ExtraCommandsListener { @@ -645,14 +651,30 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure bottomAppBar.menu.children.forEach { m -> m.isEnabled = !loading } } - private fun updateFileSize(items: List){ - if (items.all { it > 5L }){ - val size = FileUtil.convertFileSize(items.sum()) - if (size != "?"){ - filesize.visibility = View.VISIBLE - filesize.text = "${getString(R.string.file_size)}: >~ $size" + private fun updateFileSize(items: List){ + val fileSizes = mutableListOf() + items.forEach { + if (it.type == DownloadViewModel.Type.video){ + if (it.format.filesize <= 5L) { + fileSizes.add(0) + }else{ + val preferredAudioFormatIDs = it.videoPreferences.audioFormatIDs + val audioFormatSize = if (it.videoPreferences.removeAudio) { + 0 + }else{ + it.allFormats + .filter { f -> preferredAudioFormatIDs.contains(f.format_id) } + .sumOf { f -> f.filesize } + } + fileSizes.add(it.format.filesize + audioFormatSize) + } } + } + if (fileSizes.all { it > 5L }){ + val size = FileUtil.convertFileSize(fileSizes.sum()) + filesize.isVisible = size != "?" + filesize.text = "${getString(R.string.file_size)}: >~ $size" }else{ filesize.visibility = View.GONE } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt index b58f5620..7bd9573d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt @@ -15,6 +15,7 @@ import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.LinearLayout import android.widget.TextView +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible @@ -104,6 +105,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private thumb = resultItem!!.thumb website = resultItem!!.website url = resultItem!!.url + videoPreferences.apply { + audioFormatIDs = downloadViewModel.getPreferredAudioFormats(allFormats) + } } }else if (currentDownloadItem != null){ val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java) @@ -376,6 +380,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private downloadItem.videoPreferences.removeAudio = it UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, if (it) listOf() else downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) }) }, + recodeVideoClicked = { + downloadItem.videoPreferences.recodeVideo = it + }, alsoDownloadAsAudioClicked = { downloadItem.videoPreferences.alsoDownloadAsAudio = it }, @@ -416,7 +423,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private @SuppressLint("RestrictedApi") fun updateSelectedAudioFormat(format: Format){ - if (genericAudioFormats.contains(format)) { + if (downloadItem.videoPreferences.audioFormatIDs.contains(format.format_id)) { return } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt index c91a4364..3a664102 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/CancelledDownloadsFragment.kt @@ -331,7 +331,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic downloadViewModel.getItemByID(itemID) } downloadViewModel.deleteDownload(deletedItem.id) - Snackbar.make(cancelledRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG) + Snackbar.make(cancelledRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title.ifEmpty { deletedItem.url }, Snackbar.LENGTH_LONG) .setAction(getString(R.string.undo)) { downloadViewModel.insert(deletedItem) }.show() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt index dbdfed6a..3d363350 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ErroredDownloadsFragment.kt @@ -334,7 +334,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL downloadViewModel.getItemByID(itemID) } downloadViewModel.deleteDownload(deletedItem.id) - Snackbar.make(erroredRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG) + Snackbar.make(erroredRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title.ifEmpty { deletedItem.url }, Snackbar.LENGTH_LONG) .setAction(getString(R.string.undo)) { downloadViewModel.insert(deletedItem) }.show() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/QueuedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/QueuedDownloadsFragment.kt index df64c695..d1bfbecc 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/QueuedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/QueuedDownloadsFragment.kt @@ -146,7 +146,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis downloadViewModel.getItemByID(id) } val deleteDialog = MaterialAlertDialogBuilder(requireContext()) - deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title + "\"!") + deleteDialog.setTitle(getString(R.string.you_are_going_to_delete) + " \"" + item.title.ifEmpty { item.url } + "\"!") deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() } deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int -> item.status = DownloadRepository.Status.Cancelled.toString() @@ -154,7 +154,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis downloadViewModel.updateDownload(item) } - Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + item.title, Snackbar.LENGTH_LONG) + Snackbar.make(queuedRecyclerView, getString(R.string.cancelled) + ": " + item.title.ifEmpty { item.url }, Snackbar.LENGTH_LONG) .setAction(getString(R.string.undo)) { lifecycleScope.launch(Dispatchers.IO) { downloadViewModel.deleteDownload(item.id) diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt index d11ad0b3..daa152d1 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/SavedDownloadsFragment.kt @@ -321,7 +321,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis downloadViewModel.getItemByID(itemID) } downloadViewModel.deleteDownload(deletedItem.id) - Snackbar.make(savedRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG) + Snackbar.make(savedRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title.ifEmpty { deletedItem.url }, Snackbar.LENGTH_LONG) .setAction(getString(R.string.undo)) { downloadViewModel.insert(deletedItem) }.show() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt index fbadea65..a8cd3f3d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/ScheduledDownloadsFragment.kt @@ -336,7 +336,7 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl downloadViewModel.getItemByID(itemID) } downloadViewModel.deleteDownload(deletedItem.id) - Snackbar.make(scheduledRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_LONG) + Snackbar.make(scheduledRecyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title.ifEmpty { deletedItem.url }, Snackbar.LENGTH_LONG) .setAction(getString(R.string.undo)) { downloadViewModel.insert(deletedItem) }.show() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt index bc5036a4..7d25eae4 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt @@ -84,7 +84,9 @@ class DownloadLogFragment : Fragment() { val clipboard: ClipboardManager = mainActivity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager clipboard.setText(content.text) - Snackbar.make(bottomAppBar, getString(R.string.copied_to_clipboard), Snackbar.LENGTH_LONG).show() + Snackbar.make(bottomAppBar, getString(R.string.copied_to_clipboard), Snackbar.LENGTH_LONG) + .setAnchorView(bottomAppBar) + .show() } val id = arguments?.getLong("logID") diff --git a/app/src/main/java/com/deniscerri/ytdl/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/InfoUtil.kt index 044f5684..28848d00 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/InfoUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/InfoUtil.kt @@ -202,7 +202,7 @@ class InfoUtil(private val context: Context) { Html.fromHtml(obj.getString("uploader").toString()).toString() }catch (e: Exception){ Html.fromHtml(obj.getString("uploaderName").toString()).toString() - } + }.replace(" - Topic", "") val duration = obj.getInt("duration").toStringDuration(Locale.US) val thumb = "https://i.ytimg.com/vi/$id/hqdefault.jpg" @@ -658,8 +658,12 @@ class InfoUtil(private val context: Context) { } } val lang = sharedPreferences.getString("app_language", "en") - if (searchEngine == "ytsearch" && context.getStringArray(R.array.subtitle_langs).contains(lang)){ - request.addOption("--extractor-args", "youtube:lang=$lang") + if (searchEngine == "ytsearch" || query.isYoutubeURL()) { + var extractorArgs = "player_client=default,mediaconnect,android" + if (context.getStringArray(R.array.subtitle_langs).contains(lang)) { + extractorArgs += ";lang=$lang" + } + request.addOption("--extractor-args", "youtube:$extractorArgs") } request.addOption("--flat-playlist") @@ -708,7 +712,7 @@ class InfoUtil(private val context: Context) { val title = jsonObject.getStringByAny("alt_title", "title", "webpage_url_basename") if (title == "[Private video]" || title == "[Deleted video]") continue - var author = jsonObject.getStringByAny("uploader", "channel", "playlist_uploader", "uploader_id") + var author = jsonObject.getStringByAny("artist", "uploader", "channel", "playlist_uploader", "uploader_id") var duration = jsonObject.getIntByAny("duration").toString() if (duration != "-1"){ duration = jsonObject.getInt("duration").toStringDuration(Locale.US) @@ -1179,6 +1183,15 @@ class InfoUtil(private val context: Context) { if (sharedPreferences.getBoolean("write_description", false)){ request.addOption("--write-description") } + + if (downloadItem.url.isYoutubeURL()) { + var extractorArgs = "player_client=default,mediaconnect,android" + val lang = sharedPreferences.getString("app_language", "en") + if (context.getStringArray(R.array.subtitle_langs).contains(lang)) { + extractorArgs += ";lang=$lang" + } + request.addOption("--extractor-args", "youtube:$extractorArgs") + } } if (sharedPreferences.getString("prevent_duplicate_downloads", "")!! == "download_archive"){ @@ -1234,7 +1247,7 @@ class InfoUtil(private val context: Context) { } if (abrSort.isNotBlank()){ - formatSorting.append(",abr~${abrSort}") + formatSorting.append(",abr:${abrSort}") } if(ext.isNotBlank()){ @@ -1254,15 +1267,15 @@ class InfoUtil(private val context: Context) { if (embedMetadata){ request.addOption("--embed-metadata") - request.addOption("--parse-metadata", "%(artist,uploader)s:^(?P[^,]*)") + request.addOption("--parse-metadata", "%(artist,uploader|)s:^(?P[^,]*)") request.addOption("--parse-metadata", "%(album_artist,meta_album_artist|)s:%(album_artist)s") request.addOption("--parse-metadata", "description:(?:Released on: )(?P\\d{4})") - request.addOption("--parse-metadata", "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:%(meta_date)s") + request.addOption("--parse-metadata", "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P\\d+)") if (downloadItem.playlistTitle.isNotEmpty()) { request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s") - request.addOption("--parse-metadata", "%(track_number,playlist_index)d:%(track_number)s") + request.addOption("--parse-metadata", "%(track_number,playlist_index)d:(?P\\d+)") } else { request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s") } @@ -1320,8 +1333,14 @@ class InfoUtil(private val context: Context) { supportedContainers.contains(outputContainer) ){ cont = outputContainer - request.addOption("--merge-output-format", outputContainer.lowercase()) - if (outputContainer != "webm") { + + if (downloadItem.videoPreferences.recodeVideo) { + request.addOption("--recode-video", outputContainer.lowercase()) + }else{ + request.addOption("--merge-output-format", outputContainer.lowercase()) + } + + if (!listOf("webm", "avi", "flv").contains(outputContainer.lowercase())) { val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) if (embedThumb) { request.addOption("--embed-thumbnail") diff --git a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt index 164b3e04..05ab1c08 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt @@ -163,7 +163,7 @@ object UiUtil { var filesize = chosenFormat.filesize - if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize } + if (!audioFormats.isNullOrEmpty() && filesize > 10L) filesize += audioFormats.sumOf { it.filesize } formatCard.findViewById(R.id.file_size).apply { text = FileUtil.convertFileSize(filesize) setOnClickListener { @@ -1130,29 +1130,18 @@ object UiUtil { saveAutoSubtitlesClicked: (Boolean) -> Unit, subtitleLanguagesSet: (String) -> Unit, removeAudioClicked: (Boolean) -> Unit, + recodeVideoClicked: (Boolean) -> Unit, alsoDownloadAsAudioClicked: (Boolean) -> Unit, extraCommandsClicked: () -> Unit ){ val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - val embedSubs = view.findViewById(R.id.embed_subtitles) - val saveSubtitles = view.findViewById(R.id.save_subtitles) - val saveAutoSubtitles = view.findViewById(R.id.save_auto_subtitles) - val subtitleLanguages = view.findViewById(R.id.subtitle_languages) - - embedSubs!!.isChecked = items.all { it.videoPreferences.embedSubs } - embedSubs.setOnClickListener { - subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked - embedSubsClicked(embedSubs.isChecked) - } - val addChapters = view.findViewById(R.id.add_chapters) addChapters!!.isChecked = items.all { it.videoPreferences.addChapters } addChapters.setOnClickListener{ addChaptersClicked(addChapters.isChecked) } - val splitByChapters = view.findViewById(R.id.split_by_chapters) if(items.size == 1 && items[0].downloadSections.isNotBlank()){ splitByChapters.isEnabled = false @@ -1183,6 +1172,111 @@ object UiUtil { saveThumbnailClicked(saveThumbnail.isChecked) } + + val adjustSubtitles = view.findViewById(R.id.adjust_subtitles) + adjustSubtitles.setOnClickListener { + val adjustSubtitleView = context.layoutInflater.inflate(R.layout.subtitle_download_preferences_dialog, null) + val embedSubs = adjustSubtitleView.findViewById(R.id.embed_subtitles) + val saveSubtitles = adjustSubtitleView.findViewById(R.id.save_subs) + val saveAutoSubtitles = adjustSubtitleView.findViewById(R.id.save_auto_subs) + val subtitleLanguages = adjustSubtitleView.findViewById(R.id.subtitle_languages) + val subtitleLanguagesDescription = adjustSubtitleView.findViewById(R.id.subtitle) + subtitleLanguagesDescription.text = items.first().videoPreferences.subsLanguages + subtitleLanguages.isClickable = embedSubs.isChecked || saveSubtitles.isChecked + + embedSubs!!.isChecked = items.all { it.videoPreferences.embedSubs } + embedSubs.setOnClickListener { + subtitleLanguages.isClickable = embedSubs.isChecked || saveSubtitles.isChecked + embedSubsClicked(embedSubs.isChecked) + } + + if (items.all { it.videoPreferences.writeSubs}) { + saveSubtitles.isChecked = true + subtitleLanguages.visibility = View.VISIBLE + } + + if (items.all { it.videoPreferences.writeAutoSubs}) { + saveAutoSubtitles.isChecked = true + subtitleLanguages.visibility = View.VISIBLE + } + + saveSubtitles.setOnCheckedChangeListener { _, _ -> + subtitleLanguages.isClickable = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked + saveSubtitlesClicked(saveSubtitles.isChecked) + } + + saveAutoSubtitles.setOnCheckedChangeListener { _, _ -> + subtitleLanguages.isClickable = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked + saveAutoSubtitlesClicked(saveAutoSubtitles.isChecked) + } + + subtitleLanguages.isClickable = embedSubs.isChecked || saveSubtitles.isChecked + subtitleLanguages.setOnClickListener { + val currentSubtitleLang = if (items.size == 1 || items.all { it.videoPreferences.subsLanguages == items[0].videoPreferences.subsLanguages }){ + items[0].videoPreferences.subsLanguages + }else { + "" + }.ifEmpty { sharedPreferences.getString("subs_lang", "en.*,.*-orig")!! } + + showSubtitleLanguagesDialog(context, currentSubtitleLang){ + subtitleLanguagesSet(it) + subtitleLanguagesDescription.text = it + } + } + + val adjustSubtitleDialog = MaterialAlertDialogBuilder(context) + .setTitle(context.getString(R.string.subtitles)) + .setView(adjustSubtitleView) + .setIcon(R.drawable.ic_subtitles) + .setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> } + + adjustSubtitleDialog.show() + } + + if (items.size == 1 && items.first().id == 0L){ + val adjustAudio = view.findViewById(R.id.adjust_audio) + adjustAudio.setOnClickListener { + val adjustAudioView = context.layoutInflater.inflate(R.layout.audio_download_preferences_dialog, null) + adjustAudioView.findViewById(R.id.remove_audio).apply { + isChecked = items.first().videoPreferences.removeAudio + setOnCheckedChangeListener { _, b -> + removeAudioClicked(b) + } + } + + adjustAudioView.findViewById(R.id.also_download_audio).apply { + isChecked = items.first().videoPreferences.alsoDownloadAsAudio + setOnCheckedChangeListener { _, b -> + alsoDownloadAsAudioClicked(b) + } + } + + val adjustAudioDialog = MaterialAlertDialogBuilder(context) + .setTitle(context.getString(R.string.audio)) + .setView(adjustAudioView) + .setIcon(R.drawable.ic_music) + .setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> } + + adjustAudioDialog.show() + } + }else{ + val adjustAudio = view.findViewById(R.id.adjust_audio) + adjustAudio.isVisible = false + val removeAudio = view.findViewById(R.id.remove_audio) + removeAudio.isVisible = true + removeAudio.isChecked = items.all { it.videoPreferences.removeAudio } + removeAudio.setOnCheckedChangeListener { _, _ -> + removeAudioClicked(removeAudio.isChecked) + } + } + + val recodeVideo = view.findViewById(R.id.recode_video) + recodeVideo.isChecked = items.all { it.videoPreferences.recodeVideo } + recodeVideo.setOnCheckedChangeListener { _, _ -> + recodeVideoClicked(recodeVideo.isChecked) + } + + val sponsorBlock = view.findViewById(R.id.sponsorblock_filters) sponsorBlock.isEnabled = sharedPreferences.getBoolean("use_sponsorblock", true) sponsorBlock!!.setOnClickListener { @@ -1284,78 +1378,6 @@ object UiUtil { } } - - - items.forEach { it.videoPreferences.subsLanguages = sharedPreferences.getString("subs_lang", "en.*,.*-orig")!! } - if (items.all { it.videoPreferences.writeSubs}) { - saveSubtitles.isChecked = true - subtitleLanguages.visibility = View.VISIBLE - } - - if (items.all { it.videoPreferences.writeAutoSubs}) { - saveAutoSubtitles.isChecked = true - subtitleLanguages.visibility = View.VISIBLE - } - - saveSubtitles.setOnCheckedChangeListener { _, _ -> - subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked - saveSubtitlesClicked(saveSubtitles.isChecked) - } - - saveAutoSubtitles.setOnCheckedChangeListener { _, _ -> - subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked || saveAutoSubtitles.isChecked - saveAutoSubtitlesClicked(saveAutoSubtitles.isChecked) - } - - subtitleLanguages.isEnabled = embedSubs.isChecked || saveSubtitles.isChecked - subtitleLanguages.setOnClickListener { - val currentSubtitleLang = if (items.size == 1 || items.all { it.videoPreferences.subsLanguages == items[0].videoPreferences.subsLanguages }){ - items[0].videoPreferences.subsLanguages - }else { - "" - } - showSubtitleLanguagesDialog(context, currentSubtitleLang){ - subtitleLanguagesSet(it) - } - } - - if (items.size == 1 && items.first().id == 0L){ - val adjustAudio = view.findViewById(R.id.adjust_audio) - adjustAudio.setOnClickListener { - val adjustAudioView = context.layoutInflater.inflate(R.layout.audio_download_preferences_dialog, null) - adjustAudioView.findViewById(R.id.remove_audio).apply { - isChecked = items.first().videoPreferences.removeAudio - setOnCheckedChangeListener { _, b -> - removeAudioClicked(b) - } - } - - adjustAudioView.findViewById(R.id.also_download_audio).apply { - isChecked = items.first().videoPreferences.alsoDownloadAsAudio - setOnCheckedChangeListener { _, b -> - alsoDownloadAsAudioClicked(b) - } - } - - val adjustAudioDialog = MaterialAlertDialogBuilder(context) - .setTitle(context.getString(R.string.adjust_audio)) - .setView(adjustAudioView) - .setIcon(R.drawable.ic_music) - .setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> } - - adjustAudioDialog.show() - } - }else{ - val adjustAudio = view.findViewById(R.id.adjust_audio) - adjustAudio.isVisible = false - val removeAudio = view.findViewById(R.id.remove_audio) - removeAudio.isVisible = true - removeAudio.isChecked = items.all { it.videoPreferences.removeAudio } - removeAudio.setOnCheckedChangeListener { _, _ -> - removeAudioClicked(removeAudio.isChecked) - } - } - val extraCommands = view.findViewById(R.id.extra_commands) if (sharedPreferences.getBoolean("use_extra_commands", false)){ extraCommands.visibility = View.VISIBLE @@ -1849,7 +1871,7 @@ object UiUtil { withContext(Dispatchers.Main){ view.findViewById(R.id.suggested).visibility = View.VISIBLE chips.forEach { - it.isChecked = editText.text.contains(it.text) + it.isChecked = editText.text == it.text chipGroup!!.addView(it) } diff --git a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt index c3ce6601..46215f49 100644 --- a/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdl/work/DownloadWorker.kt @@ -179,7 +179,9 @@ class DownloadWorker( } } }.onSuccess { - resultRepo.updateDownloadItem(downloadItem) + resultRepo.updateDownloadItem(downloadItem)?.apply { + dao.updateWithoutUpsert(this) + } val wasQuickDownloaded = resultDao.getCountInt() == 0 runBlocking { var finalPaths : MutableList? diff --git a/app/src/main/res/layout/adjust_audio.xml b/app/src/main/res/layout/adjust_audio.xml index 1004361a..64ce03d2 100644 --- a/app/src/main/res/layout/adjust_audio.xml +++ b/app/src/main/res/layout/adjust_audio.xml @@ -96,6 +96,34 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + + + - - + + \ No newline at end of file diff --git a/app/src/main/res/layout/adjust_video.xml b/app/src/main/res/layout/adjust_video.xml index 4678176f..e2ef2ef6 100644 --- a/app/src/main/res/layout/adjust_video.xml +++ b/app/src/main/res/layout/adjust_video.xml @@ -27,12 +27,12 @@ app:singleLine="true"> + android:text="@string/save_thumb" /> + android:layout_height="wrap_content" + android:scrollbars="none"> + + + + - - + android:text="@string/recode_video" /> - - - - - - - - - - - - - - - - - - @@ -196,21 +163,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - @@ -227,7 +179,6 @@ + + diff --git a/app/src/main/res/layout/format_item.xml b/app/src/main/res/layout/format_item.xml index 43d76500..f63f1e31 100644 --- a/app/src/main/res/layout/format_item.xml +++ b/app/src/main/res/layout/format_item.xml @@ -86,7 +86,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/format_note"> + app:layout_constraintTop_toBottomOf="@id/formatnote_formatid"> + + diff --git a/app/src/main/res/layout/subtitle_download_preferences_dialog.xml b/app/src/main/res/layout/subtitle_download_preferences_dialog.xml new file mode 100644 index 00000000..dbde0165 --- /dev/null +++ b/app/src/main/res/layout/subtitle_download_preferences_dialog.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-sq-rAL/strings.xml b/app/src/main/res/values-sq-rAL/strings.xml index 41a632b6..a68a5424 100644 --- a/app/src/main/res/values-sq-rAL/strings.xml +++ b/app/src/main/res/values-sq-rAL/strings.xml @@ -358,6 +358,7 @@ Ditë Muaj Arkiva e Shkarkimeve [Përdor skedarin e arkivës të yt-dlp për të parandaluar shkarkimet duplicate] + Fikur Gjestet e rrëshqitjes në kartën e shkarkimit Shfaq numrat në faqen e rradhës së shkarkimeve Shkarko Gjithashtu si Audio @@ -397,5 +398,17 @@ Vendi Duhet të aktivizoni lejen SCHEDULE_EXACT_ALARM në cilësimet e aplikacionit. Shkarkimet janë ende duke u ngarkuar. Vazhdoni t\'i përpunoni ato në sfond dhe të filloni shkarkimin? + Navigimi + Pamja e Titujve në Navigim + Gjithmonë + Sinkronizo Me Burimin Rradha + Rivendos + Përdor AlarmManager në vend të WorkManager për Skedulim + Aktivizo këtë ku WorkManager është i limituar në paisje ose punët e skeduluara nuk ekzekutohen në kohë + Përdor URL e videos në vend të URL së playlist + E dobishme për playlistet Libretube qe nuk njihen nga yt-dlp. Metadata e playlist nuk do te shtohen dot kur ky opsion është i aktivizuar + Rikodo Videon + Rikodon skedarin e videos sipas formatit të cilësuar nga përdoruesi + Shtyp \'Rikthe\' për të kombinuar të dhënat e ruajtura me të dhënat aktuale. Shtyp \'Rivendos\' për të fshirë gjithçka dhe vetëm përdor të dhënat e ruajtura në skedar. \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index b82368b4..396865fd 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -26,9 +26,9 @@ mp4 webm mkv + mov avi flv - mov @@ -36,9 +36,9 @@ mp4 webm mkv + mov avi flv - mov diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 766f3928..f23eefe1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -412,4 +412,6 @@ Enable this if WorkManager is restricted by your device vendor or scheduled jobs are not too precise Use Item URL instead of Playlist URL Useful for Libretube offline playlists that are not recognised by yt-dlp. Playlist metadata wont be embedded with this enabled + Recode Video + Recodes the video file to the specified video format \ No newline at end of file diff --git a/app/src/main/res/xml/processing_preferences.xml b/app/src/main/res/xml/processing_preferences.xml index 968abc8b..b2442c11 100644 --- a/app/src/main/res/xml/processing_preferences.xml +++ b/app/src/main/res/xml/processing_preferences.xml @@ -180,6 +180,15 @@ app:useSimpleSummaryProvider="true" app:title="@string/video_format" /> + + + --parse-metadata "%(track_number,playlist_index)d:(?P\d+)" --parse-metadata "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P\d+)" + +Removed NA from meta album artist --parse-metadata "%(artist,uploader|)s:^(?P[^,]*)" + +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 \ No newline at end of file