alot of stuff
-added download now option when u highlight only scheduled items in the context menu -tappin on the errored notification will send you to errored tab, if you have logs disabled. If logs on it will send u to the log -added preferred format ordering. By ID, FILESIZE, CONTAINER. Formats grouped by container will also be sorted by filesize -fixed app not showing formats when u try to modify a current download item and its a different type (audio formats not showing if its a video type and vice versa) -fixed app showing generic formats in cases where the format length was the same as that of generic formats (silly mistake) -made the app store static strings for 'best' and 'worst' so that there isnt any confusion when u try to change the language and the stored downloads will have the other language's string -added collected filesize on top of the download queue -potentially fixed app not crashing when going to queued screen? idk -fixed app not moving files when its a fresh install and u havent tried to change the download path. Now the app will just straight up download in that path if it finds that it can write in it. If not, it will use the old method of moving through URI's -added download retries options --retries and --fragment-retries
This commit is contained in:
parent
b25d8410d1
commit
b1b5dbc608
31 changed files with 315 additions and 150 deletions
|
|
@ -10,7 +10,7 @@ def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 6
|
def versionMinor = 6
|
||||||
def versionPatch = 3
|
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 = ""
|
def versionExt = ""
|
||||||
|
|
||||||
if (versionBuild > 0){
|
if (versionBuild > 0){
|
||||||
|
|
@ -64,7 +64,8 @@ android {
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
shrinkResources true
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
debuggable false
|
debuggable false
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +158,7 @@ dependencies {
|
||||||
androidTestImplementation "junit:junit:$junitVer"
|
androidTestImplementation "junit:junit:$junitVer"
|
||||||
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVer"
|
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:runner:1.5.2"
|
||||||
androidTestImplementation "androidx.test:core:1.5.0"
|
androidTestImplementation "androidx.test:core:1.5.0"
|
||||||
androidTestImplementation "androidx.test:rules:1.5.0"
|
androidTestImplementation "androidx.test:rules:1.5.0"
|
||||||
|
|
@ -175,10 +176,10 @@ dependencies {
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime-ktx:$workVer"
|
implementation "androidx.work:work-runtime-ktx:$workVer"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:2.5.1"
|
implementation "androidx.room:room-runtime:2.5.2"
|
||||||
implementation "androidx.room:room-ktx:2.5.1"
|
implementation "androidx.room:room-ktx:2.5.2"
|
||||||
kapt "androidx.room:room-compiler:2.5.1"
|
kapt "androidx.room:room-compiler:2.5.2"
|
||||||
androidTestImplementation "androidx.room:room-testing:2.5.1"
|
androidTestImplementation "androidx.room:room-testing:2.5.2"
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||||
implementation "androidx.compose.runtime:runtime:$composeVer"
|
implementation "androidx.compose.runtime:runtime:$composeVer"
|
||||||
androidTestImplementation "com.google.truth:truth:1.1.3"
|
androidTestImplementation "com.google.truth:truth:1.1.3"
|
||||||
|
|
@ -190,7 +191,7 @@ dependencies {
|
||||||
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
|
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
|
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 'it.xabaras.android:recyclerview-swipedecorator:1.4'
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
|
||||||
implementation "com.github.Irineu333:Highlight-KT:1.0.4"
|
implementation "com.github.Irineu333:Highlight-KT:1.0.4"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package com.deniscerri.ytdlnis.database.dao
|
package com.deniscerri.ytdlnis.database.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CommandTemplateDao {
|
interface CommandTemplateDao {
|
||||||
|
|
@ -11,10 +11,10 @@ interface CommandTemplateDao {
|
||||||
fun getAllTemplates() : List<CommandTemplate>
|
fun getAllTemplates() : List<CommandTemplate>
|
||||||
|
|
||||||
@Query("SELECT * FROM commandTemplates ORDER BY id DESC")
|
@Query("SELECT * FROM commandTemplates ORDER BY id DESC")
|
||||||
fun getAllTemplatesLiveData() : LiveData<List<CommandTemplate>>
|
fun getAllTemplatesFlow() : Flow<List<CommandTemplate>>
|
||||||
|
|
||||||
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
|
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
|
||||||
fun getAllShortcutsLiveData() : LiveData<List<TemplateShortcut>>
|
fun getAllShortcutsFlow() : Flow<List<TemplateShortcut>>
|
||||||
|
|
||||||
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
|
@Query("SELECT * FROM templateShortcuts ORDER BY id DESC")
|
||||||
fun getAllShortcuts() : List<TemplateShortcut>
|
fun getAllShortcuts() : List<TemplateShortcut>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package com.deniscerri.ytdlnis.database.dao
|
package com.deniscerri.ytdlnis.database.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface CookieDao {
|
interface CookieDao {
|
||||||
|
|
@ -10,7 +10,7 @@ interface CookieDao {
|
||||||
fun getAllCookies() : List<CookieItem>
|
fun getAllCookies() : List<CookieItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM cookies ORDER BY id DESC")
|
@Query("SELECT * FROM cookies ORDER BY id DESC")
|
||||||
fun getAllCookiesLiveData() : LiveData<List<CookieItem>>
|
fun getAllCookiesFlow() : Flow<List<CookieItem>>
|
||||||
|
|
||||||
@Query("SELECT EXISTS(SELECT * FROM cookies WHERE url=:url LIMIT 1)")
|
@Query("SELECT EXISTS(SELECT * FROM cookies WHERE url=:url LIMIT 1)")
|
||||||
fun checkIfExistsWithSameURL(url: String) : Boolean
|
fun checkIfExistsWithSameURL(url: String) : Boolean
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
package com.deniscerri.ytdlnis.database.dao
|
package com.deniscerri.ytdlnis.database.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface DownloadDao {
|
interface DownloadDao {
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads ORDER BY status")
|
@Query("SELECT * FROM downloads ORDER BY status")
|
||||||
fun getAllDownloads() : LiveData<List<DownloadItem>>
|
fun getAllDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloads() : LiveData<List<DownloadItem>>
|
fun getActiveDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active'")
|
@Query("SELECT COUNT(*) FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloadsCount() : LiveData<Int>
|
fun getActiveDownloadsCount() : Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Active'")
|
@Query("SELECT * FROM downloads WHERE status='Active'")
|
||||||
fun getActiveDownloadsList() : List<DownloadItem>
|
fun getActiveDownloadsList() : List<DownloadItem>
|
||||||
|
|
@ -23,25 +23,25 @@ interface DownloadDao {
|
||||||
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
fun getActiveAndQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloads() : LiveData<List<DownloadItem>>
|
fun getQueuedDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloadsList() : List<DownloadItem>
|
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
fun getCancelledDownloads() : LiveData<List<DownloadItem>>
|
fun getCancelledDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
fun getCancelledDownloadsList() : List<DownloadItem>
|
fun getCancelledDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloads() : LiveData<List<DownloadItem>>
|
fun getErroredDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloadsList() : List<DownloadItem>
|
fun getErroredDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC")
|
||||||
fun getProcessingDownloads() : LiveData<List<DownloadItem>>
|
fun getProcessingDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
@Query("SELECT * FROM downloads WHERE id=:id LIMIT 1")
|
||||||
fun getDownloadById(id: Long) : DownloadItem
|
fun getDownloadById(id: Long) : DownloadItem
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
package com.deniscerri.ytdlnis.database.dao
|
package com.deniscerri.ytdlnis.database.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface HistoryDao {
|
interface HistoryDao {
|
||||||
|
|
@ -27,7 +27,7 @@ interface HistoryDao {
|
||||||
|
|
||||||
|
|
||||||
@Query("SELECT * FROM history")
|
@Query("SELECT * FROM history")
|
||||||
fun getAllHistory() : LiveData<List<HistoryItem>>
|
fun getAllHistory() : Flow<List<HistoryItem>>
|
||||||
|
|
||||||
@Query("SELECT * FROM history")
|
@Query("SELECT * FROM history")
|
||||||
fun getAllHistoryList() : List<HistoryItem>
|
fun getAllHistoryList() : List<HistoryItem>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
package com.deniscerri.ytdlnis.database.dao
|
package com.deniscerri.ytdlnis.database.dao
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ResultDao {
|
interface ResultDao {
|
||||||
@Query("SELECT * FROM results")
|
@Query("SELECT * FROM results")
|
||||||
fun getResults() : LiveData<List<ResultItem>>
|
fun getResults() : Flow<List<ResultItem>>
|
||||||
|
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
fun getCount() : LiveData<Int>
|
fun getCount() : Flow<Int>
|
||||||
|
|
||||||
@Query("SELECT COUNT(id) FROM results")
|
@Query("SELECT COUNT(id) FROM results")
|
||||||
fun getCountInt() :Int
|
fun getCountInt() :Int
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import androidx.lifecycle.LiveData
|
||||||
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
|
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
import com.deniscerri.ytdlnis.database.models.TemplateShortcut
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
|
class CommandTemplateRepository(private val commandDao: CommandTemplateDao) {
|
||||||
val items : LiveData<List<CommandTemplate>> = commandDao.getAllTemplatesLiveData()
|
val items : Flow<List<CommandTemplate>> = commandDao.getAllTemplatesFlow()
|
||||||
val shortcuts : LiveData<List<TemplateShortcut>> = commandDao.getAllShortcutsLiveData()
|
val shortcuts : Flow<List<TemplateShortcut>> = commandDao.getAllShortcutsFlow()
|
||||||
|
|
||||||
fun getAll() : List<CommandTemplate> {
|
fun getAll() : List<CommandTemplate> {
|
||||||
return commandDao.getAllTemplates()
|
return commandDao.getAllTemplates()
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@ package com.deniscerri.ytdlnis.database.repository
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.deniscerri.ytdlnis.database.dao.CookieDao
|
import com.deniscerri.ytdlnis.database.dao.CookieDao
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class CookieRepository(private val cookieDao: CookieDao) {
|
class CookieRepository(private val cookieDao: CookieDao) {
|
||||||
val items : LiveData<List<CookieItem>> = cookieDao.getAllCookiesLiveData()
|
val items : Flow<List<CookieItem>> = cookieDao.getAllCookiesFlow()
|
||||||
|
|
||||||
fun getAll() : List<CookieItem> {
|
fun getAll() : List<CookieItem> {
|
||||||
return cookieDao.getAllCookies()
|
return cookieDao.getAllCookies()
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,16 @@ import androidx.lifecycle.LiveData
|
||||||
import com.deniscerri.ytdlnis.database.Converters
|
import com.deniscerri.ytdlnis.database.Converters
|
||||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
class DownloadRepository(private val downloadDao: DownloadDao) {
|
class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
val allDownloads : LiveData<List<DownloadItem>> = downloadDao.getAllDownloads()
|
val allDownloads : Flow<List<DownloadItem>> = downloadDao.getAllDownloads()
|
||||||
val activeDownloads : LiveData<List<DownloadItem>> = downloadDao.getActiveDownloads()
|
val activeDownloads : Flow<List<DownloadItem>> = downloadDao.getActiveDownloads()
|
||||||
val activeDownloadsCount : LiveData<Int> = downloadDao.getActiveDownloadsCount()
|
val activeDownloadsCount : Flow<Int> = downloadDao.getActiveDownloadsCount()
|
||||||
val queuedDownloads : LiveData<List<DownloadItem>> = downloadDao.getQueuedDownloads()
|
val queuedDownloads : Flow<List<DownloadItem>> = downloadDao.getQueuedDownloads()
|
||||||
val cancelledDownloads : LiveData<List<DownloadItem>> = downloadDao.getCancelledDownloads()
|
val cancelledDownloads : Flow<List<DownloadItem>> = downloadDao.getCancelledDownloads()
|
||||||
val erroredDownloads : LiveData<List<DownloadItem>> = downloadDao.getErroredDownloads()
|
val erroredDownloads : Flow<List<DownloadItem>> = downloadDao.getErroredDownloads()
|
||||||
val processingDownloads : LiveData<List<DownloadItem>> = downloadDao.getProcessingDownloads()
|
val processingDownloads : Flow<List<DownloadItem>> = downloadDao.getProcessingDownloads()
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Active, Queued, Error, Processing, Cancelled
|
Active, Queued, Error, Processing, Cancelled
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@ import androidx.lifecycle.LiveData
|
||||||
import com.deniscerri.ytdlnis.database.dao.HistoryDao
|
import com.deniscerri.ytdlnis.database.dao.HistoryDao
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
|
||||||
class HistoryRepository(private val historyDao: HistoryDao) {
|
class HistoryRepository(private val historyDao: HistoryDao) {
|
||||||
val items : LiveData<List<HistoryItem>> = historyDao.getAllHistory()
|
val items : Flow<List<HistoryItem>> = historyDao.getAllHistory()
|
||||||
enum class HistorySort{
|
enum class HistorySort{
|
||||||
DESC, ASC
|
DESC, ASC
|
||||||
}
|
}
|
||||||
|
|
@ -59,9 +61,11 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun clearDeletedHistory(){
|
suspend fun clearDeletedHistory(){
|
||||||
items.value?.forEach { item ->
|
items.collectLatest {
|
||||||
if (!FileUtil.exists(item.downloadPath)){
|
it.forEach { item ->
|
||||||
historyDao.delete(item.id)
|
if (!FileUtil.exists(item.downloadPath)){
|
||||||
|
historyDao.delete(item.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,15 @@ package com.deniscerri.ytdlnis.database.repository
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
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.dao.ResultDao
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
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"
|
private val tag: String = "ResultRepository"
|
||||||
val allResults : LiveData<List<ResultItem>> = resultDao.getResults()
|
val allResults : Flow<List<ResultItem>> = resultDao.getResults()
|
||||||
var itemCount = MutableStateFlow(-1)
|
var itemCount = MutableStateFlow(-1)
|
||||||
|
|
||||||
suspend fun insert(it: ResultItem){
|
suspend fun insert(it: ResultItem){
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import android.content.ClipboardManager
|
||||||
import android.content.Context.CLIPBOARD_SERVICE
|
import android.content.Context.CLIPBOARD_SERVICE
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
import com.deniscerri.ytdlnis.database.models.CommandTemplate
|
||||||
|
|
@ -27,8 +28,8 @@ class CommandTemplateViewModel(private val application: Application) : AndroidVi
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).commandTemplateDao
|
val dao = DBManager.getInstance(application).commandTemplateDao
|
||||||
repository = CommandTemplateRepository(dao)
|
repository = CommandTemplateRepository(dao)
|
||||||
items = repository.items
|
items = repository.items.asLiveData()
|
||||||
shortcuts = repository.shortcuts
|
shortcuts = repository.shortcuts.asLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTemplate(itemId: Long): CommandTemplate {
|
fun getTemplate(itemId: Long): CommandTemplate {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import android.util.Log
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
import com.deniscerri.ytdlnis.database.models.CookieItem
|
import com.deniscerri.ytdlnis.database.models.CookieItem
|
||||||
|
|
@ -31,7 +32,7 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).cookieDao
|
val dao = DBManager.getInstance(application).cookieDao
|
||||||
repository = CookieRepository(dao)
|
repository = CookieRepository(dao)
|
||||||
items = repository.items
|
items = repository.items.asLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAll(): List<CookieItem> {
|
fun getAll(): List<CookieItem> {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import android.util.DisplayMetrics
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
|
|
@ -77,13 +78,13 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
commandTemplateDao = DBManager.getInstance(application).commandTemplateDao
|
commandTemplateDao = DBManager.getInstance(application).commandTemplateDao
|
||||||
infoUtil = InfoUtil(application)
|
infoUtil = InfoUtil(application)
|
||||||
|
|
||||||
allDownloads = repository.allDownloads
|
allDownloads = repository.allDownloads.asLiveData()
|
||||||
queuedDownloads = repository.queuedDownloads
|
queuedDownloads = repository.queuedDownloads.asLiveData()
|
||||||
activeDownloads = repository.activeDownloads
|
activeDownloads = repository.activeDownloads.asLiveData()
|
||||||
activeDownloadsCount = repository.activeDownloadsCount
|
activeDownloadsCount = repository.activeDownloadsCount.asLiveData()
|
||||||
processingDownloads = repository.processingDownloads
|
processingDownloads = repository.processingDownloads.asLiveData()
|
||||||
cancelledDownloads = repository.cancelledDownloads
|
cancelledDownloads = repository.cancelledDownloads.asLiveData()
|
||||||
erroredDownloads = repository.erroredDownloads
|
erroredDownloads = repository.erroredDownloads.asLiveData()
|
||||||
|
|
||||||
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString()
|
||||||
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
|
formatIDPreference = sharedPreferences.getString("format_id", "").toString()
|
||||||
|
|
@ -95,15 +96,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
resources = Resources(application.assets, metrics, confTmp)
|
resources = Resources(application.assets, metrics, confTmp)
|
||||||
|
|
||||||
|
|
||||||
val videoFormat = resources.getStringArray(R.array.video_formats)
|
|
||||||
val videoFormatValues = resources.getStringArray(R.array.video_formats_values)
|
val videoFormatValues = resources.getStringArray(R.array.video_formats_values)
|
||||||
var videoContainer = sharedPreferences.getString("video_format", "Default")
|
var videoContainer = sharedPreferences.getString("video_format", "Default")
|
||||||
if (videoContainer == "Default") videoContainer = App.instance.getString(R.string.defaultValue)
|
if (videoContainer == "Default") videoContainer = App.instance.getString(R.string.defaultValue)
|
||||||
|
|
||||||
defaultVideoFormats = mutableListOf()
|
defaultVideoFormats = mutableListOf()
|
||||||
videoFormat.forEachIndexed { index , it ->
|
videoFormatValues.forEach {
|
||||||
val tmp = Format(
|
val tmp = Format(
|
||||||
videoFormatValues[index],
|
it,
|
||||||
videoContainer!!,
|
videoContainer!!,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
|
|
@ -125,7 +125,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
resources.getString(R.string.best_quality)
|
"best"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -381,7 +381,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGenericVideoFormats() : MutableList<Format>{
|
fun getGenericVideoFormats() : MutableList<Format>{
|
||||||
val videoFormats = resources.getStringArray(R.array.video_formats)
|
val videoFormats = resources.getStringArray(R.array.video_formats_values)
|
||||||
val formats = mutableListOf<Format>()
|
val formats = mutableListOf<Format>()
|
||||||
var containerPreference = sharedPreferences.getString("video_format", "Default")
|
var containerPreference = sharedPreferences.getString("video_format", "Default")
|
||||||
if (containerPreference == "Default") containerPreference = resources.getString(R.string.defaultValue)
|
if (containerPreference == "Default") containerPreference = resources.getString(R.string.defaultValue)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MediatorLiveData
|
import androidx.lifecycle.MediatorLiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.deniscerri.ytdlnis.database.DBManager
|
import com.deniscerri.ytdlnis.database.DBManager
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
|
|
@ -28,7 +29,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).historyDao
|
val dao = DBManager.getInstance(application).historyDao
|
||||||
repository = HistoryRepository(dao)
|
repository = HistoryRepository(dao)
|
||||||
allItems = repository.items
|
allItems = repository.items.asLiveData()
|
||||||
|
|
||||||
_items.addSource(allItems){
|
_items.addSource(allItems){
|
||||||
filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
|
filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ package com.deniscerri.ytdlnis.database.viewmodel
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.App
|
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.ResultRepository
|
||||||
import com.deniscerri.ytdlnis.database.repository.SearchHistoryRepository
|
import com.deniscerri.ytdlnis.database.repository.SearchHistoryRepository
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
@ -33,9 +32,9 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
init {
|
init {
|
||||||
val dao = DBManager.getInstance(application).resultDao
|
val dao = DBManager.getInstance(application).resultDao
|
||||||
val commandDao = DBManager.getInstance(application).commandTemplateDao
|
val commandDao = DBManager.getInstance(application).commandTemplateDao
|
||||||
repository = ResultRepository(dao, commandDao, getApplication<Application>().applicationContext)
|
repository = ResultRepository(dao, getApplication<Application>().applicationContext)
|
||||||
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao)
|
||||||
items = repository.allResults
|
items = repository.allResults.asLiveData()
|
||||||
loadingItems.postValue(false)
|
loadingItems.postValue(false)
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadItem = withContext(Dispatchers.IO) {
|
downloadItem = withContext(Dispatchers.IO) {
|
||||||
if (currentDownloadItem != null && currentDownloadItem!!.type == Type.audio){
|
if (currentDownloadItem != null){
|
||||||
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
||||||
Gson().fromJson(string, DownloadItem::class.java)
|
Gson().fromJson(string, DownloadItem::class.java)
|
||||||
}else{
|
}else{
|
||||||
|
|
@ -126,7 +126,17 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
if (free == "?") freeSpace.visibility = View.GONE
|
if (free == "?") freeSpace.visibility = View.GONE
|
||||||
|
|
||||||
var formats = mutableListOf<Format>()
|
var formats = mutableListOf<Format>()
|
||||||
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) })
|
if (formats.isEmpty()) formats.addAll(downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) })
|
||||||
|
|
||||||
val containers = requireContext().resources.getStringArray(R.array.audio_containers)
|
val containers = requireContext().resources.getStringArray(R.array.audio_containers)
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadItem = withContext(Dispatchers.IO){
|
downloadItem = withContext(Dispatchers.IO){
|
||||||
if (currentDownloadItem != null && currentDownloadItem!!.type == Type.video){
|
if (currentDownloadItem != null){
|
||||||
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
val string = Gson().toJson(currentDownloadItem, DownloadItem::class.java)
|
||||||
Gson().fromJson(string, DownloadItem::class.java)
|
Gson().fromJson(string, DownloadItem::class.java)
|
||||||
}else{
|
}else{
|
||||||
|
|
@ -127,8 +127,19 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
|
freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
|
||||||
if (free == "?") freeSpace.visibility = View.GONE
|
if (free == "?") freeSpace.visibility = View.GONE
|
||||||
|
|
||||||
|
|
||||||
var formats = mutableListOf<Format>()
|
var formats = mutableListOf<Format>()
|
||||||
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)
|
if (formats.isEmpty()) formats.addAll(downloadItem.allFormats)
|
||||||
|
|
||||||
val containers = requireContext().resources.getStringArray(R.array.video_containers)
|
val containers = requireContext().resources.getStringArray(R.array.video_containers)
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
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.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.facebook.shimmer.ShimmerFrameLayout
|
import com.facebook.shimmer.ShimmerFrameLayout
|
||||||
|
|
@ -27,6 +25,7 @@ import com.google.android.material.card.MaterialCardView
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import okhttp3.internal.format
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -45,6 +44,11 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
private lateinit var videoTitle : TextView
|
private lateinit var videoTitle : TextView
|
||||||
private lateinit var audioTitle : TextView
|
private lateinit var audioTitle : TextView
|
||||||
|
|
||||||
|
private lateinit var sortBy : FormatSorting
|
||||||
|
enum class FormatSorting {
|
||||||
|
filesize, container, id
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
infoUtil = InfoUtil(requireActivity().applicationContext)
|
infoUtil = InfoUtil(requireActivity().applicationContext)
|
||||||
|
|
@ -61,6 +65,8 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
val view = LayoutInflater.from(context).inflate(R.layout.format_select_bottom_sheet, null)
|
val view = LayoutInflater.from(context).inflate(R.layout.format_select_bottom_sheet, null)
|
||||||
dialog.setContentView(view)
|
dialog.setContentView(view)
|
||||||
|
|
||||||
|
sortBy = FormatSorting.valueOf(sharedPreferences.getString("format_order", "filesize")!!)
|
||||||
|
|
||||||
dialog.setOnShowListener {
|
dialog.setOnShowListener {
|
||||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
|
|
@ -78,10 +84,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
okBtn = view.findViewById(R.id.format_ok)
|
okBtn = view.findViewById(R.id.format_ok)
|
||||||
|
|
||||||
shimmers.visibility = View.GONE
|
shimmers.visibility = View.GONE
|
||||||
val hasGenericFormats = when(items.first()!!.type){
|
val hasGenericFormats = formats.first().isEmpty() || formats.last().any { it.format_id == "best" }
|
||||||
Type.audio -> formats.first().size == resources.getStringArray(R.array.audio_formats).size
|
|
||||||
else -> formats.first().size == resources.getStringArray(R.array.video_formats).size
|
|
||||||
}
|
|
||||||
if (items.size > 1){
|
if (items.size > 1){
|
||||||
|
|
||||||
if (!hasGenericFormats){
|
if (!hasGenericFormats){
|
||||||
|
|
@ -131,10 +134,10 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
infoUtil.getFormats(items.first()!!.url)
|
infoUtil.getFormats(items.first()!!.url)
|
||||||
}
|
}
|
||||||
res.filter { it.format_note != "storyboard" }
|
res.filter { it.format_note != "storyboard" }
|
||||||
if(items.first()?.type == Type.audio){
|
chosenFormats = if(items.first()?.type == Type.audio){
|
||||||
chosenFormats = res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||||
}else{
|
}else{
|
||||||
chosenFormats = res
|
res
|
||||||
}
|
}
|
||||||
if (chosenFormats.isEmpty()) throw Exception()
|
if (chosenFormats.isEmpty()) throw Exception()
|
||||||
//playlist format filtering
|
//playlist format filtering
|
||||||
|
|
@ -216,7 +219,14 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun addFormatsToView(){
|
private fun addFormatsToView(){
|
||||||
val canMultiSelectAudio = items.first()?.type == Type.video && chosenFormats.find { it.format_note.contains("audio", ignoreCase = true) } != null
|
//sort
|
||||||
|
val finalFormats: List<Format> = 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()
|
videoFormatList.removeAllViews()
|
||||||
audioFormatList.removeAllViews()
|
audioFormatList.removeAllViews()
|
||||||
|
|
||||||
|
|
@ -226,7 +236,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
audioTitle.visibility = View.GONE
|
audioTitle.visibility = View.GONE
|
||||||
okBtn.visibility = View.GONE
|
okBtn.visibility = View.GONE
|
||||||
}else{
|
}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
|
audioFormatList.visibility = View.GONE
|
||||||
audioTitle.visibility = View.GONE
|
audioTitle.visibility = View.GONE
|
||||||
videoTitle.visibility = View.GONE
|
videoTitle.visibility = View.GONE
|
||||||
|
|
@ -239,8 +249,8 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in chosenFormats.lastIndex downTo 0){
|
for (i in finalFormats.lastIndex downTo 0){
|
||||||
val format = chosenFormats[i]
|
val format = finalFormats[i]
|
||||||
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
|
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
|
||||||
formatItem.tag = "${format.format_id}${format.format_note}"
|
formatItem.tag = "${format.format_id}${format.format_note}"
|
||||||
UiUtil.populateFormatCard(formatItem as MaterialCardView, format, null)
|
UiUtil.populateFormatCard(formatItem as MaterialCardView, format, null)
|
||||||
|
|
@ -250,7 +260,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
val clickedCard = (clickedformat as MaterialCardView)
|
val clickedCard = (clickedformat as MaterialCardView)
|
||||||
if (format.vcodec.isNotBlank() && format.vcodec != "none") {
|
if (format.vcodec.isNotBlank() && format.vcodec != "none") {
|
||||||
if (clickedCard.isChecked) {
|
if (clickedCard.isChecked) {
|
||||||
listener.onFormatClick(List(items.size){chosenFormats}, listOf(FormatTuple(format, null)))
|
listener.onFormatClick(List(items.size){finalFormats}, listOf(FormatTuple(format, null)))
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
videoFormatList.forEach { (it as MaterialCardView).isChecked = false }
|
videoFormatList.forEach { (it as MaterialCardView).isChecked = false }
|
||||||
|
|
@ -269,14 +279,14 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if (items.size == 1){
|
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{
|
}else{
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
formatCollection.forEach {
|
formatCollection.forEach {
|
||||||
selectedFormats.add(it.first{ f -> f.format_id == format.format_id})
|
selectedFormats.add(it.first{ f -> f.format_id == format.format_id})
|
||||||
}
|
}
|
||||||
if (selectedFormats.isEmpty()) {
|
if (selectedFormats.isEmpty()) {
|
||||||
items.forEach {
|
items.forEach { _ ->
|
||||||
selectedFormats.add(format)
|
selectedFormats.add(format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,10 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
})
|
})
|
||||||
mainActivity.hideBottomNavigation()
|
mainActivity.hideBottomNavigation()
|
||||||
initMenu()
|
initMenu()
|
||||||
|
|
||||||
|
if (arguments?.getString("tab") != null){
|
||||||
|
tabLayout.selectTab(tabLayout.getTabAt(3))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initMenu() {
|
private fun initMenu() {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.deniscerri.ytdlnis.ui.downloads
|
package com.deniscerri.ytdlnis.ui.downloads
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
|
|
@ -59,6 +60,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
private lateinit var queuedRecyclerView : RecyclerView
|
private lateinit var queuedRecyclerView : RecyclerView
|
||||||
private lateinit var queuedDownloads : GenericDownloadAdapter
|
private lateinit var queuedDownloads : GenericDownloadAdapter
|
||||||
private lateinit var notificationUtil: NotificationUtil
|
private lateinit var notificationUtil: NotificationUtil
|
||||||
|
private lateinit var fileSize: TextView
|
||||||
private var selectedObjects: ArrayList<DownloadItem>? = null
|
private var selectedObjects: ArrayList<DownloadItem>? = null
|
||||||
private var actionMode : ActionMode? = null
|
private var actionMode : ActionMode? = null
|
||||||
private lateinit var items : MutableList<DownloadItem>
|
private lateinit var items : MutableList<DownloadItem>
|
||||||
|
|
@ -68,7 +70,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_generic_download_queue, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_inqueue, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
notificationUtil = NotificationUtil(requireContext())
|
notificationUtil = NotificationUtil(requireContext())
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
|
|
@ -77,9 +79,10 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
return fragmentView
|
return fragmentView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
fileSize = view.findViewById(R.id.filesize)
|
||||||
queuedDownloads =
|
queuedDownloads =
|
||||||
GenericDownloadAdapter(
|
GenericDownloadAdapter(
|
||||||
this,
|
this,
|
||||||
|
|
@ -97,6 +100,11 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
|
|
||||||
downloadViewModel.queuedDownloads.observe(viewLifecycleOwner) {
|
downloadViewModel.queuedDownloads.observe(viewLifecycleOwner) {
|
||||||
items = it.toMutableList()
|
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)
|
queuedDownloads.submitList(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -232,6 +240,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
|
|
||||||
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
override fun onCardSelect(itemID: Long, isChecked: Boolean) {
|
||||||
val item = items.find { it.id == itemID }
|
val item = items.find { it.id == itemID }
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
selectedObjects!!.add(item!!)
|
selectedObjects!!.add(item!!)
|
||||||
if (actionMode == null){
|
if (actionMode == null){
|
||||||
|
|
@ -248,6 +257,9 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (actionMode != null){
|
||||||
|
actionMode!!.menu.getItem(1).isVisible = selectedObjects!!.all { it.downloadStartTime > now }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeItem(itemID: Long){
|
private fun removeItem(itemID: Long){
|
||||||
|
|
@ -311,6 +323,16 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
true
|
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 -> {
|
R.id.select_all -> {
|
||||||
queuedDownloads.checkAll(items)
|
queuedDownloads.checkAll(items)
|
||||||
selectedObjects?.clear()
|
selectedObjects?.clear()
|
||||||
|
|
|
||||||
|
|
@ -90,39 +90,28 @@ object FileUtil {
|
||||||
|
|
||||||
if(it.name.contains(".part-Frag")) return@forEach
|
if(it.name.contains(".part-Frag")) return@forEach
|
||||||
|
|
||||||
//sending to main or SD CARD
|
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(it.extension) ?: "*/*"
|
||||||
if (currentDirectory.exists() && Build.VERSION.SDK_INT >= 26){
|
destFile = File(currentDirectory.absolutePath + "/${it.name}")
|
||||||
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 dest = Uri.parse(destDir).run {
|
val dest = Uri.parse(destDir).run {
|
||||||
DocumentsContract.buildDocumentUriUsingTree(
|
DocumentsContract.buildDocumentUriUsingTree(
|
||||||
this,
|
this,
|
||||||
DocumentsContract.getTreeDocumentId(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 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)
|
fileList.add(destFile)
|
||||||
}catch (e: java.lang.Exception) {
|
}catch (e: java.lang.Exception) {
|
||||||
|
|
|
||||||
|
|
@ -245,6 +245,15 @@ class NotificationUtil(var context: Context) {
|
||||||
.setArguments(bundle)
|
.setArguments(bundle)
|
||||||
.createPendingIntent()
|
.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
|
notificationBuilder
|
||||||
.setContentTitle("${context.getString(R.string.failed_download)}: $title")
|
.setContentTitle("${context.getString(R.string.failed_download)}: $title")
|
||||||
.setContentText(error)
|
.setContentText(error)
|
||||||
|
|
@ -255,6 +264,7 @@ class NotificationUtil(var context: Context) {
|
||||||
R.drawable.ic_launcher_foreground_large
|
R.drawable.ic_launcher_foreground_large
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.setContentIntent(errorTabPendingIntent)
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.clearActions()
|
.clearActions()
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.deniscerri.ytdlnis.work
|
package com.deniscerri.ytdlnis.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
@ -91,6 +92,16 @@ class DownloadWorker(
|
||||||
tempFileDir.delete()
|
tempFileDir.delete()
|
||||||
tempFileDir.mkdirs()
|
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)
|
val aria2 = sharedPreferences.getBoolean("aria2", false)
|
||||||
if (aria2) {
|
if (aria2) {
|
||||||
request.addOption("--downloader", "libaria2c.so")
|
request.addOption("--downloader", "libaria2c.so")
|
||||||
|
|
@ -99,6 +110,13 @@ class DownloadWorker(
|
||||||
val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1)
|
val concurrentFragments = sharedPreferences.getInt("concurrent_fragments", 1)
|
||||||
if (concurrentFragments > 1) request.addOption("-N", concurrentFragments)
|
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", "")
|
val limitRate = sharedPreferences.getString("limit_rate", "")
|
||||||
if (limitRate != "") request.addOption("-r", limitRate!!)
|
if (limitRate != "") request.addOption("-r", limitRate!!)
|
||||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||||
|
|
@ -215,9 +233,9 @@ class DownloadWorker(
|
||||||
|
|
||||||
if (downloadItem.audioPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
|
if (downloadItem.audioPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
|
||||||
request.addOption("--split-chapters")
|
request.addOption("--split-chapters")
|
||||||
request.addOption("-P", tempFileDir.absolutePath)
|
request.addOption("-P", pathUsed)
|
||||||
}else{
|
}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()){
|
if (downloadItem.videoPreferences.splitByChapters && downloadItem.downloadSections.isBlank()){
|
||||||
request.addOption("--split-chapters")
|
request.addOption("--split-chapters")
|
||||||
request.addOption("-P", tempFileDir.absolutePath)
|
request.addOption("-P", pathUsed)
|
||||||
}else{
|
}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)
|
writeText(downloadItem.format.format_note)
|
||||||
}.absolutePath
|
}.absolutePath
|
||||||
)
|
)
|
||||||
request.addOption("-P", tempFileDir.absolutePath)
|
request.addOption("-P", pathUsed)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -327,33 +345,43 @@ class DownloadWorker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onSuccess {
|
}.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<String>?
|
|
||||||
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)
|
val wasQuickDownloaded = updateDownloadItem(downloadItem, infoUtil, dao, resultDao)
|
||||||
|
var finalPaths: List<String>
|
||||||
|
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
|
//put download in history
|
||||||
val incognito = sharedPreferences.getBoolean("incognito", false)
|
val incognito = sharedPreferences.getBoolean("incognito", false)
|
||||||
if (!incognito) {
|
if (!incognito) {
|
||||||
val unixtime = System.currentTimeMillis() / 1000
|
val unixtime = System.currentTimeMillis() / 1000
|
||||||
val file = File(finalPaths?.first()!!)
|
val file = File(finalPaths.first()!!)
|
||||||
downloadItem.format.filesize = file.length()
|
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)
|
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 {
|
runBlocking {
|
||||||
|
|
@ -363,9 +391,7 @@ class DownloadWorker(
|
||||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||||
|
|
||||||
notificationUtil.createDownloadFinished(
|
notificationUtil.createDownloadFinished(
|
||||||
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else listOf(
|
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths,
|
||||||
finalPaths!!.maxByOrNull { it.length }!!
|
|
||||||
),
|
|
||||||
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
NotificationUtil.DOWNLOAD_FINISHED_CHANNEL_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
4
app/src/main/res/drawable/baseline_sort_24.xml
Normal file
4
app/src/main/res/drawable/baseline_sort_24.xml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<vector android:autoMirrored="true" android:height="24dp" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?android:colorAccent" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
|
||||||
|
</vector>
|
||||||
33
app/src/main/res/layout/fragment_inqueue.xml
Normal file
33
app/src/main/res/layout/fragment_inqueue.xml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/filesize"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingHorizontal="30dp"
|
||||||
|
android:paddingVertical="10dp"
|
||||||
|
android:text="@string/file_size"
|
||||||
|
android:textStyle="bold"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/download_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/filesize">
|
||||||
|
|
||||||
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
@ -8,6 +8,13 @@
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
android:icon="@drawable/baseline_delete_24"
|
android:icon="@drawable/baseline_delete_24"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/download"
|
||||||
|
android:title="@string/download"
|
||||||
|
android:icon="@drawable/baseline_download_24"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/select_all"
|
android:id="@+id/select_all"
|
||||||
android:title="@string/select_all"
|
android:title="@string/select_all"
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,20 @@
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="audio_formats">
|
<string-array name="audio_formats">
|
||||||
<item>@string/worst_quality</item>
|
<item>worst</item>
|
||||||
<item>@string/best_quality</item>
|
<item>best</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="format_ordering">
|
||||||
|
<item>@string/file_size</item>
|
||||||
|
<item>@string/container</item>
|
||||||
|
<item>ID</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="format_ordering_values">
|
||||||
|
<item>filesize</item>
|
||||||
|
<item>container</item>
|
||||||
|
<item>id</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="sponsorblock_settings_entries">
|
<string-array name="sponsorblock_settings_entries">
|
||||||
|
|
|
||||||
|
|
@ -279,4 +279,7 @@
|
||||||
<string name="piped_instance">Piped Instance</string>
|
<string name="piped_instance">Piped Instance</string>
|
||||||
<string name="piped_instance_summary">Write a PIPED API Server, the app can use for YouTube queries and formats</string>
|
<string name="piped_instance_summary">Write a PIPED API Server, the app can use for YouTube queries and formats</string>
|
||||||
<string name="custom_audio_quality">Use Custom Audio Quality</string>
|
<string name="custom_audio_quality">Use Custom Audio Quality</string>
|
||||||
|
<string name="retries">Retries</string>
|
||||||
|
<string name="fragment_retries">Fragment Retries</string>
|
||||||
|
<string name="format_order">Format Order</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -104,6 +104,14 @@
|
||||||
app:summary="@string/log_downloads_summary"
|
app:summary="@string/log_downloads_summary"
|
||||||
app:title="@string/log_downloads" />
|
app:title="@string/log_downloads" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
app:key="retries"
|
||||||
|
app:title="@string/retries" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
app:key="fragment_retries"
|
||||||
|
app:title="@string/fragment_retries" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:icon="@drawable/ic_battery"
|
app:icon="@drawable/ic_battery"
|
||||||
app:key="ignore_battery"
|
app:key="ignore_battery"
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,15 @@
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:title="@string/format_source" />
|
app:title="@string/format_source" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:defaultValue="filesize"
|
||||||
|
android:entries="@array/format_ordering"
|
||||||
|
android:entryValues="@array/format_ordering_values"
|
||||||
|
android:icon="@drawable/baseline_sort_24"
|
||||||
|
app:key="format_order"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
app:title="@string/format_order" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
app:key="piped_instance"
|
app:key="piped_instance"
|
||||||
app:defaultValue="https://pipedapi.kavin.rocks"
|
app:defaultValue="https://pipedapi.kavin.rocks"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue