.
This commit is contained in:
parent
3b3873de49
commit
ef5b68ffc1
30 changed files with 980 additions and 165 deletions
598
app/schemas/com.deniscerri.ytdlnis.database.DBManager/13.json
Normal file
598
app/schemas/com.deniscerri.ytdlnis.database.DBManager/13.json
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 13,
|
||||
"identityHash": "5cea521e1a26473b2ec5298791f10ac4",
|
||||
"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, `playlistURL` TEXT DEFAULT '', `playlistIndex` INTEGER, `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": "playlistURL",
|
||||
"columnName": "playlistURL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistIndex",
|
||||
"columnName": "playlistIndex",
|
||||
"affinity": "INTEGER",
|
||||
"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, `playlistURL` TEXT DEFAULT '', `playlistIndex` 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
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistURL",
|
||||
"columnName": "playlistURL",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false,
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistIndex",
|
||||
"columnName": "playlistIndex",
|
||||
"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, `useAsExtraCommandAudio` INTEGER NOT NULL DEFAULT 1, `useAsExtraCommandVideo` INTEGER NOT NULL DEFAULT 1)",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "useAsExtraCommandAudio",
|
||||
"columnName": "useAsExtraCommandAudio",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
},
|
||||
{
|
||||
"fieldPath": "useAsExtraCommandVideo",
|
||||
"columnName": "useAsExtraCommandVideo",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true,
|
||||
"defaultValue": "1"
|
||||
}
|
||||
],
|
||||
"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, '5cea521e1a26473b2ec5298791f10ac4')"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import com.deniscerri.ytdlnis.database.models.*
|
|||
LogItem::class,
|
||||
TerminalItem::class
|
||||
],
|
||||
version = 12,
|
||||
version = 13,
|
||||
autoMigrations = [
|
||||
AutoMigration (from = 1, to = 2),
|
||||
AutoMigration (from = 2, to = 3),
|
||||
|
|
@ -35,7 +35,8 @@ import com.deniscerri.ytdlnis.database.models.*
|
|||
AutoMigration (from = 8, to = 9),
|
||||
AutoMigration (from = 9, to = 10),
|
||||
AutoMigration (from = 10, to = 11),
|
||||
AutoMigration (from = 11, to = 12)
|
||||
AutoMigration (from = 11, to = 12),
|
||||
AutoMigration (from = 12, to = 13)
|
||||
]
|
||||
)
|
||||
abstract class DBManager : RoomDatabase(){
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ interface DownloadDao {
|
|||
@Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
|
||||
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
||||
fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ interface DownloadDao {
|
|||
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
||||
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||
fun getCancelledDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||
|
||||
|
|
@ -64,12 +66,14 @@ interface DownloadDao {
|
|||
@Query("SELECT * FROM downloads WHERE status LIKE '%Paused%'")
|
||||
fun getPausedDownloadsList() : List<DownloadItem>
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||
fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||
|
||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||
fun getErroredDownloadsList() : List<DownloadItem>
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC")
|
||||
fun getSavedDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ interface ResultDao {
|
|||
suspend fun delete(id: Long)
|
||||
|
||||
@Query("SELECT * FROM results WHERE url=:url LIMIT 1")
|
||||
fun getResultByURL(url: String) : ResultItem
|
||||
fun getResultByURL(url: String) : ResultItem?
|
||||
|
||||
}
|
||||
|
|
@ -38,5 +38,9 @@ data class DownloadItem(
|
|||
var status: String,
|
||||
@ColumnInfo(defaultValue = "0")
|
||||
var downloadStartTime: Long,
|
||||
var logID: Long?
|
||||
var logID: Long?,
|
||||
@ColumnInfo(defaultValue = "")
|
||||
var playlistURL: String? = "",
|
||||
@ColumnInfo(defaultValue = "")
|
||||
var playlistIndex: Int? = null
|
||||
) : Parcelable
|
||||
|
|
@ -22,5 +22,9 @@ data class ResultItem(
|
|||
@ColumnInfo(defaultValue = "")
|
||||
var urls: String,
|
||||
var chapters: MutableList<ChapterItem>?,
|
||||
@ColumnInfo(defaultValue = "")
|
||||
var playlistURL: String? = "",
|
||||
@ColumnInfo(defaultValue = "")
|
||||
var playlistIndex: Int? = null,
|
||||
var creationTime: Long = System.currentTimeMillis() / 1000,
|
||||
) : Parcelable
|
||||
|
|
@ -52,7 +52,12 @@ class HistoryRepository(private val historyDao: HistoryDao) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun deleteAll(){
|
||||
suspend fun deleteAll(deleteFile: Boolean = false){
|
||||
if (deleteFile){
|
||||
historyDao.getAllHistoryList().forEach { item ->
|
||||
FileUtil.deleteFile(item.downloadPath)
|
||||
}
|
||||
}
|
||||
historyDao.deleteAll()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
deleteAll()
|
||||
itemCount.value = v.size
|
||||
}else{
|
||||
v.forEach { it?.playlistTitle = "ytdlnis-Search" }
|
||||
v.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it?.playlistTitle = "ytdlnis-Search" }
|
||||
}
|
||||
resultDao.insertMultiple(v)
|
||||
return ArrayList(v)
|
||||
|
|
@ -77,7 +77,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
deleteAll()
|
||||
itemCount.value = items.size
|
||||
}else{
|
||||
items.forEach { it!!.playlistTitle = "ytdlnis-Search" }
|
||||
items.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it!!.playlistTitle = "ytdlnis-Search" }
|
||||
}
|
||||
resultDao.insertMultiple(items.toList())
|
||||
return items
|
||||
|
|
@ -96,7 +96,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
|||
resultDao.update(item)
|
||||
}
|
||||
|
||||
fun getItemByURL(url: String): ResultItem {
|
||||
fun getItemByURL(url: String): ResultItem? {
|
||||
return resultDao.getResultByURL(url)
|
||||
}
|
||||
}
|
||||
|
|
@ -152,15 +152,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
|
||||
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
||||
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
||||
Format(
|
||||
"best",
|
||||
audioContainer!!,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
"best"
|
||||
)
|
||||
infoUtil.getGenericAudioFormats(resources).last()
|
||||
}else{
|
||||
Format(
|
||||
audioFormatIDPreference.first().split("+").first(),
|
||||
|
|
@ -260,23 +252,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
|
||||
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!))
|
||||
|
||||
val preferredAudioFormats = arrayListOf<String>()
|
||||
for (f in resultItem.formats.sortedBy { it.format_id }){
|
||||
val fId = audioFormatIDPreference.sorted().find { it.contains(f.format_id) }
|
||||
if (fId != null) {
|
||||
if (fId.split("+").all { resultItem.formats.map { f-> f.format_id }.contains(it) }){
|
||||
preferredAudioFormats.addAll(fId.split("+"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preferredAudioFormats.isEmpty()){
|
||||
val audioF = getFormat(resultItem.formats, Type.audio)
|
||||
if (!infoUtil.getGenericAudioFormats(resources).contains(audioF)){
|
||||
preferredAudioFormats.add(audioF.format_id)
|
||||
}
|
||||
}
|
||||
val preferredAudioFormats = getPreferredAudioFormats(resultItem.formats)
|
||||
|
||||
val videoPreferences = VideoPreferences(
|
||||
embedSubs,
|
||||
|
|
@ -303,7 +280,15 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
container!!,
|
||||
"",
|
||||
resultItem.formats,
|
||||
downloadPath!!, resultItem.website, "", resultItem.playlistTitle, audioPreferences, videoPreferences, extraCommands, customFileNameTemplate!!, saveThumb, DownloadRepository.Status.Queued.toString(), 0, null
|
||||
downloadPath!!, resultItem.website,
|
||||
"",
|
||||
resultItem.playlistTitle,
|
||||
audioPreferences,
|
||||
videoPreferences,
|
||||
extraCommands,
|
||||
customFileNameTemplate!!,
|
||||
saveThumb,
|
||||
DownloadRepository.Status.Queued.toString(), 0, null, playlistURL = resultItem.playlistURL, playlistIndex = resultItem.playlistIndex
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -321,6 +306,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
downloadItem.allFormats,
|
||||
"",
|
||||
arrayListOf(),
|
||||
downloadItem.playlistURL,
|
||||
downloadItem.playlistIndex,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
|
||||
|
|
@ -339,6 +326,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
arrayListOf(),
|
||||
"",
|
||||
arrayListOf(),
|
||||
"",
|
||||
null,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
|
||||
|
|
@ -357,6 +346,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
arrayListOf(),
|
||||
"",
|
||||
arrayListOf(),
|
||||
"",
|
||||
null,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
|
||||
|
|
@ -493,6 +484,26 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
}
|
||||
}
|
||||
|
||||
fun getPreferredAudioFormats(formats: List<Format>) : ArrayList<String>{
|
||||
val preferredAudioFormats = arrayListOf<String>()
|
||||
for (f in formats.sortedBy { it.format_id }){
|
||||
val fId = audioFormatIDPreference.sorted().find { it.contains(f.format_id) }
|
||||
if (fId != null) {
|
||||
if (fId.split("+").all { formats.map { f-> f.format_id }.contains(it) }){
|
||||
preferredAudioFormats.addAll(fId.split("+"))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (preferredAudioFormats.isEmpty()){
|
||||
val audioF = getFormat(formats, Type.audio)
|
||||
if (!infoUtil.getGenericAudioFormats(resources).contains(audioF)){
|
||||
preferredAudioFormats.add(audioF.format_id)
|
||||
}
|
||||
}
|
||||
return preferredAudioFormats
|
||||
}
|
||||
|
||||
fun generateCommandFormat(c: CommandTemplate) : Format {
|
||||
return Format(
|
||||
c.title,
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
|||
repository.delete(item, deleteFile)
|
||||
}
|
||||
|
||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
||||
repository.deleteAll()
|
||||
fun deleteAll(deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
||||
repository.deleteAll(deleteFile)
|
||||
}
|
||||
|
||||
fun deleteDuplicates() = viewModelScope.launch(Dispatchers.IO) {
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
|||
repository.update(item)
|
||||
}
|
||||
|
||||
fun getItemByURL(url: String) : ResultItem {
|
||||
fun getItemByURL(url: String) : ResultItem? {
|
||||
return repository.getItemByURL(url)
|
||||
}
|
||||
|
||||
|
|
@ -233,11 +233,12 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
|||
val formats = infoUtil.getFormats(result.url)
|
||||
updatingFormats.emit(false)
|
||||
if (formats.isNotEmpty() && isActive) {
|
||||
val res = getItemByURL(result.url)
|
||||
res.formats = formats.toMutableList()
|
||||
update(res)
|
||||
getItemByURL(result.url)?.apply {
|
||||
this.formats = formats.toMutableList()
|
||||
update(this)
|
||||
}
|
||||
updateFormatsResultData.emit(formats.toMutableList())
|
||||
}
|
||||
updateFormatsResultData.emit(formats.toMutableList())
|
||||
}
|
||||
updateFormatsResultDataJob?.start()
|
||||
updateFormatsResultDataJob?.invokeOnCompletion {
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
|||
|
||||
// TITLE ----------------------------------
|
||||
val videoTitle = card.findViewById<TextView>(R.id.result_title)
|
||||
var title = video!!.title
|
||||
var title = video!!.title.ifBlank { video.url }
|
||||
if (title.length > 100) {
|
||||
title = title.substring(0, 40) + "..."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,24 +198,27 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
|||
val chosenFormat = downloadItem.format
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, null)
|
||||
val listener = object : OnFormatClickListener {
|
||||
override fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>) {
|
||||
override fun onFormatClick(item: List<FormatTuple>) {
|
||||
downloadItem.format = item.first().format
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, null)
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
resultItem?.formats?.removeAll(formats.toSet())
|
||||
resultItem?.formats?.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
|
||||
if (resultItem != null){
|
||||
resultViewModel.update(resultItem!!)
|
||||
kotlin.runCatching {
|
||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.updateUI(resultItem)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
resultItem?.formats?.removeAll(formats.toSet())
|
||||
resultItem?.formats?.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
|
||||
if (resultItem != null){
|
||||
resultViewModel.update(resultItem!!)
|
||||
kotlin.runCatching {
|
||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.updateUI(resultItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList()
|
||||
formats.removeAll(genericAudioFormats)
|
||||
val preferredFormat = downloadViewModel.getFormat(formats, Type.audio)
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, null)
|
||||
}
|
||||
}
|
||||
formatCard.setOnClickListener{
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import android.widget.LinearLayout
|
|||
import android.widget.Toast
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
|
@ -440,20 +441,42 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
delay(500)
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = true
|
||||
isClickable = true
|
||||
setOnClickListener {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.GONE
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.apply {
|
||||
isVisible = false
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -569,12 +592,22 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
|
||||
when(viewPager2.currentItem){
|
||||
0 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
kotlin.runCatching {
|
||||
val f = fragmentManager?.findFragmentByTag("f2") as DownloadCommandFragment
|
||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import kotlinx.coroutines.launch
|
|||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
class DownloadCommandFragment(private val resultItem: ResultItem? = null, private var currentDownloadItem: DownloadItem? = null, private var url: String = "") : Fragment() {
|
||||
class DownloadCommandFragment(private val resultItem: ResultItem? = null, private var currentDownloadItem: DownloadItem? = null, private var url: String = "") : Fragment(), GUISync {
|
||||
private var fragmentView: View? = null
|
||||
private var activity: Activity? = null
|
||||
private lateinit var downloadViewModel : DownloadViewModel
|
||||
|
|
@ -296,4 +296,12 @@ class DownloadCommandFragment(private val resultItem: ResultItem? = null, privat
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateTitleAuthor(t: String, a: String) {
|
||||
downloadItem.title = t
|
||||
downloadItem.author = a
|
||||
}
|
||||
|
||||
override fun updateUI(res: ResultItem?) {
|
||||
}
|
||||
}
|
||||
|
|
@ -208,18 +208,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
|
||||
val formatListener = object : OnFormatClickListener {
|
||||
override fun onFormatClick(allFormats: List<List<Format>>, selectedFormats: List<FormatTuple>) {
|
||||
val formatCollection = mutableListOf<List<Format>>()
|
||||
allFormats.forEach {f ->
|
||||
formatCollection.add(f.mapTo(mutableListOf()) {it.copy()})
|
||||
}
|
||||
override fun onFormatClick(selectedFormats: List<FormatTuple>) {
|
||||
items.forEachIndexed { index, i ->
|
||||
i.allFormats.clear()
|
||||
if (formatCollection.size == items.size && formatCollection[index].isNotEmpty()) {
|
||||
runCatching {
|
||||
i.allFormats.addAll(formatCollection[index])
|
||||
}
|
||||
}
|
||||
i.format = selectedFormats[index].format
|
||||
if (i.type == DownloadViewModel.Type.video) selectedFormats[index].audioFormats?.map { it.format_id }?.let { i.videoPreferences.audioFormatIDs.addAll(it) }
|
||||
}
|
||||
|
|
@ -229,6 +219,35 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
}
|
||||
|
||||
|
||||
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
val formatCollection = mutableListOf<List<Format>>()
|
||||
allFormats.forEach {f ->
|
||||
formatCollection.add(f.mapTo(mutableListOf()) {it.copy()})
|
||||
}
|
||||
items.forEachIndexed { index, i ->
|
||||
i.allFormats.clear()
|
||||
if (formatCollection.size == items.size && formatCollection[index].isNotEmpty()) {
|
||||
runCatching {
|
||||
i.allFormats.addAll(formatCollection[index])
|
||||
}
|
||||
}
|
||||
i.format = downloadViewModel.getFormat(i.allFormats, i.type)
|
||||
kotlin.runCatching {
|
||||
val resultItem = resultViewModel.getItemByURL(i.url)!!
|
||||
resultItem.formats = formatCollection[index].toMutableList()
|
||||
resultViewModel.update(resultItem)
|
||||
}
|
||||
}
|
||||
withContext(Dispatchers.Main){
|
||||
listAdapter.submitList(items.toList())
|
||||
listAdapter.notifyDataSetChanged()
|
||||
updateFileSize(items.map { it.format.filesize })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onContinueOnBackground() {
|
||||
requireActivity().lifecycleScope.launch {
|
||||
val ids = mutableListOf<Long>()
|
||||
|
|
@ -637,7 +656,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
lifecycleScope.launch{
|
||||
val downloadItem = items.find { it.url == itemURL }
|
||||
val resultItem = withContext(Dispatchers.IO){
|
||||
resultViewModel.getItemByURL(downloadItem!!.url)
|
||||
resultViewModel.getItemByURL(downloadItem!!.url)!!
|
||||
}
|
||||
if (parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet") == null){
|
||||
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, downloadItem!!, this@DownloadMultipleBottomSheetDialog)
|
||||
|
|
|
|||
|
|
@ -210,12 +210,17 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
|||
val chosenFormat = downloadItem.format
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
|
||||
val listener = object : OnFormatClickListener {
|
||||
override fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>) {
|
||||
override fun onFormatClick(item: List<FormatTuple>) {
|
||||
downloadItem.format = item.first().format
|
||||
item.first().audioFormats?.map { it.format_id }?.let {
|
||||
downloadItem.videoPreferences.audioFormatIDs.clear()
|
||||
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
||||
}
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, item.first().audioFormats)
|
||||
|
||||
}
|
||||
|
||||
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO){
|
||||
resultItem?.formats?.removeAll(formats)
|
||||
|
|
@ -230,8 +235,11 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
|||
}
|
||||
}
|
||||
formats = allFormats.first().filter { !genericVideoFormats.contains(it) }.toMutableList()
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, item.first().audioFormats)
|
||||
val preferredFormat = downloadViewModel.getFormat(formats, Type.video)
|
||||
val preferredAudioFormats = downloadViewModel.getPreferredAudioFormats(formats)
|
||||
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, formats.filter { preferredAudioFormats.contains(it.format_id) })
|
||||
}
|
||||
|
||||
}
|
||||
formatCard.setOnClickListener{
|
||||
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
|||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
|
@ -48,6 +50,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
|
||||
private lateinit var continueInBackgroundSnackBar : Snackbar
|
||||
private lateinit var view: View
|
||||
private var updateFormatsJob: Job? = null
|
||||
|
||||
enum class FormatSorting {
|
||||
filesize, container, codec, id
|
||||
|
|
@ -88,7 +91,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
okBtn = view.findViewById(R.id.format_ok)
|
||||
|
||||
shimmers.visibility = View.GONE
|
||||
val hasGenericFormats = formats.first().isEmpty() || formats.last().any { it.format_id == "best" }
|
||||
val hasGenericFormats = formats.first().isEmpty() || formats.last().any { it.format_id == "best" || it.format_id == "ba" }
|
||||
if (items.size > 1){
|
||||
|
||||
if (!hasGenericFormats){
|
||||
|
|
@ -141,56 +144,77 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
formatListLinearLayout.visibility = View.GONE
|
||||
shimmers.visibility = View.VISIBLE
|
||||
shimmers.startShimmer()
|
||||
updateFormatsJob = launch(Dispatchers.IO) {
|
||||
//simple download
|
||||
if (items.size == 1) {
|
||||
val res = infoUtil.getFormats(items.first()!!.url)
|
||||
res.filter { it.format_note != "storyboard" }
|
||||
chosenFormats = if (items.first()?.type == Type.audio) {
|
||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
} else {
|
||||
res
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
|
||||
//simple download
|
||||
if (items.size == 1){
|
||||
val res = withContext(Dispatchers.IO){
|
||||
infoUtil.getFormats(items.first()!!.url)
|
||||
}
|
||||
res.filter { it.format_note != "storyboard" }
|
||||
chosenFormats = if(items.first()?.type == Type.audio){
|
||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
formats = listOf(res)
|
||||
withContext(Dispatchers.Main){
|
||||
listener.onFormatsUpdated(formats)
|
||||
}
|
||||
//list format filtering
|
||||
}else{
|
||||
res
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
|
||||
formats = listOf(res)
|
||||
|
||||
//list format filtering
|
||||
}else{
|
||||
var progress = "0/${items.size}"
|
||||
formatCollection.clear()
|
||||
refreshBtn.text = progress
|
||||
withContext(Dispatchers.IO){
|
||||
var progress = "0/${items.size}"
|
||||
formatCollection.clear()
|
||||
withContext(Dispatchers.Main) {
|
||||
refreshBtn.text = progress
|
||||
}
|
||||
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
|
||||
lifecycleScope.launch(Dispatchers.Main){
|
||||
progress = "${formatCollection.size}/${items.size}"
|
||||
refreshBtn.text = progress
|
||||
formatCollection.add(it)
|
||||
if (isActive) {
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
formatCollection.add(it)
|
||||
progress = "${formatCollection.size}/${items.size}"
|
||||
refreshBtn.text = progress
|
||||
}
|
||||
}
|
||||
}
|
||||
formats = formatCollection
|
||||
withContext(Dispatchers.Main){
|
||||
listener.onFormatsUpdated(formats)
|
||||
}
|
||||
|
||||
val flatFormatCollection = formatCollection.flatten()
|
||||
val commonFormats =
|
||||
flatFormatCollection.groupingBy { it.format_id }.eachCount()
|
||||
.filter { it.value == items.size }
|
||||
.mapValues { flatFormatCollection.first { f -> f.format_id == it.key } }
|
||||
.map { it.value }
|
||||
chosenFormats = commonFormats.filter { it.filesize != 0L }
|
||||
.mapTo(mutableListOf()) { it.copy() }
|
||||
chosenFormats = when (items.first()?.type) {
|
||||
Type.audio -> chosenFormats.filter {
|
||||
it.format_note.contains(
|
||||
"audio",
|
||||
ignoreCase = true
|
||||
)
|
||||
}
|
||||
|
||||
else -> chosenFormats
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
chosenFormats.forEach {
|
||||
it.filesize =
|
||||
flatFormatCollection.filter { f -> f.format_id == it.format_id }
|
||||
.sumOf { itt -> itt.filesize }
|
||||
}
|
||||
}
|
||||
formats = formatCollection
|
||||
val flatFormatCollection = formatCollection.flatten()
|
||||
val commonFormats = flatFormatCollection.groupingBy { it.format_id }.eachCount().filter { it.value == items.size }.mapValues { flatFormatCollection.first { f -> f.format_id == it.key } }.map { it.value }
|
||||
chosenFormats = commonFormats.filter { it.filesize != 0L }.mapTo(mutableListOf()) {it.copy()}
|
||||
chosenFormats = when(items.first()?.type){
|
||||
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||
else -> chosenFormats
|
||||
}
|
||||
if (chosenFormats.isEmpty()) throw Exception()
|
||||
chosenFormats.forEach {
|
||||
it.filesize =
|
||||
flatFormatCollection.filter { f -> f.format_id == it.format_id }
|
||||
.sumOf { itt -> itt.filesize }
|
||||
withContext(Dispatchers.Main){
|
||||
shimmers.visibility = View.GONE
|
||||
shimmers.stopShimmer()
|
||||
addFormatsToView()
|
||||
refreshBtn.visibility = View.GONE
|
||||
formatListLinearLayout.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
shimmers.visibility = View.GONE
|
||||
shimmers.stopShimmer()
|
||||
addFormatsToView()
|
||||
refreshBtn.visibility = View.GONE
|
||||
formatListLinearLayout.visibility = View.VISIBLE
|
||||
updateFormatsJob?.start()
|
||||
}catch (e: Exception){
|
||||
runCatching {
|
||||
refreshBtn.isEnabled = true
|
||||
|
|
@ -223,7 +247,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
private fun returnFormats(){
|
||||
//simple video format selection
|
||||
if (items.size == 1){
|
||||
listener.onFormatClick(formats, listOf(FormatTuple(selectedVideo, selectedAudios)))
|
||||
listener.onFormatClick(listOf(FormatTuple(selectedVideo, selectedAudios)))
|
||||
}else{
|
||||
//playlist format selection
|
||||
val selectedFormats = mutableListOf<Format>()
|
||||
|
|
@ -235,7 +259,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
selectedFormats.add(selectedVideo)
|
||||
}
|
||||
}
|
||||
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, selectedAudios) })
|
||||
listener.onFormatClick(selectedFormats.map { FormatTuple(it, selectedAudios) })
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -317,7 +341,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
}
|
||||
}else{
|
||||
if (items.size == 1){
|
||||
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
|
||||
listener.onFormatClick(listOf(FormatTuple(format, null)))
|
||||
}else{
|
||||
val selectedFormats = mutableListOf<Format>()
|
||||
formatCollection.forEach {
|
||||
|
|
@ -328,7 +352,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
selectedFormats.add(format)
|
||||
}
|
||||
}
|
||||
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, null) })
|
||||
listener.onFormatClick(selectedFormats.map { FormatTuple(it, null) })
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
|
@ -360,14 +384,16 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
|||
|
||||
private fun cleanUp(){
|
||||
kotlin.runCatching {
|
||||
updateFormatsJob?.cancel()
|
||||
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnFormatClickListener{
|
||||
fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>)
|
||||
fun onFormatClick(selectedFormats: List<FormatTuple>)
|
||||
fun onContinueOnBackground() {}
|
||||
fun onFormatsUpdated(allFormats: List<List<Format>>)
|
||||
}
|
||||
|
||||
class FormatTuple internal constructor(
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
|||
val checkedItems = listAdapter.getCheckedItems()
|
||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
||||
if (checkedResultItems.size == 1){
|
||||
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)
|
||||
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)!!
|
||||
withContext(Dispatchers.Main){
|
||||
findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf(
|
||||
Pair("result", resultItem),
|
||||
|
|
|
|||
|
|
@ -252,12 +252,16 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
if(allhistoryList!!.isEmpty()){
|
||||
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
|
||||
}else{
|
||||
val deleteFile = booleanArrayOf(false)
|
||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||
deleteDialog.setTitle(getString(R.string.confirm_delete_history))
|
||||
deleteDialog.setMessage(getString(R.string.confirm_delete_history_desc))
|
||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||
deleteDialog.setMultiChoiceItems(
|
||||
arrayOf(getString(R.string.delete_files_too)),
|
||||
booleanArrayOf(false)
|
||||
) { _: DialogInterface?, _: Int, b: Boolean -> deleteFile[0] = b }
|
||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||
historyViewModel.deleteAll()
|
||||
historyViewModel.deleteAll(deleteFile[0])
|
||||
}
|
||||
deleteDialog.show()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package com.deniscerri.ytdlnis.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.OvalShape
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
|
||||
import android.net.Uri
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
|
|
@ -15,6 +19,7 @@ import com.neo.highlight.core.Highlight
|
|||
import com.neo.highlight.util.listener.HighlightTextWatcher
|
||||
import com.neo.highlight.util.scheme.ColorScheme
|
||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||
import java.io.File
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
|
|
@ -91,4 +96,14 @@ object Extensions {
|
|||
.setTrackDrawable(drawable)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun File.getMediaDuration(context: Context): Int {
|
||||
if (!exists()) return 0
|
||||
val retriever = MediaMetadataRetriever()
|
||||
retriever.setDataSource(context, Uri.parse(absolutePath))
|
||||
val duration = retriever.extractMetadata(METADATA_KEY_DURATION)
|
||||
retriever.release()
|
||||
|
||||
return duration?.toIntOrNull()?.div(1000) ?: 0
|
||||
}
|
||||
}
|
||||
|
|
@ -247,7 +247,7 @@ object FileUtil {
|
|||
|
||||
fun scanMedia(files: List<String>, context: Context) : List<String> {
|
||||
try {
|
||||
val paths = files.sortedByDescending { File(it).length() }
|
||||
val paths = files.distinct().sortedByDescending { File(it).length() }
|
||||
runCatching {
|
||||
paths.forEach {
|
||||
MediaScannerConnection.scanFile(context, arrayOf(it), null, null)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ 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.CancellationException
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
|
@ -115,7 +116,9 @@ class InfoUtil(private val context: Context) {
|
|||
kotlin.runCatching {
|
||||
val obj = dataArray.getJSONObject(i)
|
||||
val itm = createVideoFromPipedJSON(obj, "https://youtube.com" + obj.getString("url"))
|
||||
itm?.playlistTitle = res.getString("name") + "[${i+1}]"
|
||||
itm?.playlistTitle = res.getString("name")
|
||||
itm?.playlistURL = "https://www.youtube.com/playlist?list=$id"
|
||||
itm?.playlistIndex = (i+1)
|
||||
items.add(itm)
|
||||
}
|
||||
}
|
||||
|
|
@ -432,6 +435,7 @@ class InfoUtil(private val context: Context) {
|
|||
getFormatsFromYTDL(url)
|
||||
}
|
||||
}catch (e: Exception){
|
||||
if (e is CancellationException) throw e
|
||||
getFormatsFromYTDL(url)
|
||||
}
|
||||
}
|
||||
|
|
@ -607,7 +611,9 @@ class InfoUtil(private val context: Context) {
|
|||
youtubeDLResponse.out.split(lineSeparator!!)
|
||||
} catch (e: Exception) {
|
||||
listOf(youtubeDLResponse.out)
|
||||
}.filter { it.isNotBlank() }
|
||||
}.filter { it.isNotBlank() }.apply {
|
||||
if (this.isEmpty()) throw Exception("Command Used: \n yt-dlp ${parseYTDLRequestString(request)}")
|
||||
}
|
||||
for (result in results) {
|
||||
if (result.isNullOrBlank()) continue
|
||||
val jsonObject = JSONObject(result)
|
||||
|
|
@ -641,12 +647,17 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
val website = jsonObject.getString(listOf("ie_key", "extractor_key", "extractor").first { jsonObject.has(it) })
|
||||
|
||||
var playlistTitle: String? = ""
|
||||
var playlistURL: String? = ""
|
||||
var playlistIndex: Int? = null
|
||||
|
||||
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
|
||||
if(playlistTitle?.removeSurrounding("\"").equals(query)) playlistTitle = ""
|
||||
|
||||
if (playlistTitle?.isNotBlank() == true){
|
||||
playlistTitle += "[${jsonObject.getString("playlist_index")}]"
|
||||
playlistURL = query
|
||||
kotlin.runCatching { playlistIndex = jsonObject.getInt("playlist_index") }
|
||||
}
|
||||
|
||||
val formatsInJSON = if (jsonObject.has("formats") && jsonObject.get("formats") is JSONArray) jsonObject.getJSONArray("formats") else null
|
||||
|
|
@ -692,7 +703,9 @@ class InfoUtil(private val context: Context) {
|
|||
playlistTitle!!,
|
||||
formats,
|
||||
urls,
|
||||
chapters
|
||||
chapters,
|
||||
playlistURL,
|
||||
playlistIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -814,6 +827,8 @@ class InfoUtil(private val context: Context) {
|
|||
arrayListOf(),
|
||||
"",
|
||||
arrayListOf(),
|
||||
"",
|
||||
null,
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
|
|
@ -961,8 +976,15 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
|
||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
|
||||
val url = downloadItem.url
|
||||
val request = YoutubeDLRequest(url)
|
||||
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || downloadItem.playlistIndex == null){
|
||||
YoutubeDLRequest(downloadItem.url)
|
||||
}else{
|
||||
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
||||
addOption("--playlist-start", downloadItem.playlistIndex!!)
|
||||
addOption("--playlist-end", downloadItem.playlistIndex!!)
|
||||
}
|
||||
}
|
||||
|
||||
val type = downloadItem.type
|
||||
|
||||
val downDir : File
|
||||
|
|
@ -996,6 +1018,12 @@ class InfoUtil(private val context: Context) {
|
|||
val limitRate = sharedPreferences.getString("limit_rate", "")!!
|
||||
if (limitRate.isNotBlank()) request.addOption("-r", limitRate)
|
||||
|
||||
val bufferSize = sharedPreferences.getString("buffer_size", "")!!
|
||||
if (bufferSize.isNotBlank()){
|
||||
request.addOption("--buffer-size", bufferSize)
|
||||
request.addOption("--no-resize-buffer")
|
||||
}
|
||||
|
||||
val sponsorblockURL = sharedPreferences.getString("sponsorblock_url", "")!!
|
||||
if (sponsorblockURL.isNotBlank()) request.addOption("--sponsorblock-api", sponsorblockURL)
|
||||
|
||||
|
|
@ -1019,6 +1047,8 @@ class InfoUtil(private val context: Context) {
|
|||
|
||||
val preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!!
|
||||
val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
|
||||
val embedMetadata = sharedPreferences.getBoolean("embed_metadata", true)
|
||||
var filenameTemplate = downloadItem.customFileNameTemplate
|
||||
|
||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||
request.addOption("--trim-filenames", downDir.absolutePath.length + 117)
|
||||
|
|
@ -1075,9 +1105,9 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--force-keyframes-at-cuts")
|
||||
}
|
||||
}
|
||||
downloadItem.customFileNameTemplate += " %(section_title|)s "
|
||||
filenameTemplate += " %(section_title|)s "
|
||||
if (downloadItem.downloadSections.split(";").size > 1){
|
||||
downloadItem.customFileNameTemplate += "%(autonumber)s"
|
||||
filenameTemplate += "%(autonumber)s"
|
||||
}
|
||||
request.addOption("--output-na-placeholder", " ")
|
||||
}
|
||||
|
|
@ -1102,18 +1132,14 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--write-description")
|
||||
}
|
||||
|
||||
downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader,channel)s")
|
||||
filenameTemplate = filenameTemplate.replace("%(uploader)s", "%(uploader,channel)s")
|
||||
}
|
||||
|
||||
if (downloadItem.playlistTitle.isNotBlank()){
|
||||
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
|
||||
request.addOption("--parse-metadata","${downloadItem.playlistTitle}:%(playlist)s")
|
||||
runCatching {
|
||||
request.addOption("--parse-metadata",
|
||||
downloadItem.playlistTitle
|
||||
.substring(
|
||||
downloadItem.playlistTitle.indexOf("[") + 1,
|
||||
downloadItem.playlistTitle.indexOf("]"),
|
||||
) + ":%(playlist_index)s"
|
||||
downloadItem.playlistIndex.toString() + ":%(playlist_index)s"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1149,7 +1175,7 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
||||
}else{
|
||||
|
||||
if (sharedPreferences.getBoolean("embed_metadata", true)){
|
||||
if (embedMetadata){
|
||||
request.addOption("--embed-metadata")
|
||||
|
||||
request.addOption("--parse-metadata", "%(release_year,release_date>%Y,upload_date>%Y)s:%(meta_date)s")
|
||||
|
|
@ -1180,8 +1206,8 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
if (downloadItem.customFileNameTemplate.isNotBlank()){
|
||||
request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
||||
if (filenameTemplate.isNotBlank()){
|
||||
request.addOption("-o", "${filenameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1355,8 +1381,8 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("--split-chapters")
|
||||
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
||||
}else{
|
||||
if (downloadItem.customFileNameTemplate.isNotBlank()){
|
||||
request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
||||
if (filenameTemplate.isNotBlank()){
|
||||
request.addOption("-o", "${filenameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1407,10 +1433,12 @@ class InfoUtil(private val context: Context) {
|
|||
fun getGenericAudioFormats(resources: Resources) : MutableList<Format>{
|
||||
val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
||||
val audioFormats = resources.getStringArray(R.array.audio_formats)
|
||||
val audioFormatsValues = resources.getStringArray(R.array.audio_formats_values)
|
||||
val formats = mutableListOf<Format>()
|
||||
val containerPreference = sharedPreferences.getString("audio_format", "")
|
||||
val acodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")
|
||||
audioFormats.reversed().forEach { formats.add(Format(it, containerPreference!!,"",acodecPreference!!, "",0, it)) }
|
||||
audioFormats.forEachIndexed { idx, it -> formats.add(Format(audioFormatsValues[idx], containerPreference!!,"",acodecPreference!!, "",0, it)) }
|
||||
audioFormats.reverse()
|
||||
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"",acodecPreference!!, "",1, it)) }
|
||||
return formats
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import java.util.Locale
|
|||
|
||||
class NotificationUtil(var context: Context) {
|
||||
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
|
||||
private val workerNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_WORKER_CHANNEL_ID)
|
||||
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)
|
||||
|
|
@ -97,6 +98,7 @@ class NotificationUtil(var context: Context) {
|
|||
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
|
||||
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
|
||||
DOWNLOAD_FINISHED_CHANNEL_ID -> { return finishedDownloadNotificationBuilder }
|
||||
DOWNLOAD_WORKER_CHANNEL_ID -> { return workerNotificationBuilder }
|
||||
}
|
||||
return downloadNotificationBuilder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ 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.util.Extensions.getMediaDuration
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||
|
|
@ -217,10 +218,36 @@ class DownloadWorker(
|
|||
}
|
||||
}else{
|
||||
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, commandString)
|
||||
historyDao.insert(historyItem)
|
||||
finalPaths?.forEachIndexed {idx, ff ->
|
||||
val file = File(ff)
|
||||
var index = ""
|
||||
var duration = downloadItem.duration
|
||||
if (idx > 0) {
|
||||
index = "[${idx + 1}] "
|
||||
downloadItem.author = file.nameWithoutExtension
|
||||
}
|
||||
val d = file.getMediaDuration(context)
|
||||
if (d > 0) duration = infoUtil.formatIntegerDuration(d, Locale.US)
|
||||
|
||||
downloadItem.format.filesize = file.length()
|
||||
downloadItem.format.container = file.extension
|
||||
downloadItem.duration = duration
|
||||
val historyItem = HistoryItem(0,
|
||||
downloadItem.url,
|
||||
index + downloadItem.title,
|
||||
downloadItem.author,
|
||||
downloadItem.duration,
|
||||
downloadItem.thumb,
|
||||
downloadItem.type,
|
||||
unixTime,
|
||||
ff,
|
||||
downloadItem.website,
|
||||
downloadItem.format,
|
||||
downloadItem.id,
|
||||
commandString)
|
||||
historyDao.insert(historyItem)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class UpdatePlaylistFormatsWorker(
|
|||
ids.forEach {
|
||||
if (!isStopped){
|
||||
val d = dao.getDownloadById(it)
|
||||
val r = resDao.getResultByURL(d.url)
|
||||
val r = resDao.getResultByURL(d.url)!!
|
||||
|
||||
if (d.allFormats.isNotEmpty()){
|
||||
count++
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M5,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4L1,6v6h6L7,6L5,6L5,2zM9,16c0,1.3 0.84,2.4 2,2.82L11,23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2L9,14v2zM1,16c0,1.3 0.84,2.4 2,2.82L3,23h2v-4.18C6.16,18.4 7,17.3 7,16v-2L1,14v2zM21,6L21,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4h-2v6h6L23,6h-2zM13,2c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v4L9,6v6h6L15,6h-2L13,2zM17,16c0,1.3 0.84,2.4 2,2.82L19,23h2v-4.18c1.16,-0.41 2,-1.51 2,-2.82v-2h-6v2z"/>
|
||||
</vector>
|
||||
|
|
@ -62,6 +62,11 @@
|
|||
<item>best</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="audio_formats_values">
|
||||
<item>wa</item>
|
||||
<item>ba</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="format_ordering">
|
||||
<item>@string/file_size</item>
|
||||
<item>@string/container</item>
|
||||
|
|
@ -222,33 +227,29 @@
|
|||
<array name="search_engines">
|
||||
<item>YouTube</item>
|
||||
<item>YouTube Music</item>
|
||||
<item>Soundcloud</item>
|
||||
<item>Bilibili</item>
|
||||
<item>Nico video</item>
|
||||
<item>PRX Series</item>
|
||||
<item>PRX Stories</item>
|
||||
<item>Rokfin</item>
|
||||
<item>Yahoo screen</item>
|
||||
<item>Soundcloud</item>
|
||||
<item>Google Video</item>
|
||||
<item>Bilibili</item>
|
||||
<item>Netverse</item>
|
||||
<item>All Clips of a trovo.live channel</item>
|
||||
<item>All VODs of a trovo.live channel</item>
|
||||
</array>
|
||||
|
||||
<array name="search_engines_values">
|
||||
<item>ytsearch</item>
|
||||
<item>ytsearchmusic</item>
|
||||
<item>scsearch</item>
|
||||
<item>bilisearch</item>
|
||||
<item>nicosearch</item>
|
||||
<item>PRX Series</item>
|
||||
<item>PRX Stories</item>
|
||||
<item>prxseries</item>
|
||||
<item>prxstories</item>
|
||||
<item>rkfnsearch</item>
|
||||
<item>yvsearch</item>
|
||||
<item>scsearch</item>
|
||||
<item>gvsearch</item>
|
||||
<item>bilisearch</item>
|
||||
<item>netsearch</item>
|
||||
<item>trovoclip</item>
|
||||
<item>trovovod</item>
|
||||
</array>
|
||||
|
||||
<array name="start_destination">
|
||||
|
|
|
|||
|
|
@ -323,4 +323,6 @@
|
|||
<string name="export_file">Export File</string>
|
||||
<string name="ytdl_source">yt-dlp Source</string>
|
||||
<string name="update_ytdl_master">Master Version of yt-dlp</string>
|
||||
<string name="buffer_size">Buffer Size</string>
|
||||
<string name="buffer_size_summary">Size of download buffer, e.g. 1024 or 16K (default is 1024)</string>
|
||||
</resources>
|
||||
|
|
@ -99,6 +99,12 @@
|
|||
app:summary="@string/limit_rate_summary"
|
||||
app:title="@string/limit_rate" />
|
||||
|
||||
<EditTextPreference
|
||||
android:icon="@drawable/baseline_settings_input_component_24"
|
||||
app:key="buffer_size"
|
||||
app:summary="@string/buffer_size_summary"
|
||||
app:title="@string/buffer_size" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
app:defaultValue="false"
|
||||
|
|
|
|||
Loading…
Reference in a new issue