add queueOrder for downloads, remove updateDownloadID bugs

pull/1274/head
deniscerri 2 months ago
parent c66d0fcabf
commit e032aacf41
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -237,7 +237,7 @@ dependencies {
implementation 'com.google.accompanist:accompanist-webview:0.36.0'
implementation 'androidx.compose.material3:material3-android:1.4.0'
implementation "io.noties.markwon:core:4.6.2"
implementation("com.github.teamnewpipe:newpipeextractor:-SNAPSHOT")
implementation("com.github.teamnewpipe:newpipeextractor:v0.26.3")
implementation("commons-io:commons-io:2.5")
implementation("org.apache.commons:commons-compress:1.12")

@ -0,0 +1,775 @@
{
"formatVersion": 1,
"database": {
"version": 28,
"identityHash": "a72cd9c71e75598a05be1225e0e6b541",
"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, `availableSubtitles` 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": "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"
},
{
"fieldPath": "playlistURL",
"columnName": "playlistURL",
"affinity": "TEXT",
"defaultValue": "''"
},
{
"fieldPath": "playlistIndex",
"columnName": "playlistIndex",
"affinity": "INTEGER"
},
{
"fieldPath": "creationTime",
"columnName": "creationTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "availableSubtitles",
"columnName": "availableSubtitles",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'[]'"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"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, `filesize` INTEGER NOT NULL DEFAULT 0, `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": "filesize",
"columnName": "filesize",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "downloadId",
"columnName": "downloadId",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "command",
"columnName": "command",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"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, `incognito` INTEGER NOT NULL DEFAULT 0, `availableSubtitles` TEXT NOT NULL DEFAULT '[]', `rowNumber` INTEGER NOT NULL, `queueOrder` INTEGER NOT NULL DEFAULT 0)",
"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"
},
{
"fieldPath": "playlistURL",
"columnName": "playlistURL",
"affinity": "TEXT",
"defaultValue": "''"
},
{
"fieldPath": "playlistIndex",
"columnName": "playlistIndex",
"affinity": "INTEGER"
},
{
"fieldPath": "incognito",
"columnName": "incognito",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "availableSubtitles",
"columnName": "availableSubtitles",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'[]'"
},
{
"fieldPath": "rowNumber",
"columnName": "rowNumber",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "queueOrder",
"columnName": "queueOrder",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"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, `useAsExtraCommandDataFetching` INTEGER NOT NULL DEFAULT 0, `preferredCommandTemplate` INTEGER NOT NULL DEFAULT 0, `urlRegex` TEXT 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": "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"
},
{
"fieldPath": "useAsExtraCommandDataFetching",
"columnName": "useAsExtraCommandDataFetching",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "preferredCommandTemplate",
"columnName": "preferredCommandTemplate",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "urlRegex",
"columnName": "urlRegex",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"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"
]
}
},
{
"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"
]
}
},
{
"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, `description` TEXT NOT NULL, `enabled` INTEGER 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
},
{
"fieldPath": "description",
"columnName": "description",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"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"
]
}
},
{
"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",
"defaultValue": "''"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
},
{
"tableName": "sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyTime` INTEGER NOT NULL, `weeklyConfig` TEXT, `monthlyConfig` TEXT, `status` TEXT NOT NULL, `startsTime` INTEGER NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `getOnlyNewUploads` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `ignoredLinks` TEXT NOT NULL DEFAULT '[]', `alreadyProcessedLinks` TEXT NOT NULL, `syncWithSource` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "downloadItemTemplate",
"columnName": "downloadItemTemplate",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "everyNr",
"columnName": "everyNr",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "everyCategory",
"columnName": "everyCategory",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "everyTime",
"columnName": "everyTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "weeklyConfig",
"columnName": "weeklyConfig",
"affinity": "TEXT"
},
{
"fieldPath": "monthlyConfig",
"columnName": "monthlyConfig",
"affinity": "TEXT"
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "startsTime",
"columnName": "startsTime",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "endsDate",
"columnName": "endsDate",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "endsAfterCount",
"columnName": "endsAfterCount",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "runCount",
"columnName": "runCount",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "getOnlyNewUploads",
"columnName": "getOnlyNewUploads",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "retryMissingDownloads",
"columnName": "retryMissingDownloads",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ignoredLinks",
"columnName": "ignoredLinks",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'[]'"
},
{
"fieldPath": "alreadyProcessedLinks",
"columnName": "alreadyProcessedLinks",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "syncWithSource",
"columnName": "syncWithSource",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
}
],
"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, 'a72cd9c71e75598a05be1225e0e6b541')"
]
}
}

@ -40,7 +40,7 @@ import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
TerminalItem::class,
ObserveSourcesItem::class
],
version = 27,
version = 28,
autoMigrations = [
AutoMigration (from = 1, to = 2),
AutoMigration (from = 2, to = 3),
@ -67,7 +67,8 @@ import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem
//AutoMigration(from = 23, to = 24) MANUALLY HANDLED
//AutoMigration(from = 24, to = 25) MANUALLY HANDLED
//AutoMigration(from = 25, to = 26) MANUALLY HANDLED
AutoMigration(from = 26, to = 27)
AutoMigration(from = 26, to = 27),
AutoMigration(from = 27, to = 28)
]
)
abstract class DBManager : RoomDatabase(){

@ -36,7 +36,7 @@ interface DownloadDao {
fun getPausedDownloadsList() : List<DownloadItem>
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
@Query("SELECT * FROM downloads WHERE status = 'Processing' ORDER BY queueOrder, id")
fun getProcessingDownloads() : PagingSource<Int, DownloadItemConfigureMultiple>
@Query("SELECT id FROM downloads WHERE status = 'Processing'")
@ -81,7 +81,7 @@ interface DownloadDao {
fun getFirstProcessingDownloadFlow() : Flow<DownloadItem?>
@Query("SELECT * FROM downloads WHERE status = 'Processing'")
@Query("SELECT * FROM downloads WHERE status = 'Processing' ORDER BY queueOrder, id")
fun getProcessingDownloadsList() : List<DownloadItem>
@Query("UPDATE downloads set downloadPath=:path WHERE status ='Processing'")
@ -118,7 +118,7 @@ interface DownloadDao {
fun getActiveAndQueuedDownloads() : Flow<List<DownloadItem>>
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY id")
@Query("SELECT * FROM downloads WHERE status='Queued' ORDER BY queueOrder, id")
fun getQueuedDownloads() : PagingSource<Int, DownloadItemSimple>
@Query("SELECT format FROM downloads WHERE status='Queued'")
@ -127,7 +127,7 @@ interface DownloadDao {
@Query("""
SELECT * FROM downloads
WHERE status in ('Queued', 'Scheduled') AND downloadStartTime <= :currentTime
ORDER BY downloadStartTime, id
ORDER BY downloadStartTime, queueOrder, id
LIMIT 10
""")
fun getQueuedScheduledDownloadsUntil(currentTime: Long) : Flow<List<DownloadItem>>
@ -140,7 +140,7 @@ interface DownloadDao {
WHEN id in (:priorityItems) THEN 0
ELSE 1
END,
downloadStartTime, id
downloadStartTime, queueOrder, id
LIMIT 10
""")
fun getQueuedScheduledDownloadsUntilWithPriority(currentTime: Long, priorityItems: List<Long>) : Flow<List<DownloadItem>>
@ -151,7 +151,7 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status='Scheduled' ORDER BY downloadStartTime, id")
fun getScheduledDownloadsList() : List<DownloadItem>
@Query("SELECT id FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, id")
@Query("SELECT id FROM downloads WHERE status='Queued' ORDER BY downloadStartTime, queueOrder, id")
fun getQueuedDownloadsListIDs() : List<Long>
@RewriteQueriesToDropUnusedColumns
@ -327,59 +327,80 @@ interface DownloadDao {
@Query("Update downloads SET status='Saved' WHERE status='Processing'")
suspend fun updateProcessingtoSavedStatus()
@Query("SELECT COALESCE(MAX(queueOrder), 0) FROM downloads where status='Queued'")
suspend fun getLastQueueOrder() : Int
@Query("SELECT COALESCE(MIN(queueOrder), 0) FROM downloads where status='Queued'")
suspend fun getFirstQueueOrder() : Int
@Query("SELECT queueOrder FROM downloads where id=:id")
suspend fun getQueueOrder(id: Long) : Int
@Query("UPDATE downloads set queueOrder = queueOrder + 1 WHERE id=:id")
suspend fun shiftQueueOrderDownForDownload(id: Long)
@Query("UPDATE downloads set queueOrder = queueOrder - 1 WHERE id=:id")
suspend fun shiftQueueOrderUpForDownload(id: Long)
@Query("UPDATE downloads set queueOrder=:queueOrder WHERE id=:id")
suspend fun updateQueueOrder(queueOrder: Int, id: Long)
@Transaction
suspend fun putAtTopOfTheQueue(existingIDs: List<Long>){
val downloads = getQueuedDownloadsListIDs()
val newIDs = downloads.sortedBy { it }.take(existingIDs.size)
resetScheduleTimeForItems(existingIDs)
existingIDs.forEach { updateDownloadID(it, -it) }
downloads.filter { !existingIDs.contains(it) }.toMutableList().apply {
this.reverse()
this.forEach {
updateDownloadID(it, it + existingIDs.size)
}
suspend fun reverseProcessingDownloads() {
val items = getProcessingDownloadsList()
var idx = 0
items.reversed().forEach {
updateQueueOrder(idx, it.id)
idx++
}
}
@Transaction
suspend fun putAtTopOfQueue(ids: List<Long>) {
val firstQueueOrder = getFirstQueueOrder()
val downloads = getQueuedDownloadsListIDs()
resetScheduleTimeForItems(ids)
existingIDs.forEachIndexed { idx, it ->
updateDownloadID(-it, newIDs[idx])
downloads.filter { !ids.contains(it) }.forEach {
shiftQueueOrderDownForDownload(it)
}
ids.forEach { it ->
updateQueueOrder(firstQueueOrder, it)
}
}
@Transaction
suspend fun putAtBottomOfTheQueue(existingIDs: List<Long>){
suspend fun putAtBottomOfQueue(ids: List<Long>) {
val lastQueueOrder = getLastQueueOrder()
val downloads = getQueuedDownloadsListIDs()
val newIDs = downloads.sortedByDescending { it }.take(existingIDs.size)
resetScheduleTimeForItems(existingIDs)
existingIDs.forEach { updateDownloadID(it, -it) }
downloads.filter { !existingIDs.contains(it) }.toMutableList().apply {
this.reverse()
this.forEach {
updateDownloadID(it, it + existingIDs.size)
}
}
resetScheduleTimeForItems(ids)
existingIDs.forEachIndexed { idx, it ->
updateDownloadID(-it, newIDs[idx])
downloads.filter { !ids.contains(it) }.forEach {
shiftQueueOrderUpForDownload(it)
}
ids.forEach { it ->
updateQueueOrder(lastQueueOrder, it)
}
}
@Transaction
suspend fun reverseProcessingDownloads() {
val items = getProcessingDownloadsList()
val newIDs = items.reversed().map { it.id }
items.forEach { updateDownloadID(it.id, -(it.id)) }
suspend fun putQueueDownloadAtPosition(mutableIds: MutableList<Long>, startIdx: Int, endIdx: Int) {
val baseOrder = if (startIdx == 0) {
0
} else {
val previousItemId = mutableIds[startIdx - 1]
getQueueOrder(previousItemId) + 1
}
items.forEachIndexed { idx, it ->
updateDownloadID(-(it.id), newIDs[idx])
updateDownloadRowNumber(newIDs[idx], items.size - idx)
for (i in startIdx..endIdx) {
val idToUpdate = mutableIds[i]
val newOrder = baseOrder + (i - startIdx)
updateQueueOrder(newOrder, idToUpdate)
}
}
@Query("Update downloads set id=:newId where id=:id")
suspend fun updateDownloadID(id: Long, newId: Long)
@Query("Update downloads set rowNumber=:newNr where id=:id")
suspend fun updateDownloadRowNumber(id: Long, newNr: Int)

@ -47,5 +47,7 @@ data class DownloadItem(
var incognito: Boolean = false,
@ColumnInfo(defaultValue = "[]")
var availableSubtitles: List<String> = listOf(),
var rowNumber: Int = 0
var rowNumber: Int = 0,
@ColumnInfo(defaultValue = "0")
var queueOrder: Int = 0
) : Parcelable

@ -22,5 +22,6 @@ data class DownloadItemSimple(
var type: DownloadType,
@ColumnInfo(defaultValue = "0")
var downloadStartTime: Long,
var incognito: Boolean = false
var incognito: Boolean = false,
var queueOrder: Int = 0
)

@ -7,6 +7,7 @@ import android.content.res.Resources
import android.os.Build
import android.os.Parcelable
import android.util.DisplayMetrics
import android.util.Log
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
@ -859,69 +860,32 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.startDownloadWorker(emptyList(), application)
}
suspend fun putAtTopOfQueue(ids: List<Long>) = CoroutineScope(Dispatchers.IO).launch{
val downloads = dao.getQueuedDownloadsListIDs()
val lastID = ids.maxOf { it }
ids.forEach { dao.updateDownloadID(it, -it) }
val newIDs = downloads.take(ids.size)
//other ids that need to move around
val takenPositions = mutableListOf<Long>()
downloads.filter { !ids.contains(it) && it < lastID }.toMutableList().apply {
this.reverse()
this.forEach { dID ->
val newID = downloads.last { !newIDs.contains(it) && !takenPositions.contains(it) && it <= lastID }
takenPositions.add(newID)
dao.updateDownloadID(dID, newID)
}
}
ids.forEachIndexed { idx, it ->
dao.updateDownloadID(-it, newIDs[idx])
}
suspend fun putAtTopOfQueue(ids: List<Long>) {
dao.putAtTopOfQueue(ids)
}
suspend fun putAtBottomOfQueue(ids: List<Long>) {
dao.putAtBottomOfQueue(ids)
}
suspend fun putAtBottomOfQueue(ids: List<Long>) = CoroutineScope(Dispatchers.IO).launch{
val downloads = dao.getQueuedDownloadsListIDs()
ids.forEach { dao.updateDownloadID(it, -it)}
val newIDs = downloads.takeLast(ids.size)
//other ids that need to move around
val takenPositions = mutableListOf<Long>()
for (dID in downloads.filter { !ids.contains(it) }){
val newID = downloads.first { !newIDs.contains(it) && !takenPositions.contains(it) }
takenPositions.add(newID)
dao.updateDownloadID(dID, newID)
}
fun putAtPosition(currentId: Long, targetId: Long) = CoroutineScope(Dispatchers.IO).launch {
if (currentId == targetId) return@launch
ids.toMutableList().apply {
this.reverse()
this.forEachIndexed { idx, it ->
dao.updateDownloadID(-it, newIDs[idx])
}
}
}
val orderedIds = dao.getQueuedDownloadsListIDs()
val currentIndex = orderedIds.indexOf(currentId)
val targetIndex = orderedIds.indexOf(targetId)
if (currentIndex == -1 || targetIndex == -1) return@launch
val mutableIds = orderedIds.toMutableList()
mutableIds.removeAt(currentIndex)
mutableIds.add(targetIndex, currentId)
fun putAtPosition(current: Long, id: Long) = CoroutineScope(Dispatchers.IO).launch {
val downloads = dao.getQueuedDownloadsListIDs()
dao.updateDownloadID(current, -current)
//Figure out the bounds of what actually moved to optimize DB writes
val startIdx = minOf(currentIndex, targetIndex)
val endIdx = maxOf(currentIndex, targetIndex)
if (current > id){
downloads.filter { it in id until current }.toMutableList().apply {
this.reverse()
this.forEach { dID ->
val index = downloads.indexOf(dID)
dao.updateDownloadID(dID, downloads[index + 1])
}
}
}else{
for (dID in downloads.filter { it in (current + 1)..id }){
val index = downloads.indexOf(dID)
dao.updateDownloadID(dID, downloads[index - 1])
}
}
dao.updateDownloadID(-current, id)
dao.putQueueDownloadAtPosition(mutableIds, startIdx, endIdx)
}
fun reQueueDownloadItems(items: List<Long>) = viewModelScope.launch(Dispatchers.IO) {
@ -960,6 +924,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
repository.getActiveAndQueuedDownloads()
}
var lastQueueOrder = withContext(Dispatchers.IO) {
dao.getLastQueueOrder()
}
items.forEachIndexed { idx, it ->
if (it.downloadStartTime > 0) {
it.status = DownloadRepository.Status.Scheduled.toString()
@ -969,6 +937,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
if (it.rowNumber == 0 && items.size > 1) {
it.rowNumber = idx + 1
}
it.queueOrder = lastQueueOrder + 1
lastQueueOrder++
//CHECK DUPLICATES
var isDuplicate = false

@ -232,8 +232,6 @@ class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
notifyDataSetChanged()
}
private fun checkCard(card: MaterialCardView, itemID: Long, position: Int) {
if (card.isChecked) {
card.strokeWidth = 0
@ -265,7 +263,7 @@ class QueuedDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
}
override fun areContentsTheSame(oldItem: DownloadItemSimple, newItem: DownloadItemSimple): Boolean {
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb
return oldItem.id == newItem.id && oldItem.title == newItem.title && oldItem.author == newItem.author && oldItem.thumb == newItem.thumb && oldItem.queueOrder == newItem.queueOrder
}
}
}

@ -431,13 +431,17 @@ class QueuedDownloadsFragment : Fragment(), QueuedDownloadAdapter.OnItemClickLis
override fun onMoveQueuedItemToTop(itemID: Long) {
lifecycleScope.launch {
downloadViewModel.putAtTopOfQueue(listOf(itemID))
withContext(Dispatchers.IO) {
downloadViewModel.putAtTopOfQueue(listOf(itemID))
}
}
}
override fun onMoveQueuedItemToBottom(itemID: Long) {
lifecycleScope.launch {
downloadViewModel.putAtBottomOfQueue(listOf(itemID))
withContext(Dispatchers.IO) {
downloadViewModel.putAtBottomOfQueue(listOf(itemID))
}
}
}

@ -213,17 +213,17 @@ class YoutubePlayerClientFragment : Fragment(), YoutubePlayerClientAdapter.OnIte
okBtn.setOnClickListener {
val titleVal = title.editText!!.text.toString()
if (titleVal.isBlank()) {
title.error = "Player Client tag shouldn't be empty"
title.error = getString(R.string.player_client_not_empty)
return@setOnClickListener
}
if(existingConfigs.any { it2 -> it2.playerClient == titleVal && it2.enabled } && ( currentValue == null || currentValue.playerClient != titleVal )) {
title.error = "Player Client is already created"
title.error = getString(R.string.player_client_already_created)
return@setOnClickListener
}
if (useOnlyPOToken.isChecked && (poTokenInputs.filter { it.tag.toString().startsWith("potoken") }.all { it.editText!!.text.isBlank() })) {
poTokenInputs.first().error = "You need to write at least one PO Token"
poTokenInputs.first().error = getString(R.string.write_atleast_one_potoken)
return@setOnClickListener
}

@ -56,8 +56,8 @@ class UpdateUtil(var context: Context) {
var v: GithubRelease
if (useBeta) {
v = res.firstOrNull { it.tag_name.contains("beta", true) && it.tag_name.contains("github", true) } ?: res.first()
val stableV = res.first { !it.tag_name.contains("beta", true) && it.tag_name.contains("github", true) }
v = res.firstOrNull { it.tag_name.contains("beta", true) } ?: res.first()
val stableV = res.first { !it.tag_name.contains("beta", true) }
val incomingVerNumber = v.tag_name.removePrefix("v").tagNameToVersionNumber()
val incomingStableVerNumber = stableV.tag_name.removePrefix("v").tagNameToVersionNumber()
@ -70,7 +70,7 @@ class UpdateUtil(var context: Context) {
isInLatest = currentVerNumber >= incomingVerNumber
}
}else {
v = res.first { !it.tag_name.contains("beta", true) && it.tag_name.contains("github", true) }
v = res.first { !it.tag_name.contains("beta", true) }
val incomingVerNumber = v.tag_name.removePrefix("v").tagNameToVersionNumber()
//if current version is beta but wants to downgrade to stable, allow it

@ -157,6 +157,7 @@
<item>조선말</item>
<item>latviešu</item>
<item>بهاس ملايو</item>
<item>Mara Reih</item>
<item>Norsk Bokmål</item>
<item>Norsk Nynorsk</item>
<item>पंजाबी</item>
@ -203,6 +204,7 @@
<item>ko</item>
<item>lv</item>
<item>ms</item>
<item>mrh</item>
<item>nb</item>
<item>nn</item>
<item>pa</item>

@ -534,4 +534,7 @@
<string name="y_offset">Y Offset</string>
<string name="width">Width</string>
<string name="height">Height</string>
<string name="player_client_not_empty">Player Client tag shouldn\'t be empty</string>
<string name="player_client_already_created">Player Client is already created</string>
<string name="write_atleast_one_potoken">You need to write at least one PO Token</string>
</resources>

Loading…
Cancel
Save