diff --git a/app/build.gradle b/app/build.gradle
index 0360c55b..76376731 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,8 +9,8 @@ plugins {
def properties = new Properties()
def versionMajor = 1
def versionMinor = 6
-def versionPatch = 7
-def versionBuild = 0 // bump for dogfood builds, public betas, etc.
+def versionPatch = 8
+def versionBuild = 2 // bump for dogfood builds, public betas, etc.
def versionExt = ""
if (versionBuild > 0){
@@ -136,6 +136,7 @@ dependencies {
implementation "androidx.navigation:navigation-ui-ktx:$navVer"
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.test.ext:junit-ktx:1.1.5'
+ implementation 'androidx.compose.ui:ui-android:1.5.3'
testImplementation "junit:junit:$junitVer"
androidTestImplementation "junit:junit:$junitVer"
androidTestImplementation "androidx.test.ext:junit:$androidJunitVer"
@@ -186,4 +187,6 @@ dependencies {
implementation "com.anggrayudi:storage:1.5.5"
implementation 'me.zhanghai.android.fastscroll:library:1.3.0'
+ implementation 'com.google.accompanist:accompanist-webview:0.30.1'
+ implementation 'androidx.compose.material3:material3-android:1.2.0-alpha08'
}
diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/10.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/10.json
index 385a6ccb..d182a497 100644
--- a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/10.json
+++ b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/10.json
@@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 10,
- "identityHash": "6b5efd73749a7138035e1f00349715c8",
+ "identityHash": "0647c4b527efea884e1dc26157cd4189",
"entities": [
{
"tableName": "results",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `creationTime` INTEGER NOT NULL)",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL DEFAULT '', `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `creationTime` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
@@ -42,7 +42,8 @@
"fieldPath": "thumb",
"columnName": "thumb",
"affinity": "TEXT",
- "notNull": true
+ "notNull": true,
+ "defaultValue": "''"
},
{
"fieldPath": "website",
@@ -93,7 +94,7 @@
},
{
"tableName": "history",
- "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0)",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0, `command` TEXT NOT NULL DEFAULT '')",
"fields": [
{
"fieldPath": "id",
@@ -167,6 +168,13 @@
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
+ },
+ {
+ "fieldPath": "command",
+ "columnName": "command",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
}
],
"primaryKey": {
@@ -538,7 +546,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
- "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6b5efd73749a7138035e1f00349715c8')"
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0647c4b527efea884e1dc26157cd4189')"
]
}
}
\ No newline at end of file
diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/11.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/11.json
new file mode 100644
index 00000000..ea52b871
--- /dev/null
+++ b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/11.json
@@ -0,0 +1,558 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 11,
+ "identityHash": "09798517d2b7ce96999c879bcd381c7f",
+ "entities": [
+ {
+ "tableName": "results",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `website` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `formats` TEXT NOT NULL, `urls` TEXT NOT NULL DEFAULT '', `chapters` TEXT, `creationTime` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "thumb",
+ "columnName": "thumb",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "website",
+ "columnName": "website",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistTitle",
+ "columnName": "playlistTitle",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "formats",
+ "columnName": "formats",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "urls",
+ "columnName": "urls",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "chapters",
+ "columnName": "chapters",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "creationTime",
+ "columnName": "creationTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `duration` TEXT NOT NULL, `thumb` TEXT NOT NULL, `type` TEXT NOT NULL, `time` INTEGER NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadId` INTEGER NOT NULL DEFAULT 0, `command` TEXT NOT NULL DEFAULT '')",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "thumb",
+ "columnName": "thumb",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "time",
+ "columnName": "time",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadPath",
+ "columnName": "downloadPath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "website",
+ "columnName": "website",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "format",
+ "columnName": "format",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadId",
+ "columnName": "downloadId",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "command",
+ "columnName": "command",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "downloads",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `title` TEXT NOT NULL, `author` TEXT NOT NULL, `thumb` TEXT NOT NULL, `duration` TEXT NOT NULL, `type` TEXT NOT NULL, `format` TEXT NOT NULL, `container` TEXT NOT NULL DEFAULT 'Default', `downloadSections` TEXT NOT NULL DEFAULT '', `allFormats` TEXT NOT NULL, `downloadPath` TEXT NOT NULL, `website` TEXT NOT NULL, `downloadSize` TEXT NOT NULL, `playlistTitle` TEXT NOT NULL, `audioPreferences` TEXT NOT NULL, `videoPreferences` TEXT NOT NULL, `extraCommands` TEXT NOT NULL DEFAULT '', `customFileNameTemplate` TEXT NOT NULL, `SaveThumb` INTEGER NOT NULL, `status` TEXT NOT NULL DEFAULT 'Queued', `downloadStartTime` INTEGER NOT NULL DEFAULT 0, `logID` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "author",
+ "columnName": "author",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "thumb",
+ "columnName": "thumb",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "duration",
+ "columnName": "duration",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "format",
+ "columnName": "format",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "container",
+ "columnName": "container",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'Default'"
+ },
+ {
+ "fieldPath": "downloadSections",
+ "columnName": "downloadSections",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "allFormats",
+ "columnName": "allFormats",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadPath",
+ "columnName": "downloadPath",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "website",
+ "columnName": "website",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadSize",
+ "columnName": "downloadSize",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "playlistTitle",
+ "columnName": "playlistTitle",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "audioPreferences",
+ "columnName": "audioPreferences",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "videoPreferences",
+ "columnName": "videoPreferences",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "extraCommands",
+ "columnName": "extraCommands",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "''"
+ },
+ {
+ "fieldPath": "customFileNameTemplate",
+ "columnName": "customFileNameTemplate",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "SaveThumb",
+ "columnName": "SaveThumb",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "status",
+ "columnName": "status",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'Queued'"
+ },
+ {
+ "fieldPath": "downloadStartTime",
+ "columnName": "downloadStartTime",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "logID",
+ "columnName": "logID",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "commandTemplates",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `useAsExtraCommand` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "useAsExtraCommand",
+ "columnName": "useAsExtraCommand",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "searchHistory",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "query",
+ "columnName": "query",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "templateShortcuts",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "cookies",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `url` TEXT NOT NULL, `content` TEXT NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "logs",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `format` TEXT NOT NULL, `downloadType` TEXT NOT NULL, `downloadTime` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "title",
+ "columnName": "title",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "content",
+ "columnName": "content",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "format",
+ "columnName": "format",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadType",
+ "columnName": "downloadType",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "downloadTime",
+ "columnName": "downloadTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "terminalDownloads",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `command` TEXT NOT NULL, `log` TEXT DEFAULT '')",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "command",
+ "columnName": "command",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "log",
+ "columnName": "log",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "''"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '09798517d2b7ce96999c879bcd381c7f')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8c04e1a9..1daf5700 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -194,6 +194,7 @@
+
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/App.kt b/app/src/main/java/com/deniscerri/ytdlnis/App.kt
index 9e2f3753..dc94b265 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/App.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/App.kt
@@ -5,19 +5,7 @@ import android.os.Looper
import android.widget.Toast
import androidx.core.content.edit
import androidx.preference.PreferenceManager
-import androidx.work.Configuration
-import androidx.work.Constraints
-import androidx.work.ExistingPeriodicWorkPolicy
-import androidx.work.NetworkType
-import androidx.work.PeriodicWorkRequestBuilder
-import androidx.work.WorkManager
-import com.deniscerri.ytdlnis.database.DBManager
-import com.deniscerri.ytdlnis.database.repository.DownloadRepository
-import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.util.NotificationUtil
-import com.deniscerri.ytdlnis.util.UpdateUtil
-import com.deniscerri.ytdlnis.work.UpdateYTDLWorker
-import com.google.android.material.snackbar.Snackbar
import com.yausername.aria2c.Aria2c
import com.yausername.ffmpeg.FFmpeg
import com.yausername.youtubedl_android.YoutubeDL
@@ -27,8 +15,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.util.*
-import java.util.concurrent.Executors
-import java.util.concurrent.TimeUnit
class App : Application() {
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt
index 22c98177..326bda3c 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/ConfigureMultipleDownloadsAdapter.kt
@@ -58,8 +58,8 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
// THUMBNAIL ----------------------------------
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
- val imageURL = item!!.thumb
- if (imageURL.isNotEmpty()) {
+ val imageURL = item?.thumb
+ if (!imageURL.isNullOrBlank()) {
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
} else {
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
index 7b936abc..a5c38ee5 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
@@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.Flow
LogItem::class,
TerminalItem::class
],
- version = 10,
+ version = 11,
autoMigrations = [
AutoMigration (from = 1, to = 2),
AutoMigration (from = 2, to = 3),
@@ -33,7 +33,8 @@ import kotlinx.coroutines.flow.Flow
AutoMigration (from = 6, to = 7),
AutoMigration (from = 7, to = 8),
AutoMigration (from = 8, to = 9),
- AutoMigration (from = 9, to = 10)
+ AutoMigration (from = 9, to = 10),
+ AutoMigration (from = 10, to = 11)
]
)
abstract class DBManager : RoomDatabase(){
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 3fedfefe..55539251 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
@@ -34,6 +34,9 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'")
fun getActiveAndQueuedDownloadsList() : List
+ @Query("SELECT id FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'")
+ fun getActiveAndQueuedDownloadIDs() : List
+
@Query("SELECT * FROM downloads WHERE status='Active' or status='Queued' or status='QueuedPaused' or status='Paused'")
fun getActiveAndQueuedDownloads() : Flow>
@@ -122,8 +125,8 @@ interface DownloadDao {
@Query("DELETE FROM downloads WHERE id in (:list)")
suspend fun deleteAllWithIDs(list: List)
- @Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued' or status='QueuedPaused'")
- suspend fun cancelQueued()
+ @Query("UPDATE downloads SET status='Cancelled' WHERE status='Queued' or status='QueuedPaused' or status='Active' or status='Paused'")
+ suspend fun cancelActiveQueued()
@Query("DELETE FROM downloads WHERE status='Processing' AND id=:id")
suspend fun deleteSingleProcessing(id: Long)
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 cf21c4f2..e6bb8e6a 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
@@ -35,6 +35,9 @@ interface HistoryDao {
@Query("SELECT * FROM history WHERE id=:id LIMIT 1")
suspend fun getHistoryItem(id: Int) : HistoryItem
+ @Query("SELECT * FROM history WHERE url=:url")
+ fun getAllHistoryByURL(url: String) : List
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: HistoryItem)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/TerminalDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/TerminalDao.kt
index f9ccf873..548771ff 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/TerminalDao.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/TerminalDao.kt
@@ -11,15 +11,38 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface TerminalDao {
- @Query("SELECT * FROM terminalDownloads ORDER BY id")
+ @Query("SELECT id,command FROM terminalDownloads ORDER BY id")
fun getActiveTerminalDownloads() : List
- @Query("SELECT * FROM terminalDownloads ORDER BY id")
+ @Query("SELECT id,command FROM terminalDownloads ORDER BY id")
fun getActiveTerminalDownloadsFlow() : Flow>
+ @Query("SELECT * from terminalDownloads where id=:id")
+ fun getActiveTerminalFlow(id: Long) : Flow
+
@Query("SELECT COUNT(*) FROM terminalDownloads")
fun getActiveTerminalsCount() : Int
+ @Query("UPDATE terminalDownloads set log=:l where id=:id")
+ fun updateTerminalLog(l: String, id: Long)
+
+ @Query("SELECT * FROM terminalDownloads WHERE id=:id LIMIT 1")
+ fun getTerminalById(id: Long) : TerminalItem?
+ @Transaction
+ fun updateLog(line: String, id: Long){
+ val t = getTerminalById(id) ?: return
+ val log = t.log ?: ""
+ val lines = log.split("\n")
+ //clean dublicate progress + add newline
+ val l = if (listOf(line, lines.last()).all { it.contains("[download")}){
+ lines.dropLast(1).joinToString("\n")
+ }else{
+ log
+ } + "\n${line}"
+
+ updateTerminalLog(l, id)
+ }
+
@Insert
suspend fun insert(item: TerminalItem) : Long
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
index 5f9385ed..23f67300 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/HistoryItem.kt
@@ -20,5 +20,7 @@ data class HistoryItem(
val website: String,
val format: Format,
@ColumnInfo(defaultValue = "0")
- val downloadId: Long
+ val downloadId: Long,
+ @ColumnInfo(defaultValue = "")
+ val command: String = ""
)
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/TerminalItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/TerminalItem.kt
index fc5433a4..4744d312 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/TerminalItem.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/TerminalItem.kt
@@ -9,5 +9,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
data class TerminalItem(
@PrimaryKey(autoGenerate = true)
var id: Long = 0,
- var command: String
+ var command: String,
+ @ColumnInfo(defaultValue = "")
+ var log: String? = "",
)
\ No newline at end of file
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 1a536f01..f8dd8f15 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
@@ -83,6 +83,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
return downloadDao.getActiveAndQueuedDownloadsList()
}
+ fun getActiveAndQueuedDownloadIDs() : List {
+ return downloadDao.getActiveAndQueuedDownloadIDs()
+ }
+
fun getQueuedDownloads() : List {
return downloadDao.getQueuedDownloadsList()
}
@@ -120,8 +124,8 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
}
- suspend fun cancelQueued(){
- downloadDao.cancelQueued()
+ suspend fun cancelActiveQueued(){
+ downloadDao.cancelActiveQueued()
}
fun pauseDownloads(){
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 694fa1af..cc4125d7 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
@@ -2,6 +2,7 @@ package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Application
import android.content.Context
+import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.content.res.Resources
@@ -28,6 +29,7 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.CommandTemplateDao
import com.deniscerri.ytdlnis.database.dao.DownloadDao
+import com.deniscerri.ytdlnis.database.dao.HistoryDao
import com.deniscerri.ytdlnis.database.dao.ResultDao
import com.deniscerri.ytdlnis.database.models.AudioPreferences
import com.deniscerri.ytdlnis.database.models.CommandTemplate
@@ -38,6 +40,7 @@ import com.deniscerri.ytdlnis.database.models.HistoryItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.models.VideoPreferences
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
+import com.deniscerri.ytdlnis.ui.ErrorDialogActivity
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.work.AlarmScheduler
@@ -82,6 +85,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private var videoCodec: String?
private var audioCodec: String?
private val dao: DownloadDao
+ private val historyDao: HistoryDao
enum class Type {
auto, audio, video, command
@@ -96,6 +100,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
init {
dbManager = DBManager.getInstance(application)
dao = dbManager.downloadDao
+ historyDao = dbManager.historyDao
repository = DownloadRepository(dao)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application)
commandTemplateDao = DBManager.getInstance(application).commandTemplateDao
@@ -173,8 +178,20 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return repository.getItemByID(id)
}
- fun getDownloadType(t: Type, url: String) : Type {
- return when(t){
+ fun getDownloadType(t: Type? = null, url: String) : Type {
+ var type = t
+
+ if (type == null){
+ val preferredDownloadType = sharedPreferences.getString("preferred_download_type", Type.auto.toString())
+ type = if (sharedPreferences.getBoolean("remember_download_type", false)){
+ Type.valueOf(sharedPreferences.getString("last_used_download_type",
+ preferredDownloadType)!!)
+ }else{
+ Type.valueOf(preferredDownloadType!!)
+ }
+ }
+
+ return when(type){
Type.auto -> {
if (urlsForAudioType.any { url.contains(it) }){
Type.audio
@@ -182,7 +199,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.video
}
}
- else -> t
+
+ else -> type
}
}
@@ -448,10 +466,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
fun turnResultItemsToDownloadItems(items: List) : List {
val list : MutableList = mutableListOf()
- var preferredType = sharedPreferences.getString("preferred_download_type", "video")
items.forEach {
- preferredType = getDownloadType(Type.valueOf(preferredType!!), it!!.url).toString()
- list.add(createDownloadItemFromResult(result = it, givenType = Type.valueOf(preferredType!!)))
+ val preferredType = getDownloadType(url = it!!.url).toString()
+ list.add(createDownloadItemFromResult(result = it, givenType = Type.valueOf(
+ preferredType
+ )))
}
return list
}
@@ -487,12 +506,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
fun deleteAllWithID(ids: List) = viewModelScope.launch(Dispatchers.IO){
- Log.e("Aa", ids.size.toString())
repository.deleteAllWithIDs(ids)
}
- fun cancelQueued() = viewModelScope.launch(Dispatchers.IO) {
- repository.cancelQueued()
+ fun cancelActiveQueued() = viewModelScope.launch(Dispatchers.IO) {
+ repository.cancelActiveQueued()
}
fun getQueued() : List {
@@ -528,6 +546,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return repository.getActiveAndQueuedDownloads()
}
+ fun getActiveAndQueuedDownloadIDs() : List{
+ return repository.getActiveAndQueuedDownloadIDs()
+ }
+
suspend fun resetScheduleTimeForItemsAndStartDownload(items: List) = CoroutineScope(Dispatchers.IO).launch {
dbManager.downloadDao.resetScheduleTimeForItems(items)
startDownloadWorker(emptyList())
@@ -548,7 +570,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val alarmScheduler = AlarmScheduler(context)
val activeAndQueuedDownloads = repository.getActiveAndQueuedDownloads()
val queuedItems = mutableListOf()
- var exists = false
+ val existing = mutableListOf()
//if scheduler is on
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
@@ -556,8 +578,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
alarmScheduler.schedule()
}
+
items.forEach {
if (it.status != DownloadRepository.Status.Paused.toString()) it.status = DownloadRepository.Status.Queued.toString()
+ val currentCommand = infoUtil.buildYoutubeDLRequest(it)
+ val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand)
if (activeAndQueuedDownloads.firstOrNull{d ->
d.id = 0
d.logID = null
@@ -565,23 +590,33 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
d.status = DownloadRepository.Status.Queued.toString()
d.toString() == it.toString()
} != null) {
- exists = true
+ existing.add(it)
}else{
- if (it.id == 0L){
- val id = runBlocking {repository.insert(it)}
- it.id = id
- }else if (it.status == DownloadRepository.Status.Queued.toString()){
- repository.update(it)
- }
+ //check if downloaded and file exists
+ val history = historyDao.getAllHistoryByURL(it.url).filter { item -> FileUtil.exists(item.downloadPath) }
+ if (history.any { h -> h.command.replace("-P \"(.*?)\"".toRegex(), "") == parsedCurrentCommand.replace("-P \"(.*?)\"".toRegex(), "") }){
+ existing.add(it)
+ }else{
+ if (it.id == 0L){
+ val id = runBlocking {repository.insert(it)}
+ it.id = id
+ }else if (it.status == DownloadRepository.Status.Queued.toString()){
+ repository.update(it)
+ }
- queuedItems.add(it)
+ queuedItems.add(it)
+ }
}
}
- if (exists){
- Looper.prepare().run {
- Toast.makeText(context, context.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show()
- }
+ if (existing.isNotEmpty()){
+ val intent = Intent(context, ErrorDialogActivity::class.java)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ intent.putExtra("title", context.resources.getString(R.string.download_already_exists))
+ intent.putExtra("message",
+ "${existing.size}/${items.size}\n" +
+ existing.mapIndexed { index, downloadItem -> "${index+1}. ${downloadItem.title}" }.joinToString("\n"))
+ context.startActivity(intent)
}
if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || items.any { it.downloadStartTime > 0L } ){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/TerminalViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/TerminalViewModel.kt
index 736ca623..35aae7c6 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/TerminalViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/TerminalViewModel.kt
@@ -71,6 +71,10 @@ class TerminalViewModel(private val application: Application) : AndroidViewModel
return dao.getActiveTerminalDownloadsFlow()
}
+ fun getTerminal(id: Long) : Flow {
+ return dao.getActiveTerminalFlow(id)
+ }
+
suspend fun insert(item: TerminalItem) : Long {
return dao.insert(item)
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt
index d3074039..fcdbe3ec 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt
@@ -27,7 +27,6 @@ class AlarmStopReceiver : BroadcastReceiver() {
CoroutineScope(Dispatchers.IO).launch {
runCatching {
- Log.e("assa", "assssssssssssssssssssssssssssss")
val active = dbManager.downloadDao.getActiveDownloadsList()
val startingTime = preferences.getString("schedule_start", "00:00")!!
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelWorkReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelWorkReceiver.kt
index 3632fde5..ae2c415b 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelWorkReceiver.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/CancelWorkReceiver.kt
@@ -16,12 +16,9 @@ import kotlinx.coroutines.runBlocking
class CancelWorkReceiver : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
- val id = intent.getStringExtra("workID")
- if (!id.isNullOrBlank()) {
- runCatching {
- WorkManager.getInstance(c).cancelAllWorkByTag(id)
- }
-
+ val id = intent.getStringExtra("workTag")
+ if (!id.isNullOrBlank()){
+ WorkManager.getInstance(c).cancelAllWorkByTag(id)
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
index a51e66d8..f583aab8 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
@@ -108,65 +108,56 @@ class ShareActivity : BaseActivity() {
runCatching { supportFragmentManager.popBackStack() }
quickDownload = intent.getBooleanExtra("quick_download", sharedPreferences.getBoolean("quick_download", false) || sharedPreferences.getString("preferred_download_type", "video") == "command")
- val intentData = when(action){
+ val url = when(action){
Intent.ACTION_SEND -> intent.getStringExtra(Intent.EXTRA_TEXT)!!
else -> intent.dataString!!
}
- val matcher = Patterns.WEB_URL.matcher(intentData)
+ val matcher = Patterns.WEB_URL.matcher(url)
val inputQuery = if (matcher.find()){
matcher.group()
}else{
- intentData
+ url
}
- lifecycleScope.launch {
- try {
- val result = withContext(Dispatchers.IO){
- resultViewModel.getItemByURL(inputQuery)
- }
- if (result == null) throw Exception()
- showDownloadSheet(result)
- }catch (e: Exception){
- resultViewModel.deleteAll()
- showDownloadSheet(downloadViewModel.createEmptyResultItem(inputQuery))
- if (matcher.matches()){
+ val type = intent.getStringExtra("TYPE")
+ val background = intent.getBooleanExtra("BACKGROUND", false)
+ val command = intent.getStringExtra("COMMAND") ?: ""
- }else{
- val res = withContext(Dispatchers.IO){
- resultViewModel.parseQuery(inputQuery, true)
- }
- if (res.isEmpty()) {
- Toast.makeText(this@ShareActivity, "No Results Found!", Toast.LENGTH_SHORT).show()
- exit()
+ lifecycleScope.launch {
+ var result = withContext(Dispatchers.IO){
+ resultViewModel.getItemByURL(inputQuery)
+ }
+ if (result == null) {
+ resultViewModel.deleteAll()
+ result = downloadViewModel.createEmptyResultItem(inputQuery)
+ }
+ val downloadType = DownloadViewModel.Type.valueOf(type ?: downloadViewModel.getDownloadType(url = result.url).toString())
+ if (sharedPreferences.getBoolean("download_card", true) && !background){
+ val bottomSheet = DownloadBottomSheetDialog(
+ result = result,
+ type = downloadType)
+ bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
+ }else{
+ lifecycleScope.launch(Dispatchers.IO){
+ val downloadItem = downloadViewModel.createDownloadItemFromResult(
+ result = result,
+ givenType = downloadType)
+
+ if (downloadType == DownloadViewModel.Type.command && command.isNotBlank()){
+ downloadItem.format.format_note = command
}else{
- if (res.size == 1){
- showDownloadSheet(res[0]!!)
- }else{
- showSelectPlaylistItems(res.toList())
- }
+ downloadItem.extraCommands = downloadItem.extraCommands + " $command"
}
+ downloadViewModel.queueDownloads(listOf(downloadItem))
}
+ this@ShareActivity.finish()
}
}
}
}
private fun showDownloadSheet(it: ResultItem){
- val downloadType = downloadViewModel.getDownloadType(DownloadViewModel.Type.valueOf(sharedPreferences.getString("preferred_download_type", "video")!!), it.url)
- if (sharedPreferences.getBoolean("download_card", true)){
- val bottomSheet = DownloadBottomSheetDialog(
- result = it,
- type = downloadType)
- bottomSheet.show(supportFragmentManager, "downloadSingleSheet")
- }else{
- lifecycleScope.launch(Dispatchers.IO){
- val downloadItem = downloadViewModel.createDownloadItemFromResult(
- result = it,
- givenType = downloadType)
- downloadViewModel.queueDownloads(listOf(downloadItem))
- }
- this.finish()
- }
+
}
private fun showSelectPlaylistItems(it: List){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/ErrorDialogActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/ErrorDialogActivity.kt
index baaaa980..57db0860 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/ErrorDialogActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/ErrorDialogActivity.kt
@@ -51,7 +51,9 @@ open class ErrorDialogActivity : BaseActivity() {
}.setOnDismissListener {
this.finish()
}
- errDialog.show()
+ this.runOnUiThread {
+ errDialog.show()
+ }
super.onCreate(savedInstanceState)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
index c71affa0..b67af116 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
@@ -706,7 +706,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
if (sharedPreferences!!.getBoolean("download_card", true) && selectedObjects!!.size == 1) {
showSingleDownloadSheet(
selectedObjects!![0],
- DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
+ downloadViewModel.getDownloadType(url = selectedObjects!![0].url)
)
}else{
val downloadList = withContext(Dispatchers.IO){
@@ -714,15 +714,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
}
if (sharedPreferences!!.getBoolean("download_card", true)) {
- if (selectedObjects!!.size == 1){
- showSingleDownloadSheet(
- selectedObjects!![0],
- DownloadViewModel.Type.valueOf(sharedPreferences!!.getString("preferred_download_type", "video")!!)
- )
- }else{
- val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList())
- bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
- }
+ val bottomSheet = DownloadMultipleBottomSheetDialog(downloadList.toMutableList())
+ bottomSheet.show(parentFragmentManager, "downloadMultipleSheet")
} else {
downloadViewModel.queueDownloads(downloadList)
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ConfigureDownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ConfigureDownloadBottomSheetDialog.kt
index baef007a..9164d506 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ConfigureDownloadBottomSheetDialog.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ConfigureDownloadBottomSheetDialog.kt
@@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.ui.downloadcard
import android.annotation.SuppressLint
import android.app.Dialog
import android.content.DialogInterface
+import android.content.SharedPreferences
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
@@ -10,9 +11,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
+import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
+import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.deniscerri.ytdlnis.R
@@ -41,6 +44,7 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
private lateinit var commandTemplateDao: CommandTemplateDao
private lateinit var behavior: BottomSheetBehavior
private lateinit var onDownloadItemUpdateListener: OnDownloadItemUpdateListener
+ private lateinit var sharedPreferences : SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -48,6 +52,7 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
onDownloadItemUpdateListener = listener
+ sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -153,6 +158,10 @@ class ConfigureDownloadBottomSheetDialog(private var result: ResultItem, private
override fun onPageSelected(position: Int) {
tabLayout.selectTab(tabLayout.getTabAt(position))
runCatching {
+ sharedPreferences.edit(commit = true) {
+ putString("last_used_download_type",
+ listOf(Type.audio, Type.video, Type.command)[position].toString())
+ }
updateTitleAuthorWhenSwitching()
}
}
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 e6979f7d..196422d2 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
@@ -12,6 +12,8 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.TextView
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.view.isEmpty
import androidx.fragment.app.Fragment
@@ -140,17 +142,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- val callback = FileUtil.getURIFromResult(this@DownloadAudioFragment, requireActivity()){result ->
- downloadItem.downloadPath = result
- //downloadViewModel.updateDownload(downloadItem)
- saveDir.editText?.setText(FileUtil.formatPath(result), TextView.BufferType.EDITABLE)
-
- val free = FileUtil.convertFileSize(
- File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
- freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
- if (free == "?") freeSpace.visibility = View.GONE
- }
- callback.launch(intent)
+ pathResultLauncher.launch(intent)
}
freeSpace = view.findViewById(R.id.freespace)
val free = FileUtil.convertFileSize(
@@ -296,4 +288,28 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
state.putBoolean("updated", true)
onViewCreated(requireView(),savedInstanceState = state)
}
+
+ private var pathResultLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ result.data?.data?.let {
+ activity?.contentResolver?.takePersistableUriPermission(
+ it,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
+ }
+
+ downloadItem.downloadPath = result.data?.data.toString()
+ //downloadViewModel.updateDownload(downloadItem)
+ saveDir.editText?.setText(FileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
+
+ val free = FileUtil.convertFileSize(
+ File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
+ freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
+ if (free == "?") freeSpace.visibility = View.GONE
+
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt
index 6e3afd86..ed0d8a82 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadBottomSheetDialog.kt
@@ -1,12 +1,15 @@
package com.deniscerri.ytdlnis.ui.downloadcard
import android.annotation.SuppressLint
+import android.app.ActivityManager
import android.app.Dialog
+import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Bundle
+import android.os.Looper
import android.text.format.DateFormat
import android.util.DisplayMetrics
import android.util.Patterns
@@ -16,6 +19,7 @@ import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.Toast
+import androidx.core.content.edit
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@@ -158,6 +162,11 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
}
}
+ sharedPreferences.edit(commit = true) {
+ putString("last_used_download_type",
+ type.toString())
+ }
+
tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
@@ -184,6 +193,10 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
override fun onPageSelected(position: Int) {
tabLayout.selectTab(tabLayout.getTabAt(position))
runCatching {
+ sharedPreferences.edit(commit = true) {
+ putString("last_used_download_type",
+ listOf(Type.audio, Type.video, Type.command)[position].toString())
+ }
updateTitleAuthorWhenSwitching()
}
}
@@ -326,13 +339,18 @@ class DownloadBottomSheetDialog(private var result: ResultItem, private val type
if (result.url.isBlank()){
withContext(Dispatchers.Main){dismiss()}
+ return@launch
}
- val result = resultViewModel.parseQueries(listOf(result.url))
+ val result = resultViewModel.parseQuery(result.url, true)
if (result.isEmpty()){
withContext(Dispatchers.Main){
+ Looper.prepare().run {
+ Toast.makeText(requireContext(), "No Results Found!", Toast.LENGTH_SHORT).show()
+ }
dismiss()
}
+ return@launch
}
if (result.size == 1 && result[0] != null){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt
index f8b57796..8b543e08 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt
@@ -55,7 +55,9 @@ import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@@ -132,7 +134,10 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList) {
+ override fun onContinueOnBackground() {
requireActivity().lifecycleScope.launch {
val ids = mutableListOf()
runBlocking {
- items.map { it!!.status = DownloadRepository.Status.Saved.toString() }
+ items.map { it.status = DownloadRepository.Status.Saved.toString() }
items.forEach {
- ids.add(downloadViewModel.repository.insert(it!!))
+ ids.add(downloadViewModel.repository.insert(it))
}
}
val id = System.currentTimeMillis().toInt()
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 0bf86ea9..79be8d3c 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
@@ -14,6 +14,7 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.AutoCompleteTextView
import android.widget.TextView
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@@ -130,16 +131,7 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- val callback = FileUtil.getURIFromResult(this@DownloadVideoFragment, requireActivity()){ result ->
- downloadItem.downloadPath = result
- saveDir.editText?.setText(FileUtil.formatPath(result), TextView.BufferType.EDITABLE)
-
- val free = FileUtil.convertFileSize(
- File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
- freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
- if (free == "?") freeSpace.visibility = View.GONE
- }
- callback.launch(intent)
+ pathResultLauncher.launch(intent)
}
freeSpace = view.findViewById(R.id.freespace)
@@ -311,5 +303,27 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
onViewCreated(requireView(),savedInstanceState = state)
}
+ private var pathResultLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ if (result.resultCode == Activity.RESULT_OK) {
+ result.data?.data?.let {
+ activity?.contentResolver?.takePersistableUriPermission(
+ it,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
+ }
+
+ downloadItem.downloadPath = result.data?.data.toString()
+ saveDir.editText?.setText(FileUtil.formatPath(result.data?.data.toString()), TextView.BufferType.EDITABLE)
+
+ val free = FileUtil.convertFileSize(
+ File(FileUtil.formatPath(downloadItem.downloadPath)).freeSpace)
+ freeSpace.text = String.format( getString(R.string.freespace) + ": " + free)
+ if (free == "?") freeSpace.visibility = View.GONE
+
+ }
+ }
}
\ No newline at end of file
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 95e23133..164e0bf0 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
@@ -130,7 +130,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p
if (items.size > 10){
continueInBackgroundSnackBar = Snackbar.make(view, R.string.update_formats_background, Snackbar.LENGTH_LONG)
continueInBackgroundSnackBar.setAction(R.string.ok) {
- listener.onContinueOnBackground(items)
+ listener.onContinueOnBackground()
this@FormatSelectionBottomSheetDialog.dismiss()
}
continueInBackgroundSnackBar.show()
@@ -369,7 +369,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p
interface OnFormatClickListener{
fun onFormatClick(allFormats: List>, item: List)
- fun onContinueOnBackground(items : List) {}
+ fun onContinueOnBackground() {}
}
class FormatTuple internal constructor(
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt
index fd89e166..e48d5fed 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ResultCardDetailsDialog.kt
@@ -89,7 +89,6 @@ class ResultCardDetailsDialog(private val item: ResultItem) : BottomSheetDialogF
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
infoUtil = InfoUtil(requireActivity())
- notificationUtil = NotificationUtil(requireActivity().applicationContext)
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
downloadManager = requireContext().getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
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 8a8e91f4..4ab277f3 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
@@ -160,14 +160,12 @@ class DownloadQueueMainFragment : Fragment(){
lifecycleScope.launch {
val notificationUtil = NotificationUtil(requireContext())
val activeAndQueued = withContext(Dispatchers.IO){
- downloadViewModel.getActiveAndQueuedDownloads()
+ downloadViewModel.getActiveAndQueuedDownloadIDs()
}
- activeAndQueued.forEach {
- it.status = DownloadRepository.Status.Cancelled.toString()
- downloadViewModel.updateDownload(it)
- val id = it.id.toInt()
+ downloadViewModel.cancelActiveQueued()
+ activeAndQueued.forEach { id ->
YoutubeDL.getInstance().destroyProcessById(id.toString())
- notificationUtil.cancelDownloadNotification(id)
+ notificationUtil.cancelDownloadNotification(id.toInt())
}
}
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt
index 6107b2b8..256ab035 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesFragment.kt
@@ -106,7 +106,15 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
newCookie.isEnabled = useCookiesPref
}
+ private fun initChips() {
+ val new = view?.findViewById(R.id.newCookie)
+ new?.setOnClickListener {
+ showDialog("")
+ }
+ }
+
private fun initMenu() {
+ topAppBar.menu.getItem(1).isChecked = preferences.getBoolean("use_header", false)
topAppBar.setOnMenuItemClickListener { m: MenuItem ->
when (m.itemId) {
R.id.delete_cookies -> {
@@ -131,18 +139,15 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
cookiesViewModel.exportToClipboard()
Snackbar.make(recyclerView, getString(R.string.copied_to_clipboard), Snackbar.LENGTH_LONG).show()
}
+ R.id.use_header -> {
+ m.isChecked = !m.isChecked
+ preferences.edit().putBoolean("use_header", m.isChecked).apply()
+ }
}
true
}
}
- private fun initChips() {
- val new = view?.findViewById(R.id.newCookie)
- new?.setOnClickListener {
- showDialog("")
- }
- }
-
private fun showDialog(url: String){
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setTitle(getString(R.string.cookies))
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
index a8cf98f0..5cc4af0d 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
@@ -1,18 +1,29 @@
package com.deniscerri.ytdlnis.ui.more
import android.annotation.SuppressLint
+import android.content.SharedPreferences
import android.os.Bundle
-import android.util.Log
import android.webkit.CookieManager
import android.webkit.WebView
-import android.webkit.WebViewClient
import android.widget.Toast
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
+import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CookieItem
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
import com.deniscerri.ytdlnis.ui.BaseActivity
+import com.google.accompanist.web.AccompanistWebChromeClient
+import com.google.accompanist.web.AccompanistWebViewClient
+import com.google.accompanist.web.WebView
+import com.google.accompanist.web.rememberWebViewState
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
@@ -24,12 +35,14 @@ import kotlinx.coroutines.withContext
class WebViewActivity : BaseActivity() {
private lateinit var cookiesViewModel: CookieViewModel
private lateinit var webView: WebView
+ private lateinit var webViewCompose: ComposeView
private lateinit var toolbar: MaterialToolbar
private lateinit var generateBtn: MaterialButton
private lateinit var cookieManager: CookieManager
private lateinit var url: String
private lateinit var cookies: String
-
+ private lateinit var webViewClient: AccompanistWebViewClient
+ private lateinit var preferences: SharedPreferences
@SuppressLint("SetJavaScriptEnabled")
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -40,13 +53,20 @@ class WebViewActivity : BaseActivity() {
val appbar = findViewById(R.id.webview_appbarlayout)
toolbar = appbar.findViewById(R.id.webviewToolbar)
generateBtn = toolbar.findViewById(R.id.generate)
+ webViewCompose = findViewById(R.id.webview_compose)
cookieManager = CookieManager.getInstance()
cookieManager.removeAllCookies(null)
+ preferences = PreferenceManager.getDefaultSharedPreferences(this)
- webView = findViewById(R.id.webview)
- webView.settings.javaScriptEnabled = true
- webView.settings.domStorageEnabled = true
- webView.settings.javaScriptCanOpenWindowsAutomatically = true
+ webViewClient = object : AccompanistWebViewClient() {
+ override fun onPageFinished(view: WebView?, url: String?) {
+ super.onPageFinished(view, url)
+ kotlin.runCatching {
+ toolbar.title = view?.title ?: ""
+ cookies = CookieManager.getInstance().getCookie(view?.url)
+ }
+ }
+ }
cookieManager = CookieManager.getInstance()
@@ -55,21 +75,8 @@ class WebViewActivity : BaseActivity() {
onBackPressedDispatcher.onBackPressed()
}
- val webViewClient: WebViewClient = object : WebViewClient() {
-
- override fun onPageFinished(view: WebView, url: String) {
- super.onPageFinished(view, url)
- kotlin.runCatching {
- toolbar.title = view.title
- cookies = CookieManager.getInstance().getCookie(view.url)
- }
- }
- }
- webView.webViewClient = webViewClient
-
generateBtn.setOnClickListener {
cookiesViewModel.getCookiesFromDB().getOrNull()?.let {
- Log.e("asas", it)
kotlin.runCatching {
lifecycleScope.launch {
withContext(Dispatchers.IO){
@@ -91,8 +98,40 @@ class WebViewActivity : BaseActivity() {
onBackPressedDispatcher.onBackPressed()
}
- webView.loadUrl(url)
+ webViewCompose.apply {
+ setContent { WebViewView() }
+ }
+ }
+ @SuppressLint("SetJavaScriptEnabled")
+ @Composable
+ fun WebViewView() {
+ val webViewChromeClient = remember {
+ object : AccompanistWebChromeClient() {
+ }
+ }
+
+ Scaffold(modifier = Modifier.fillMaxSize()) { paddingValues ->
+ WebView(
+ state = rememberWebViewState(url), client = webViewClient, chromeClient = webViewChromeClient,
+ modifier = Modifier
+ .padding(paddingValues)
+ .fillMaxSize(),
+ captureBackPresses = false, factory = { context ->
+ WebView(context).apply {
+ settings.run {
+ javaScriptEnabled = true
+ domStorageEnabled = true
+ javaScriptCanOpenWindowsAutomatically = true
+ preferences.edit().putString("useragent_header", userAgentString).apply()
+ }
+ cookieManager.setAcceptThirdPartyCookies(this, true)
+ }
+ }
+ )
+
+
+ }
}
companion object {
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/DownloadSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/DownloadSettingsFragment.kt
index d00d8d58..cc1e1ed8 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/DownloadSettingsFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/DownloadSettingsFragment.kt
@@ -1,14 +1,13 @@
package com.deniscerri.ytdlnis.ui.more.settings
-import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
+import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.preference.SwitchPreferenceCompat
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.receiver.AlarmStartReceiver
-import com.deniscerri.ytdlnis.receiver.AlarmStopReceiver
import com.deniscerri.ytdlnis.util.UiUtil
import com.deniscerri.ytdlnis.work.AlarmScheduler
import java.util.Calendar
@@ -21,6 +20,14 @@ class DownloadSettingsFragment : BaseSettingsFragment() {
setPreferencesFromResource(R.xml.downloading_preferences, rootKey)
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ val rememberDownloadType = findPreference("remember_download_type")
+ val downloadType = findPreference("preferred_download_type")
+ downloadType?.isEnabled = rememberDownloadType?.isChecked == false
+ rememberDownloadType?.setOnPreferenceClickListener {
+ downloadType?.isEnabled = !rememberDownloadType.isChecked
+ true
+ }
+
val useScheduler = findPreference("use_scheduler")
val scheduleStart = findPreference("schedule_start")
scheduleStart?.summary = preferences.getString("schedule_start", "00:00")
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt
index aefa168d..af174caf 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalDownloadsListFragment.kt
@@ -11,6 +11,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.LinearLayout
+import android.widget.RelativeLayout
import android.widget.TextView
import androidx.activity.addCallback
import androidx.constraintlayout.widget.ConstraintLayout
@@ -39,6 +40,7 @@ import kotlinx.coroutines.launch
class TerminalDownloadsListFragment : Fragment(), TerminalDownloadsAdapter.OnItemClickListener {
private var topAppBar: MaterialToolbar? = null
+ private lateinit var noResults: RelativeLayout
private lateinit var terminalViewModel: TerminalViewModel
override fun onCreateView(
@@ -52,6 +54,7 @@ class TerminalDownloadsListFragment : Fragment(), TerminalDownloadsAdapter.OnIte
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
lifecycleScope.launch {
+ noResults = view.findViewById(R.id.no_results)
val recycler = view.findViewById(R.id.terminal_recycler)
val adapter = TerminalDownloadsAdapter(this@TerminalDownloadsListFragment, requireActivity())
recycler.adapter = adapter
@@ -73,9 +76,7 @@ class TerminalDownloadsListFragment : Fragment(), TerminalDownloadsAdapter.OnIte
terminalViewModel.getTerminals().collectLatest {
adapter.submitList(it)
- if (it.isEmpty()){
- findNavController().navigate(R.id.terminalFragment)
- }
+ noResults.visibility = if (it.isEmpty()) View.VISIBLE else View.GONE
}
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt
index 32d50783..c349d81c 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/terminal/TerminalFragment.kt
@@ -48,9 +48,12 @@ import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.yausername.youtubedl_android.YoutubeDL
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import java.lang.StringBuilder
import kotlin.properties.Delegates
@@ -102,11 +105,13 @@ class TerminalFragment : Fragment() {
topAppBar!!.setNavigationOnClickListener { requireActivity().finish() }
input = view.findViewById(R.id.command_edittext)
+ fab = view.findViewById(R.id.command_fab)
if (arguments?.getLong("id") != null){
downloadID = requireArguments().getLong("id")
if(downloadID != 0L){
input!!.visibility = View.GONE
+ showCancelFab()
}
}
@@ -145,6 +150,7 @@ class TerminalFragment : Fragment() {
}else{
bottomAppBar.menu.getItem(1).icon?.alpha = 255
}
+
}
bottomAppBar.setOnMenuItemClickListener {
when(it.itemId){
@@ -195,7 +201,6 @@ class TerminalFragment : Fragment() {
output!!.setTextIsSelectable(true)
output!!.layoutParams!!.width = LayoutParams.WRAP_CONTENT
input!!.requestFocus()
- fab = view.findViewById(R.id.command_fab)
fab!!.setOnClickListener {
if (fab!!.text == getString(R.string.run_command)){
input!!.visibility = View.GONE
@@ -205,7 +210,7 @@ class TerminalFragment : Fragment() {
lifecycleScope.launch {
val command = input!!.text.toString().replace("yt-dlp", "")
downloadID = withContext(Dispatchers.IO){
- terminalViewModel.insert(TerminalItem(command = command))
+ terminalViewModel.insert(TerminalItem(command = command, log = output!!.text.toString()))
}
terminalViewModel.startTerminalDownloadWorker(TerminalItem(downloadID, command))
runWorkerListener()
@@ -293,6 +298,22 @@ class TerminalFragment : Fragment() {
}
private fun runWorkerListener(){
+ CoroutineScope(Dispatchers.IO).launch {
+ terminalViewModel.getTerminal(downloadID).collectLatest {
+ kotlin.runCatching {
+ requireActivity().runOnUiThread{
+ if (it != null){
+ if (!it.log.isNullOrBlank()) {
+ output?.text = it.log
+ }
+ output?.scrollTo(0, output!!.height)
+ scrollView?.fullScroll(View.FOCUS_DOWN)
+ }
+ }
+ }
+
+ }
+ }
WorkManager.getInstance(requireContext())
.getWorkInfosByTagLiveData(downloadID.toString())
@@ -310,25 +331,9 @@ class TerminalFragment : Fragment() {
}
return@forEach
}
- val outputData = work.progress.getString("output")
- if (!outputData.isNullOrBlank()){
- val pieces = output!!.text.split("\n").toMutableList()
- val lastLine = pieces.last()
- requireActivity().runOnUiThread{
- kotlin.runCatching {
- showCancelFab()
- if (listOf(lastLine, outputData).all { it.contains("[download]") }){
- output!!.text = pieces.dropLast(1).joinToString("\n")
- }
- output!!.append("\n$outputData")
- output!!.scrollTo(0, output!!.height)
- scrollView!!.fullScroll(View.FOCUS_DOWN)
- }
- }
- }
-
}
}
+
}
}
\ No newline at end of file
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 d8f926e5..ffefe18b 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/util/FileUtil.kt
@@ -309,19 +309,4 @@ object FileUtil {
return "${DecimalFormat("#,##0.#", symbols).format(s / 1024.0.pow(digitGroups.toDouble()))} ${units[digitGroups]}"
}
- fun getURIFromResult(f: Fragment, a: FragmentActivity?, callback: (data: String) -> Unit) = f.registerForActivityResult(
- ActivityResultContracts.StartActivityForResult()
- ) { result ->
- if (result.resultCode == Activity.RESULT_OK) {
- result.data?.data?.let {
- a?.contentResolver?.takePersistableUriPermission(
- it,
- Intent.FLAG_GRANT_READ_URI_PERMISSION or
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION
- )
- }
-
- callback(result.data?.data.toString())
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
index 1664c5e8..d8bd2f28 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
@@ -1,49 +1,26 @@
package com.deniscerri.ytdlnis.util
-import android.app.Activity
-import android.app.DownloadManager
-import android.content.ClipboardManager
import android.content.Context
-import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Resources
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
-import android.os.Environment
import android.os.Looper
import android.text.Html
import android.util.Log
import android.util.Patterns
-import android.util.TypedValue
-import android.view.WindowManager
import android.widget.Toast
-import androidx.navigation.findNavController
-import androidx.preference.AndroidResources
import androidx.preference.PreferenceManager
-import com.deniscerri.ytdlnis.App
-import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
-import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.ChapterItem
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.Format
-import com.deniscerri.ytdlnis.database.models.LogItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.deniscerri.ytdlnis.ui.ErrorDialogActivity
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.yausername.youtubedl_android.YoutubeDL
import com.yausername.youtubedl_android.YoutubeDLRequest
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
@@ -475,6 +452,12 @@ class InfoUtil(private val context: Context) {
FileUtil.getCookieFile(context){
request.addOption("--cookies", it)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
val proxy = sharedPreferences.getString("proxy", "")
@@ -482,6 +465,8 @@ class InfoUtil(private val context: Context) {
request.addOption("--proxy", proxy)
}
+
+
val res = YoutubeDL.getInstance().execute(request)
val results: Array = try {
val lineSeparator = System.getProperty("line.separator")
@@ -528,6 +513,12 @@ class InfoUtil(private val context: Context) {
FileUtil.getCookieFile(context){
request.addOption("--cookies", it)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
@@ -536,6 +527,8 @@ class InfoUtil(private val context: Context) {
request.addOption("--proxy", proxy)
}
+
+
YoutubeDL.getInstance().execute(request){ progress, _, line ->
try{
if (line.isNotBlank()){
@@ -592,6 +585,12 @@ class InfoUtil(private val context: Context) {
FileUtil.getCookieFile(context){
request.addOption("--cookies", it)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
val proxy = sharedPreferences.getString("proxy", "")
@@ -599,6 +598,8 @@ class InfoUtil(private val context: Context) {
request.addOption("--proxy", proxy)
}
+
+
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val results: List = try {
val lineSeparator = System.getProperty("line.separator")
@@ -759,6 +760,12 @@ class InfoUtil(private val context: Context) {
FileUtil.getCookieFile(context){
request.addOption("--cookies", it)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
@@ -768,6 +775,8 @@ class InfoUtil(private val context: Context) {
request.addOption("--proxy", proxy)
}
+
+
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val jsonObject = JSONObject(youtubeDLResponse.out)
@@ -921,6 +930,12 @@ class InfoUtil(private val context: Context) {
FileUtil.getCookieFile(context){
request.addOption("--cookies", it)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
@@ -930,6 +945,8 @@ class InfoUtil(private val context: Context) {
request.addOption("--proxy", proxy)
}
+
+
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
val results: Array = try {
val lineSeparator = System.getProperty("line.separator")
@@ -958,22 +975,6 @@ class InfoUtil(private val context: Context) {
}
}
- fun getFilePaths(request: YoutubeDLRequest) : List{
- return try{
- request.addOption("--print", "filename")
- val res = YoutubeDL.getInstance().execute(request)
- val results: Array = try {
- val lineSeparator = System.getProperty("line.separator")
- res.out.split(lineSeparator!!).toTypedArray()
- } catch (e: Exception) {
- arrayOf(res.out)
- }
- results.filter { !it.isNullOrBlank() }.map { it!!.substring(0, it.lastIndexOf(".")) }.map { it.substring(it.lastIndexOf("/") + 1, it.length) }
- }catch (e: Exception){
- listOf()
- }
- }
-
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
val url = downloadItem.url
val request = YoutubeDLRequest(url)
@@ -982,6 +983,9 @@ class InfoUtil(private val context: Context) {
val downDir : File
if (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()){
downDir = File(FileUtil.formatPath(downloadItem.downloadPath))
+ request.addOption("--print", "after_video:'%(filename)s'")
+ request.addOption("--progress")
+ request.addOption("-v")
}else{
val cacheDir = FileUtil.getCachePath(context)
downDir = File(cacheDir, downloadItem.id.toString())
@@ -1010,6 +1014,27 @@ class InfoUtil(private val context: Context) {
val sponsorblockURL = sharedPreferences.getString("sponsorblock_url", "")!!
if (sponsorblockURL.isNotBlank()) request.addOption("--sponsorblock-api", sponsorblockURL)
+ if (sharedPreferences.getBoolean("restrict_filenames", true)) request.addOption("--restrict-filenames")
+ if (sharedPreferences.getBoolean("use_cookies", false)){
+ FileUtil.getCookieFile(context){
+ request.addOption("--cookies", it)
+ }
+ }
+
+ val proxy = sharedPreferences.getString("proxy", "")
+ if (proxy!!.isNotBlank()){
+ request.addOption("--proxy", proxy)
+ }
+
+ val keepCache = sharedPreferences.getBoolean("keep_cache", false)
+ if(keepCache){
+ request.addOption("--part")
+ request.addOption("--keep-fragments")
+ }
+
+ val preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!!
+ val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
+
if(downloadItem.type != DownloadViewModel.Type.command){
request.addOption("--trim-filenames", downDir.absolutePath.length + 120)
@@ -1106,34 +1131,19 @@ class InfoUtil(private val context: Context) {
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader,channel)s")
}
- if (sharedPreferences.getBoolean("restrict_filenames", true)) {
- request.addOption("--restrict-filenames")
- }
-
- if (sharedPreferences.getBoolean("use_cookies", false)){
- FileUtil.getCookieFile(context){
- request.addOption("--cookies", it)
- }
- }
-
- val proxy = sharedPreferences.getString("proxy", "")
- if (proxy!!.isNotBlank()){
- request.addOption("--proxy", proxy)
- }
-
- val keepCache = sharedPreferences.getBoolean("keep_cache", false)
- if(keepCache){
- request.addOption("--part")
- request.addOption("--keep-fragments")
- }
-
when(type){
DownloadViewModel.Type.audio -> {
val supportedContainers = context.resources.getStringArray(R.array.audio_containers)
var audioQualityId : String = downloadItem.format.format_id
- if (audioQualityId.isBlank() || audioQualityId == "0" || audioQualityId == context.getString(R.string.best_quality) || audioQualityId == "best") audioQualityId = ""
- else if (audioQualityId == context.getString(R.string.worst_quality) || audioQualityId == "worst") audioQualityId = "worstaudio"
+ if (audioQualityId.isBlank() || listOf("0", context.getString(R.string.best_quality), "best", "").contains(audioQualityId)){
+ audioQualityId = ""
+ if (preferredAudioCodec.isNotBlank()){
+ audioQualityId = "${aCodecPref}/bestaudio"
+ }
+ }else if (listOf(context.getString(R.string.worst_quality), "worst").contains(audioQualityId)){
+ audioQualityId = "worstaudio"
+ }
val ext = downloadItem.container
if (audioQualityId.isNotBlank()) request.addOption("-f", audioQualityId)
@@ -1230,6 +1240,9 @@ class InfoUtil(private val context: Context) {
val f = StringBuilder()
if(!usingGenericFormat){
+ if (preferredAudioCodec.isNotBlank()){
+ f.append("$videof+$aCodecPref/")
+ }
val aa = if (audiof.isNotBlank()) "+$audiof" else ""
f.append("$videof$aa/$videof/best")
@@ -1257,7 +1270,12 @@ class InfoUtil(private val context: Context) {
.toString()
.split(",")
.filter { it.isNotEmpty() }
- .ifEmpty { listOf(audiof) }
+ .ifEmpty {
+ val list = mutableListOf()
+ if (preferredAudioCodec.isNotBlank()) list.add("ba[acodec~='^($preferredAudioCodec)']")
+ list.add(audiof)
+ list
+ }
val preferredAudioFormats = if (downloadItem.videoPreferences.audioFormatIDs.isEmpty()) {
preferredAudioFormatIDs
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 2876a6ab..d638b96a 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt
@@ -7,19 +7,26 @@ import android.app.PendingIntent
import android.app.TaskStackBuilder
import android.content.Context
import android.content.Intent
+import android.content.SharedPreferences
+import android.content.res.Configuration
+import android.content.res.Resources
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
+import android.util.DisplayMetrics
import androidx.core.app.NotificationCompat
import androidx.core.content.FileProvider
import androidx.navigation.NavDeepLinkBuilder
+import androidx.preference.PreferenceManager
import com.deniscerri.ytdlnis.R
+import com.deniscerri.ytdlnis.App.Companion.instance
import com.deniscerri.ytdlnis.receiver.CancelDownloadNotificationReceiver
import com.deniscerri.ytdlnis.receiver.CancelWorkReceiver
import com.deniscerri.ytdlnis.receiver.PauseDownloadNotificationReceiver
import com.deniscerri.ytdlnis.receiver.ResumeActivity
import com.deniscerri.ytdlnis.receiver.ShareFileActivity
import java.io.File
+import java.util.Locale
class NotificationUtil(var context: Context) {
@@ -27,6 +34,15 @@ class NotificationUtil(var context: Context) {
private val commandDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID)
private val finishedDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_FINISHED_CHANNEL_ID)
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
+ private val resources: Resources
+ private var sharedPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
+
+ init {
+ val confTmp = Configuration(context.resources.configuration)
+ confTmp.setLocale(Locale(sharedPreferences.getString("app_language", "en")!!))
+ val metrics = DisplayMetrics()
+ resources = Resources(context.assets, metrics, confTmp)
+ }
fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -35,7 +51,7 @@ class NotificationUtil(var context: Context) {
)
//downloading worker notification
- var name: CharSequence = context.getString(R.string.downloading)
+ var name: CharSequence = resources.getString(R.string.downloading)
var description = "WorkManager Default Notification"
val importance = NotificationManager.IMPORTANCE_LOW
var channel = NotificationChannel(DOWNLOAD_WORKER_CHANNEL_ID, name, importance)
@@ -43,30 +59,30 @@ class NotificationUtil(var context: Context) {
notificationManager.createNotificationChannel(channel)
//gui downloads
- name = context.getString(R.string.download_notification_channel_name)
- description = context.getString(R.string.download_notification_channel_description)
+ name = resources.getString(R.string.download_notification_channel_name)
+ description = resources.getString(R.string.download_notification_channel_description)
channel = NotificationChannel(DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
//command downloads
- name = context.getString(R.string.command_download_notification_channel_name)
+ name = resources.getString(R.string.command_download_notification_channel_name)
description =
- context.getString(R.string.command_download_notification_channel_description)
+ resources.getString(R.string.command_download_notification_channel_description)
channel = NotificationChannel(COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID, name, importance)
channel.description = description
notificationManager.createNotificationChannel(channel)
//finished or errored downloads
- name = context.getString(R.string.finished_download_notification_channel_name)
+ name = resources.getString(R.string.finished_download_notification_channel_name)
description =
- context.getString(R.string.finished_download_notification_channel_description)
+ resources.getString(R.string.finished_download_notification_channel_description)
channel = NotificationChannel(DOWNLOAD_FINISHED_CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH)
channel.description = description
notificationManager.createNotificationChannel(channel)
//misc
- name = context.getString(R.string.misc)
+ name = resources.getString(R.string.misc)
description = ""
channel = NotificationChannel(DOWNLOAD_MISC_CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH)
channel.description = description
@@ -87,12 +103,12 @@ class NotificationUtil(var context: Context) {
val notificationBuilder = getBuilder(DOWNLOAD_WORKER_CHANNEL_ID)
return notificationBuilder
- .setContentTitle(context.getString(R.string.downloading))
+ .setContentTitle(resources.getString(R.string.downloading))
.setOngoing(true)
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
R.drawable.ic_launcher_foreground_large
)
)
@@ -118,7 +134,7 @@ class NotificationUtil(var context: Context) {
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
android.R.drawable.stat_sys_download
)
)
@@ -140,7 +156,7 @@ class NotificationUtil(var context: Context) {
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
R.drawable.ic_launcher_foreground_large
)
)
@@ -157,7 +173,7 @@ class NotificationUtil(var context: Context) {
PendingIntent.FLAG_IMMUTABLE
)
- notificationBuilder.addAction(0, context.getString(R.string.resume), resumeNotificationPendingIntent)
+ notificationBuilder.addAction(0, resources.getString(R.string.resume), resumeNotificationPendingIntent)
notificationManager.notify(DOWNLOAD_RESUME_NOTIFICATION_ID + itemID, notificationBuilder.build())
}
@@ -165,11 +181,11 @@ class NotificationUtil(var context: Context) {
val notificationBuilder = getBuilder(channel)
notificationBuilder
- .setContentTitle(context.getString(R.string.updating_download_data))
+ .setContentTitle(resources.getString(R.string.updating_download_data))
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
android.R.drawable.stat_sys_download
)
)
@@ -185,11 +201,11 @@ class NotificationUtil(var context: Context) {
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)
notificationBuilder
- .setContentTitle("${context.getString(R.string.downloaded)} $title")
+ .setContentTitle("${resources.getString(R.string.downloaded)} $title")
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
R.drawable.ic_launcher_foreground_large
)
)
@@ -228,8 +244,8 @@ class NotificationUtil(var context: Context) {
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
- notificationBuilder.addAction(0, context.getString(R.string.Open_File), openNotificationPendingIntent)
- notificationBuilder.addAction(0, context.getString(R.string.share), shareNotificationPendingIntent)
+ notificationBuilder.addAction(0, resources.getString(R.string.Open_File), openNotificationPendingIntent)
+ notificationBuilder.addAction(0, resources.getString(R.string.share), shareNotificationPendingIntent)
}catch (_: Exception){}
}
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
@@ -263,12 +279,12 @@ class NotificationUtil(var context: Context) {
.createPendingIntent()
notificationBuilder
- .setContentTitle("${context.getString(R.string.failed_download)}: $title")
+ .setContentTitle("${resources.getString(R.string.failed_download)}: $title")
.setContentText(error)
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
R.drawable.ic_launcher_foreground_large
)
)
@@ -278,7 +294,7 @@ class NotificationUtil(var context: Context) {
.clearActions()
if (logID != null){
notificationBuilder.setContentIntent(errorPendingIntent)
- notificationBuilder.addAction(0, context.getString(R.string.logs), errorPendingIntent)
+ notificationBuilder.addAction(0, resources.getString(R.string.logs), errorPendingIntent)
}
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
}
@@ -299,7 +315,7 @@ class NotificationUtil(var context: Context) {
val notificationBuilder = getBuilder(channel)
var contentText = ""
- if (queue > 1) contentText += """${queue - 1} ${context.getString(R.string.items_left)}""" + "\n"
+ if (queue > 1) contentText += """${queue - 1} ${resources.getString(R.string.items_left)}""" + "\n"
contentText += desc.replace("\\[.*?\\] ".toRegex(), "")
val pauseIntent = Intent(context, PauseDownloadNotificationReceiver::class.java)
@@ -326,8 +342,8 @@ class NotificationUtil(var context: Context) {
.setContentTitle(title)
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
.clearActions()
- .addAction(0, context.getString(R.string.pause), pauseNotificationPendingIntent)
- .addAction(0, context.getString(R.string.cancel), cancelNotificationPendingIntent)
+ .addAction(0, resources.getString(R.string.pause), pauseNotificationPendingIntent)
+ .addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
notificationManager.notify(id, notificationBuilder.build())
} catch (e: Exception) {
e.printStackTrace()
@@ -342,13 +358,13 @@ class NotificationUtil(var context: Context) {
val notificationBuilder = getBuilder(downloadMiscChannelId)
return notificationBuilder
- .setContentTitle(context.getString(R.string.move_temporary_files))
+ .setContentTitle(resources.getString(R.string.move_temporary_files))
.setOngoing(true)
.setCategory(Notification.CATEGORY_PROGRESS)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
android.R.drawable.stat_sys_download
)
)
@@ -367,7 +383,7 @@ class NotificationUtil(var context: Context) {
val contentText = "${progress}/${totalFiles}"
try {
notificationBuilder.setProgress(100, progress, false)
- .setContentTitle(context.getString(R.string.move_temporary_files))
+ .setContentTitle(resources.getString(R.string.move_temporary_files))
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
notificationManager.notify(id, notificationBuilder.build())
} catch (e: Exception) {
@@ -383,7 +399,7 @@ class NotificationUtil(var context: Context) {
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
android.R.drawable.stat_sys_download
)
)
@@ -394,26 +410,16 @@ class NotificationUtil(var context: Context) {
return notificationBuilder.build()
}
- fun createFormatsUpdateNotification(workID: Int): Notification {
+ fun createFormatsUpdateNotification(): Notification {
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
-
- val cancelIntent = Intent(context, CancelWorkReceiver::class.java)
- cancelIntent.putExtra("workID", workID)
- val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
- context,
- workID,
- cancelIntent,
- PendingIntent.FLAG_IMMUTABLE
- )
-
return notificationBuilder
- .setContentTitle(context.getString(R.string.update_formats_background))
+ .setContentTitle(resources.getString(R.string.update_formats_background))
.setOngoing(true)
.setCategory(Notification.CATEGORY_PROGRESS)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
android.R.drawable.stat_sys_download
)
)
@@ -423,23 +429,37 @@ class NotificationUtil(var context: Context) {
.setProgress(PROGRESS_MAX, PROGRESS_CURR, false)
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
.clearActions()
- .addAction(0, context.getString(R.string.cancel), cancelNotificationPendingIntent)
.build()
}
fun updateFormatUpdateNotification(
- id: Int,
+ workID: Int,
+ workTag: String,
progress: Int,
queue: Int,
) {
val notificationBuilder = getBuilder(DOWNLOAD_MISC_CHANNEL_ID)
- val contentText = """${queue - progress} ${context.getString(R.string.items_left)}"""
+ val contentText = """${queue - progress} ${resources.getString(R.string.items_left)}"""
+
+
+ val cancelIntent = Intent(context, CancelWorkReceiver::class.java)
+ cancelIntent.putExtra("workTag", workTag)
+ val cancelNotificationPendingIntent = PendingIntent.getBroadcast(
+ context,
+ workID,
+ cancelIntent,
+ PendingIntent.FLAG_IMMUTABLE
+ )
+
+
try {
notificationBuilder.setProgress(queue, progress, false)
- .setContentTitle(context.getString(R.string.update_formats_background))
+ .setContentTitle(resources.getString(R.string.update_formats_background))
.setStyle(NotificationCompat.BigTextStyle().bigText(contentText))
- notificationManager.notify(id, notificationBuilder.build())
+ .clearActions()
+ .addAction(0, resources.getString(R.string.cancel), cancelNotificationPendingIntent)
+ notificationManager.notify(workID, notificationBuilder.build())
} catch (e: Exception) {
e.printStackTrace()
}
@@ -460,11 +480,11 @@ class NotificationUtil(var context: Context) {
.createPendingIntent()
notificationBuilder
- .setContentTitle(context.getString(R.string.finished_download_notification_channel_name))
+ .setContentTitle(resources.getString(R.string.finished_download_notification_channel_name))
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
.setLargeIcon(
BitmapFactory.decodeResource(
- context.resources,
+ resources,
R.drawable.ic_launcher_foreground_large
)
)
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 84a39a53..d820c363 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
@@ -17,6 +17,7 @@ import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.await
import androidx.work.workDataOf
+import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.dao.DownloadDao
@@ -49,7 +50,7 @@ class DownloadWorker(
override suspend fun doWork(): Result {
if (isStopped) return Result.success()
- val notificationUtil = NotificationUtil(context)
+ val notificationUtil = NotificationUtil(App.instance)
val infoUtil = InfoUtil(context)
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
@@ -119,6 +120,8 @@ class DownloadWorker(
val keepCache = sharedPreferences.getBoolean("keep_cache", false)
val logDownloads = sharedPreferences.getBoolean("log_downloads", false) && !sharedPreferences.getBoolean("incognito", false)
+
+ val commandString = infoUtil.parseYTDLRequestString(request)
val logItem = LogItem(
0,
downloadItem.title.ifEmpty { downloadItem.url },
@@ -126,7 +129,7 @@ class DownloadWorker(
"Title: ${downloadItem.title}\n" +
"URL: ${downloadItem.url}\n" +
"Type: ${downloadItem.type}\n" +
- "Command:\n ${infoUtil.parseYTDLRequestString(request)}\n\n",
+ "Command:\n ${commandString}\n\n",
downloadItem.format,
downloadItem.type,
System.currentTimeMillis(),
@@ -142,6 +145,8 @@ class DownloadWorker(
}
+ val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()
+
runCatching {
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to downloadItem.id))
@@ -162,16 +167,18 @@ class DownloadWorker(
runBlocking {
var finalPaths : List?
- //if there was no cache used
- if (!sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()){
+ if (noCache){
setProgressAsync(workDataOf("progress" to 100, "output" to "Scanning Files", "id" to downloadItem.id))
- val p = infoUtil.getFilePaths(request)
+ val p = it.out.split("\r")
+ .filter { it.startsWith("'/storage") }
+ .map { it.removePrefix("'") }
+ .map { it.removeSuffix("'\n") }
finalPaths = File(FileUtil.formatPath(downloadLocation))
- .walkTopDown()
- .filter { it.isFile && p.contains(it.nameWithoutExtension) }
- .sortedByDescending { it.length() }
- .map { it.absolutePath }
- .toList()
+ .walkTopDown()
+ .filter { it.isFile && p.any { f -> f.contains(it.nameWithoutExtension) }}
+ .sortedByDescending { it.length() }
+ .map { it.absolutePath }
+ .toList()
FileUtil.scanMedia(finalPaths, context)
if (finalPaths.isEmpty()){
finalPaths = listOf(context.getString(R.string.unfound_file))
@@ -208,7 +215,7 @@ class DownloadWorker(
val unixtime = System.currentTimeMillis() / 1000
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)
+ 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, commandString)
historyDao.insert(historyItem)
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/MoveCacheFilesWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/MoveCacheFilesWorker.kt
index 9ddba70f..6c35cf02 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/MoveCacheFilesWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/MoveCacheFilesWorker.kt
@@ -15,6 +15,7 @@ import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
+import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
@@ -43,7 +44,7 @@ class MoveCacheFilesWorker(
workerParams: WorkerParameters
) : Worker(context, workerParams) {
override fun doWork(): Result {
- val notificationUtil = NotificationUtil(context)
+ val notificationUtil = NotificationUtil(App.instance)
val id = System.currentTimeMillis().toInt()
val downloadFolders = File(context.cacheDir.absolutePath + "/downloads")
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
index 1d32f8fc..f4e75faa 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
@@ -72,6 +72,12 @@ class TerminalDownloadWorker(
if (cookiesFile.exists()){
request.addOption("--cookies", cookiesFile.absolutePath)
}
+
+ val useHeader = sharedPreferences.getBoolean("use_header", false)
+ val header = sharedPreferences.getString("useragent_header", "")
+ if (useHeader && !header.isNullOrBlank()){
+ request.addOption("--add-header","User-Agent:${header}")
+ }
}
request.addOption("-P", FileUtil.getDefaultCommandPath() + "/" + itemId)
@@ -83,7 +89,7 @@ class TerminalDownloadWorker(
"Terminal Download",
"Downloading:\n" +
"Terminal Download\n" +
- "Command: ${infoUtil.parseYTDLRequestString(request)}\n\n",
+ "Command: ${command}\n\n",
Format(),
DownloadViewModel.Type.command,
System.currentTimeMillis(),
@@ -110,10 +116,9 @@ class TerminalDownloadWorker(
line, progress.toInt(), 0, title,
NotificationUtil.DOWNLOAD_SERVICE_CHANNEL_ID
)
- if (logDownloads){
- CoroutineScope(Dispatchers.IO).launch {
- logRepo.update(line, logItem.id)
- }
+ CoroutineScope(Dispatchers.IO).launch {
+ if (logDownloads) logRepo.update(line, logItem.id)
+ dao.updateLog(line, itemId.toLong())
}
}
}.onSuccess {
@@ -132,39 +137,30 @@ class TerminalDownloadWorker(
}
return runBlocking {
- it.out.split("\n").forEach {
- setProgress(workDataOf("progress" to 100, "output" to it, "id" to itemId, "log" to logDownloads))
- Thread.sleep(50)
- }
-
- if (logDownloads){
- CoroutineScope(Dispatchers.IO).launch {
- logRepo.update(it.out, logItem.id)
- }
+ CoroutineScope(Dispatchers.IO).launch {
+ if (logDownloads) logRepo.update(it.out, logItem.id)
+ dao.updateLog(it.out, itemId.toLong())
+ Thread.sleep(1000)
+ dao.delete(itemId.toLong())
}
notificationUtil.cancelDownloadNotification(itemId)
- dao.delete(itemId.toLong())
- return@runBlocking Result.success()
+
+ Result.success()
}
}.onFailure {
return runBlocking {
- it.message?.chunked(10000)?.forEach {
- setProgress(workDataOf("progress" to 100, "output" to it, "id" to itemId, "log" to logDownloads))
- Thread.sleep(100)
- }
-
- if (logDownloads){
- CoroutineScope(Dispatchers.IO).launch {
- if (it.message != null){
- logRepo.update(it.message!!, logItem.id)
- }
+ CoroutineScope(Dispatchers.IO).launch {
+ if (it.message != null){
+ if (logDownloads) logRepo.update(it.message!!, logItem.id)
+ dao.updateLog(it.message!!, itemId.toLong())
+ Thread.sleep(1000)
+ dao.delete(itemId.toLong())
}
}
+ notificationUtil.cancelDownloadNotification(itemId)
File(FileUtil.getDefaultCommandPath() + "/" + itemId).deleteRecursively()
Log.e(TAG, context.getString(R.string.failed_download), it)
- notificationUtil.cancelDownloadNotification(itemId)
- dao.delete(itemId.toLong())
- return@runBlocking Result.failure()
+ Result.failure()
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/UpdatePlaylistFormatsWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/UpdatePlaylistFormatsWorker.kt
index f3c0c1a6..b3cb6648 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/UpdatePlaylistFormatsWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/UpdatePlaylistFormatsWorker.kt
@@ -1,50 +1,23 @@
package com.deniscerri.ytdlnis.work
import android.content.Context
-import android.os.Handler
-import android.os.Looper
import android.util.Log
-import android.widget.Toast
-import androidx.lifecycle.ViewModelProvider
-import androidx.navigation.NavDeepLinkBuilder
-import androidx.preference.PreferenceManager
-import androidx.work.CoroutineWorker
-import androidx.work.Data
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
-import androidx.work.workDataOf
import com.deniscerri.ytdlnis.App
-import com.deniscerri.ytdlnis.R
-import com.deniscerri.ytdlnis.database.Converters
import com.deniscerri.ytdlnis.database.DBManager
-import com.deniscerri.ytdlnis.database.dao.DownloadDao
-import com.deniscerri.ytdlnis.database.dao.ResultDao
-import com.deniscerri.ytdlnis.database.models.DownloadItem
-import com.deniscerri.ytdlnis.database.models.HistoryItem
-import com.deniscerri.ytdlnis.database.models.LogItem
-import com.deniscerri.ytdlnis.database.repository.DownloadRepository
-import com.deniscerri.ytdlnis.database.repository.LogRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
-import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.NotificationUtil
-import com.deniscerri.ytdlnis.util.NotificationUtil.Companion.FORMAT_UPDATING_NOTIFICATION_ID
-import com.deniscerri.ytdlnis.util.UpdateUtil
-import com.yausername.youtubedl_android.YoutubeDL
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
-import java.io.File
class UpdatePlaylistFormatsWorker(
private val context: Context,
workerParams: WorkerParameters
-) : CoroutineWorker(context, workerParams) {
- override suspend fun doWork(): Result {
+) : Worker(context, workerParams) {
+ override fun doWork(): Result {
val dbManager = DBManager.getInstance(context)
val dao = dbManager.downloadDao
val resDao = dbManager.resultDao
@@ -55,47 +28,59 @@ class UpdatePlaylistFormatsWorker(
val workID = inputData.getInt("id", 0)
if (workID == 0) return Result.failure()
- val notification = notificationUtil.createFormatsUpdateNotification(workID)
- val foregroundInfo = ForegroundInfo(FORMAT_UPDATING_NOTIFICATION_ID, notification)
- setForeground(foregroundInfo)
+ val notification = notificationUtil.createFormatsUpdateNotification()
+ val foregroundInfo = ForegroundInfo(workID, notification)
+ setForegroundAsync(foregroundInfo)
var count = 0
- ids.forEach {
- if (!isStopped){
- val d = dao.getDownloadById(it)
- val r = resDao.getResultByURL(d.url)
- if (d.allFormats.isNotEmpty()){
+ return try{
+ ids.forEach {
+ if (!isStopped){
+ val d = dao.getDownloadById(it)
+ val r = resDao.getResultByURL(d.url)
+
+ if (d.allFormats.isNotEmpty()){
+ count++
+ return@forEach
+ }
+
+ runCatching {
+ d.allFormats.clear()
+ d.allFormats.addAll(infoUtil.getFormats(d.url))
+ d.format = vm.getFormat(d.allFormats,d.type)
+
+ r.formats.clear()
+ r.formats.addAll(d.allFormats)
+
+ runBlocking {
+ resDao.update(r)
+ dao.delete(d.id)
+ }
+ }
+
+
count++
- return@forEach
+ notificationUtil.updateFormatUpdateNotification(workID, UpdatePlaylistFormatsWorker::class.java.name, count, ids.size)
+ }else{
+ throw Exception()
}
+ }
- runCatching {
- d.allFormats.clear()
- d.allFormats.addAll(infoUtil.getFormats(d.url))
- d.format = vm.getFormat(d.allFormats,d.type)
-
- r.formats.clear()
- r.formats.addAll(d.allFormats)
-
- dao.update(d)
- resDao.update(r)
- }
-
-
- count++
- notificationUtil.updateFormatUpdateNotification(FORMAT_UPDATING_NOTIFICATION_ID, count, ids.size)
- }else{
- notificationUtil.cancelDownloadNotification(FORMAT_UPDATING_NOTIFICATION_ID)
+ Result.success()
+ }catch (e: Exception){
+ notificationUtil.cancelDownloadNotification(workID)
+ ids.clear()
+ Result.failure()
+ }finally {
+ if (ids.isNotEmpty()){
+ notificationUtil.showFormatsUpdatedNotification(ids)
}
}
+ }
-
-
- runBlocking {
- notificationUtil.showFormatsUpdatedNotification(ids)
- }
-
- return Result.success()
+ override fun onStopped() {
+ Log.e("asd", "Asd")
+ super.onStopped()
}
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_terminal_download_list.xml b/app/src/main/res/layout/fragment_terminal_download_list.xml
index ff257ec7..6f9ac26c 100644
--- a/app/src/main/res/layout/fragment_terminal_download_list.xml
+++ b/app/src/main/res/layout/fragment_terminal_download_list.xml
@@ -18,4 +18,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/webview_activity.xml b/app/src/main/res/layout/webview_activity.xml
index 36c0430d..7cab57b7 100644
--- a/app/src/main/res/layout/webview_activity.xml
+++ b/app/src/main/res/layout/webview_activity.xml
@@ -43,14 +43,16 @@
-
+
diff --git a/app/src/main/res/menu/cookies_menu.xml b/app/src/main/res/menu/cookies_menu.xml
index c1c1ac35..1806fce9 100644
--- a/app/src/main/res/menu/cookies_menu.xml
+++ b/app/src/main/res/menu/cookies_menu.xml
@@ -6,6 +6,12 @@
android:title="@string/delete_all_cookies"
app:showAsAction="never" />
+
+
- Preferred Audio Codec
Loading
Sponsorblock API URL
+ Remember Download Type
+ Select the last opened download type for the current item
+ User-Agent header
\ 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 d9d1f6db..48d3a404 100644
--- a/app/src/main/res/xml/downloading_preferences.xml
+++ b/app/src/main/res/xml/downloading_preferences.xml
@@ -53,6 +53,19 @@
app:summary="@string/preferred_download_type_summary"
app:title="@string/preferred_download_type" />
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index c740938e..fda45d67 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -1,38 +1,49 @@
-download audio/video files from more than 1000 websites
-process playlists
-edit every playlist item separately just like in a normal download item.
-select a common format for all items and/or select multiple audio formats in case you are downloading them as a video
-select a download path for all items
-select a filename template for all items
-batch update download type to audio/video/custom command in one click
-queue downloads and schedule them by date and time
-you can also schedule multiple items at the same time
-download multiple items at the same time
-use custom commands and templates or go full yt-dlp mode with a built in terminal
-you can backup and restore templates, so you can share with your buddies
-COOKIES support. Log in with your accounts and download private/unavailable videos, unlock premium formats etc.
-cut videos based on timestamps and video chapters (This yt-dlp feature is experimental on the original project)
-you can make unlimited cuts
-remove sponsorblock elements from the item
-embed them as a chapters in your video
-embed subtitles/metadata/chapters etc
-modify metadata such as title and author
-split item into separate files depending on its chapters
-select different download formats
-bottom card right from the share menu, no need to open the app
-you can create a txt file and fill it with links/playlists/search queries separate by a new line and the app will process them
-search or insert a link from the app
-you can stack searches so you can process them at the same time
-log downloads in case of problems
-re-download cancelled or failed downloads
-you can use gestures to swipe left to redownload and right to delete
-you can long click the redownload button in the details sheet to show the download card for more functionality
-incognito mode when you don't want to save a download history or logs
-quick download mode
-download immediately without having to wait for data to process. Turn off the bottom card and it will instantly start
-open / share downloaded files right from the finished notification
-most yt-dlp features are implemented, suggestions are welcome
-Material You interface
-Theming options
-Backup and restore features. (Almost everything can be backed up)
-MVVM Architecture w/ WorkManager
\ No newline at end of file
+YTDLnis lets you download audio/video files from more than 1000 websites, and offers a lot of features. For example:
+
+process playlists
+
+- edit every playlist item separately just like in a normal download item.
+- select a common format for all items and/or select multiple audio formats in case you are downloading them as a video
+- select a download path for all items
+- select a filename template for all items
+- batch update download type to audio/video/custom command in one click
+- queue downloads and schedule them by date and time
+- you can also schedule multiple items at the same time
+- download multiple items at the same time
+- use custom commands and templates or go full yt-dlp mode with a built in terminal
+- you can backup and restore templates, so you can share with your buddies
+
+COOKIES support
+
+- Log in with your accounts and download private/unavailable videos, unlock premium formats etc.
+
+Different ways of modifying the file
+
+- cut videos based on timestamps and video chapters (This yt-dlp feature is experimental on the original project)
+- you can make unlimited cuts
+- remove sponsorblock elements from the item
+- embed them as a chapters in your video
+- embed subtitles/metadata/chapters etc
+- modify metadata such as title and author
+- split item into separate files depending on its chapters
+- select different download formats
+
+Other features
+
+- bottom card right from the share menu, no need to open the app
+- you can create a txt file and fill it with links/playlists/search queries separate by a new line and the app will process them
+- search or insert a link from the app
+- you can stack searches so you can process them at the same time
+- log downloads in case of problems
+- re-download cancelled or failed downloads
+- you can use gestures to swipe left to redownload and right to delete
+- you can long click the redownload button in the details sheet to show the download card for more functionality
+- incognito mode when you don't want to save a download history or logs
+- quick download mode
+- download immediately without having to wait for data to process. Turn off the bottom card and it will instantly start
+- open / share downloaded files right from the finished notification
+- most yt-dlp features are implemented, suggestions are welcome
+- Material You interface
+- Theming options
+- Backup and restore features. (Almost everything can be backed up)
+- MVVM Architecture w/ WorkManager
\ No newline at end of file