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(DBManager.getInstance(this@MainActivity).downloadDao.getDownloadsCountByStatus(listOf("Active", "Queued")) == 0){
|
||||||
if (UpdateUtil(this@MainActivity).updateYoutubeDL() == YoutubeDL.UpdateStatus.DONE) {
|
if (UpdateUtil(this@MainActivity).updateYoutubeDL() == YoutubeDL.UpdateStatus.DONE) {
|
||||||
val version = YoutubeDL.getInstance().version(context)
|
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}]",
|
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.Update
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
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.DownloadItemSimple
|
||||||
import com.deniscerri.ytdl.database.models.Format
|
import com.deniscerri.ytdl.database.models.Format
|
||||||
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
import com.deniscerri.ytdl.database.repository.DownloadRepository
|
||||||
|
|
@ -25,8 +26,9 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloads() : Flow<List<DownloadItem>>
|
fun getActiveDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
|
@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)")
|
@Query("SELECT COUNT(*) FROM downloads WHERE status in (:statuses)")
|
||||||
fun getDownloadsCountFlow(statuses: List<String>) : Flow<Int>
|
fun getDownloadsCountFlow(statuses: List<String>) : Flow<Int>
|
||||||
|
|
@ -181,13 +183,17 @@ interface DownloadDao {
|
||||||
suspend fun deleteSingleProcessing(id: Long)
|
suspend fun deleteSingleProcessing(id: Long)
|
||||||
|
|
||||||
@Upsert
|
@Upsert
|
||||||
suspend fun update(item: DownloadItem)
|
suspend fun update(item: DownloadItem) : Long
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
suspend fun updateAll(list: List<DownloadItem>){
|
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {
|
||||||
|
val toReturn = mutableListOf<DownloadItem>()
|
||||||
list.forEach {
|
list.forEach {
|
||||||
update(it)
|
it.id = update(it)
|
||||||
|
toReturn.add(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return toReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query("UPDATE downloads set status=:status where id=:id")
|
@Query("UPDATE downloads set status=:status where id=:id")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import com.deniscerri.ytdl.database.models.LogItem
|
import com.deniscerri.ytdl.database.models.LogItem
|
||||||
|
import com.deniscerri.ytdl.util.Extensions.appendLineToLog
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@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)
|
@Update(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun update(item: LogItem)
|
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 subsLanguages: String = "en.*,.*-orig",
|
||||||
var audioFormatIDs : ArrayList<String> = arrayListOf(),
|
var audioFormatIDs : ArrayList<String> = arrayListOf(),
|
||||||
var removeAudio: Boolean = false,
|
var removeAudio: Boolean = false,
|
||||||
var alsoDownloadAsAudio: Boolean = false
|
var alsoDownloadAsAudio: Boolean = false,
|
||||||
|
var recodeVideo: Boolean = false
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import com.deniscerri.ytdl.App
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.dao.DownloadDao
|
import com.deniscerri.ytdl.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
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.DownloadItemSimple
|
||||||
import com.deniscerri.ytdl.util.Extensions.toListString
|
import com.deniscerri.ytdl.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
|
|
@ -43,7 +44,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
pagingSourceFactory = {downloadDao.getAllDownloads()}
|
pagingSourceFactory = {downloadDao.getAllDownloads()}
|
||||||
)
|
)
|
||||||
val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads().distinctUntilChanged()
|
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(
|
val queuedDownloads : Pager<Int, DownloadItemSimple> = Pager(
|
||||||
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
|
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
|
||||||
pagingSourceFactory = {downloadDao.getQueuedDownloads()}
|
pagingSourceFactory = {downloadDao.getQueuedDownloads()}
|
||||||
|
|
@ -74,7 +75,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val scheduledDownloadsCount : Flow<Int> = downloadDao.getDownloadsCountByStatusFlow(listOf(Status.Scheduled).toListString())
|
val scheduledDownloadsCount : Flow<Int> = downloadDao.getDownloadsCountByStatusFlow(listOf(Status.Scheduled).toListString())
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Active, Queued, Error, Cancelled, Saved, Processing, Scheduled
|
Active, Queued, Error, Cancelled, Saved, Processing, Scheduled, Duplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun insert(item: DownloadItem) : Long {
|
suspend fun insert(item: DownloadItem) : Long {
|
||||||
|
|
@ -102,8 +103,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
downloadDao.update(item)
|
downloadDao.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateAll(list: List<DownloadItem>) {
|
suspend fun updateAll(list: List<DownloadItem>) : List<DownloadItem> {
|
||||||
downloadDao.updateAll(list)
|
return downloadDao.updateAll(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateWithoutUpsert(item: DownloadItem){
|
suspend fun updateWithoutUpsert(item: DownloadItem){
|
||||||
|
|
|
||||||
|
|
@ -39,12 +39,8 @@ class LogRepository(private val logDao: LogDao) {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun update(line: String, id: Long){
|
suspend fun update(line: String, id: Long){
|
||||||
runCatching {
|
kotlin.runCatching {
|
||||||
val item = getItem(id) ?: return
|
logDao.updateLog(line, id)
|
||||||
val log = item.content ?: ""
|
|
||||||
item.content = log.appendLineToLog(line)
|
|
||||||
//item.content += "\n$line"
|
|
||||||
logDao.update(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import com.deniscerri.ytdl.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdl.database.models.AudioPreferences
|
import com.deniscerri.ytdl.database.models.AudioPreferences
|
||||||
import com.deniscerri.ytdl.database.models.CommandTemplate
|
import com.deniscerri.ytdl.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
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.DownloadItemSimple
|
||||||
import com.deniscerri.ytdl.database.models.Format
|
import com.deniscerri.ytdl.database.models.Format
|
||||||
import com.deniscerri.ytdl.database.models.HistoryItem
|
import com.deniscerri.ytdl.database.models.HistoryItem
|
||||||
|
|
@ -68,7 +69,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
val allDownloads : Flow<PagingData<DownloadItem>>
|
val allDownloads : Flow<PagingData<DownloadItem>>
|
||||||
val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
|
val queuedDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val activeDownloads : Flow<List<DownloadItem>>
|
val activeDownloads : Flow<List<DownloadItem>>
|
||||||
val processingDownloads : Flow<List<DownloadItemSimple>>
|
val processingDownloads : Flow<List<DownloadItemConfigureMultiple>>
|
||||||
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
|
val cancelledDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
|
val erroredDownloads : Flow<PagingData<DownloadItemSimple>>
|
||||||
val savedDownloads : 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 embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
|
||||||
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
|
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
|
||||||
val saveAutoSubs = sharedPreferences.getBoolean("write_auto_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 addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
|
|
@ -250,7 +252,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
ArrayList(sponsorblock),
|
ArrayList(sponsorblock),
|
||||||
saveSubs,
|
saveSubs,
|
||||||
saveAutoSubs,
|
saveAutoSubs,
|
||||||
audioFormatIDs = preferredAudioFormats
|
audioFormatIDs = preferredAudioFormats,
|
||||||
|
recodeVideo = recodeVideo
|
||||||
)
|
)
|
||||||
|
|
||||||
val extraCommands = when(type){
|
val extraCommands = when(type){
|
||||||
|
|
@ -383,6 +386,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
|
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
|
||||||
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
|
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
|
||||||
val saveAutoSubs = sharedPreferences.getBoolean("write_auto_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 addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||||
val embedThumb = sharedPreferences.getBoolean("embed_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 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
|
var path = defaultPath
|
||||||
historyItem.downloadPath.first().apply {
|
historyItem.downloadPath.first().apply {
|
||||||
File(this).parent?.apply {
|
File(this).parent?.apply {
|
||||||
|
|
@ -789,43 +793,36 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
//CHECK DUPLICATES
|
//CHECK DUPLICATES
|
||||||
var alreadyExists = false
|
var isDuplicate = false
|
||||||
if (checkDuplicate.isNotEmpty() && !ignoreDuplicates){
|
if (checkDuplicate.isNotEmpty() && !ignoreDuplicates){
|
||||||
when(checkDuplicate){
|
when(checkDuplicate){
|
||||||
"download_archive" -> {
|
"download_archive" -> {
|
||||||
if (downloadArchive.any { d -> it.url.contains(d) }){
|
if (downloadArchive.any { d -> it.url.contains(d) }){
|
||||||
alreadyExists = true
|
isDuplicate = true
|
||||||
if (it.id == 0L) {
|
if (it.id == 0L){
|
||||||
it.status = DownloadRepository.Status.Processing.toString()
|
|
||||||
val id = runBlocking {
|
val id = runBlocking {
|
||||||
repository.insert(it)
|
repository.insert(it)
|
||||||
}
|
}
|
||||||
it.id = id
|
it.id = id
|
||||||
}
|
}
|
||||||
existingItemIDs.add(
|
it.status = DownloadRepository.Status.Duplicate.toString()
|
||||||
AlreadyExistsIDs(
|
repository.update(it)
|
||||||
it.id,
|
existingItemIDs.add(AlreadyExistsIDs(it.id,null))
|
||||||
null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"url_type" -> {
|
"url_type" -> {
|
||||||
val existingDownload = activeAndQueuedDownloads.firstOrNull { a -> a.type == it.type && a.url == it.url }
|
val existingDownload = activeAndQueuedDownloads.firstOrNull { a -> a.type == it.type && a.url == it.url }
|
||||||
if (existingDownload != null){
|
if (existingDownload != null){
|
||||||
it.status = DownloadRepository.Status.Processing.toString()
|
isDuplicate = true
|
||||||
val id = runBlocking {
|
if (it.id == 0L){
|
||||||
repository.insert(it)
|
val id = runBlocking {
|
||||||
|
repository.insert(it)
|
||||||
|
}
|
||||||
|
it.id = id
|
||||||
}
|
}
|
||||||
it.id = id
|
it.status = DownloadRepository.Status.Duplicate.toString()
|
||||||
|
repository.update(it)
|
||||||
alreadyExists = true
|
existingItemIDs.add(AlreadyExistsIDs(it.id,null))
|
||||||
existingItemIDs.add(
|
|
||||||
AlreadyExistsIDs(
|
|
||||||
it.id,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}else{
|
}else{
|
||||||
//check if downloaded and file exists
|
//check if downloaded and file exists
|
||||||
val history = withContext(Dispatchers.IO){
|
val history = withContext(Dispatchers.IO){
|
||||||
|
|
@ -837,17 +834,16 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingHistoryItem != null){
|
if (existingHistoryItem != null){
|
||||||
alreadyExists = true
|
isDuplicate = true
|
||||||
it.status = DownloadRepository.Status.Processing.toString()
|
if (it.id == 0L){
|
||||||
val id = runBlocking {
|
val id = runBlocking {
|
||||||
repository.insert(it)
|
repository.insert(it)
|
||||||
|
}
|
||||||
|
it.id = id
|
||||||
}
|
}
|
||||||
existingItemIDs.add(
|
it.status = DownloadRepository.Status.Duplicate.toString()
|
||||||
AlreadyExistsIDs(
|
repository.update(it)
|
||||||
id,
|
existingItemIDs.add(AlreadyExistsIDs(it.id,existingHistoryItem.id))
|
||||||
existingHistoryItem.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -863,12 +859,16 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingDownload != null){
|
if (existingDownload != null){
|
||||||
it.status = DownloadRepository.Status.Processing.toString()
|
isDuplicate = true
|
||||||
val id = runBlocking {
|
if (it.id == 0L){
|
||||||
repository.insert(it)
|
val id = runBlocking {
|
||||||
|
repository.insert(it)
|
||||||
|
}
|
||||||
|
it.id = id
|
||||||
}
|
}
|
||||||
alreadyExists = true
|
it.status = DownloadRepository.Status.Duplicate.toString()
|
||||||
existingItemIDs.add(AlreadyExistsIDs(id, null))
|
repository.update(it)
|
||||||
|
existingItemIDs.add(AlreadyExistsIDs(it.id, null))
|
||||||
}else{
|
}else{
|
||||||
//check if downloaded and file exists
|
//check if downloaded and file exists
|
||||||
val history = withContext(Dispatchers.IO){
|
val history = withContext(Dispatchers.IO){
|
||||||
|
|
@ -880,31 +880,30 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingHistoryItem != null){
|
if (existingHistoryItem != null){
|
||||||
alreadyExists = true
|
isDuplicate = true
|
||||||
it.status = DownloadRepository.Status.Processing.toString()
|
if (it.id == 0L){
|
||||||
val id = runBlocking {
|
val id = runBlocking {
|
||||||
repository.insert(it)
|
repository.insert(it)
|
||||||
|
}
|
||||||
|
it.id = id
|
||||||
}
|
}
|
||||||
existingItemIDs.add(
|
it.status = DownloadRepository.Status.Duplicate.toString()
|
||||||
AlreadyExistsIDs(
|
repository.update(it)
|
||||||
id,
|
existingItemIDs.add(AlreadyExistsIDs(it.id, existingHistoryItem.id))
|
||||||
existingHistoryItem.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alreadyExists){
|
if (!isDuplicate){
|
||||||
queuedItems.add(it)
|
queuedItems.add(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repository.updateAll(queuedItems)
|
val queued = repository.updateAll(queuedItems)
|
||||||
|
|
||||||
//if scheduler is on
|
//if scheduler is on
|
||||||
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
|
||||||
|
|
@ -919,12 +918,12 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if (!sharedPreferences.getBoolean("paused_downloads", false)) {
|
if (!sharedPreferences.getBoolean("paused_downloads", false)) {
|
||||||
repository.startDownloadWorker(queuedItems, context)
|
repository.startDownloadWorker(queued, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!useScheduler){
|
if(!useScheduler){
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
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 {
|
kotlin.runCatching {
|
||||||
resultRepository.updateDownloadItem(it)?.apply {
|
resultRepository.updateDownloadItem(it)?.apply {
|
||||||
repository.updateWithoutUpsert(this)
|
repository.updateWithoutUpsert(this)
|
||||||
|
|
@ -934,7 +933,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
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 {
|
kotlin.runCatching {
|
||||||
resultRepository.updateDownloadItem(it)?.apply {
|
resultRepository.updateDownloadItem(it)?.apply {
|
||||||
repository.updateWithoutUpsert(this)
|
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.progressindicator.LinearProgressIndicator
|
||||||
import com.google.android.material.search.SearchBar
|
import com.google.android.material.search.SearchBar
|
||||||
import com.google.android.material.search.SearchView
|
import com.google.android.material.search.SearchView
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
|
|
||||||
|
|
@ -18,15 +18,17 @@ import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdl.R
|
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.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdl.util.Extensions.loadThumbnail
|
import com.deniscerri.ytdl.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdl.util.Extensions.popup
|
import com.deniscerri.ytdl.util.Extensions.popup
|
||||||
import com.deniscerri.ytdl.util.FileUtil
|
import com.deniscerri.ytdl.util.FileUtil
|
||||||
|
import com.deniscerri.ytdl.util.InfoUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import java.util.Locale
|
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(
|
AsyncDifferConfig.Builder(
|
||||||
DIFF_CALLBACK
|
DIFF_CALLBACK
|
||||||
).build()) {
|
).build()) {
|
||||||
|
|
@ -111,7 +113,25 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileSize = card.findViewById<TextView>(R.id.file_size)
|
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
|
if (fileSizeReadable == "?") fileSize.visibility = View.GONE
|
||||||
else {
|
else {
|
||||||
fileSize.text = fileSizeReadable
|
fileSize.text = fileSizeReadable
|
||||||
|
|
@ -157,15 +177,15 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItemSimple> = object : DiffUtil.ItemCallback<DownloadItemSimple>() {
|
private val DIFF_CALLBACK: DiffUtil.ItemCallback<DownloadItemConfigureMultiple> = object : DiffUtil.ItemCallback<DownloadItemConfigureMultiple>() {
|
||||||
override fun areItemsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean {
|
override fun areItemsTheSame(oldItem: DownloadItemConfigureMultiple, newItem: DownloadItemConfigureMultiple): Boolean {
|
||||||
return oldItem.url == newItem.url
|
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 &&
|
return oldItem.title == newItem.title &&
|
||||||
oldItem.author == newItem.author &&
|
|
||||||
oldItem.type == newItem.type &&
|
oldItem.type == newItem.type &&
|
||||||
|
oldItem.videoPreferences == newItem.videoPreferences &&
|
||||||
oldItem.format == newItem.format &&
|
oldItem.format == newItem.format &&
|
||||||
oldItem.incognito == newItem.incognito
|
oldItem.incognito == newItem.incognito
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import android.widget.ArrayAdapter
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
|
@ -359,7 +360,8 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun updateSelectedAudioFormat(formatID: String){
|
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
|
downloadItem.format = this
|
||||||
val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
|
val formatCard = requireView().findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, listOf())
|
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.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdl.R
|
import com.deniscerri.ytdl.R
|
||||||
import com.deniscerri.ytdl.database.models.DownloadItem
|
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.models.Format
|
||||||
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel
|
||||||
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel
|
||||||
|
|
@ -161,7 +162,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
processingItemsCount = items.size
|
processingItemsCount = items.size
|
||||||
count.text = "${processingItemsCount} ${getString(R.string.selected)}"
|
count.text = "${processingItemsCount} ${getString(R.string.selected)}"
|
||||||
listAdapter.submitList(items)
|
listAdapter.submitList(items)
|
||||||
updateFileSize(items.map { it2 -> it2.format.filesize })
|
|
||||||
|
updateFileSize(items)
|
||||||
|
|
||||||
if (items.isNotEmpty()){
|
if (items.isNotEmpty()){
|
||||||
if (items.all { it2 -> it2.type == items[0].type }) {
|
if (items.all { it2 -> it2.type == items[0].type }) {
|
||||||
|
|
@ -445,7 +447,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
bottomSheet.setContentView(R.layout.adjust_audio)
|
bottomSheet.setContentView(R.layout.adjust_audio)
|
||||||
val sheetView = bottomSheet.findViewById<View>(android.R.id.content)!!
|
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){
|
val items = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getProcessingDownloads()
|
downloadViewModel.getProcessingDownloads()
|
||||||
|
|
@ -526,7 +528,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
bottomSheet.requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||||
bottomSheet.setContentView(R.layout.adjust_video)
|
bottomSheet.setContentView(R.layout.adjust_video)
|
||||||
val sheetView = bottomSheet.findViewById<View>(android.R.id.content)!!
|
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){
|
val items = withContext(Dispatchers.IO){
|
||||||
downloadViewModel.getProcessingDownloads()
|
downloadViewModel.getProcessingDownloads()
|
||||||
|
|
@ -586,6 +588,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
items.forEach { it.videoPreferences.removeAudio = checked }
|
items.forEach { it.videoPreferences.removeAudio = checked }
|
||||||
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }
|
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 = {},
|
alsoDownloadAsAudioClicked = {},
|
||||||
extraCommandsClicked = {
|
extraCommandsClicked = {
|
||||||
val callback = object : ExtraCommandsListener {
|
val callback = object : ExtraCommandsListener {
|
||||||
|
|
@ -645,14 +651,30 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
bottomAppBar.menu.children.forEach { m -> m.isEnabled = !loading }
|
bottomAppBar.menu.children.forEach { m -> m.isEnabled = !loading }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFileSize(items: List<Long>){
|
private fun updateFileSize(items: List<DownloadItemConfigureMultiple>){
|
||||||
if (items.all { it > 5L }){
|
val fileSizes = mutableListOf<Long>()
|
||||||
val size = FileUtil.convertFileSize(items.sum())
|
items.forEach {
|
||||||
if (size != "?"){
|
if (it.type == DownloadViewModel.Type.video){
|
||||||
filesize.visibility = View.VISIBLE
|
if (it.format.filesize <= 5L) {
|
||||||
filesize.text = "${getString(R.string.file_size)}: >~ $size"
|
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{
|
}else{
|
||||||
filesize.visibility = View.GONE
|
filesize.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import android.widget.ArrayAdapter
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
|
@ -104,6 +105,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
thumb = resultItem!!.thumb
|
thumb = resultItem!!.thumb
|
||||||
website = resultItem!!.website
|
website = resultItem!!.website
|
||||||
url = resultItem!!.url
|
url = resultItem!!.url
|
||||||
|
videoPreferences.apply {
|
||||||
|
audioFormatIDs = downloadViewModel.getPreferredAudioFormats(allFormats)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else if (currentDownloadItem != null){
|
}else if (currentDownloadItem != null){
|
||||||
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
||||||
|
|
@ -376,6 +380,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
downloadItem.videoPreferences.removeAudio = it
|
downloadItem.videoPreferences.removeAudio = it
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, downloadItem.format, if (it) listOf() else downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
|
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 = {
|
alsoDownloadAsAudioClicked = {
|
||||||
downloadItem.videoPreferences.alsoDownloadAsAudio = it
|
downloadItem.videoPreferences.alsoDownloadAsAudio = it
|
||||||
},
|
},
|
||||||
|
|
@ -416,7 +423,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun updateSelectedAudioFormat(format: Format){
|
fun updateSelectedAudioFormat(format: Format){
|
||||||
if (genericAudioFormats.contains(format)) {
|
if (downloadItem.videoPreferences.audioFormatIDs.contains(format.format_id)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,7 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
downloadViewModel.getItemByID(itemID)
|
downloadViewModel.getItemByID(itemID)
|
||||||
}
|
}
|
||||||
downloadViewModel.deleteDownload(deletedItem.id)
|
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)) {
|
.setAction(getString(R.string.undo)) {
|
||||||
downloadViewModel.insert(deletedItem)
|
downloadViewModel.insert(deletedItem)
|
||||||
}.show()
|
}.show()
|
||||||
|
|
|
||||||
|
|
@ -334,7 +334,7 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
downloadViewModel.getItemByID(itemID)
|
downloadViewModel.getItemByID(itemID)
|
||||||
}
|
}
|
||||||
downloadViewModel.deleteDownload(deletedItem.id)
|
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)) {
|
.setAction(getString(R.string.undo)) {
|
||||||
downloadViewModel.insert(deletedItem)
|
downloadViewModel.insert(deletedItem)
|
||||||
}.show()
|
}.show()
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
|
||||||
downloadViewModel.getItemByID(id)
|
downloadViewModel.getItemByID(id)
|
||||||
}
|
}
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
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.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
item.status = DownloadRepository.Status.Cancelled.toString()
|
item.status = DownloadRepository.Status.Cancelled.toString()
|
||||||
|
|
@ -154,7 +154,7 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
|
||||||
downloadViewModel.updateDownload(item)
|
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)) {
|
.setAction(getString(R.string.undo)) {
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
downloadViewModel.deleteDownload(item.id)
|
downloadViewModel.deleteDownload(item.id)
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
downloadViewModel.getItemByID(itemID)
|
downloadViewModel.getItemByID(itemID)
|
||||||
}
|
}
|
||||||
downloadViewModel.deleteDownload(deletedItem.id)
|
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)) {
|
.setAction(getString(R.string.undo)) {
|
||||||
downloadViewModel.insert(deletedItem)
|
downloadViewModel.insert(deletedItem)
|
||||||
}.show()
|
}.show()
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ class ScheduledDownloadsFragment : Fragment(), ScheduledDownloadAdapter.OnItemCl
|
||||||
downloadViewModel.getItemByID(itemID)
|
downloadViewModel.getItemByID(itemID)
|
||||||
}
|
}
|
||||||
downloadViewModel.deleteDownload(deletedItem.id)
|
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)) {
|
.setAction(getString(R.string.undo)) {
|
||||||
downloadViewModel.insert(deletedItem)
|
downloadViewModel.insert(deletedItem)
|
||||||
}.show()
|
}.show()
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,9 @@ class DownloadLogFragment : Fragment() {
|
||||||
val clipboard: ClipboardManager =
|
val clipboard: ClipboardManager =
|
||||||
mainActivity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
mainActivity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
clipboard.setText(content.text)
|
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")
|
val id = arguments?.getLong("logID")
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ class InfoUtil(private val context: Context) {
|
||||||
Html.fromHtml(obj.getString("uploader").toString()).toString()
|
Html.fromHtml(obj.getString("uploader").toString()).toString()
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Html.fromHtml(obj.getString("uploaderName").toString()).toString()
|
Html.fromHtml(obj.getString("uploaderName").toString()).toString()
|
||||||
}
|
}.replace(" - Topic", "")
|
||||||
|
|
||||||
val duration = obj.getInt("duration").toStringDuration(Locale.US)
|
val duration = obj.getInt("duration").toStringDuration(Locale.US)
|
||||||
val thumb = "https://i.ytimg.com/vi/$id/hqdefault.jpg"
|
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")
|
val lang = sharedPreferences.getString("app_language", "en")
|
||||||
if (searchEngine == "ytsearch" && context.getStringArray(R.array.subtitle_langs).contains(lang)){
|
if (searchEngine == "ytsearch" || query.isYoutubeURL()) {
|
||||||
request.addOption("--extractor-args", "youtube:lang=$lang")
|
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")
|
request.addOption("--flat-playlist")
|
||||||
|
|
@ -708,7 +712,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val title = jsonObject.getStringByAny("alt_title", "title", "webpage_url_basename")
|
val title = jsonObject.getStringByAny("alt_title", "title", "webpage_url_basename")
|
||||||
if (title == "[Private video]" || title == "[Deleted video]") continue
|
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()
|
var duration = jsonObject.getIntByAny("duration").toString()
|
||||||
if (duration != "-1"){
|
if (duration != "-1"){
|
||||||
duration = jsonObject.getInt("duration").toStringDuration(Locale.US)
|
duration = jsonObject.getInt("duration").toStringDuration(Locale.US)
|
||||||
|
|
@ -1179,6 +1183,15 @@ class InfoUtil(private val context: Context) {
|
||||||
if (sharedPreferences.getBoolean("write_description", false)){
|
if (sharedPreferences.getBoolean("write_description", false)){
|
||||||
request.addOption("--write-description")
|
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"){
|
if (sharedPreferences.getString("prevent_duplicate_downloads", "")!! == "download_archive"){
|
||||||
|
|
@ -1234,7 +1247,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abrSort.isNotBlank()){
|
if (abrSort.isNotBlank()){
|
||||||
formatSorting.append(",abr~${abrSort}")
|
formatSorting.append(",abr:${abrSort}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ext.isNotBlank()){
|
if(ext.isNotBlank()){
|
||||||
|
|
@ -1254,15 +1267,15 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
if (embedMetadata){
|
if (embedMetadata){
|
||||||
request.addOption("--embed-metadata")
|
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", "%(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", "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()) {
|
if (downloadItem.playlistTitle.isNotEmpty()) {
|
||||||
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
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 {
|
} else {
|
||||||
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
request.addOption("--parse-metadata", "%(album,title)s:%(meta_album)s")
|
||||||
}
|
}
|
||||||
|
|
@ -1320,8 +1333,14 @@ class InfoUtil(private val context: Context) {
|
||||||
supportedContainers.contains(outputContainer)
|
supportedContainers.contains(outputContainer)
|
||||||
){
|
){
|
||||||
cont = 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)
|
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||||
if (embedThumb) {
|
if (embedThumb) {
|
||||||
request.addOption("--embed-thumbnail")
|
request.addOption("--embed-thumbnail")
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ object UiUtil {
|
||||||
|
|
||||||
|
|
||||||
var filesize = chosenFormat.filesize
|
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 {
|
formatCard.findViewById<TextView>(R.id.file_size).apply {
|
||||||
text = FileUtil.convertFileSize(filesize)
|
text = FileUtil.convertFileSize(filesize)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
|
|
@ -1130,29 +1130,18 @@ object UiUtil {
|
||||||
saveAutoSubtitlesClicked: (Boolean) -> Unit,
|
saveAutoSubtitlesClicked: (Boolean) -> Unit,
|
||||||
subtitleLanguagesSet: (String) -> Unit,
|
subtitleLanguagesSet: (String) -> Unit,
|
||||||
removeAudioClicked: (Boolean) -> Unit,
|
removeAudioClicked: (Boolean) -> Unit,
|
||||||
|
recodeVideoClicked: (Boolean) -> Unit,
|
||||||
alsoDownloadAsAudioClicked: (Boolean) -> Unit,
|
alsoDownloadAsAudioClicked: (Boolean) -> Unit,
|
||||||
extraCommandsClicked: () -> Unit
|
extraCommandsClicked: () -> Unit
|
||||||
){
|
){
|
||||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
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)
|
val addChapters = view.findViewById<Chip>(R.id.add_chapters)
|
||||||
addChapters!!.isChecked = items.all { it.videoPreferences.addChapters }
|
addChapters!!.isChecked = items.all { it.videoPreferences.addChapters }
|
||||||
addChapters.setOnClickListener{
|
addChapters.setOnClickListener{
|
||||||
addChaptersClicked(addChapters.isChecked)
|
addChaptersClicked(addChapters.isChecked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val splitByChapters = view.findViewById<Chip>(R.id.split_by_chapters)
|
val splitByChapters = view.findViewById<Chip>(R.id.split_by_chapters)
|
||||||
if(items.size == 1 && items[0].downloadSections.isNotBlank()){
|
if(items.size == 1 && items[0].downloadSections.isNotBlank()){
|
||||||
splitByChapters.isEnabled = false
|
splitByChapters.isEnabled = false
|
||||||
|
|
@ -1183,6 +1172,111 @@ object UiUtil {
|
||||||
saveThumbnailClicked(saveThumbnail.isChecked)
|
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)
|
val sponsorBlock = view.findViewById<Chip>(R.id.sponsorblock_filters)
|
||||||
sponsorBlock.isEnabled = sharedPreferences.getBoolean("use_sponsorblock", true)
|
sponsorBlock.isEnabled = sharedPreferences.getBoolean("use_sponsorblock", true)
|
||||||
sponsorBlock!!.setOnClickListener {
|
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)
|
val extraCommands = view.findViewById<Chip>(R.id.extra_commands)
|
||||||
if (sharedPreferences.getBoolean("use_extra_commands", false)){
|
if (sharedPreferences.getBoolean("use_extra_commands", false)){
|
||||||
extraCommands.visibility = View.VISIBLE
|
extraCommands.visibility = View.VISIBLE
|
||||||
|
|
@ -1849,7 +1871,7 @@ object UiUtil {
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
view.findViewById<View>(R.id.suggested).visibility = View.VISIBLE
|
view.findViewById<View>(R.id.suggested).visibility = View.VISIBLE
|
||||||
chips.forEach {
|
chips.forEach {
|
||||||
it.isChecked = editText.text.contains(it.text)
|
it.isChecked = editText.text == it.text
|
||||||
chipGroup!!.addView(it)
|
chipGroup!!.addView(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,9 @@ class DownloadWorker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onSuccess {
|
}.onSuccess {
|
||||||
resultRepo.updateDownloadItem(downloadItem)
|
resultRepo.updateDownloadItem(downloadItem)?.apply {
|
||||||
|
dao.updateWithoutUpsert(this)
|
||||||
|
}
|
||||||
val wasQuickDownloaded = resultDao.getCountInt() == 0
|
val wasQuickDownloaded = resultDao.getCountInt() == 0
|
||||||
runBlocking {
|
runBlocking {
|
||||||
var finalPaths : MutableList<String>?
|
var finalPaths : MutableList<String>?
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,34 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/cut"
|
android:id="@+id/cut"
|
||||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
|
|
@ -111,24 +139,10 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</com.google.android.material.chip.ChipGroup>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</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>
|
</LinearLayout>
|
||||||
|
|
@ -27,12 +27,12 @@
|
||||||
app:singleLine="true">
|
app:singleLine="true">
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/embed_subtitles"
|
android:id="@+id/save_thumbnail"
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/embed_subtitles" />
|
android:text="@string/save_thumb" />
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/add_chapters"
|
android:id="@+id/add_chapters"
|
||||||
|
|
@ -57,15 +57,44 @@
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:scrollbars="none"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:scrollbars="none">
|
||||||
|
|
||||||
<com.google.android.material.chip.ChipGroup
|
<com.google.android.material.chip.ChipGroup
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:singleLine="true">
|
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
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/adjust_audio"
|
android:id="@+id/adjust_audio"
|
||||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
|
|
@ -82,76 +111,14 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/remove_audio"
|
android:id="@+id/recode_video"
|
||||||
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"
|
|
||||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:checked="false"
|
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>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
@ -196,21 +163,6 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
</HorizontalScrollView>
|
</HorizontalScrollView>
|
||||||
|
|
@ -227,7 +179,6 @@
|
||||||
|
|
||||||
<com.google.android.material.chip.Chip
|
<com.google.android.material.chip.Chip
|
||||||
android:id="@+id/extra_commands"
|
android:id="@+id/extra_commands"
|
||||||
android:visibility="gone"
|
|
||||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -241,6 +192,21 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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>
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/format_note">
|
app:layout_constraintTop_toBottomOf="@id/formatnote_formatid">
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
@ -113,7 +113,6 @@
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:maxLength="10"
|
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
|
|
@ -180,13 +179,19 @@
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="bottom|end"
|
android:gravity="bottom|end"
|
||||||
android:maxLength="10"
|
android:maxWidth="70dp"
|
||||||
android:layout_marginEnd="5dp"
|
android:maxLines="2"
|
||||||
app:cornerRadius="10dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="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>
|
</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="day">Ditë</string>
|
||||||
<string name="month">Muaj</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="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="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="show_download_count">Shfaq numrat në faqen e rradhës së shkarkimeve</string>
|
||||||
<string name="also_download_audio">Shkarko Gjithashtu si Audio</string>
|
<string name="also_download_audio">Shkarko Gjithashtu si Audio</string>
|
||||||
|
|
@ -397,5 +398,17 @@
|
||||||
<string name="location">Vendi</string>
|
<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="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="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="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>
|
</resources>
|
||||||
|
|
@ -26,9 +26,9 @@
|
||||||
<item>mp4</item>
|
<item>mp4</item>
|
||||||
<item>webm</item>
|
<item>webm</item>
|
||||||
<item>mkv</item>
|
<item>mkv</item>
|
||||||
|
<item>mov</item>
|
||||||
<item>avi</item>
|
<item>avi</item>
|
||||||
<item>flv</item>
|
<item>flv</item>
|
||||||
<item>mov</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_containers_values">
|
<string-array name="video_containers_values">
|
||||||
|
|
@ -36,9 +36,9 @@
|
||||||
<item>mp4</item>
|
<item>mp4</item>
|
||||||
<item>webm</item>
|
<item>webm</item>
|
||||||
<item>mkv</item>
|
<item>mkv</item>
|
||||||
|
<item>mov</item>
|
||||||
<item>avi</item>
|
<item>avi</item>
|
||||||
<item>flv</item>
|
<item>flv</item>
|
||||||
<item>mov</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_formats">
|
<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_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">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="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>
|
</resources>
|
||||||
|
|
@ -180,6 +180,15 @@
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:title="@string/video_format" />
|
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
|
<ListPreference
|
||||||
android:defaultValue=""
|
android:defaultValue=""
|
||||||
android:entries="@array/video_codec"
|
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