.
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,
|
LogItem::class,
|
||||||
TerminalItem::class
|
TerminalItem::class
|
||||||
],
|
],
|
||||||
version = 12,
|
version = 13,
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
AutoMigration (from = 1, to = 2),
|
AutoMigration (from = 1, to = 2),
|
||||||
AutoMigration (from = 2, to = 3),
|
AutoMigration (from = 2, to = 3),
|
||||||
|
|
@ -35,7 +35,8 @@ import com.deniscerri.ytdlnis.database.models.*
|
||||||
AutoMigration (from = 8, to = 9),
|
AutoMigration (from = 8, to = 9),
|
||||||
AutoMigration (from = 9, to = 10),
|
AutoMigration (from = 9, to = 10),
|
||||||
AutoMigration (from = 10, to = 11),
|
AutoMigration (from = 10, to = 11),
|
||||||
AutoMigration (from = 11, to = 12)
|
AutoMigration (from = 11, to = 12),
|
||||||
|
AutoMigration (from = 12, to = 13)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
abstract class DBManager : RoomDatabase(){
|
abstract class DBManager : RoomDatabase(){
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
|
@Query("SELECT * FROM downloads WHERE status in('Active','Queued','QueuedPaused','ActivePaused','PausedReQueued')")
|
||||||
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
|
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
|
fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||||
|
|
||||||
|
|
@ -55,6 +56,7 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
@Query("SELECT * FROM downloads WHERE status in('Queued','QueuedPaused') ORDER BY downloadStartTime, id")
|
||||||
fun getQueuedDownloadsList() : List<DownloadItem>
|
fun getQueuedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Cancelled' ORDER BY id DESC")
|
||||||
fun getCancelledDownloads() : PagingSource<Int, DownloadItemSimple>
|
fun getCancelledDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||||
|
|
||||||
|
|
@ -64,12 +66,14 @@ interface DownloadDao {
|
||||||
@Query("SELECT * FROM downloads WHERE status LIKE '%Paused%'")
|
@Query("SELECT * FROM downloads WHERE status LIKE '%Paused%'")
|
||||||
fun getPausedDownloadsList() : List<DownloadItem>
|
fun getPausedDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
|
fun getErroredDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||||
|
|
||||||
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC")
|
||||||
fun getErroredDownloadsList() : List<DownloadItem>
|
fun getErroredDownloadsList() : List<DownloadItem>
|
||||||
|
|
||||||
|
@RewriteQueriesToDropUnusedColumns
|
||||||
@Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC")
|
@Query("SELECT * FROM downloads WHERE status='Saved' ORDER BY id DESC")
|
||||||
fun getSavedDownloads() : PagingSource<Int, DownloadItemSimple>
|
fun getSavedDownloads() : PagingSource<Int, DownloadItemSimple>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,6 @@ interface ResultDao {
|
||||||
suspend fun delete(id: Long)
|
suspend fun delete(id: Long)
|
||||||
|
|
||||||
@Query("SELECT * FROM results WHERE url=:url LIMIT 1")
|
@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,
|
var status: String,
|
||||||
@ColumnInfo(defaultValue = "0")
|
@ColumnInfo(defaultValue = "0")
|
||||||
var downloadStartTime: Long,
|
var downloadStartTime: Long,
|
||||||
var logID: Long?
|
var logID: Long?,
|
||||||
|
@ColumnInfo(defaultValue = "")
|
||||||
|
var playlistURL: String? = "",
|
||||||
|
@ColumnInfo(defaultValue = "")
|
||||||
|
var playlistIndex: Int? = null
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
@ -22,5 +22,9 @@ data class ResultItem(
|
||||||
@ColumnInfo(defaultValue = "")
|
@ColumnInfo(defaultValue = "")
|
||||||
var urls: String,
|
var urls: String,
|
||||||
var chapters: MutableList<ChapterItem>?,
|
var chapters: MutableList<ChapterItem>?,
|
||||||
|
@ColumnInfo(defaultValue = "")
|
||||||
|
var playlistURL: String? = "",
|
||||||
|
@ColumnInfo(defaultValue = "")
|
||||||
|
var playlistIndex: Int? = null,
|
||||||
var creationTime: Long = System.currentTimeMillis() / 1000,
|
var creationTime: Long = System.currentTimeMillis() / 1000,
|
||||||
) : Parcelable
|
) : 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()
|
historyDao.deleteAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
deleteAll()
|
deleteAll()
|
||||||
itemCount.value = v.size
|
itemCount.value = v.size
|
||||||
}else{
|
}else{
|
||||||
v.forEach { it?.playlistTitle = "ytdlnis-Search" }
|
v.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it?.playlistTitle = "ytdlnis-Search" }
|
||||||
}
|
}
|
||||||
resultDao.insertMultiple(v)
|
resultDao.insertMultiple(v)
|
||||||
return ArrayList(v)
|
return ArrayList(v)
|
||||||
|
|
@ -77,7 +77,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
deleteAll()
|
deleteAll()
|
||||||
itemCount.value = items.size
|
itemCount.value = items.size
|
||||||
}else{
|
}else{
|
||||||
items.forEach { it!!.playlistTitle = "ytdlnis-Search" }
|
items.filter { it?.playlistTitle.isNullOrBlank() }.forEach { it!!.playlistTitle = "ytdlnis-Search" }
|
||||||
}
|
}
|
||||||
resultDao.insertMultiple(items.toList())
|
resultDao.insertMultiple(items.toList())
|
||||||
return items
|
return items
|
||||||
|
|
@ -96,7 +96,7 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
|
||||||
resultDao.update(item)
|
resultDao.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemByURL(url: String): ResultItem {
|
fun getItemByURL(url: String): ResultItem? {
|
||||||
return resultDao.getResultByURL(url)
|
return resultDao.getResultByURL(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,15 +152,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
|
|
||||||
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
||||||
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
||||||
Format(
|
infoUtil.getGenericAudioFormats(resources).last()
|
||||||
"best",
|
|
||||||
audioContainer!!,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
0,
|
|
||||||
"best"
|
|
||||||
)
|
|
||||||
}else{
|
}else{
|
||||||
Format(
|
Format(
|
||||||
audioFormatIDPreference.first().split("+").first(),
|
audioFormatIDPreference.first().split("+").first(),
|
||||||
|
|
@ -260,23 +252,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
|
|
||||||
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!))
|
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 preferredAudioFormats = getPreferredAudioFormats(resultItem.formats)
|
||||||
val audioF = getFormat(resultItem.formats, Type.audio)
|
|
||||||
if (!infoUtil.getGenericAudioFormats(resources).contains(audioF)){
|
|
||||||
preferredAudioFormats.add(audioF.format_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val videoPreferences = VideoPreferences(
|
val videoPreferences = VideoPreferences(
|
||||||
embedSubs,
|
embedSubs,
|
||||||
|
|
@ -303,7 +280,15 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
container!!,
|
container!!,
|
||||||
"",
|
"",
|
||||||
resultItem.formats,
|
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,
|
downloadItem.allFormats,
|
||||||
"",
|
"",
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
|
downloadItem.playlistURL,
|
||||||
|
downloadItem.playlistIndex,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -339,6 +326,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
"",
|
"",
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
|
"",
|
||||||
|
null,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -357,6 +346,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
"",
|
"",
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
|
"",
|
||||||
|
null,
|
||||||
System.currentTimeMillis()
|
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 {
|
fun generateCommandFormat(c: CommandTemplate) : Format {
|
||||||
return Format(
|
return Format(
|
||||||
c.title,
|
c.title,
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,8 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
|
||||||
repository.delete(item, deleteFile)
|
repository.delete(item, deleteFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteAll(deleteFile: Boolean = false) = viewModelScope.launch(Dispatchers.IO) {
|
||||||
repository.deleteAll()
|
repository.deleteAll(deleteFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteDuplicates() = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteDuplicates() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
repository.update(item)
|
repository.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getItemByURL(url: String) : ResultItem {
|
fun getItemByURL(url: String) : ResultItem? {
|
||||||
return repository.getItemByURL(url)
|
return repository.getItemByURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,11 +233,12 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
||||||
val formats = infoUtil.getFormats(result.url)
|
val formats = infoUtil.getFormats(result.url)
|
||||||
updatingFormats.emit(false)
|
updatingFormats.emit(false)
|
||||||
if (formats.isNotEmpty() && isActive) {
|
if (formats.isNotEmpty() && isActive) {
|
||||||
val res = getItemByURL(result.url)
|
getItemByURL(result.url)?.apply {
|
||||||
res.formats = formats.toMutableList()
|
this.formats = formats.toMutableList()
|
||||||
update(res)
|
update(this)
|
||||||
|
}
|
||||||
|
updateFormatsResultData.emit(formats.toMutableList())
|
||||||
}
|
}
|
||||||
updateFormatsResultData.emit(formats.toMutableList())
|
|
||||||
}
|
}
|
||||||
updateFormatsResultDataJob?.start()
|
updateFormatsResultDataJob?.start()
|
||||||
updateFormatsResultDataJob?.invokeOnCompletion {
|
updateFormatsResultDataJob?.invokeOnCompletion {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
|
|
||||||
// TITLE ----------------------------------
|
// TITLE ----------------------------------
|
||||||
val videoTitle = card.findViewById<TextView>(R.id.result_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) {
|
if (title.length > 100) {
|
||||||
title = title.substring(0, 40) + "..."
|
title = title.substring(0, 40) + "..."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -198,24 +198,27 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
||||||
val chosenFormat = downloadItem.format
|
val chosenFormat = downloadItem.format
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, null)
|
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, null)
|
||||||
val listener = object : OnFormatClickListener {
|
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
|
downloadItem.format = item.first().format
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, null)
|
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, null)
|
||||||
lifecycleScope.launch {
|
}
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
resultItem?.formats?.removeAll(formats.toSet())
|
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||||
resultItem?.formats?.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
if (resultItem != null){
|
resultItem?.formats?.removeAll(formats.toSet())
|
||||||
resultViewModel.update(resultItem!!)
|
resultItem?.formats?.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
|
||||||
kotlin.runCatching {
|
if (resultItem != null){
|
||||||
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
resultViewModel.update(resultItem!!)
|
||||||
f1.updateUI(resultItem)
|
kotlin.runCatching {
|
||||||
}
|
val f1 = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
||||||
|
f1.updateUI(resultItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList()
|
formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList()
|
||||||
formats.removeAll(genericAudioFormats)
|
formats.removeAll(genericAudioFormats)
|
||||||
|
val preferredFormat = downloadViewModel.getFormat(formats, Type.audio)
|
||||||
|
UiUtil.populateFormatCard(requireContext(), formatCard, preferredFormat, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formatCard.setOnClickListener{
|
formatCard.setOnClickListener{
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import android.widget.LinearLayout
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
|
@ -440,20 +441,42 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
delay(500)
|
delay(500)
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
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 {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
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{
|
}else{
|
||||||
runCatching {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
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 {
|
runCatching {
|
||||||
val f1 = fragmentManager.findFragmentByTag("f1") as DownloadVideoFragment
|
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){
|
when(viewPager2.currentItem){
|
||||||
0 -> {
|
0 -> {
|
||||||
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
kotlin.runCatching {
|
||||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
val f = fragmentManager?.findFragmentByTag("f0") as DownloadAudioFragment
|
||||||
|
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
val f = fragmentManager?.findFragmentByTag("f1") as DownloadVideoFragment
|
kotlin.runCatching {
|
||||||
f.updateTitleAuthor(prevDownloadItem.title, prevDownloadItem.author)
|
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 -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
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 fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
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 {
|
val formatListener = object : OnFormatClickListener {
|
||||||
override fun onFormatClick(allFormats: List<List<Format>>, selectedFormats: List<FormatTuple>) {
|
override fun onFormatClick(selectedFormats: List<FormatTuple>) {
|
||||||
val formatCollection = mutableListOf<List<Format>>()
|
|
||||||
allFormats.forEach {f ->
|
|
||||||
formatCollection.add(f.mapTo(mutableListOf()) {it.copy()})
|
|
||||||
}
|
|
||||||
items.forEachIndexed { index, i ->
|
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
|
i.format = selectedFormats[index].format
|
||||||
if (i.type == DownloadViewModel.Type.video) selectedFormats[index].audioFormats?.map { it.format_id }?.let { i.videoPreferences.audioFormatIDs.addAll(it) }
|
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() {
|
override fun onContinueOnBackground() {
|
||||||
requireActivity().lifecycleScope.launch {
|
requireActivity().lifecycleScope.launch {
|
||||||
val ids = mutableListOf<Long>()
|
val ids = mutableListOf<Long>()
|
||||||
|
|
@ -637,7 +656,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
lifecycleScope.launch{
|
lifecycleScope.launch{
|
||||||
val downloadItem = items.find { it.url == itemURL }
|
val downloadItem = items.find { it.url == itemURL }
|
||||||
val resultItem = withContext(Dispatchers.IO){
|
val resultItem = withContext(Dispatchers.IO){
|
||||||
resultViewModel.getItemByURL(downloadItem!!.url)
|
resultViewModel.getItemByURL(downloadItem!!.url)!!
|
||||||
}
|
}
|
||||||
if (parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet") == null){
|
if (parentFragmentManager.findFragmentByTag("configureDownloadSingleSheet") == null){
|
||||||
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, downloadItem!!, this@DownloadMultipleBottomSheetDialog)
|
val bottomSheet = ConfigureDownloadBottomSheetDialog(resultItem, downloadItem!!, this@DownloadMultipleBottomSheetDialog)
|
||||||
|
|
|
||||||
|
|
@ -210,12 +210,17 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
val chosenFormat = downloadItem.format
|
val chosenFormat = downloadItem.format
|
||||||
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
|
UiUtil.populateFormatCard(requireContext(), formatCard, chosenFormat, downloadItem.allFormats.filter { downloadItem.videoPreferences.audioFormatIDs.contains(it.format_id) })
|
||||||
val listener = object : OnFormatClickListener {
|
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
|
downloadItem.format = item.first().format
|
||||||
item.first().audioFormats?.map { it.format_id }?.let {
|
item.first().audioFormats?.map { it.format_id }?.let {
|
||||||
downloadItem.videoPreferences.audioFormatIDs.clear()
|
downloadItem.videoPreferences.audioFormatIDs.clear()
|
||||||
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
||||||
}
|
}
|
||||||
|
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, item.first().audioFormats)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFormatsUpdated(allFormats: List<List<Format>>) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
resultItem?.formats?.removeAll(formats)
|
resultItem?.formats?.removeAll(formats)
|
||||||
|
|
@ -230,8 +235,11 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formats = allFormats.first().filter { !genericVideoFormats.contains(it) }.toMutableList()
|
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{
|
formatCard.setOnClickListener{
|
||||||
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
|
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.card.MaterialCardView
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -48,6 +50,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
|
|
||||||
private lateinit var continueInBackgroundSnackBar : Snackbar
|
private lateinit var continueInBackgroundSnackBar : Snackbar
|
||||||
private lateinit var view: View
|
private lateinit var view: View
|
||||||
|
private var updateFormatsJob: Job? = null
|
||||||
|
|
||||||
enum class FormatSorting {
|
enum class FormatSorting {
|
||||||
filesize, container, codec, id
|
filesize, container, codec, id
|
||||||
|
|
@ -88,7 +91,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
okBtn = view.findViewById(R.id.format_ok)
|
okBtn = view.findViewById(R.id.format_ok)
|
||||||
|
|
||||||
shimmers.visibility = View.GONE
|
shimmers.visibility = View.GONE
|
||||||
val hasGenericFormats = 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 (items.size > 1){
|
||||||
|
|
||||||
if (!hasGenericFormats){
|
if (!hasGenericFormats){
|
||||||
|
|
@ -141,56 +144,77 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
formatListLinearLayout.visibility = View.GONE
|
formatListLinearLayout.visibility = View.GONE
|
||||||
shimmers.visibility = View.VISIBLE
|
shimmers.visibility = View.VISIBLE
|
||||||
shimmers.startShimmer()
|
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
|
formats = listOf(res)
|
||||||
if (items.size == 1){
|
withContext(Dispatchers.Main){
|
||||||
val res = withContext(Dispatchers.IO){
|
listener.onFormatsUpdated(formats)
|
||||||
infoUtil.getFormats(items.first()!!.url)
|
}
|
||||||
}
|
//list format filtering
|
||||||
res.filter { it.format_note != "storyboard" }
|
|
||||||
chosenFormats = if(items.first()?.type == Type.audio){
|
|
||||||
res.filter { it.format_note.contains("audio", ignoreCase = true) }
|
|
||||||
}else{
|
}else{
|
||||||
res
|
var progress = "0/${items.size}"
|
||||||
}
|
formatCollection.clear()
|
||||||
if (chosenFormats.isEmpty()) throw Exception()
|
withContext(Dispatchers.Main) {
|
||||||
|
refreshBtn.text = progress
|
||||||
formats = listOf(res)
|
}
|
||||||
|
|
||||||
//list format filtering
|
|
||||||
}else{
|
|
||||||
var progress = "0/${items.size}"
|
|
||||||
formatCollection.clear()
|
|
||||||
refreshBtn.text = progress
|
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
|
infoUtil.getFormatsMultiple(items.map { it!!.url }) {
|
||||||
lifecycleScope.launch(Dispatchers.Main){
|
if (isActive) {
|
||||||
progress = "${formatCollection.size}/${items.size}"
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
refreshBtn.text = progress
|
formatCollection.add(it)
|
||||||
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
|
withContext(Dispatchers.Main){
|
||||||
val flatFormatCollection = formatCollection.flatten()
|
shimmers.visibility = View.GONE
|
||||||
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 }
|
shimmers.stopShimmer()
|
||||||
chosenFormats = commonFormats.filter { it.filesize != 0L }.mapTo(mutableListOf()) {it.copy()}
|
addFormatsToView()
|
||||||
chosenFormats = when(items.first()?.type){
|
refreshBtn.visibility = View.GONE
|
||||||
Type.audio -> chosenFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
|
formatListLinearLayout.visibility = View.VISIBLE
|
||||||
else -> chosenFormats
|
|
||||||
}
|
|
||||||
if (chosenFormats.isEmpty()) throw Exception()
|
|
||||||
chosenFormats.forEach {
|
|
||||||
it.filesize =
|
|
||||||
flatFormatCollection.filter { f -> f.format_id == it.format_id }
|
|
||||||
.sumOf { itt -> itt.filesize }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shimmers.visibility = View.GONE
|
updateFormatsJob?.start()
|
||||||
shimmers.stopShimmer()
|
|
||||||
addFormatsToView()
|
|
||||||
refreshBtn.visibility = View.GONE
|
|
||||||
formatListLinearLayout.visibility = View.VISIBLE
|
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
runCatching {
|
runCatching {
|
||||||
refreshBtn.isEnabled = true
|
refreshBtn.isEnabled = true
|
||||||
|
|
@ -223,7 +247,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
private fun returnFormats(){
|
private fun returnFormats(){
|
||||||
//simple video format selection
|
//simple video format selection
|
||||||
if (items.size == 1){
|
if (items.size == 1){
|
||||||
listener.onFormatClick(formats, listOf(FormatTuple(selectedVideo, selectedAudios)))
|
listener.onFormatClick(listOf(FormatTuple(selectedVideo, selectedAudios)))
|
||||||
}else{
|
}else{
|
||||||
//playlist format selection
|
//playlist format selection
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
|
|
@ -235,7 +259,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
selectedFormats.add(selectedVideo)
|
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{
|
}else{
|
||||||
if (items.size == 1){
|
if (items.size == 1){
|
||||||
listener.onFormatClick(formats, listOf(FormatTuple(format, null)))
|
listener.onFormatClick(listOf(FormatTuple(format, null)))
|
||||||
}else{
|
}else{
|
||||||
val selectedFormats = mutableListOf<Format>()
|
val selectedFormats = mutableListOf<Format>()
|
||||||
formatCollection.forEach {
|
formatCollection.forEach {
|
||||||
|
|
@ -328,7 +352,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
selectedFormats.add(format)
|
selectedFormats.add(format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.onFormatClick(formats, selectedFormats.map { FormatTuple(it, null) })
|
listener.onFormatClick(selectedFormats.map { FormatTuple(it, null) })
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
@ -360,14 +384,16 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, p
|
||||||
|
|
||||||
private fun cleanUp(){
|
private fun cleanUp(){
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
|
updateFormatsJob?.cancel()
|
||||||
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
|
parentFragmentManager.beginTransaction().remove(parentFragmentManager.findFragmentByTag("formatSheet")!!).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnFormatClickListener{
|
interface OnFormatClickListener{
|
||||||
fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>)
|
fun onFormatClick(selectedFormats: List<FormatTuple>)
|
||||||
fun onContinueOnBackground() {}
|
fun onContinueOnBackground() {}
|
||||||
|
fun onFormatsUpdated(allFormats: List<List<Format>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
class FormatTuple internal constructor(
|
class FormatTuple internal constructor(
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
val checkedItems = listAdapter.getCheckedItems()
|
val checkedItems = listAdapter.getCheckedItems()
|
||||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
||||||
if (checkedResultItems.size == 1){
|
if (checkedResultItems.size == 1){
|
||||||
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)
|
val resultItem = resultViewModel.getItemByURL(checkedResultItems[0]!!.url)!!
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf(
|
findNavController().navigate(R.id.action_selectPlaylistItemsDialog_to_downloadBottomSheetDialog, bundleOf(
|
||||||
Pair("result", resultItem),
|
Pair("result", resultItem),
|
||||||
|
|
|
||||||
|
|
@ -252,12 +252,16 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
||||||
if(allhistoryList!!.isEmpty()){
|
if(allhistoryList!!.isEmpty()){
|
||||||
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
|
||||||
}else{
|
}else{
|
||||||
|
val deleteFile = booleanArrayOf(false)
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
val deleteDialog = MaterialAlertDialogBuilder(fragmentContext!!)
|
||||||
deleteDialog.setTitle(getString(R.string.confirm_delete_history))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setMessage(getString(R.string.confirm_delete_history_desc))
|
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.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
historyViewModel.deleteAll()
|
historyViewModel.deleteAll(deleteFile[0])
|
||||||
}
|
}
|
||||||
deleteDialog.show()
|
deleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package com.deniscerri.ytdlnis.util
|
package com.deniscerri.ytdlnis.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ShapeDrawable
|
import android.graphics.drawable.ShapeDrawable
|
||||||
import android.graphics.drawable.shapes.OvalShape
|
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.util.DisplayMetrics
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
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.listener.HighlightTextWatcher
|
||||||
import com.neo.highlight.util.scheme.ColorScheme
|
import com.neo.highlight.util.scheme.ColorScheme
|
||||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||||
|
import java.io.File
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -91,4 +96,14 @@ object Extensions {
|
||||||
.setTrackDrawable(drawable)
|
.setTrackDrawable(drawable)
|
||||||
.build()
|
.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> {
|
fun scanMedia(files: List<String>, context: Context) : List<String> {
|
||||||
try {
|
try {
|
||||||
val paths = files.sortedByDescending { File(it).length() }
|
val paths = files.distinct().sortedByDescending { File(it).length() }
|
||||||
runCatching {
|
runCatching {
|
||||||
paths.forEach {
|
paths.forEach {
|
||||||
MediaScannerConnection.scanFile(context, arrayOf(it), null, null)
|
MediaScannerConnection.scanFile(context, arrayOf(it), null, null)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.yausername.youtubedl_android.YoutubeDL
|
import com.yausername.youtubedl_android.YoutubeDL
|
||||||
import com.yausername.youtubedl_android.YoutubeDLRequest
|
import com.yausername.youtubedl_android.YoutubeDLRequest
|
||||||
|
import kotlinx.coroutines.CancellationException
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -115,7 +116,9 @@ class InfoUtil(private val context: Context) {
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
val obj = dataArray.getJSONObject(i)
|
val obj = dataArray.getJSONObject(i)
|
||||||
val itm = createVideoFromPipedJSON(obj, "https://youtube.com" + obj.getString("url"))
|
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)
|
items.add(itm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -432,6 +435,7 @@ class InfoUtil(private val context: Context) {
|
||||||
getFormatsFromYTDL(url)
|
getFormatsFromYTDL(url)
|
||||||
}
|
}
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
|
if (e is CancellationException) throw e
|
||||||
getFormatsFromYTDL(url)
|
getFormatsFromYTDL(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -607,7 +611,9 @@ class InfoUtil(private val context: Context) {
|
||||||
youtubeDLResponse.out.split(lineSeparator!!)
|
youtubeDLResponse.out.split(lineSeparator!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
listOf(youtubeDLResponse.out)
|
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) {
|
for (result in results) {
|
||||||
if (result.isNullOrBlank()) continue
|
if (result.isNullOrBlank()) continue
|
||||||
val jsonObject = JSONObject(result)
|
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) })
|
val website = jsonObject.getString(listOf("ie_key", "extractor_key", "extractor").first { jsonObject.has(it) })
|
||||||
|
|
||||||
var playlistTitle: String? = ""
|
var playlistTitle: String? = ""
|
||||||
|
var playlistURL: String? = ""
|
||||||
|
var playlistIndex: Int? = null
|
||||||
|
|
||||||
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
|
if (jsonObject.has("playlist_title")) playlistTitle = jsonObject.getString("playlist_title")
|
||||||
if(playlistTitle?.removeSurrounding("\"").equals(query)) playlistTitle = ""
|
if(playlistTitle?.removeSurrounding("\"").equals(query)) playlistTitle = ""
|
||||||
|
|
||||||
if (playlistTitle?.isNotBlank() == true){
|
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
|
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!!,
|
playlistTitle!!,
|
||||||
formats,
|
formats,
|
||||||
urls,
|
urls,
|
||||||
chapters
|
chapters,
|
||||||
|
playlistURL,
|
||||||
|
playlistIndex
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -814,6 +827,8 @@ class InfoUtil(private val context: Context) {
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
"",
|
"",
|
||||||
arrayListOf(),
|
arrayListOf(),
|
||||||
|
"",
|
||||||
|
null,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -961,8 +976,15 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
|
fun buildYoutubeDLRequest(downloadItem: DownloadItem) : YoutubeDLRequest{
|
||||||
val url = downloadItem.url
|
val request = if (downloadItem.playlistURL.isNullOrBlank() || downloadItem.playlistTitle.isBlank() || downloadItem.playlistIndex == null){
|
||||||
val request = YoutubeDLRequest(url)
|
YoutubeDLRequest(downloadItem.url)
|
||||||
|
}else{
|
||||||
|
YoutubeDLRequest(downloadItem.playlistURL!!).apply {
|
||||||
|
addOption("--playlist-start", downloadItem.playlistIndex!!)
|
||||||
|
addOption("--playlist-end", downloadItem.playlistIndex!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val type = downloadItem.type
|
val type = downloadItem.type
|
||||||
|
|
||||||
val downDir : File
|
val downDir : File
|
||||||
|
|
@ -996,6 +1018,12 @@ class InfoUtil(private val context: Context) {
|
||||||
val limitRate = sharedPreferences.getString("limit_rate", "")!!
|
val limitRate = sharedPreferences.getString("limit_rate", "")!!
|
||||||
if (limitRate.isNotBlank()) request.addOption("-r", limitRate)
|
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", "")!!
|
val sponsorblockURL = sharedPreferences.getString("sponsorblock_url", "")!!
|
||||||
if (sponsorblockURL.isNotBlank()) request.addOption("--sponsorblock-api", sponsorblockURL)
|
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 preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!!
|
||||||
val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
|
val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
|
||||||
|
val embedMetadata = sharedPreferences.getBoolean("embed_metadata", true)
|
||||||
|
var filenameTemplate = downloadItem.customFileNameTemplate
|
||||||
|
|
||||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||||
request.addOption("--trim-filenames", downDir.absolutePath.length + 117)
|
request.addOption("--trim-filenames", downDir.absolutePath.length + 117)
|
||||||
|
|
@ -1075,9 +1105,9 @@ class InfoUtil(private val context: Context) {
|
||||||
request.addOption("--force-keyframes-at-cuts")
|
request.addOption("--force-keyframes-at-cuts")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downloadItem.customFileNameTemplate += " %(section_title|)s "
|
filenameTemplate += " %(section_title|)s "
|
||||||
if (downloadItem.downloadSections.split(";").size > 1){
|
if (downloadItem.downloadSections.split(";").size > 1){
|
||||||
downloadItem.customFileNameTemplate += "%(autonumber)s"
|
filenameTemplate += "%(autonumber)s"
|
||||||
}
|
}
|
||||||
request.addOption("--output-na-placeholder", " ")
|
request.addOption("--output-na-placeholder", " ")
|
||||||
}
|
}
|
||||||
|
|
@ -1102,18 +1132,14 @@ class InfoUtil(private val context: Context) {
|
||||||
request.addOption("--write-description")
|
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()){
|
if (downloadItem.playlistTitle.isNotBlank()){
|
||||||
request.addOption("--parse-metadata","${downloadItem.playlistTitle.split("[")[0]}:%(playlist)s")
|
request.addOption("--parse-metadata","${downloadItem.playlistTitle}:%(playlist)s")
|
||||||
runCatching {
|
runCatching {
|
||||||
request.addOption("--parse-metadata",
|
request.addOption("--parse-metadata",
|
||||||
downloadItem.playlistTitle
|
downloadItem.playlistIndex.toString() + ":%(playlist_index)s"
|
||||||
.substring(
|
|
||||||
downloadItem.playlistTitle.indexOf("[") + 1,
|
|
||||||
downloadItem.playlistTitle.indexOf("]"),
|
|
||||||
) + ":%(playlist_index)s"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1149,7 +1175,7 @@ class InfoUtil(private val context: Context) {
|
||||||
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
||||||
}else{
|
}else{
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("embed_metadata", true)){
|
if (embedMetadata){
|
||||||
request.addOption("--embed-metadata")
|
request.addOption("--embed-metadata")
|
||||||
|
|
||||||
request.addOption("--parse-metadata", "%(release_year,release_date>%Y,upload_date>%Y)s:%(meta_date)s")
|
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()){
|
if (filenameTemplate.isNotBlank()){
|
||||||
request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
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("--split-chapters")
|
||||||
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
request.addOption("-o", "chapter:%(section_title)s.%(ext)s")
|
||||||
}else{
|
}else{
|
||||||
if (downloadItem.customFileNameTemplate.isNotBlank()){
|
if (filenameTemplate.isNotBlank()){
|
||||||
request.addOption("-o", "${downloadItem.customFileNameTemplate.removeSuffix(".%(ext)s")}.%(ext)s")
|
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>{
|
fun getGenericAudioFormats(resources: Resources) : MutableList<Format>{
|
||||||
val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
||||||
val audioFormats = resources.getStringArray(R.array.audio_formats)
|
val audioFormats = resources.getStringArray(R.array.audio_formats)
|
||||||
|
val audioFormatsValues = resources.getStringArray(R.array.audio_formats_values)
|
||||||
val formats = mutableListOf<Format>()
|
val formats = mutableListOf<Format>()
|
||||||
val containerPreference = sharedPreferences.getString("audio_format", "")
|
val containerPreference = sharedPreferences.getString("audio_format", "")
|
||||||
val acodecPreference = sharedPreferences.getString("audio_codec", "m4a|mp4a|aac")
|
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)) }
|
audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"",acodecPreference!!, "",1, it)) }
|
||||||
return formats
|
return formats
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import java.util.Locale
|
||||||
|
|
||||||
class NotificationUtil(var context: Context) {
|
class NotificationUtil(var context: Context) {
|
||||||
private val downloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_SERVICE_CHANNEL_ID)
|
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 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 finishedDownloadNotificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(context, DOWNLOAD_FINISHED_CHANNEL_ID)
|
||||||
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
|
private val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
|
||||||
|
|
@ -97,6 +98,7 @@ class NotificationUtil(var context: Context) {
|
||||||
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
|
DOWNLOAD_SERVICE_CHANNEL_ID -> { return downloadNotificationBuilder}
|
||||||
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
|
COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID -> { return commandDownloadNotificationBuilder }
|
||||||
DOWNLOAD_FINISHED_CHANNEL_ID -> { return finishedDownloadNotificationBuilder }
|
DOWNLOAD_FINISHED_CHANNEL_ID -> { return finishedDownloadNotificationBuilder }
|
||||||
|
DOWNLOAD_WORKER_CHANNEL_ID -> { return workerNotificationBuilder }
|
||||||
}
|
}
|
||||||
return downloadNotificationBuilder
|
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.models.LogItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdlnis.database.repository.LogRepository
|
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.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
|
|
@ -217,10 +218,36 @@ class DownloadWorker(
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
val unixTime = System.currentTimeMillis() / 1000
|
val unixTime = System.currentTimeMillis() / 1000
|
||||||
val file = File(finalPaths?.first()!!)
|
finalPaths?.forEachIndexed {idx, ff ->
|
||||||
downloadItem.format.filesize = file.length()
|
val file = File(ff)
|
||||||
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)
|
var index = ""
|
||||||
historyDao.insert(historyItem)
|
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 {
|
ids.forEach {
|
||||||
if (!isStopped){
|
if (!isStopped){
|
||||||
val d = dao.getDownloadById(it)
|
val d = dao.getDownloadById(it)
|
||||||
val r = resDao.getResultByURL(d.url)
|
val r = resDao.getResultByURL(d.url)!!
|
||||||
|
|
||||||
if (d.allFormats.isNotEmpty()){
|
if (d.allFormats.isNotEmpty()){
|
||||||
count++
|
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>
|
<item>best</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_formats_values">
|
||||||
|
<item>wa</item>
|
||||||
|
<item>ba</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="format_ordering">
|
<string-array name="format_ordering">
|
||||||
<item>@string/file_size</item>
|
<item>@string/file_size</item>
|
||||||
<item>@string/container</item>
|
<item>@string/container</item>
|
||||||
|
|
@ -222,33 +227,29 @@
|
||||||
<array name="search_engines">
|
<array name="search_engines">
|
||||||
<item>YouTube</item>
|
<item>YouTube</item>
|
||||||
<item>YouTube Music</item>
|
<item>YouTube Music</item>
|
||||||
|
<item>Soundcloud</item>
|
||||||
|
<item>Bilibili</item>
|
||||||
<item>Nico video</item>
|
<item>Nico video</item>
|
||||||
<item>PRX Series</item>
|
<item>PRX Series</item>
|
||||||
<item>PRX Stories</item>
|
<item>PRX Stories</item>
|
||||||
<item>Rokfin</item>
|
<item>Rokfin</item>
|
||||||
<item>Yahoo screen</item>
|
<item>Yahoo screen</item>
|
||||||
<item>Soundcloud</item>
|
|
||||||
<item>Google Video</item>
|
<item>Google Video</item>
|
||||||
<item>Bilibili</item>
|
|
||||||
<item>Netverse</item>
|
<item>Netverse</item>
|
||||||
<item>All Clips of a trovo.live channel</item>
|
|
||||||
<item>All VODs of a trovo.live channel</item>
|
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<array name="search_engines_values">
|
<array name="search_engines_values">
|
||||||
<item>ytsearch</item>
|
<item>ytsearch</item>
|
||||||
<item>ytsearchmusic</item>
|
<item>ytsearchmusic</item>
|
||||||
|
<item>scsearch</item>
|
||||||
|
<item>bilisearch</item>
|
||||||
<item>nicosearch</item>
|
<item>nicosearch</item>
|
||||||
<item>PRX Series</item>
|
<item>prxseries</item>
|
||||||
<item>PRX Stories</item>
|
<item>prxstories</item>
|
||||||
<item>rkfnsearch</item>
|
<item>rkfnsearch</item>
|
||||||
<item>yvsearch</item>
|
<item>yvsearch</item>
|
||||||
<item>scsearch</item>
|
|
||||||
<item>gvsearch</item>
|
<item>gvsearch</item>
|
||||||
<item>bilisearch</item>
|
|
||||||
<item>netsearch</item>
|
<item>netsearch</item>
|
||||||
<item>trovoclip</item>
|
|
||||||
<item>trovovod</item>
|
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<array name="start_destination">
|
<array name="start_destination">
|
||||||
|
|
|
||||||
|
|
@ -323,4 +323,6 @@
|
||||||
<string name="export_file">Export File</string>
|
<string name="export_file">Export File</string>
|
||||||
<string name="ytdl_source">yt-dlp Source</string>
|
<string name="ytdl_source">yt-dlp Source</string>
|
||||||
<string name="update_ytdl_master">Master Version of yt-dlp</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>
|
</resources>
|
||||||
|
|
@ -99,6 +99,12 @@
|
||||||
app:summary="@string/limit_rate_summary"
|
app:summary="@string/limit_rate_summary"
|
||||||
app:title="@string/limit_rate" />
|
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
|
<SwitchPreferenceCompat
|
||||||
android:widgetLayout="@layout/preferece_material_switch"
|
android:widgetLayout="@layout/preferece_material_switch"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue