diff --git a/app/build.gradle b/app/build.gradle index c9e1065a..7434ed7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ def properties = new Properties() def versionMajor = 1 def versionMinor = 6 def versionPatch = 3 -def versionBuild = 1 // bump for dogfood builds, public betas, etc. +def versionBuild = 2 // bump for dogfood builds, public betas, etc. def versionExt = "" if (versionBuild > 0){ @@ -64,7 +64,8 @@ android { buildTypes { release { minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + shrinkResources true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' debuggable false signingConfig signingConfigs.debug } @@ -157,7 +158,7 @@ dependencies { androidTestImplementation "junit:junit:$junitVer" androidTestImplementation "androidx.test.ext:junit:$androidJunitVer" androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer" - androidTestImplementation "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha13" + androidTestImplementation "androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha16" androidTestImplementation "androidx.test:runner:1.5.2" androidTestImplementation "androidx.test:core:1.5.0" androidTestImplementation "androidx.test:rules:1.5.0" @@ -175,10 +176,10 @@ dependencies { implementation "androidx.work:work-runtime-ktx:$workVer" - implementation "androidx.room:room-runtime:2.5.1" - implementation "androidx.room:room-ktx:2.5.1" - kapt "androidx.room:room-compiler:2.5.1" - androidTestImplementation "androidx.room:room-testing:2.5.1" + implementation "androidx.room:room-runtime:2.5.2" + implementation "androidx.room:room-ktx:2.5.2" + kapt "androidx.room:room-compiler:2.5.2" + androidTestImplementation "androidx.room:room-testing:2.5.2" implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' implementation "androidx.compose.runtime:runtime:$composeVer" androidTestImplementation "com.google.truth:truth:1.1.3" @@ -190,7 +191,7 @@ dependencies { implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10" implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0' - implementation "com.google.android.exoplayer:exoplayer:2.18.5" + implementation "com.google.android.exoplayer:exoplayer:2.18.7" implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4' implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1" implementation "com.github.Irineu333:Highlight-KT:1.0.4" diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt index c94d4853..9d3a9143 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CommandTemplateDao.kt @@ -1,9 +1,9 @@ package com.deniscerri.ytdlnis.database.dao -import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.CommandTemplate import com.deniscerri.ytdlnis.database.models.TemplateShortcut +import kotlinx.coroutines.flow.Flow @Dao interface CommandTemplateDao { @@ -11,10 +11,10 @@ interface CommandTemplateDao { fun getAllTemplates() : List @Query("SELECT * FROM commandTemplates ORDER BY id DESC") - fun getAllTemplatesLiveData() : LiveData> + fun getAllTemplatesFlow() : Flow> @Query("SELECT * FROM templateShortcuts ORDER BY id DESC") - fun getAllShortcutsLiveData() : LiveData> + fun getAllShortcutsFlow() : Flow> @Query("SELECT * FROM templateShortcuts ORDER BY id DESC") fun getAllShortcuts() : List diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt index b640af75..f570b5e7 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt @@ -1,8 +1,8 @@ package com.deniscerri.ytdlnis.database.dao -import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.CookieItem +import kotlinx.coroutines.flow.Flow @Dao interface CookieDao { @@ -10,7 +10,7 @@ interface CookieDao { fun getAllCookies() : List @Query("SELECT * FROM cookies ORDER BY id DESC") - fun getAllCookiesLiveData() : LiveData> + fun getAllCookiesFlow() : Flow> @Query("SELECT EXISTS(SELECT * FROM cookies WHERE url=:url LIMIT 1)") fun checkIfExistsWithSameURL(url: String) : Boolean diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt index 55cdc014..4dc8f4fa 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt @@ -1,20 +1,20 @@ package com.deniscerri.ytdlnis.database.dao -import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.DownloadItem +import kotlinx.coroutines.flow.Flow @Dao interface DownloadDao { @Query("SELECT * FROM downloads ORDER BY status") - fun getAllDownloads() : LiveData> + fun getAllDownloads() : Flow> @Query("SELECT * FROM downloads WHERE status='Active'") - fun getActiveDownloads() : LiveData> + fun getActiveDownloads() : Flow> @Query("SELECT COUNT(*) FROM downloads WHERE status='Active'") - fun getActiveDownloadsCount() : LiveData + fun getActiveDownloadsCount() : Flow @Query("SELECT * FROM downloads WHERE status='Active'") fun getActiveDownloadsList() : List @@ -23,25 +23,25 @@ interface DownloadDao { fun getActiveAndQueuedDownloadsList() : List @Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id") - fun getQueuedDownloads() : LiveData> + fun getQueuedDownloads() : Flow> @Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id") fun getQueuedDownloadsList() : List @Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC") - fun getCancelledDownloads() : LiveData> + fun getCancelledDownloads() : Flow> @Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC") fun getCancelledDownloadsList() : List @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") - fun getErroredDownloads() : LiveData> + fun getErroredDownloads() : Flow> @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") fun getErroredDownloadsList() : List @Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC") - fun getProcessingDownloads() : LiveData> + fun getProcessingDownloads() : Flow> @Query("SELECT * FROM downloads WHERE id=:id LIMIT 1") fun getDownloadById(id: Long) : DownloadItem diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/HistoryDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/HistoryDao.kt index 1d12d37d..cf21c4f2 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/HistoryDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/HistoryDao.kt @@ -1,8 +1,8 @@ package com.deniscerri.ytdlnis.database.dao -import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.HistoryItem +import kotlinx.coroutines.flow.Flow @Dao interface HistoryDao { @@ -27,7 +27,7 @@ interface HistoryDao { @Query("SELECT * FROM history") - fun getAllHistory() : LiveData> + fun getAllHistory() : Flow> @Query("SELECT * FROM history") fun getAllHistoryList() : List diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt index 66b4a861..5bebde4f 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ResultDao.kt @@ -1,16 +1,16 @@ package com.deniscerri.ytdlnis.database.dao -import androidx.lifecycle.LiveData import androidx.room.* import com.deniscerri.ytdlnis.database.models.ResultItem +import kotlinx.coroutines.flow.Flow @Dao interface ResultDao { @Query("SELECT * FROM results") - fun getResults() : LiveData> + fun getResults() : Flow> @Query("SELECT COUNT(id) FROM results") - fun getCount() : LiveData + fun getCount() : Flow @Query("SELECT COUNT(id) FROM results") fun getCountInt() :Int diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CommandTemplateRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CommandTemplateRepository.kt index 31721162..f64a08d7 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CommandTemplateRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CommandTemplateRepository.kt @@ -4,10 +4,11 @@ import androidx.lifecycle.LiveData import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao import com.deniscerri.ytdlnis.database.models.CommandTemplate import com.deniscerri.ytdlnis.database.models.TemplateShortcut +import kotlinx.coroutines.flow.Flow class CommandTemplateRepository(private val commandDao: CommandTemplateDao) { - val items : LiveData> = commandDao.getAllTemplatesLiveData() - val shortcuts : LiveData> = commandDao.getAllShortcutsLiveData() + val items : Flow> = commandDao.getAllTemplatesFlow() + val shortcuts : Flow> = commandDao.getAllShortcutsFlow() fun getAll() : List { return commandDao.getAllTemplates() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt index 1bc0b44a..2c042fd4 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt @@ -3,9 +3,10 @@ package com.deniscerri.ytdlnis.database.repository import androidx.lifecycle.LiveData import com.deniscerri.ytdlnis.database.dao.CookieDao import com.deniscerri.ytdlnis.database.models.CookieItem +import kotlinx.coroutines.flow.Flow class CookieRepository(private val cookieDao: CookieDao) { - val items : LiveData> = cookieDao.getAllCookiesLiveData() + val items : Flow> = cookieDao.getAllCookiesFlow() fun getAll() : List { return cookieDao.getAllCookies() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt index 006a6eb1..7b01a504 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt @@ -4,15 +4,16 @@ import androidx.lifecycle.LiveData import com.deniscerri.ytdlnis.database.Converters import com.deniscerri.ytdlnis.database.dao.DownloadDao import com.deniscerri.ytdlnis.database.models.DownloadItem +import kotlinx.coroutines.flow.Flow class DownloadRepository(private val downloadDao: DownloadDao) { - val allDownloads : LiveData> = downloadDao.getAllDownloads() - val activeDownloads : LiveData> = downloadDao.getActiveDownloads() - val activeDownloadsCount : LiveData = downloadDao.getActiveDownloadsCount() - val queuedDownloads : LiveData> = downloadDao.getQueuedDownloads() - val cancelledDownloads : LiveData> = downloadDao.getCancelledDownloads() - val erroredDownloads : LiveData> = downloadDao.getErroredDownloads() - val processingDownloads : LiveData> = downloadDao.getProcessingDownloads() + val allDownloads : Flow> = downloadDao.getAllDownloads() + val activeDownloads : Flow> = downloadDao.getActiveDownloads() + val activeDownloadsCount : Flow = downloadDao.getActiveDownloadsCount() + val queuedDownloads : Flow> = downloadDao.getQueuedDownloads() + val cancelledDownloads : Flow> = downloadDao.getCancelledDownloads() + val erroredDownloads : Flow> = downloadDao.getErroredDownloads() + val processingDownloads : Flow> = downloadDao.getProcessingDownloads() enum class Status { Active, Queued, Error, Processing, Cancelled diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt index ba31c176..0517d3ef 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt @@ -4,9 +4,11 @@ import androidx.lifecycle.LiveData import com.deniscerri.ytdlnis.database.dao.HistoryDao import com.deniscerri.ytdlnis.database.models.HistoryItem import com.deniscerri.ytdlnis.util.FileUtil +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collectLatest class HistoryRepository(private val historyDao: HistoryDao) { - val items : LiveData> = historyDao.getAllHistory() + val items : Flow> = historyDao.getAllHistory() enum class HistorySort{ DESC, ASC } @@ -59,9 +61,11 @@ class HistoryRepository(private val historyDao: HistoryDao) { } suspend fun clearDeletedHistory(){ - items.value?.forEach { item -> - if (!FileUtil.exists(item.downloadPath)){ - historyDao.delete(item.id) + items.collectLatest { + it.forEach { item -> + if (!FileUtil.exists(item.downloadPath)){ + historyDao.delete(item.id) + } } } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt index e34381b2..a501c140 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt @@ -2,18 +2,15 @@ package com.deniscerri.ytdlnis.database.repository import android.content.Context import android.util.Log -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao import com.deniscerri.ytdlnis.database.dao.ResultDao -import com.deniscerri.ytdlnis.database.models.CommandTemplate import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.util.InfoUtil +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -class ResultRepository(private val resultDao: ResultDao, private val commandTemplateDao: CommandTemplateDao, private val context: Context) { +class ResultRepository(private val resultDao: ResultDao, private val context: Context) { private val tag: String = "ResultRepository" - val allResults : LiveData> = resultDao.getResults() + val allResults : Flow> = resultDao.getResults() var itemCount = MutableStateFlow(-1) suspend fun insert(it: ResultItem){ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt index f5b2f628..3c6c8c48 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CommandTemplateViewModel.kt @@ -5,6 +5,7 @@ import android.content.ClipboardManager import android.content.Context.CLIPBOARD_SERVICE import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.models.CommandTemplate @@ -27,8 +28,8 @@ class CommandTemplateViewModel(private val application: Application) : AndroidVi init { val dao = DBManager.getInstance(application).commandTemplateDao repository = CommandTemplateRepository(dao) - items = repository.items - shortcuts = repository.shortcuts + items = repository.items.asLiveData() + shortcuts = repository.shortcuts.asLiveData() } fun getTemplate(itemId: Long): CommandTemplate { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt index a80df524..20966e99 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt @@ -10,6 +10,7 @@ import android.util.Log import android.webkit.CookieManager import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.models.CookieItem @@ -31,7 +32,7 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a init { val dao = DBManager.getInstance(application).cookieDao repository = CookieRepository(dao) - items = repository.items + items = repository.items.asLiveData() } fun getAll(): List { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt index 72d7f831..cc4b5063 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt @@ -11,6 +11,7 @@ import android.util.DisplayMetrics import android.widget.Toast import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import androidx.preference.PreferenceManager import androidx.work.Constraints @@ -77,13 +78,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application commandTemplateDao = DBManager.getInstance(application).commandTemplateDao infoUtil = InfoUtil(application) - allDownloads = repository.allDownloads - queuedDownloads = repository.queuedDownloads - activeDownloads = repository.activeDownloads - activeDownloadsCount = repository.activeDownloadsCount - processingDownloads = repository.processingDownloads - cancelledDownloads = repository.cancelledDownloads - erroredDownloads = repository.erroredDownloads + allDownloads = repository.allDownloads.asLiveData() + queuedDownloads = repository.queuedDownloads.asLiveData() + activeDownloads = repository.activeDownloads.asLiveData() + activeDownloadsCount = repository.activeDownloadsCount.asLiveData() + processingDownloads = repository.processingDownloads.asLiveData() + cancelledDownloads = repository.cancelledDownloads.asLiveData() + erroredDownloads = repository.erroredDownloads.asLiveData() videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString() formatIDPreference = sharedPreferences.getString("format_id", "").toString() @@ -95,15 +96,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application resources = Resources(application.assets, metrics, confTmp) - val videoFormat = resources.getStringArray(R.array.video_formats) val videoFormatValues = resources.getStringArray(R.array.video_formats_values) var videoContainer = sharedPreferences.getString("video_format", "Default") if (videoContainer == "Default") videoContainer = App.instance.getString(R.string.defaultValue) defaultVideoFormats = mutableListOf() - videoFormat.forEachIndexed { index , it -> + videoFormatValues.forEach { val tmp = Format( - videoFormatValues[index], + it, videoContainer!!, "", "", @@ -125,7 +125,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application "", "", 0, - resources.getString(R.string.best_quality) + "best" ) } @@ -381,7 +381,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application } fun getGenericVideoFormats() : MutableList{ - val videoFormats = resources.getStringArray(R.array.video_formats) + val videoFormats = resources.getStringArray(R.array.video_formats_values) val formats = mutableListOf() var containerPreference = sharedPreferences.getString("video_format", "Default") if (containerPreference == "Default") containerPreference = resources.getString(R.string.defaultValue) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt index 405bb0a3..5e3bf31f 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import com.deniscerri.ytdlnis.database.DBManager import com.deniscerri.ytdlnis.database.models.HistoryItem @@ -28,7 +29,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application) init { val dao = DBManager.getInstance(application).historyDao repository = HistoryRepository(dao) - allItems = repository.items + allItems = repository.items.asLiveData() _items.addSource(allItems){ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt index 920dd7d7..416bbd99 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt @@ -3,10 +3,10 @@ package com.deniscerri.ytdlnis.database.viewmodel import android.app.Application import android.content.SharedPreferences import android.util.Log -import androidx.compose.runtime.MutableState import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import androidx.preference.PreferenceManager import com.deniscerri.ytdlnis.App @@ -17,7 +17,6 @@ import com.deniscerri.ytdlnis.database.models.SearchHistoryItem import com.deniscerri.ytdlnis.database.repository.ResultRepository import com.deniscerri.ytdlnis.database.repository.SearchHistoryRepository import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.regex.Pattern @@ -33,9 +32,9 @@ class ResultViewModel(application: Application) : AndroidViewModel(application) init { val dao = DBManager.getInstance(application).resultDao val commandDao = DBManager.getInstance(application).commandTemplateDao - repository = ResultRepository(dao, commandDao, getApplication().applicationContext) + repository = ResultRepository(dao, getApplication().applicationContext) searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao) - items = repository.allResults + items = repository.allResults.asLiveData() loadingItems.postValue(false) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index 1729bbf8..1ba49443 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -76,7 +76,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { downloadItem = withContext(Dispatchers.IO) { - if (currentDownloadItem != null && currentDownloadItem!!.type == Type.audio){ + if (currentDownloadItem != null){ val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java) Gson().fromJson(string, DownloadItem::class.java) }else{ @@ -126,7 +126,17 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr if (free == "?") freeSpace.visibility = View.GONE var formats = mutableListOf() - formats.addAll(resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) }) + if (currentDownloadItem == null) { + formats.addAll(resultItem.formats.filter { it.format_note.contains("audio", ignoreCase = true) }) + }else{ + //if its updating a present downloaditem and its the wrong category + if (currentDownloadItem!!.type != Type.audio){ + downloadItem.type = Type.audio + downloadItem.format = + downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) } + .maxByOrNull { it.filesize }!! + } + } if (formats.isEmpty()) formats.addAll(downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) }) val containers = requireContext().resources.getStringArray(R.array.audio_containers) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index 96d87cfb..bf48c5d2 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -77,7 +77,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { downloadItem = withContext(Dispatchers.IO){ - if (currentDownloadItem != null && currentDownloadItem!!.type == Type.video){ + if (currentDownloadItem != null){ val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java) Gson().fromJson(string, DownloadItem::class.java) }else{ @@ -127,8 +127,19 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr freeSpace.text = String.format( getString(R.string.freespace) + ": " + free) if (free == "?") freeSpace.visibility = View.GONE + var formats = mutableListOf() - formats.addAll(resultItem.formats) + if (currentDownloadItem == null) { + formats.addAll(resultItem.formats) + }else{ + //if its updating a present downloaditem and its the wrong category + if (currentDownloadItem!!.type != Type.video){ + downloadItem.type = Type.video + downloadItem.format = + downloadItem.allFormats.filter { it.vcodec.isNotEmpty() } + .maxByOrNull { it.filesize }!! + } + } if (formats.isEmpty()) formats.addAll(downloadItem.allFormats) val containers = requireContext().resources.getStringArray(R.array.video_containers) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt index b6c19f64..d2516c17 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt @@ -15,9 +15,7 @@ import androidx.preference.PreferenceManager import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.database.models.DownloadItem import com.deniscerri.ytdlnis.database.models.Format -import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type -import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout @@ -27,6 +25,7 @@ import com.google.android.material.card.MaterialCardView import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import okhttp3.internal.format import java.util.* @@ -45,6 +44,11 @@ class FormatSelectionBottomSheetDialog(private val items: List, p private lateinit var videoTitle : TextView private lateinit var audioTitle : TextView + private lateinit var sortBy : FormatSorting + enum class FormatSorting { + filesize, container, id + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) infoUtil = InfoUtil(requireActivity().applicationContext) @@ -61,6 +65,8 @@ class FormatSelectionBottomSheetDialog(private val items: List, p val view = LayoutInflater.from(context).inflate(R.layout.format_select_bottom_sheet, null) dialog.setContentView(view) + sortBy = FormatSorting.valueOf(sharedPreferences.getString("format_order", "filesize")!!) + dialog.setOnShowListener { behavior = BottomSheetBehavior.from(view.parent as View) val displayMetrics = DisplayMetrics() @@ -78,10 +84,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p okBtn = view.findViewById(R.id.format_ok) shimmers.visibility = View.GONE - val hasGenericFormats = when(items.first()!!.type){ - Type.audio -> formats.first().size == resources.getStringArray(R.array.audio_formats).size - else -> formats.first().size == resources.getStringArray(R.array.video_formats).size - } + val hasGenericFormats = formats.first().isEmpty() || formats.last().any { it.format_id == "best" } if (items.size > 1){ if (!hasGenericFormats){ @@ -131,10 +134,10 @@ class FormatSelectionBottomSheetDialog(private val items: List, p infoUtil.getFormats(items.first()!!.url) } res.filter { it.format_note != "storyboard" } - if(items.first()?.type == Type.audio){ - chosenFormats = res.filter { it.format_note.contains("audio", ignoreCase = true) } + chosenFormats = if(items.first()?.type == Type.audio){ + res.filter { it.format_note.contains("audio", ignoreCase = true) } }else{ - chosenFormats = res + res } if (chosenFormats.isEmpty()) throw Exception() //playlist format filtering @@ -216,7 +219,14 @@ class FormatSelectionBottomSheetDialog(private val items: List, p } } private fun addFormatsToView(){ - val canMultiSelectAudio = items.first()?.type == Type.video && chosenFormats.find { it.format_note.contains("audio", ignoreCase = true) } != null + //sort + val finalFormats: List = when(sortBy){ + FormatSorting.container -> chosenFormats.groupBy { it.container }.flatMap { it.value } + FormatSorting.id -> chosenFormats.sortedBy { it.format_id } + FormatSorting.filesize -> chosenFormats + } + + val canMultiSelectAudio = items.first()?.type == Type.video && finalFormats.find { it.format_note.contains("audio", ignoreCase = true) } != null videoFormatList.removeAllViews() audioFormatList.removeAllViews() @@ -226,7 +236,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p audioTitle.visibility = View.GONE okBtn.visibility = View.GONE }else{ - if (chosenFormats.count { it.vcodec.isBlank() || it.vcodec == "none" } == 0){ + if (finalFormats.count { it.vcodec.isBlank() || it.vcodec == "none" } == 0){ audioFormatList.visibility = View.GONE audioTitle.visibility = View.GONE videoTitle.visibility = View.GONE @@ -239,8 +249,8 @@ class FormatSelectionBottomSheetDialog(private val items: List, p } } - for (i in chosenFormats.lastIndex downTo 0){ - val format = chosenFormats[i] + for (i in finalFormats.lastIndex downTo 0){ + val format = finalFormats[i] val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null) formatItem.tag = "${format.format_id}${format.format_note}" UiUtil.populateFormatCard(formatItem as MaterialCardView, format, null) @@ -250,7 +260,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p val clickedCard = (clickedformat as MaterialCardView) if (format.vcodec.isNotBlank() && format.vcodec != "none") { if (clickedCard.isChecked) { - listener.onFormatClick(List(items.size){chosenFormats}, listOf(FormatTuple(format, null))) + listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null))) dismiss() } videoFormatList.forEach { (it as MaterialCardView).isChecked = false } @@ -269,14 +279,14 @@ class FormatSelectionBottomSheetDialog(private val items: List, p } }else{ if (items.size == 1){ - listener.onFormatClick(List(items.size){chosenFormats}, listOf(FormatTuple(format, null))) + listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null))) }else{ val selectedFormats = mutableListOf() formatCollection.forEach { selectedFormats.add(it.first{ f -> f.format_id == format.format_id}) } if (selectedFormats.isEmpty()) { - items.forEach { + items.forEach { _ -> selectedFormats.add(format) } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/DownloadQueueMainFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/DownloadQueueMainFragment.kt index d761dc39..d134e5b8 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/DownloadQueueMainFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/DownloadQueueMainFragment.kt @@ -102,6 +102,10 @@ class DownloadQueueMainFragment : Fragment(){ }) mainActivity.hideBottomNavigation() initMenu() + + if (arguments?.getString("tab") != null){ + tabLayout.selectTab(tabLayout.getTabAt(3)) + } } private fun initMenu() { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt index 89ef0c88..924e27de 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/QueuedDownloadsFragment.kt @@ -1,5 +1,6 @@ package com.deniscerri.ytdlnis.ui.downloads +import android.annotation.SuppressLint import android.app.Activity import android.content.DialogInterface import android.graphics.Canvas @@ -59,6 +60,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi private lateinit var queuedRecyclerView : RecyclerView private lateinit var queuedDownloads : GenericDownloadAdapter private lateinit var notificationUtil: NotificationUtil + private lateinit var fileSize: TextView private var selectedObjects: ArrayList? = null private var actionMode : ActionMode? = null private lateinit var items : MutableList @@ -68,7 +70,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi container: ViewGroup?, savedInstanceState: Bundle? ): View? { - fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false) + fragmentView = inflater.inflate(R.layout.fragment_inqueue, container, false) activity = getActivity() notificationUtil = NotificationUtil(requireContext()) downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] @@ -77,9 +79,10 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi return fragmentView } + @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - + fileSize = view.findViewById(R.id.filesize) queuedDownloads = GenericDownloadAdapter( this, @@ -97,6 +100,11 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi downloadViewModel.queuedDownloads.observe(viewLifecycleOwner) { items = it.toMutableList() + if (it.isEmpty()) fileSize.visibility = View.GONE + else{ + fileSize.visibility = View.VISIBLE + fileSize.text = "${getString(R.string.file_size)}: ~ ${FileUtil.convertFileSize(it.sumOf { i -> i.format.filesize })}" + } queuedDownloads.submitList(it) } } @@ -232,6 +240,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi override fun onCardSelect(itemID: Long, isChecked: Boolean) { val item = items.find { it.id == itemID } + val now = System.currentTimeMillis() if (isChecked) { selectedObjects!!.add(item!!) if (actionMode == null){ @@ -248,6 +257,9 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi actionMode?.finish() } } + if (actionMode != null){ + actionMode!!.menu.getItem(1).isVisible = selectedObjects!!.all { it.downloadStartTime > now } + } } private fun removeItem(itemID: Long){ @@ -311,6 +323,16 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi deleteDialog.show() true } + R.id.download -> { + for (obj in selectedObjects!!){ + WorkManager.getInstance(requireContext()).cancelUniqueWork(obj.id.toInt().toString()) + } + selectedObjects!!.forEach { it.downloadStartTime = 0L } + lifecycleScope.launch(Dispatchers.IO) { + downloadViewModel.queueDownloads(selectedObjects!!) + } + true + } R.id.select_all -> { queuedDownloads.checkAll(items) selectedObjects?.clear() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt index c2f4af2c..88466186 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt @@ -90,39 +90,28 @@ object FileUtil { if(it.name.contains(".part-Frag")) return@forEach - //sending to main or SD CARD - if (currentDirectory.exists() && Build.VERSION.SDK_INT >= 26){ - if (Build.VERSION.SDK_INT >= 26 ){ - Files.move(it.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING) - }else{ - it.renameTo(destFile) - } - fileList.add(destFile) - }else{ - //sending to USB OTG - val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*" - destFile = File(currentDirectory.absolutePath + "/${it.name}") + val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*" + destFile = File(currentDirectory.absolutePath + "/${it.name}") - val dest = Uri.parse(destDir).run { - DocumentsContract.buildDocumentUriUsingTree( - this, - DocumentsContract.getTreeDocumentId(this) - ) - } - val destUri = DocumentsContract.createDocument( - context.contentResolver, - dest, - mimeType, - it.name - ) ?: return@forEach - - val inputStream = it.inputStream() - val outputStream = - context.contentResolver.openOutputStream(destUri) ?: return@forEach - inputStream.copyTo(outputStream) - inputStream.closeQuietly() - outputStream.closeQuietly() + val dest = Uri.parse(destDir).run { + DocumentsContract.buildDocumentUriUsingTree( + this, + DocumentsContract.getTreeDocumentId(this) + ) } + val destUri = DocumentsContract.createDocument( + context.contentResolver, + dest, + mimeType, + it.name + ) ?: return@forEach + + val inputStream = it.inputStream() + val outputStream = + context.contentResolver.openOutputStream(destUri) ?: return@forEach + inputStream.copyTo(outputStream) + inputStream.closeQuietly() + outputStream.closeQuietly() fileList.add(destFile) }catch (e: java.lang.Exception) { diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt index 030ef3c7..23434314 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt @@ -245,6 +245,15 @@ class NotificationUtil(var context: Context) { .setArguments(bundle) .createPendingIntent() + val tabBundle = Bundle() + tabBundle.putString("tab", "error") + + val errorTabPendingIntent = NavDeepLinkBuilder(context) + .setGraph(R.navigation.nav_graph) + .setDestination(R.id.downloadQueueMainFragment) + .setArguments(tabBundle) + .createPendingIntent() + notificationBuilder .setContentTitle("${context.getString(R.string.failed_download)}: $title") .setContentText(error) @@ -255,6 +264,7 @@ class NotificationUtil(var context: Context) { R.drawable.ic_launcher_foreground_large ) ) + .setContentIntent(errorTabPendingIntent) .setPriority(NotificationCompat.PRIORITY_MAX) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .clearActions() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt index 0087ad86..36f04bd1 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -1,6 +1,7 @@ package com.deniscerri.ytdlnis.work import android.content.Context +import android.os.Build import android.os.Handler import android.os.Looper import android.util.Log @@ -91,6 +92,16 @@ class DownloadWorker( tempFileDir.delete() tempFileDir.mkdirs() + var useCacheFolder = true + val downloadLocationFile = File(downloadLocation) + if (downloadLocationFile.canWrite()) { + File(downloadLocation).mkdirs() + if(Build.VERSION.SDK_INT > 23) request.addOption("-P", "temp:" + tempFileDir.absolutePath) + useCacheFolder = false + } + val pathUsed = if (useCacheFolder) tempFileDir.absolutePath else downloadLocation + + val aria2 = sharedPreferences.getBoolean("aria2", false) if (aria2) { request.addOption("--downloader", "libaria2c.so") @@ -99,6 +110,13 @@ class DownloadWorker( val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1) if (concurrentFragments > 1) request.addOption("-N", concurrentFragments) } + + val retries = sharedPreferences.getString("--retries", "")!! + val fragmentRetries = sharedPreferences.getString("--fragment_retries", "")!! + + if(retries.isNotEmpty()) request.addOption("retries", retries) + if(fragmentRetries.isNotEmpty()) request.addOption("fragment-retries", fragmentRetries) + val limitRate = sharedPreferences.getString("limit_rate", "") if (limitRate != "") request.addOption("-r", limitRate!!) if(downloadItem.type != DownloadViewModel.Type.command){ @@ -215,9 +233,9 @@ class DownloadWorker( if (downloadItem.audioPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){ request.addOption("--split-chapters") - request.addOption("-P", tempFileDir.absolutePath) + request.addOption("-P", pathUsed) }else{ - request.addOption("-o", tempFileDir.absolutePath + "/${downloadItem.customFileNameTemplate}.%(ext)s") + request.addOption("-o", pathUsed + "/${downloadItem.customFileNameTemplate}.%(ext)s") } } @@ -281,9 +299,9 @@ class DownloadWorker( if (downloadItem.videoPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){ request.addOption("--split-chapters") - request.addOption("-P", tempFileDir.absolutePath) + request.addOption("-P", pathUsed) }else{ - request.addOption("-o", tempFileDir.absolutePath + "/${downloadItem.customFileNameTemplate}.%(ext)s") + request.addOption("-o", pathUsed + "/${downloadItem.customFileNameTemplate}.%(ext)s") } } @@ -294,7 +312,7 @@ class DownloadWorker( writeText(downloadItem.format.format_note) }.absolutePath ) - request.addOption("-P", tempFileDir.absolutePath) + request.addOption("-P", pathUsed) } } @@ -327,33 +345,43 @@ class DownloadWorker( } } }.onSuccess { - //move file from internal to set download directory - setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) - var finalPaths : List? - try { - finalPaths = FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p -> - setProgressAsync(workDataOf("progress" to p, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) - } - if (finalPaths.isNotEmpty()){ - setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $downloadLocation", "id" to downloadItem.id, "log" to logDownloads)) - }else{ - finalPaths = listOf(context.getString(R.string.unfound_file)) - } - }catch (e: Exception){ - finalPaths = listOf(context.getString(R.string.unfound_file)) - e.printStackTrace() - handler.postDelayed({ - Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show() - }, 1000) - } - val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao) + var finalPaths: List + if (useCacheFolder){ + //move file from internal to set download directory + setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) + try { + finalPaths = FileUtil.moveFile(tempFileDir.absoluteFile,context, downloadLocation, keepCache){ p -> + setProgressAsync(workDataOf("progress" to p, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id, "log" to logDownloads)) + } + if (finalPaths.isNotEmpty()){ + setProgressAsync(workDataOf("progress" to 100, "output" to "Moved file to $downloadLocation", "id" to downloadItem.id, "log" to logDownloads)) + }else{ + finalPaths = listOf(context.getString(R.string.unfound_file)) + } + }catch (e: Exception){ + finalPaths = listOf(context.getString(R.string.unfound_file)) + e.printStackTrace() + handler.postDelayed({ + Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show() + }, 1000) + } + }else{ + val now = System.currentTimeMillis() + finalPaths = downloadLocationFile + .walkTopDown() + .filter { it.isFile && it.absolutePath.contains(downloadItem.title) && it.lastModified() > now - 10000000} + .map { it.absolutePath } + .toList() + + if (finalPaths.isEmpty()) finalPaths = listOf(context.getString(R.string.unfound_file)) + } //put download in history val incognito = sharedPreferences.getBoolean("incognito", false) if (!incognito) { val unixtime = System.currentTimeMillis() / 1000 - val file = File(finalPaths?.first()!!) + val file = File(finalPaths.first()!!) downloadItem.format.filesize = file.length() val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPaths.first() , downloadItem.website, downloadItem.format, downloadItem.id) runBlocking { @@ -363,9 +391,7 @@ class DownloadWorker( notificationUtil.cancelDownloadNotification(downloadItem.id.toInt()) notificationUtil.createDownloadFinished( - downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else listOf( - finalPaths!!.maxByOrNull { it.length }!! - ), + downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID ) diff --git a/app/src/main/res/drawable/baseline_sort_24.xml b/app/src/main/res/drawable/baseline_sort_24.xml new file mode 100644 index 00000000..193dbd79 --- /dev/null +++ b/app/src/main/res/drawable/baseline_sort_24.xml @@ -0,0 +1,4 @@ + + + diff --git a/app/src/main/res/layout/fragment_inqueue.xml b/app/src/main/res/layout/fragment_inqueue.xml new file mode 100644 index 00000000..7f5954f2 --- /dev/null +++ b/app/src/main/res/layout/fragment_inqueue.xml @@ -0,0 +1,33 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/queued_menu_context.xml b/app/src/main/res/menu/queued_menu_context.xml index 713b5dbb..eecfffeb 100644 --- a/app/src/main/res/menu/queued_menu_context.xml +++ b/app/src/main/res/menu/queued_menu_context.xml @@ -8,6 +8,13 @@ android:title="@string/remove_results" android:icon="@drawable/baseline_delete_24" app:showAsAction="ifRoom" /> + + + - @string/worst_quality - @string/best_quality + worst + best + + + + @string/file_size + @string/container + ID + + + + filesize + container + id diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49592f2c..87347608 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -279,4 +279,7 @@ Piped Instance Write a PIPED API Server, the app can use for YouTube queries and formats Use Custom Audio Quality + Retries + Fragment Retries + Format Order \ No newline at end of file diff --git a/app/src/main/res/xml/downloading_preferences.xml b/app/src/main/res/xml/downloading_preferences.xml index bdef0624..d151dd3c 100644 --- a/app/src/main/res/xml/downloading_preferences.xml +++ b/app/src/main/res/xml/downloading_preferences.xml @@ -104,6 +104,14 @@ app:summary="@string/log_downloads_summary" app:title="@string/log_downloads" /> + + + + + +