more changes
This commit is contained in:
parent
4eb6cce374
commit
f4f527721c
31 changed files with 578 additions and 301 deletions
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<List<DownloadItem>>
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
||||
fun getProcessingDownloads() : Flow<List<DownloadItemSimple>>
|
||||
fun getProcessingDownloads() : Flow<List<DownloadItemConfigureMultiple>>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM downloads WHERE status in (:statuses)")
|
||||
fun getDownloadsCountFlow(statuses: List<String>) : Flow<Int>
|
||||
|
|
@ -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<DownloadItem>){
|
||||
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {
|
||||
val toReturn = mutableListOf<DownloadItem>()
|
||||
list.forEach {
|
||||
update(it)
|
||||
it.id = update(it)
|
||||
toReturn.add(it)
|
||||
}
|
||||
|
||||
return toReturn
|
||||
}
|
||||
|
||||
@Query("UPDATE downloads set status=:status where id=:id")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Format>,
|
||||
var audioPreferences: AudioPreferences,
|
||||
var videoPreferences: VideoPreferences,
|
||||
@ColumnInfo(defaultValue = "Queued")
|
||||
var status: String,
|
||||
var type: DownloadViewModel.Type,
|
||||
var incognito: Boolean = false
|
||||
)
|
||||
|
|
@ -14,5 +14,6 @@ data class VideoPreferences (
|
|||
var subsLanguages: String = "en.*,.*-orig",
|
||||
var audioFormatIDs : ArrayList<String> = arrayListOf(),
|
||||
var removeAudio: Boolean = false,
|
||||
var alsoDownloadAsAudio: Boolean = false
|
||||
var alsoDownloadAsAudio: Boolean = false,
|
||||
var recodeVideo: Boolean = false
|
||||
) : Parcelable
|
||||
|
|
|
|||
|
|
@ -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<List<DownloadItem>> = downloadDao.getActiveDownloads().distinctUntilChanged()
|
||||
val processingDownloads : Flow<List<DownloadItemSimple>> = downloadDao.getProcessingDownloads().distinctUntilChanged()
|
||||
val processingDownloads : Flow<List<DownloadItemConfigureMultiple>> = downloadDao.getProcessingDownloads().distinctUntilChanged()
|
||||
val queuedDownloads : Pager<Int, DownloadItemSimple> = 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<Int> = 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<DownloadItem>) {
|
||||
downloadDao.updateAll(list)
|
||||
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {
|
||||
return downloadDao.updateAll(list)
|
||||
}
|
||||
|
||||
suspend fun updateWithoutUpsert(item: DownloadItem){
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<PagingData<DownloadItem>>
|
||||
val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||
val activeDownloads : Flow<List<DownloadItem>>
|
||||
val processingDownloads : Flow<List<DownloadItemSimple>>
|
||||
val processingDownloads : Flow<List<DownloadItemConfigureMultiple>>
|
||||
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||
val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||
val savedDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<DownloadItemSimple?, ConfigureMultipleDownloadsAdapter.ViewHolder>(
|
||||
class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter<DownloadItemConfigureMultiple?, ConfigureMultipleDownloadsAdapter.ViewHolder>(
|
||||
AsyncDifferConfig.Builder(
|
||||
DIFF_CALLBACK
|
||||
).build()) {
|
||||
|
|
@ -111,7 +113,25 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
|||
}
|
||||
|
||||
val fileSize = card.findViewById<TextView>(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<DownloadItemSimple> = object : DiffUtil.ItemCallback<DownloadItemSimple>() {
|
||||
override fun areItemsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean {
|
||||
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItemConfigureMultiple> = object : DiffUtil.ItemCallback<DownloadItemConfigureMultiple>() {
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<MaterialCardView>(R.id.format_card_constraintLayout)
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, listOf())
|
||||
|
|
|
|||
|
|
@ -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<View>(android.R.id.content)!!
|
||||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
||||
sheetView.findViewById<View>(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<View>(android.R.id.content)!!
|
||||
sheetView.findViewById<View>(R.id.adjust).setPadding(padding,padding,padding,padding)
|
||||
sheetView.findViewById<View>(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<Long>){
|
||||
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<DownloadItemConfigureMultiple>){
|
||||
val fileSizes = mutableListOf<Long>()
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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<meta_album_artist>[^,]*)")
|
||||
request.addOption("--parse-metadata", "%(artist,uploader|)s:^(?P<meta_album_artist>[^,]*)")
|
||||
request.addOption("--parse-metadata", "%(album_artist,meta_album_artist|)s:%(album_artist)s")
|
||||
|
||||
request.addOption("--parse-metadata", "description:(?:Released on: )(?P<dscrptn_year>\\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<meta_date>\\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<track_number>\\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")
|
||||
|
|
|
|||
|
|
@ -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<TextView>(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<Chip>(R.id.embed_subtitles)
|
||||
val saveSubtitles = view.findViewById<Chip>(R.id.save_subtitles)
|
||||
val saveAutoSubtitles = view.findViewById<Chip>(R.id.save_auto_subtitles)
|
||||
val subtitleLanguages = view.findViewById<Chip>(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<Chip>(R.id.add_chapters)
|
||||
addChapters!!.isChecked = items.all { it.videoPreferences.addChapters }
|
||||
addChapters.setOnClickListener{
|
||||
addChaptersClicked(addChapters.isChecked)
|
||||
}
|
||||
|
||||
|
||||
val splitByChapters = view.findViewById<Chip>(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<Chip>(R.id.adjust_subtitles)
|
||||
adjustSubtitles.setOnClickListener {
|
||||
val adjustSubtitleView = context.layoutInflater.inflate(R.layout.subtitle_download_preferences_dialog, null)
|
||||
val embedSubs = adjustSubtitleView.findViewById<MaterialSwitch>(R.id.embed_subtitles)
|
||||
val saveSubtitles = adjustSubtitleView.findViewById<MaterialSwitch>(R.id.save_subs)
|
||||
val saveAutoSubtitles = adjustSubtitleView.findViewById<MaterialSwitch>(R.id.save_auto_subs)
|
||||
val subtitleLanguages = adjustSubtitleView.findViewById<ConstraintLayout>(R.id.subtitle_languages)
|
||||
val subtitleLanguagesDescription = adjustSubtitleView.findViewById<TextView>(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<Chip>(R.id.adjust_audio)
|
||||
adjustAudio.setOnClickListener {
|
||||
val adjustAudioView = context.layoutInflater.inflate(R.layout.audio_download_preferences_dialog, null)
|
||||
adjustAudioView.findViewById<MaterialSwitch>(R.id.remove_audio).apply {
|
||||
isChecked = items.first().videoPreferences.removeAudio
|
||||
setOnCheckedChangeListener { _, b ->
|
||||
removeAudioClicked(b)
|
||||
}
|
||||
}
|
||||
|
||||
adjustAudioView.findViewById<MaterialSwitch>(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<Chip>(R.id.adjust_audio)
|
||||
adjustAudio.isVisible = false
|
||||
val removeAudio = view.findViewById<Chip>(R.id.remove_audio)
|
||||
removeAudio.isVisible = true
|
||||
removeAudio.isChecked = items.all { it.videoPreferences.removeAudio }
|
||||
removeAudio.setOnCheckedChangeListener { _, _ ->
|
||||
removeAudioClicked(removeAudio.isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
val recodeVideo = view.findViewById<Chip>(R.id.recode_video)
|
||||
recodeVideo.isChecked = items.all { it.videoPreferences.recodeVideo }
|
||||
recodeVideo.setOnCheckedChangeListener { _, _ ->
|
||||
recodeVideoClicked(recodeVideo.isChecked)
|
||||
}
|
||||
|
||||
|
||||
val sponsorBlock = view.findViewById<Chip>(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<Chip>(R.id.adjust_audio)
|
||||
adjustAudio.setOnClickListener {
|
||||
val adjustAudioView = context.layoutInflater.inflate(R.layout.audio_download_preferences_dialog, null)
|
||||
adjustAudioView.findViewById<MaterialSwitch>(R.id.remove_audio).apply {
|
||||
isChecked = items.first().videoPreferences.removeAudio
|
||||
setOnCheckedChangeListener { _, b ->
|
||||
removeAudioClicked(b)
|
||||
}
|
||||
}
|
||||
|
||||
adjustAudioView.findViewById<MaterialSwitch>(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<Chip>(R.id.adjust_audio)
|
||||
adjustAudio.isVisible = false
|
||||
val removeAudio = view.findViewById<Chip>(R.id.remove_audio)
|
||||
removeAudio.isVisible = true
|
||||
removeAudio.isChecked = items.all { it.videoPreferences.removeAudio }
|
||||
removeAudio.setOnCheckedChangeListener { _, _ ->
|
||||
removeAudioClicked(removeAudio.isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
val extraCommands = view.findViewById<Chip>(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<View>(R.id.suggested).visibility = View.VISIBLE
|
||||
chips.forEach {
|
||||
it.isChecked = editText.text.contains(it.text)
|
||||
it.isChecked = editText.text == it.text
|
||||
chipGroup!!.addView(it)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<String>?
|
||||
|
|
|
|||
|
|
@ -96,6 +96,34 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/extra_commands"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/use_extra_commands"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_terminal"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/cut"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
|
|
@ -111,24 +139,10 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/extra_commands"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/use_extra_commands"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_terminal"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -27,12 +27,12 @@
|
|||
app:singleLine="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/embed_subtitles"
|
||||
android:id="@+id/save_thumbnail"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/embed_subtitles" />
|
||||
android:text="@string/save_thumb" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/add_chapters"
|
||||
|
|
@ -57,15 +57,44 @@
|
|||
</HorizontalScrollView>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:scrollbars="none"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleLine="true">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/adjust_subtitles"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/subtitles"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_subtitles"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/remove_audio"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minWidth="30dp"
|
||||
android:text="@string/remove_audio"
|
||||
android:visibility="gone"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/adjust_audio"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
|
|
@ -82,76 +111,14 @@
|
|||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/remove_audio"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/remove_audio"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/save_thumbnail"
|
||||
android:id="@+id/recode_video"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/save_thumb" />
|
||||
android:text="@string/recode_video" />
|
||||
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/save_subtitles"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/save_subs"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:scrollbars="none"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/save_auto_subtitles"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:text="@string/save_auto_subs"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/subtitle_languages"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:enabled="false"
|
||||
android:checked="false"
|
||||
android:text="@string/subtitle_languages"/>
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
|
@ -196,21 +163,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/cut"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_cut"
|
||||
android:minWidth="30dp"
|
||||
android:text="@string/cut"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
|
@ -227,7 +179,6 @@
|
|||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/extra_commands"
|
||||
android:visibility="gone"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
@ -241,6 +192,21 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/cut"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_cut"
|
||||
android:minWidth="30dp"
|
||||
android:text="@string/cut"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
|
|
@ -113,7 +113,6 @@
|
|||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLength="10"
|
||||
android:minWidth="30dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:textColor="@color/white"
|
||||
|
|
@ -180,13 +179,19 @@
|
|||
android:clickable="false"
|
||||
android:ellipsize="end"
|
||||
android:gravity="bottom|end"
|
||||
android:maxLength="10"
|
||||
android:layout_marginEnd="5dp"
|
||||
app:cornerRadius="10dp"
|
||||
android:maxWidth="70dp"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/formatnote_formatid"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="format_note, format_id" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/embed_subtitles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/embed_subtitles" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/save_subs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:text="@string/save_subs"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/save_auto_subs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:text="@string/save_auto_subs"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:id="@+id/subtitle_languages"
|
||||
android:layout_marginHorizontal="20dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:textColor="?android:attr/textColorPrimaryDisableOnly"
|
||||
android:text="@string/subtitle_languages"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -358,6 +358,7 @@
|
|||
<string name="day">Ditë</string>
|
||||
<string name="month">Muaj</string>
|
||||
<string name="archive">Arkiva e Shkarkimeve [Përdor skedarin e arkivës të yt-dlp për të parandaluar shkarkimet duplicate]</string>
|
||||
<string name="disabled">Fikur</string>
|
||||
<string name="swipe_gestures_download_card">Gjestet e rrëshqitjes në kartën e shkarkimit</string>
|
||||
<string name="show_download_count">Shfaq numrat në faqen e rradhës së shkarkimeve</string>
|
||||
<string name="also_download_audio">Shkarko Gjithashtu si Audio</string>
|
||||
|
|
@ -397,5 +398,17 @@
|
|||
<string name="location">Vendi</string>
|
||||
<string name="enable_alarm_permission">Duhet të aktivizoni lejen SCHEDULE_EXACT_ALARM në cilësimet e aplikacionit.</string>
|
||||
<string name="process_downloads_background">Shkarkimet janë ende duke u ngarkuar. Vazhdoni t\'i përpunoni ato në sfond dhe të filloni shkarkimin?</string>
|
||||
<string name="navigation_bar">Navigimi</string>
|
||||
<string name="label_visibility">Pamja e Titujve në Navigim</string>
|
||||
<string name="always">Gjithmonë</string>
|
||||
<string name="sync_with_source">Sinkronizo Me Burimin</string>
|
||||
<string name="queue">Rradha</string>
|
||||
<string name="reset">Rivendos</string>
|
||||
<string name="use_alarm_manager">Përdor AlarmManager në vend të WorkManager për Skedulim</string>
|
||||
<string name="use_alarm_manager_summary">Aktivizo këtë ku WorkManager është i limituar në paisje ose punët e skeduluara nuk ekzekutohen në kohë</string>
|
||||
<string name="use_item_url_not_playlist">Përdor URL e videos në vend të URL së playlist</string>
|
||||
<string name="use_item_url_not_playlist_summary">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</string>
|
||||
<string name="recode_video">Rikodo Videon</string>
|
||||
<string name="recode_video_summary">Rikodon skedarin e videos sipas formatit të cilësuar nga përdoruesi</string>
|
||||
<string name="restore_info">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.</string>
|
||||
</resources>
|
||||
|
|
@ -26,9 +26,9 @@
|
|||
<item>mp4</item>
|
||||
<item>webm</item>
|
||||
<item>mkv</item>
|
||||
<item>mov</item>
|
||||
<item>avi</item>
|
||||
<item>flv</item>
|
||||
<item>mov</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="video_containers_values">
|
||||
|
|
@ -36,9 +36,9 @@
|
|||
<item>mp4</item>
|
||||
<item>webm</item>
|
||||
<item>mkv</item>
|
||||
<item>mov</item>
|
||||
<item>avi</item>
|
||||
<item>flv</item>
|
||||
<item>mov</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="video_formats">
|
||||
|
|
|
|||
|
|
@ -412,4 +412,6 @@
|
|||
<string name="use_alarm_manager_summary">Enable this if WorkManager is restricted by your device vendor or scheduled jobs are not too precise</string>
|
||||
<string name="use_item_url_not_playlist">Use Item URL instead of Playlist URL</string>
|
||||
<string name="use_item_url_not_playlist_summary">Useful for Libretube offline playlists that are not recognised by yt-dlp. Playlist metadata wont be embedded with this enabled</string>
|
||||
<string name="recode_video">Recode Video</string>
|
||||
<string name="recode_video_summary">Recodes the video file to the specified video format</string>
|
||||
</resources>
|
||||
|
|
@ -180,6 +180,15 @@
|
|||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/video_format" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
app:icon="@drawable/baseline_video_metadata"
|
||||
app:key="recode_video"
|
||||
android:summary="@string/recode_video_summary"
|
||||
app:title="@string/recode_video" />
|
||||
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue=""
|
||||
android:entries="@array/video_codec"
|
||||
|
|
|
|||
66
fastlane/metadata/android/en-US/changelogs/10790.txt
Normal file
66
fastlane/metadata/android/en-US/changelogs/10790.txt
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
TEMPORARY just to save changelog
|
||||
|
||||
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
|
||||
-> --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
|
||||
Loading…
Reference in a new issue