From 8d9d06ab138327ac0270c8da739b5d0980428d17 Mon Sep 17 00:00:00 2001
From: zaednasr <75589932+zaednasr@users.noreply.github.com>
Date: Sun, 4 Feb 2024 14:42:43 +0100
Subject: [PATCH] major stuff pre 1.6.2
---
.../14.json | 123 ++-
app/src/main/AndroidManifest.xml | 5 +-
.../com/deniscerri/ytdlnis/MainActivity.kt | 8 +-
.../deniscerri/ytdlnis/database/Converters.kt | 16 +
.../deniscerri/ytdlnis/database/DBManager.kt | 6 +-
.../deniscerri/ytdlnis/database/Migrations.kt | 35 +-
.../ytdlnis/database/dao/CookieDao.kt | 3 +
.../ytdlnis/database/dao/DownloadDao.kt | 32 +
.../ytdlnis/database/dao/ObserveSourcesDao.kt | 36 +
.../ytdlnis/database/models/Format.kt | 4 +-
.../database/models/ObserveSourcesItem.kt | 37 +
.../database/models/VideoPreferences.kt | 1 +
.../database/repository/CookieRepository.kt | 4 +
.../database/repository/DownloadRepository.kt | 13 +-
.../database/repository/HistoryRepository.kt | 9 +-
.../repository/ObserveSourcesRepository.kt | 60 ++
.../database/repository/ResultRepository.kt | 8 +-
.../database/viewmodel/CookieViewModel.kt | 5 +
.../database/viewmodel/DownloadViewModel.kt | 364 ++++++---
.../database/viewmodel/HistoryViewModel.kt | 23 +-
.../viewmodel/ObserveSourcesViewModel.kt | 142 ++++
.../database/viewmodel/ResultViewModel.kt | 2 +-
.../ytdlnis/receiver/ObserveAlarmReceiver.kt | 38 +
.../ytdlnis/receiver/ShareActivity.kt | 2 +
.../AlarmStartReceiver.kt | 3 +-
.../AlarmStopReceiver.kt | 2 +-
.../com/deniscerri/ytdlnis/ui/HomeFragment.kt | 77 +-
.../ui/adapter/ActiveDownloadAdapter.kt | 3 +
.../adapter/ActiveDownloadMinifiedAdapter.kt | 3 +
.../ui/adapter/AlreadyExistsAdapter.kt | 107 +++
.../ConfigureMultipleDownloadsAdapter.kt | 25 +-
.../ytdlnis/ui/adapter/CookieAdapter.kt | 2 +
.../ytdlnis/ui/adapter/DownloadLogsAdapter.kt | 2 +
.../ui/adapter/GenericDownloadAdapter.kt | 3 +
.../ytdlnis/ui/adapter/HistoryAdapter.kt | 10 +-
.../ytdlnis/ui/adapter/HomeAdapter.kt | 5 +
.../ui/adapter/ObserveSourcesAdapter.kt | 187 +++++
.../ytdlnis/ui/adapter/PlaylistAdapter.kt | 2 +
.../ui/adapter/SearchSuggestionsAdapter.kt | 2 +
.../ytdlnis/ui/adapter/TemplatesAdapter.kt | 2 +
.../ui/adapter/TerminalDownloadsAdapter.kt | 3 +
.../ConfigureDownloadBottomSheetDialog.kt | 55 +-
.../downloadcard/CutVideoBottomSheetDialog.kt | 17 +-
.../ui/downloadcard/DownloadAudioFragment.kt | 20 +-
.../downloadcard/DownloadBottomSheetDialog.kt | 34 +-
.../downloadcard/DownloadCommandFragment.kt | 10 +-
.../downloadcard/DownloadFragmentAdapter.kt | 7 +-
.../DownloadMultipleBottomSheetDialog.kt | 722 +++++++++---------
.../ui/downloadcard/DownloadVideoFragment.kt | 24 +-
.../FormatSelectionBottomSheetDialog.kt | 2 +-
.../ObserveSourcesBottomSheetDialog.kt | 530 +++++++++++++
.../downloadcard/SelectPlaylistItemsDialog.kt | 12 +-
.../ui/downloads/DownloadQueueMainFragment.kt | 21 +
.../ytdlnis/ui/downloads/HistoryFragment.kt | 45 +-
.../ui/more/CommandTemplatesFragment.kt | 1 -
.../ytdlnis/ui/more/CookiesFragment.kt | 74 +-
.../ytdlnis/ui/more/MoreFragment.kt | 6 +
.../ytdlnis/ui/more/ObserveSourcesFragment.kt | 225 ++++++
.../more/downloadLogs/DownloadLogFragment.kt | 11 +-
.../more/settings/DownloadSettingsFragment.kt | 2 +-
.../ui/more/terminal/TerminalFragment.kt | 8 +-
.../deniscerri/ytdlnis/util/DownloadUtil.kt | 92 +++
.../com/deniscerri/ytdlnis/util/Extensions.kt | 50 +-
.../com/deniscerri/ytdlnis/util/FileUtil.kt | 4 +
.../com/deniscerri/ytdlnis/util/InfoUtil.kt | 87 ++-
.../ytdlnis/util/NotificationUtil.kt | 21 +
.../com/deniscerri/ytdlnis/util/UiUtil.kt | 301 +++++---
.../deniscerri/ytdlnis/work/AlarmScheduler.kt | 4 +-
.../deniscerri/ytdlnis/work/DownloadWorker.kt | 3 +-
.../ytdlnis/work/ObserveSourceWorker.kt | 255 +++++++
.../main/res/drawable/baseline_archive_24.xml | 5 +
.../drawable/baseline_calendar_month_24.xml | 5 +
.../drawable/baseline_import_export_24.xml | 5 +
.../main/res/drawable/baseline_loop_24.xml | 5 +
app/src/main/res/drawable/circle.xml | 3 +
app/src/main/res/drawable/thumb.xml | 35 +
.../main/res/layout-land/fragment_home.xml | 10 +-
.../res/layout-sw600dp-land/fragment_home.xml | 10 +-
.../main/res/layout-sw600dp/fragment_home.xml | 10 +-
.../res/layout-sw950dp-land/fragment_home.xml | 10 +-
app/src/main/res/layout/adjust_video.xml | 8 +
.../main/res/layout/already_exists_card.xml | 17 +-
.../main/res/layout/already_exists_item.xml | 38 +-
.../layout/download_multiple_bottom_sheet.xml | 8 +-
app/src/main/res/layout/filepath_card.xml | 23 +
.../main/res/layout/format_details_sheet.xml | 35 +
app/src/main/res/layout/format_item.xml | 2 +
.../res/layout/format_select_bottom_sheet.xml | 3 +-
app/src/main/res/layout/fragment_history.xml | 10 +
app/src/main/res/layout/fragment_home.xml | 14 +-
app/src/main/res/layout/fragment_more.xml | 15 +
.../res/layout/fragment_observe_sources.xml | 63 ++
.../layout/observe_sources_bottom_sheet.xml | 476 ++++++++++++
.../main/res/layout/observe_sources_item.xml | 150 ++++
.../res/layout/search_suggestion_item.xml | 1 +
app/src/main/res/menu/download_queue_menu.xml | 6 +
.../main/res/menu/nav_menu_largescreen.xml | 4 +
.../main/res/menu/observe_sources_menu.xml | 9 +
app/src/main/res/navigation/nav_graph.xml | 25 +-
app/src/main/res/values-ckb/strings.xml | 2 +-
app/src/main/res/values/arrays.xml | 34 +
app/src/main/res/values/strings.xml | 24 +
.../main/res/xml/downloading_preferences.xml | 16 +
.../main/res/xml/processing_preferences.xml | 8 +
app/src/main/res/xml/updating_preferences.xml | 2 +-
105 files changed, 4285 insertions(+), 863 deletions(-)
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/database/dao/ObserveSourcesDao.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/database/models/ObserveSourcesItem.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/database/repository/ObserveSourcesRepository.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ObserveSourcesViewModel.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/receiver/ObserveAlarmReceiver.kt
rename app/src/main/java/com/deniscerri/ytdlnis/receiver/{ => downloadAlarmReceivers}/AlarmStartReceiver.kt (92%)
rename app/src/main/java/com/deniscerri/ytdlnis/receiver/{ => downloadAlarmReceivers}/AlarmStopReceiver.kt (96%)
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/AlreadyExistsAdapter.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ObserveSourcesAdapter.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/ObserveSourcesBottomSheetDialog.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/ui/more/ObserveSourcesFragment.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/util/DownloadUtil.kt
create mode 100644 app/src/main/java/com/deniscerri/ytdlnis/work/ObserveSourceWorker.kt
create mode 100644 app/src/main/res/drawable/baseline_archive_24.xml
create mode 100644 app/src/main/res/drawable/baseline_calendar_month_24.xml
create mode 100644 app/src/main/res/drawable/baseline_import_export_24.xml
create mode 100644 app/src/main/res/drawable/baseline_loop_24.xml
create mode 100644 app/src/main/res/drawable/circle.xml
create mode 100644 app/src/main/res/drawable/thumb.xml
create mode 100644 app/src/main/res/layout/fragment_observe_sources.xml
create mode 100644 app/src/main/res/layout/observe_sources_bottom_sheet.xml
create mode 100644 app/src/main/res/layout/observe_sources_item.xml
create mode 100644 app/src/main/res/menu/observe_sources_menu.xml
diff --git a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/14.json b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/14.json
index 1a7044f5..a03ef5bd 100644
--- a/app/schemas/com.deniscerri.ytdlnis.database.DBManager/14.json
+++ b/app/schemas/com.deniscerri.ytdlnis.database.DBManager/14.json
@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 14,
- "identityHash": "5cea521e1a26473b2ec5298791f10ac4",
+ "identityHash": "5be3b7ae26c7716f2192470a3a9249a8",
"entities": [
{
"tableName": "results",
@@ -587,12 +587,131 @@
},
"indices": [],
"foreignKeys": []
+ },
+ {
+ "tableName": "observeSources",
+ "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, `status` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyWeekDay` TEXT NOT NULL, `everyMonthDay` INTEGER NOT NULL, `everyTime` INTEGER NOT NULL, `startsTime` INTEGER NOT NULL, `startsMonth` TEXT NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `alreadyProcessedLinks` TEXT NOT NULL)",
+ "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": "status",
+ "columnName": "status",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "everyNr",
+ "columnName": "everyNr",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "everyCategory",
+ "columnName": "everyCategory",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "everyWeekDay",
+ "columnName": "everyWeekDay",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "everyMonthDay",
+ "columnName": "everyMonthDay",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "everyTime",
+ "columnName": "everyTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startsTime",
+ "columnName": "startsTime",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startsMonth",
+ "columnName": "startsMonth",
+ "affinity": "TEXT",
+ "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": "retryMissingDownloads",
+ "columnName": "retryMissingDownloads",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "alreadyProcessedLinks",
+ "columnName": "alreadyProcessedLinks",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "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')"
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5be3b7ae26c7716f2192470a3a9249a8')"
]
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1de615b5..dd009739 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -202,8 +202,9 @@
-
-
+
+
+
) = Gson().toJson(s)
+
+ @TypeConverter
+ fun stringtoMutableListofStrings(s: String) = Gson().fromJson(s, Array::class.java).toMutableList()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
index 847460f5..8c51f85e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/DBManager.kt
@@ -21,7 +21,8 @@ import com.deniscerri.ytdlnis.database.models.*
TemplateShortcut::class,
CookieItem::class,
LogItem::class,
- TerminalItem::class
+ TerminalItem::class,
+ ObserveSourcesItem::class
],
version = 14,
autoMigrations = [
@@ -37,7 +38,7 @@ import com.deniscerri.ytdlnis.database.models.*
AutoMigration (from = 10, to = 11),
AutoMigration (from = 11, to = 12),
AutoMigration (from = 12, to = 13),
- //AutoMigration (from = 13, to = 14), MANUALLY HANDLED
+ // AutoMigration (from = 13, to = 14) MANUALLY HANDLED
]
)
abstract class DBManager : RoomDatabase(){
@@ -49,6 +50,7 @@ abstract class DBManager : RoomDatabase(){
abstract val cookieDao: CookieDao
abstract val logDao: LogDao
abstract val terminalDao: TerminalDao
+ abstract val observeSourcesDao: ObserveSourcesDao
enum class SORTING{
DESC, ASC
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/Migrations.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/Migrations.kt
index 0943ca2b..e551df41 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/Migrations.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/Migrations.kt
@@ -1,39 +1,7 @@
package com.deniscerri.ytdlnis.database
import android.annotation.SuppressLint
-import android.app.Dialog
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Color
-import android.graphics.Outline
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.OvalShape
-import android.media.MediaMetadataRetriever
-import android.media.MediaMetadataRetriever.METADATA_KEY_DURATION
-import android.net.Uri
-import android.util.DisplayMetrics
-import android.view.MotionEvent
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewOutlineProvider
-import android.widget.EditText
-import android.widget.TextView
-import androidx.annotation.Px
-import androidx.core.view.updateLayoutParams
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.withStarted
-import androidx.recyclerview.widget.RecyclerView
import androidx.room.migration.Migration
-import androidx.work.impl.Migration_12_13
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.neo.highlight.core.Highlight
-import com.neo.highlight.util.listener.HighlightTextWatcher
-import com.neo.highlight.util.scheme.ColorScheme
-import kotlinx.coroutines.launch
-import me.zhanghai.android.fastscroll.FastScrollerBuilder
-import java.io.File
-import java.util.regex.Pattern
object Migrations {
@@ -48,7 +16,10 @@ object Migrations {
val path = cursor.getString(cursor.getColumnIndex("downloadPath"))
val newPath = "[\"${path.replace("\"", "\\\"").replace("'", "''")}\"]"
database.execSQL("UPDATE history SET downloadPath = '${newPath}' WHERE id = $id")
+
}
+
+ database.execSQL("CREATE TABLE IF NOT EXISTS `observeSources` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `downloadItemTemplate` TEXT NOT NULL, `status` TEXT NOT NULL, `everyNr` INTEGER NOT NULL, `everyCategory` TEXT NOT NULL, `everyWeekDay` TEXT NOT NULL, `everyMonthDay` INTEGER NOT NULL, `everyTime` INTEGER NOT NULL, `startsTime` INTEGER NOT NULL, `startsMonth` TEXT NOT NULL, `endsDate` INTEGER NOT NULL DEFAULT 0, `endsAfterCount` INTEGER NOT NULL DEFAULT 0, `runCount` INTEGER NOT NULL DEFAULT 0, `retryMissingDownloads` INTEGER NOT NULL, `alreadyProcessedLinks` TEXT NOT NULL)")
}
)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
index f570b5e7..d5ad77f0 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
@@ -9,6 +9,9 @@ interface CookieDao {
@Query("SELECT * FROM cookies ORDER BY id DESC")
fun getAllCookies() : List
+ @Query("SELECT * FROM cookies WHERE url = :url LIMIT 1")
+ fun getByURL(url: String) : CookieItem
+
@Query("SELECT * FROM cookies ORDER BY id DESC")
fun getAllCookiesFlow() : Flow>
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
index 413c1613..302c4bb0 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/DownloadDao.kt
@@ -16,6 +16,9 @@ interface DownloadDao {
@Query("SELECT * FROM downloads WHERE status in ('Active', 'ActivePaused', 'PausedReQueued')")
fun getActiveDownloads() : Flow>
+ @Query("SELECT * FROM downloads WHERE status = 'Processing'")
+ fun getProcessingDownloads() : Flow>
+
@Query("SELECT COUNT(*) FROM downloads WHERE status in ('Active', 'ActivePaused', 'PausedReQueued')")
fun getActiveDownloadsCountFlow() : Flow
@@ -25,9 +28,29 @@ interface DownloadDao {
@Query("SELECT COUNT(*) FROM downloads WHERE status in (:status)")
fun getDownloadsCountByStatus(status : List) : Int
+
+ @Query("""
+ SELECT COUNT(*) FROM downloads WHERE status = 'Processing'
+ UNION
+ SELECT COUNT(*) FROM downloads WHERE status ='Processing' AND type =
+ (SELECT type from downloads WHERE status = 'Processing' ORDER BY id LIMIT 1)
+ """)
+ fun getProcessingDownloadsCountByType() : List
+
+
+ @Query("UPDATE downloads set status = 'Processing' WHERE id in (:ids)")
+ suspend fun updateItemsToProcessing(ids: List)
+
+
+ @Query("SELECT * FROM downloads WHERE status = 'Processing' ORDER BY id LIMIT 1")
+ fun getFirstProcessingDownload() : DownloadItem
+
@Query("SELECT * FROM downloads WHERE status in('Active','ActivePaused','PausedReQueued')")
fun getActiveAndPausedDownloadsList() : List
+ @Query("SELECT * FROM downloads WHERE status = 'Processing'")
+ fun getProcessingDownloadsList() : List
+
@Query("SELECT * FROM downloads WHERE status='Active'")
fun getActiveDownloadsList() : List
@@ -126,6 +149,9 @@ interface DownloadDao {
@Query("DELETE FROM downloads WHERE status='Saved'")
suspend fun deleteSaved()
+ @Query("DELETE FROM downloads WHERE status='Processing'")
+ suspend fun deleteProcessing()
+
@Query("DELETE FROM downloads WHERE id in (:list)")
suspend fun deleteAllWithIDs(list: List)
@@ -174,12 +200,18 @@ interface DownloadDao {
@Query("Select id from downloads where id not in (:list) and status in (:status)")
fun getDownloadIDsNotPresentInList(list: List, status: List) : List
+ @Query("Select url from downloads where status in (:status)")
+ fun getURLsByStatus(status: List) : List
+
@Query("UPDATE downloads SET downloadStartTime=0 where id in (:list)")
fun resetScheduleTimeForItems(list: List)
@Query("Update downloads SET status='Queued', downloadStartTime = 0 WHERE id in (:list)")
fun reQueueDownloadItems(list: List)
+ @Query("Update downloads SET status='Saved' WHERE status='Processing'")
+ fun updateProcessingtoSavedStatus()
+
@Transaction
fun putAtTopOfTheQueue(id: Long){
val downloads = getQueuedDownloadsList()
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ObserveSourcesDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ObserveSourcesDao.kt
new file mode 100644
index 00000000..2ee57b32
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/ObserveSourcesDao.kt
@@ -0,0 +1,36 @@
+package com.deniscerri.ytdlnis.database.dao
+
+import androidx.room.*
+import com.deniscerri.ytdlnis.database.models.CookieItem
+import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface ObserveSourcesDao {
+ @Query("SELECT * FROM observeSources ORDER BY id DESC")
+ fun getAllSources() : List
+
+ @Query("SELECT * FROM observeSources WHERE url = :url LIMIT 1")
+ fun getByURL(url: String) : ObserveSourcesItem
+
+ @Query("SELECT * FROM observeSources WHERE id = :id LIMIT 1")
+ fun getByID(id: Long) : ObserveSourcesItem
+
+ @Query("SELECT * FROM observeSources ORDER BY id DESC")
+ fun getAllSourcesFlow() : Flow>
+
+ @Query("SELECT EXISTS(SELECT * FROM observeSources WHERE url=:url LIMIT 1)")
+ fun checkIfExistsWithSameURL(url: String) : Boolean
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ suspend fun insert(item: ObserveSourcesItem) : Long
+
+ @Query("DELETE FROM observeSources")
+ suspend fun deleteAll()
+
+ @Query("DELETE FROM observeSources WHERE id=:itemId")
+ suspend fun delete(itemId: Long)
+
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun update(item: ObserveSourcesItem)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt
index a924eaee..d9a338ba 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/Format.kt
@@ -27,5 +27,7 @@ data class Format(
@SerializedName(value = "url")
var url: String? = "",
@SerializedName(value = "language", alternate = ["audioTrackLocale"])
- val lang: String? = ""
+ val lang: String? = "",
+ @SerializedName(value = "tbr", alternate = ["bitrate"])
+ var tbr: String? = ""
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/ObserveSourcesItem.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ObserveSourcesItem.kt
new file mode 100644
index 00000000..4241b58c
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/ObserveSourcesItem.kt
@@ -0,0 +1,37 @@
+package com.deniscerri.ytdlnis.database.models
+
+import android.os.Parcelable
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.deniscerri.ytdlnis.database.repository.ObserveSourcesRepository
+import kotlinx.parcelize.Parcelize
+import java.time.Month
+import java.util.Calendar
+import java.util.Date
+
+@Entity(tableName = "observeSources")
+@Parcelize
+data class ObserveSourcesItem(
+ @PrimaryKey(autoGenerate = true)
+ var id: Long,
+ var name: String,
+ var url: String,
+ var downloadItemTemplate: DownloadItem,
+ var status: ObserveSourcesRepository.SourceStatus,
+ var everyNr: Int,
+ var everyCategory: ObserveSourcesRepository.EveryCategory,
+ val everyWeekDay: List,
+ val everyMonthDay: Int,
+ val everyTime: Long,
+ var startsTime: Long,
+ val startsMonth: Month,
+ @ColumnInfo(defaultValue = "0")
+ var endsDate: Long,
+ @ColumnInfo(defaultValue = "0")
+ var endsAfterCount: Int,
+ @ColumnInfo(defaultValue = "0")
+ var runCount: Int,
+ var retryMissingDownloads: Boolean,
+ var alreadyProcessedLinks : MutableList
+) : Parcelable
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt
index 3b2217fe..9fad348a 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/models/VideoPreferences.kt
@@ -10,6 +10,7 @@ data class VideoPreferences (
var splitByChapters: Boolean = false,
var sponsorBlockFilters: ArrayList = arrayListOf(),
var writeSubs: Boolean = false,
+ var writeAutoSubs: Boolean = false,
var subsLanguages: String = "en.*,.*-orig",
var audioFormatIDs : ArrayList = arrayListOf(),
var removeAudio: Boolean = false
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
index f14b9558..b42699a9 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
@@ -11,6 +11,10 @@ class CookieRepository(private val cookieDao: CookieDao) {
return cookieDao.getAllCookies()
}
+ fun getByURL(url: String) : CookieItem {
+ return cookieDao.getByURL(url)
+ }
+
suspend fun insert(item: CookieItem) : Long{
if (! cookieDao.checkIfExistsWithSameURL(item.url)){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
index 2e9e390c..b8f31293 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/DownloadRepository.kt
@@ -18,6 +18,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
pagingSourceFactory = {downloadDao.getAllDownloads()}
)
val activeDownloads : Flow> = downloadDao.getActiveDownloads().distinctUntilChanged()
+ val processingDownloads : Flow> = downloadDao.getProcessingDownloads().distinctUntilChanged()
val queuedDownloads : Pager = Pager(
config = PagingConfig(pageSize = 20, initialLoadSize = 20, prefetchDistance = 1),
pagingSourceFactory = {downloadDao.getQueuedDownloads()}
@@ -38,7 +39,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
val activeDownloadsCount : Flow = downloadDao.getActiveDownloadsCountFlow()
enum class Status {
- Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved
+ Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved, Processing
}
suspend fun insert(item: DownloadItem) : Long {
@@ -58,7 +59,7 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
private fun deleteCache(items: List) {
val cacheDir = FileUtil.getCachePath(App.instance)
items.forEach {
- File(cacheDir, it.id.toString()).deleteRecursively()
+ runCatching { File(cacheDir, it.id.toString()).deleteRecursively() }
}
}
@@ -84,6 +85,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
return downloadDao.getActiveAndPausedDownloadsList()
}
+ fun getProcessingDownloads() : List {
+ return downloadDao.getProcessingDownloadsList()
+ }
+
fun getActiveAndQueuedDownloads() : List {
return downloadDao.getActiveAndQueuedDownloadsList()
}
@@ -124,6 +129,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
downloadDao.deleteSaved()
}
+ suspend fun deleteProcessing(){
+ downloadDao.deleteProcessing()
+ }
+
suspend fun deleteAllWithIDs(ids: List){
downloadDao.deleteAllWithIDs(ids)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt
index 54cc9fd5..a90d876b 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/HistoryRepository.kt
@@ -26,8 +26,8 @@ class HistoryRepository(private val historyDao: HistoryDao) {
return historyDao.getAllHistoryByURL(url)
}
- fun getFiltered(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING) : List {
- return when(sortType){
+ fun getFiltered(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING, notDeleted: Boolean) : List {
+ var filtered = when(sortType){
HistorySortType.DATE -> historyDao.getHistorySortedByID(query, format, site, sort.toString())
HistorySortType.TITLE -> historyDao.getHistorySortedByTitle(query, format, site, sort.toString())
HistorySortType.AUTHOR -> historyDao.getHistorySortedByAuthor(query, format, site, sort.toString())
@@ -39,6 +39,11 @@ class HistoryRepository(private val historyDao: HistoryDao) {
}
}
}
+ if(notDeleted){
+ filtered = filtered.filter { it.downloadPath.any { it2 -> FileUtil.exists(it2) } }
+ }
+
+ return filtered
}
suspend fun insert(item: HistoryItem){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ObserveSourcesRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ObserveSourcesRepository.kt
new file mode 100644
index 00000000..9ee8bf3d
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ObserveSourcesRepository.kt
@@ -0,0 +1,60 @@
+package com.deniscerri.ytdlnis.database.repository
+
+import com.deniscerri.ytdlnis.R
+import com.deniscerri.ytdlnis.database.dao.ObserveSourcesDao
+import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
+import kotlinx.coroutines.flow.Flow
+
+class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao) {
+ val items : Flow> = observeSourcesDao.getAllSourcesFlow()
+ enum class SourceStatus {
+ ACTIVE, STOPPED
+ }
+
+ enum class EveryCategory {
+ DAY, WEEK, MONTH
+ }
+
+ companion object {
+ val everyCategoryName = mapOf(
+ EveryCategory.DAY to R.string.day,
+ EveryCategory.WEEK to R.string.week,
+ EveryCategory.MONTH to R.string.month
+ )
+ }
+
+
+ fun getAll() : List {
+ return observeSourcesDao.getAllSources()
+ }
+
+ fun getByURL(url: String) : ObserveSourcesItem {
+ return observeSourcesDao.getByURL(url)
+ }
+
+ fun getByID(id: Long) : ObserveSourcesItem {
+ return observeSourcesDao.getByID(id)
+ }
+
+
+ suspend fun insert(item: ObserveSourcesItem) : Long{
+ if (!observeSourcesDao.checkIfExistsWithSameURL(item.url)){
+ return observeSourcesDao.insert(item)
+ }
+ return -1
+ }
+
+ suspend fun delete(item: ObserveSourcesItem){
+ observeSourcesDao.delete(item.id)
+ }
+
+
+ suspend fun deleteAll(){
+ observeSourcesDao.deleteAll()
+ }
+
+ suspend fun update(item: ObserveSourcesItem){
+ observeSourcesDao.update(item)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt
index 5b5e6af5..afe8b8ee 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/ResultRepository.kt
@@ -65,6 +65,10 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
val items : ArrayList = arrayListOf()
do {
val tmp = infoUtil.getPlaylist(query, nextPageToken, if (items.isNotEmpty()) items[0]?.playlistTitle ?: "" else "")
+ val ids = resultDao.insertMultiple(tmp.videos.toList())
+ ids.forEachIndexed { index, id ->
+ tmp.videos[index]?.id = id
+ }
items.addAll(tmp.videos)
val tmpToken = tmp.nextPageToken
if (tmpToken.isEmpty()) break
@@ -72,10 +76,6 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co
nextPageToken = tmpToken
} while (true)
itemCount.value = items.size
- val ids = resultDao.insertMultiple(items.toList())
- ids.forEachIndexed { index, id ->
- items[index]?.id = id
- }
return items
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
index b10a588a..dde43c31 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
@@ -17,6 +17,7 @@ import com.deniscerri.ytdlnis.BuildConfig
import com.deniscerri.ytdlnis.database.DBManager
import com.deniscerri.ytdlnis.database.models.CookieItem
import com.deniscerri.ytdlnis.database.repository.CookieRepository
+import com.deniscerri.ytdlnis.ui.more.CookiesFragment
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
import com.deniscerri.ytdlnis.util.FileUtil
import kotlinx.coroutines.Dispatchers
@@ -43,6 +44,10 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
return repository.getAll()
}
+ fun getByURL(url: String) : CookieItem {
+ return repository.getByURL(url)
+ }
+
suspend fun insert(item: CookieItem) : Long {
return repository.insert(item)
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
index 80648819..1321e52e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
@@ -10,6 +10,7 @@ import android.net.ConnectivityManager
import android.os.Looper
import android.util.DisplayMetrics
import android.widget.Toast
+import androidx.compose.animation.core.updateTransition
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
@@ -17,6 +18,7 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.preference.PreferenceManager
import androidx.work.Constraints
+import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
@@ -39,10 +41,13 @@ import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.models.VideoPreferences
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository
+import com.deniscerri.ytdlnis.ui.downloadcard.FormatTuple
+import com.deniscerri.ytdlnis.util.DownloadUtil
import com.deniscerri.ytdlnis.util.FileUtil
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.work.AlarmScheduler
import com.deniscerri.ytdlnis.work.DownloadWorker
+import com.deniscerri.ytdlnis.work.UpdatePlaylistFormatsWorker
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -57,7 +62,7 @@ import java.util.Locale
import java.util.concurrent.TimeUnit
-class DownloadViewModel(application: Application) : AndroidViewModel(application) {
+class DownloadViewModel(private val application: Application) : AndroidViewModel(application) {
private val dbManager: DBManager
val repository : DownloadRepository
private val sharedPreferences: SharedPreferences
@@ -66,6 +71,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val allDownloads : Flow>
val queuedDownloads : Flow>
val activeDownloads : Flow>
+ val processingDownloads : Flow>
val activeDownloadsCount : Flow
val cancelledDownloads : Flow>
val erroredDownloads : Flow>
@@ -89,19 +95,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
private val dao: DownloadDao
private val historyRepository: HistoryRepository
- data class DownloadsUiState(
- var errorMessage: Pair?,
- //action title, type, data
- var actions: MutableList?>>?
+ data class AlreadyExistsUIState(
+ var historyItems: MutableList,
+ var downloadItems : MutableList
)
- enum class DownloadsAction {
- DOWNLOAD_ANYWAY
- }
-
- val uiState: MutableStateFlow = MutableStateFlow(DownloadsUiState(
- errorMessage = null,
- actions = null
+ val alreadyExistsUiState: MutableStateFlow = MutableStateFlow(AlreadyExistsUIState(
+ historyItems = mutableListOf(),
+ downloadItems = mutableListOf()
))
enum class Type {
@@ -126,6 +127,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
allDownloads = repository.allDownloads.flow
queuedDownloads = repository.queuedDownloads.flow
activeDownloads = repository.activeDownloads
+ processingDownloads = repository.processingDownloads
activeDownloadsCount = repository.activeDownloadsCount
savedDownloads = repository.savedDownloads.flow
cancelledDownloads = repository.cancelledDownloads.flow
@@ -223,6 +225,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
+ val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false)
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
@@ -261,6 +264,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
addChapters, false,
ArrayList(sponsorblock),
saveSubs,
+ saveAutoSubs,
audioFormatIDs = preferredAudioFormats
)
@@ -391,6 +395,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
fun createDownloadItemFromHistory(historyItem: HistoryItem) : DownloadItem {
val embedSubs = sharedPreferences.getBoolean("embed_subtitles", false)
val saveSubs = sharedPreferences.getBoolean("write_subtitles", false)
+ val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false)
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
@@ -424,7 +429,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!))
- val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs)
+ val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs)
var path = defaultPath
historyItem.downloadPath.first().apply {
File(this).parent?.apply {
@@ -480,7 +485,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.audio -> {
return cloneFormat (
try {
- val theFormats = formats.filter { it.format_note.contains("audio", ignoreCase = true) }
+ val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" }
val requirements = getPreferredAudioRequirements()
theFormats.maxByOrNull { f -> requirements.count{req -> req(f)} } ?: throw Exception()
}catch (e: Exception){
@@ -492,7 +497,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
Type.video -> {
return cloneFormat(
try {
- val theFormats = formats.filter { !it.format_note.contains("audio", true) }.ifEmpty { defaultVideoFormats.sortedByDescending { it.filesize } }
+ val theFormats = formats.filter { it.vcodec.isNotBlank() && it.vcodec != "null" }.ifEmpty { defaultVideoFormats.sortedByDescending { it.filesize } }
when (videoQualityPreference) {
"worst" -> {
theFormats.last()
@@ -587,6 +592,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
}
}
+ fun insertToProcessing(items: List)= viewModelScope.launch(Dispatchers.IO){
+ repository.deleteProcessing()
+ items.forEach{
+ it.status = DownloadRepository.Status.Processing.toString()
+ repository.insert(it)
+ }
+ }
+
fun deleteCancelled() = viewModelScope.launch(Dispatchers.IO) {
repository.deleteCancelled()
}
@@ -599,6 +612,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
repository.deleteSaved()
}
+ fun deleteProcessing() = viewModelScope.launch(Dispatchers.IO) {
+ repository.deleteProcessing()
+ }
+
fun deleteAllWithID(ids: List) = viewModelScope.launch(Dispatchers.IO){
repository.deleteAllWithIDs(ids)
}
@@ -646,27 +663,31 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
suspend fun resetScheduleTimeForItemsAndStartDownload(items: List) = CoroutineScope(Dispatchers.IO).launch {
dbManager.downloadDao.resetScheduleTimeForItems(items)
- startDownloadWorker(emptyList())
+ DownloadUtil.startDownloadWorker(emptyList(), application)
}
suspend fun putAtTopOfQueue(id: Long) = CoroutineScope(Dispatchers.IO).launch{
dbManager.downloadDao.putAtTopOfTheQueue(id)
- startDownloadWorker(emptyList())
+ DownloadUtil.startDownloadWorker(emptyList(), application)
}
suspend fun reQueueDownloadItems(items: List) = CoroutineScope(Dispatchers.IO).launch {
dbManager.downloadDao.reQueueDownloadItems(items)
- startDownloadWorker(emptyList())
+ DownloadUtil.startDownloadWorker(emptyList(), application)
}
- suspend fun queueDownloads(items: List, ignoreDuplicate : Boolean = false) = CoroutineScope(Dispatchers.IO).launch {
+ suspend fun queueDownloads(items: List, ign : Boolean = false) : Pair, List> {
val context = App.instance
val alarmScheduler = AlarmScheduler(context)
val activeAndQueuedDownloads = withContext(Dispatchers.IO){
repository.getActiveAndQueuedDownloads()
}
val queuedItems = mutableListOf()
- val existing = mutableListOf()
+ val existingDownloads = mutableListOf()
+ val existingHistory = mutableListOf()
+
+ var ignoreDuplicate = ign
+ if (sharedPreferences.getBoolean("download_archive", false)) ignoreDuplicate = true
//if scheduler is on
val useScheduler = sharedPreferences.getBoolean("use_scheduler", false)
@@ -680,122 +701,85 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
items.forEach {
if (it.status != DownloadRepository.Status.ActivePaused.toString()) it.status = DownloadRepository.Status.Queued.toString()
- val currentCommand = infoUtil.buildYoutubeDLRequest(it)
- val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand)
- val existingDownload = activeAndQueuedDownloads.firstOrNull{d ->
- d.id = 0
- d.logID = null
- d.customFileNameTemplate = it.customFileNameTemplate
- d.status = DownloadRepository.Status.Queued.toString()
- d.toString() == it.toString()
- }
- if (existingDownload != null && !ignoreDuplicate) {
- it.id = existingDownload.id
- existing.add(it)
- }else{
- //check if downloaded and file exists
- val history = withContext(Dispatchers.IO){
- historyRepository.getAllByURL(it.url).filter { item -> item.downloadPath.any { path -> FileUtil.exists(path) } }
+ var alreadyExists = false
+
+ if(!ignoreDuplicate){
+ val currentCommand = infoUtil.buildYoutubeDLRequest(it)
+ val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand)
+ val existingDownload = activeAndQueuedDownloads.firstOrNull{d ->
+ d.id = 0
+ d.logID = null
+ d.customFileNameTemplate = it.customFileNameTemplate
+ d.status = DownloadRepository.Status.Queued.toString()
+ d.toString() == it.toString()
}
- val existingHistory = history.firstOrNull {
- h -> h.command.replace("(-P \"(.*?)\")|(--trim-filenames \"(.*?)\")".toRegex(), "") == parsedCurrentCommand.replace("(-P \"(.*?)\")|(--trim-filenames \"(.*?)\")".toRegex(), "")
- }
-
- if (existingHistory != null && !ignoreDuplicate){
- it.id = existingHistory.id
- existing.add(it)
+ if (existingDownload != null){
+ it.status = DownloadRepository.Status.Saved.toString()
+ val id = runBlocking {
+ repository.insert(it)
+ }
+ alreadyExists = true
+ existingDownloads.add(id)
}else{
- if (it.id == 0L){
- val id = runBlocking {
- repository.insert(it)
- }
- it.id = id
- }else if (it.status == DownloadRepository.Status.Queued.toString()){
- withContext(Dispatchers.IO){
- repository.update(it)
- }
+ //check if downloaded and file exists
+ val history = withContext(Dispatchers.IO){
+ historyRepository.getAllByURL(it.url).filter { item -> item.downloadPath.any { path -> FileUtil.exists(path) } }
}
- queuedItems.add(it)
+ val existingHistoryItem = history.firstOrNull {
+ h -> h.command.replace("(-P \"(.*?)\")|(--trim-filenames \"(.*?)\")".toRegex(), "") == parsedCurrentCommand.replace("(-P \"(.*?)\")|(--trim-filenames \"(.*?)\")".toRegex(), "")
+ }
+
+ if (existingHistoryItem != null){
+ alreadyExists = true
+ existingHistory.add(existingHistoryItem.id)
+ }
+ }
+ }
+
+ if (!alreadyExists){
+ if (it.id == 0L){
+ val id = runBlocking {
+ repository.insert(it)
+ }
+ it.id = id
+ }else if (it.status == DownloadRepository.Status.Queued.toString()){
+ withContext(Dispatchers.IO){
+ repository.update(it)
+ }
+ }
+
+ queuedItems.add(it)
+ }
+
+ }
+
+ if (existingDownloads.isNotEmpty() || existingHistory.isNotEmpty()){
+ alreadyExistsUiState.update { u -> u.copy(existingHistory, existingDownloads) }
+ }
+
+ if (queuedItems.isNotEmpty()){
+ if (!useScheduler || alarmScheduler.isDuringTheScheduledTime()){
+ DownloadUtil.startDownloadWorker(queuedItems, context)
+
+ if(!useScheduler){
+ queuedItems.filter { it.downloadStartTime != 0L && (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach {
+ try{
+ updateDownloadItem(it, infoUtil, dbManager.downloadDao, dbManager.resultDao)
+ }catch (ignored: Exception){}
+ }
+ }else{
+ queuedItems.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach {
+ try{
+ updateDownloadItem(it, infoUtil, dbManager.downloadDao, dbManager.resultDao)
+ }catch (ignored: Exception){}
+ }
}
}
}
- if (existing.isNotEmpty()){
- uiState.update { u -> u.copy(
- errorMessage = Pair(R.string.download_already_exists, R.string.download_already_exists_summary),
- actions = mutableListOf(
- Triple(R.string.download, DownloadsAction.DOWNLOAD_ANYWAY, existing),
- )
- )
- }
- }
-
- if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || queuedItems.any { it.downloadStartTime > 0L } ){
- startDownloadWorker(queuedItems)
-
- if(!useScheduler){
- queuedItems.filter { it.downloadStartTime != 0L && (it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty()) }.forEach {
- try{
- updateDownloadItem(it, infoUtil, dbManager.downloadDao, dbManager.resultDao)
- }catch (ignored: Exception){}
- }
- }else{
- queuedItems.filter { it.title.isEmpty() || it.author.isEmpty() || it.thumb.isEmpty() }.forEach {
- try{
- updateDownloadItem(it, infoUtil, dbManager.downloadDao, dbManager.resultDao)
- }catch (ignored: Exception){}
- }
- }
- }
- }
-
- @SuppressLint("RestrictedApi")
- private suspend fun startDownloadWorker(queuedItems: List) {
- val context = App.instance
- val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
- val workManager = WorkManager.getInstance(context)
-
- val currentWork = workManager.getWorkInfosByTag("download").await()
- if (currentWork.size == 0 || currentWork.none{ it.state == WorkInfo.State.RUNNING } || (queuedItems.isNotEmpty() && queuedItems[0].downloadStartTime != 0L)){
-
- val currentTime = System.currentTimeMillis()
- var delay = 0L
- if (queuedItems.isNotEmpty()){
- delay = if (queuedItems[0].downloadStartTime != 0L){
- queuedItems[0].downloadStartTime.minus(currentTime)
- } else 0
- if (delay <= 60000L) delay = 0L
- }
-
-
- val workConstraints = Constraints.Builder()
- if (!allowMeteredNetworks) workConstraints.setRequiredNetworkType(NetworkType.UNMETERED)
-
- val workRequest = OneTimeWorkRequestBuilder()
- .addTag("download")
- .setConstraints(workConstraints.build())
- .setInitialDelay(delay, TimeUnit.MILLISECONDS)
-
- queuedItems.forEach {
- workRequest.addTag(it.id.toString())
- }
-
- workManager.enqueueUniqueWork(
- System.currentTimeMillis().toString(),
- ExistingWorkPolicy.REPLACE,
- workRequest.build()
- )
-
- }
-
- val isCurrentNetworkMetered = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).isActiveNetworkMetered
- if (!allowMeteredNetworks && isCurrentNetworkMetered){
- Looper.prepare().run {
- Toast.makeText(context, context.getString(R.string.metered_network_download_start_info), Toast.LENGTH_LONG).show()
- }
- }
+ return Pair(existingDownloads, existingHistory)
}
fun getQueuedCollectedFileSize() : Long {
@@ -814,6 +798,132 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
return dbManager.downloadDao.getDownloadIDsNotPresentInList(items.ifEmpty { listOf(-1L) }, status.map { it.toString() })
}
+ fun moveProcessingToSavedCategory(){
+ dao.updateProcessingtoSavedStatus()
+ }
+
+ suspend fun downloadProcessingDownloads(timeInMillis: Long = 0){
+ repository.getProcessingDownloads().apply {
+ if (timeInMillis > 0){
+ this.forEach {
+ it.downloadStartTime = timeInMillis
+ }
+ }
+
+ queueDownloads(this)
+ }
+ }
+
+ suspend fun updateProcessingFormat(selectedFormats: List): List {
+ val items = repository.getProcessingDownloads()
+ items.forEachIndexed { index, i ->
+ i.format = selectedFormats[index].format
+ if (i.type == Type.video) selectedFormats[index].audioFormats?.map { it.format_id }?.let { i.videoPreferences.audioFormatIDs.addAll(it) }
+ updateDownload(i)
+ }
+
+ return items.map { it.format.filesize }
+ }
+
+ suspend fun updateProcessingCommandFormat(format: Format){
+ val items = repository.getProcessingDownloads()
+ items.forEach { i ->
+ i.format = format
+ updateDownload(i)
+ }
+ }
+
+ suspend fun updateProcessingDownloadPath(path: String){
+ val items = repository.getProcessingDownloads()
+ items.forEach { i ->
+ i.downloadPath = path
+ updateDownload(i)
+ }
+ }
+
+ fun getProcessingDownloadsCount() : Int {
+ return dao.getDownloadsCountByStatus(listOf(DownloadRepository.Status.Processing.toString()))
+ }
+
+ fun getProcessingDownloads() : List {
+ return repository.getProcessingDownloads()
+ }
+
+
+ suspend fun updateProcessingAllFormats(formatCollection: List>) {
+ val items = repository.getProcessingDownloads()
+ items.forEachIndexed { index, i ->
+ i.allFormats.clear()
+ if (formatCollection.size == items.size && formatCollection[index].isNotEmpty()) {
+ runCatching {
+ i.allFormats.addAll(formatCollection[index])
+ }
+ }
+ i.format = getFormat(i.allFormats, i.type)
+ kotlin.runCatching {
+ dbManager.resultDao.getResultByURL(i.url)?.apply {
+ this.formats = formatCollection[index].toMutableList()
+ dbManager.resultDao.update(this)
+ }
+ }
+ updateDownload(i)
+ }
+ }
+
+ suspend fun continueUpdatingFormatsOnBackground(){
+ dao.getProcessingDownloadsList().apply {
+ this.forEach {
+ it.status = DownloadRepository.Status.Saved.toString()
+ updateDownload(it)
+ }
+
+ val ids = this.map { it.id }
+ val id = System.currentTimeMillis().toInt()
+ val workRequest = OneTimeWorkRequestBuilder()
+ .setInputData(
+ Data.Builder()
+ .putLongArray("ids", ids.toLongArray())
+ .putInt("id", id)
+ .build())
+ .addTag("updateFormats")
+ .build()
+ val context = App.instance
+ WorkManager.getInstance(context).enqueueUniqueWork(
+ id.toString(),
+ ExistingWorkPolicy.REPLACE,
+ workRequest
+ )
+ }
+ }
+
+ suspend fun updateProcessingType(newType: Type) {
+ repository.getProcessingDownloads().apply {
+ val new = switchDownloadType(this, newType)
+ new.forEach {
+ updateDownload(it)
+ }
+ }
+ }
+
+ fun checkIfAllProcessingItemsHaveSameType() : Pair {
+ val counts = dao.getProcessingDownloadsCountByType()
+ val sameType = counts.size == 1 || counts[0] == counts[1]
+ val first = dao.getFirstProcessingDownload()
+ return Pair(sameType, first.type)
+ }
+
+
+ suspend fun updateItemsWithIdsToProcessingStatus(ids: List) : Type {
+ repository.deleteProcessing()
+ dao.updateItemsToProcessing(ids)
+ val first = dao.getFirstProcessingDownload()
+ return first.type
+ }
+
+ fun getURLsByStatus(list: List) : List {
+ return dao.getURLsByStatus(list.map { it.toString() })
+ }
+
private fun updateDownloadItem(
downloadItem: DownloadItem,
infoUtil: InfoUtil,
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt
index c38a6b97..cb8646c2 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/HistoryViewModel.kt
@@ -22,6 +22,7 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
val websiteFilter = MutableLiveData("")
private val queryFilter = MutableLiveData("")
private val formatFilter = MutableLiveData("")
+ private val notDeletedFilter = MutableLiveData(false)
val allItems : LiveData>
private var _items = MediatorLiveData>()
@@ -32,19 +33,23 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
allItems = repository.items.asLiveData()
_items.addSource(allItems){
- filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
}
_items.addSource(formatFilter){
- filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
}
_items.addSource(sortType){
- filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
}
_items.addSource(websiteFilter){
- filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
}
_items.addSource(queryFilter){
- filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!)
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
+ }
+
+ _items.addSource(notDeletedFilter){
+ filter(queryFilter.value!!, formatFilter.value!!, websiteFilter.value!!, sortType.value!!, sortOrder.value!!, notDeletedFilter.value!!)
}
}
@@ -76,8 +81,12 @@ class HistoryViewModel(application: Application) : AndroidViewModel(application)
formatFilter.value = filter
}
- private fun filter(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING) = viewModelScope.launch(Dispatchers.IO){
- _items.postValue(repository.getFiltered(query, format, site, sortType, sort))
+ fun setNotDeleted(filter: Boolean){
+ notDeletedFilter.value = filter
+ }
+
+ private fun filter(query : String, format : String, site : String, sortType: HistorySortType, sort: SORTING, notDeleted: Boolean) = viewModelScope.launch(Dispatchers.IO){
+ _items.postValue(repository.getFiltered(query, format, site, sortType, sort, notDeleted))
}
fun getAll() : List {
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ObserveSourcesViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ObserveSourcesViewModel.kt
new file mode 100644
index 00000000..5d07cd3d
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ObserveSourcesViewModel.kt
@@ -0,0 +1,142 @@
+package com.deniscerri.ytdlnis.database.viewmodel
+
+import android.app.AlarmManager
+import android.app.Application
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.asLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.preference.PreferenceManager
+import com.deniscerri.ytdlnis.database.DBManager
+import com.deniscerri.ytdlnis.database.models.DownloadItem
+import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
+import com.deniscerri.ytdlnis.database.repository.ObserveSourcesRepository
+import com.deniscerri.ytdlnis.receiver.ObserveAlarmReceiver
+import com.deniscerri.ytdlnis.util.DownloadUtil
+import com.deniscerri.ytdlnis.util.Extensions.closestValue
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.time.Month
+import java.util.Calendar
+
+class ObserveSourcesViewModel(private val application: Application) : AndroidViewModel(application) {
+ private val repository: ObserveSourcesRepository
+ val items: LiveData>
+ private val alarmManager : AlarmManager
+ private val preferences : SharedPreferences
+
+ init {
+ val dao = DBManager.getInstance(application).observeSourcesDao
+ repository = ObserveSourcesRepository(dao)
+ items = repository.items.asLiveData()
+ alarmManager = application.getSystemService(AlarmManager::class.java)
+ preferences = PreferenceManager.getDefaultSharedPreferences(application)
+ }
+
+ fun getAll(): List {
+ return repository.getAll()
+ }
+
+ fun getByURL(url: String) : ObserveSourcesItem {
+ return repository.getByURL(url)
+ }
+
+ fun getByID(id: Long) : ObserveSourcesItem {
+ return repository.getByID(id)
+ }
+
+ suspend fun insert(item: ObserveSourcesItem) : Long {
+ if (item.id > 0) {
+ repository.update(item)
+ observeTask(item)
+ return item.id
+ }
+
+ val id = repository.insert(item)
+ item.id = id
+ if (id > 0) observeTask(item)
+ return id
+ }
+
+ fun delete(item: ObserveSourcesItem) = viewModelScope.launch(Dispatchers.IO) {
+ repository.delete(item)
+ }
+
+ fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
+ repository.deleteAll()
+ }
+
+ suspend fun update(item: ObserveSourcesItem) {
+ repository.update(item)
+ }
+
+
+ private fun observeTask(it: ObserveSourcesItem){
+ val id = it.id
+ val c = Calendar.getInstance()
+ val date = Calendar.getInstance()
+ date.timeInMillis = it.startsTime
+ val hourMin = Calendar.getInstance()
+ hourMin.timeInMillis = it.everyTime
+ c.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH))
+ c.set(Calendar.MONTH, date.get(Calendar.MONTH))
+ c.set(Calendar.YEAR, date.get(Calendar.YEAR))
+ c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
+ c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
+
+
+ DownloadUtil.cancelObservationTaskByID(application, id)
+
+ val intent = Intent(application, ObserveAlarmReceiver::class.java)
+ intent.putExtra("id", id)
+ if (it.everyNr == 0) it.everyNr = 1
+
+ when(it.everyCategory){
+ ObserveSourcesRepository.EveryCategory.DAY -> {
+ alarmManager.setExact(
+ AlarmManager.RTC,
+ c.timeInMillis,
+ PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+ )
+ }
+ ObserveSourcesRepository.EveryCategory.WEEK -> {
+ if (it.everyWeekDay.isNotEmpty()){
+ val weekDayID = c.get(Calendar.DAY_OF_WEEK).toString()
+ val followingWeekDay = (it.everyWeekDay.firstOrNull { it.toInt() > weekDayID.toInt() } ?: it.everyWeekDay.minBy { it.toInt() }).toInt()
+ c.set(Calendar.DAY_OF_WEEK, followingWeekDay)
+ if(c.timeInMillis < System.currentTimeMillis()){
+ c.add(Calendar.DAY_OF_MONTH, 7)
+ }
+ }
+
+ alarmManager.setExact(
+ AlarmManager.RTC,
+ c.timeInMillis,
+ PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+ )
+ }
+ ObserveSourcesRepository.EveryCategory.MONTH -> {
+ val theMonthIndex = Month.values().indexOf(it.startsMonth)
+ val currentMonthIndex = c.get(Calendar.MONTH)
+ if (theMonthIndex != currentMonthIndex){
+ c.set(Calendar.MONTH, theMonthIndex)
+ if (c.timeInMillis < System.currentTimeMillis()){
+ c.add(Calendar.YEAR, 1)
+ }
+ }
+ alarmManager.setExact(
+ AlarmManager.RTC,
+ c.timeInMillis,
+ PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
+ )
+ }
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt
index ef11f50d..c24f2dcf 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/ResultViewModel.kt
@@ -105,7 +105,7 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a
else -> res.addAll(repository.getDefault(inputQuery, resetResults))
}
} catch (e: Exception) {
- if (e !is kotlinx.coroutines.CancellationException){
+ if (updateResultDataJob?.isCancelled == false){
uiState.update {it.copy(
processing = false,
errorMessage = Pair(R.string.no_results, e.message.toString()),
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ObserveAlarmReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ObserveAlarmReceiver.kt
new file mode 100644
index 00000000..84ef1d1c
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ObserveAlarmReceiver.kt
@@ -0,0 +1,38 @@
+package com.deniscerri.ytdlnis.receiver
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import androidx.work.Constraints
+import androidx.work.Data
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import com.deniscerri.ytdlnis.work.DownloadWorker
+import com.deniscerri.ytdlnis.work.ObserveSourceWorker
+import java.util.concurrent.TimeUnit
+
+class ObserveAlarmReceiver : BroadcastReceiver() {
+ override fun onReceive(p0: Context?, p1: Intent?) {
+ p1?.apply {
+ val sourceID = p1.getLongExtra("id", 0)
+ if (sourceID == 0L) return
+
+ val workConstraints = Constraints.Builder()
+ val workRequest = OneTimeWorkRequestBuilder()
+ .addTag("observeSources")
+ .addTag(sourceID.toString())
+ .setConstraints(workConstraints.build())
+ .setInitialDelay(1000L, TimeUnit.MILLISECONDS)
+ .setInputData(Data.Builder().putLong("id", sourceID).build())
+
+ WorkManager.getInstance(p0!!).enqueueUniqueWork(
+ sourceID.toString(),
+ ExistingWorkPolicy.REPLACE,
+ workRequest.build()
+ )
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
index 18436b84..f233735d 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
@@ -1,6 +1,7 @@
package com.deniscerri.ytdlnis.receiver
import android.Manifest
+import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
@@ -97,6 +98,7 @@ class ShareActivity : BaseActivity() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
navController = navHostFragment.findNavController()
navController.addOnDestinationChangedListener(object: NavController.OnDestinationChangedListener{
+ @SuppressLint("RestrictedApi")
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStartReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStartReceiver.kt
similarity index 92%
rename from app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStartReceiver.kt
rename to app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStartReceiver.kt
index 4ba50807..612e7480 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStartReceiver.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStartReceiver.kt
@@ -1,4 +1,4 @@
-package com.deniscerri.ytdlnis.receiver
+package com.deniscerri.ytdlnis.receiver.downloadAlarmReceivers
import android.content.BroadcastReceiver
import android.content.Context
@@ -13,7 +13,6 @@ import java.util.concurrent.TimeUnit
class AlarmStartReceiver : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
- Log.e("assa", "Start")
val workConstraints = Constraints.Builder()
val workRequest = OneTimeWorkRequestBuilder()
.addTag("download")
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStopReceiver.kt
similarity index 96%
rename from app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt
rename to app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStopReceiver.kt
index b8f8d7f7..11f77246 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/AlarmStopReceiver.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/downloadAlarmReceivers/AlarmStopReceiver.kt
@@ -1,4 +1,4 @@
-package com.deniscerri.ytdlnis.receiver
+package com.deniscerri.ytdlnis.receiver.downloadAlarmReceivers
import android.content.BroadcastReceiver
import android.content.Context
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
index ec4a4669..e39e6f96 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
@@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.ui
+import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.content.*
@@ -14,6 +15,7 @@ import android.util.Log
import android.util.Patterns
import android.view.*
import android.view.View.*
+import android.view.animation.Interpolator
import android.widget.*
import androidx.activity.addCallback
import androidx.appcompat.app.AppCompatActivity
@@ -35,14 +37,15 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
-import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.models.SearchSuggestionItem
import com.deniscerri.ytdlnis.database.models.SearchSuggestionType
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
+import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
import com.deniscerri.ytdlnis.ui.adapter.HomeAdapter
import com.deniscerri.ytdlnis.ui.adapter.SearchSuggestionsAdapter
+import com.deniscerri.ytdlnis.util.Extensions
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
import com.deniscerri.ytdlnis.util.InfoUtil
import com.deniscerri.ytdlnis.util.ThemeUtil
@@ -56,11 +59,11 @@ import com.google.android.material.chip.ChipGroup
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
-import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.search.SearchBar
import com.google.android.material.search.SearchView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -76,13 +79,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
private var downloadSelectedFab: ExtendedFloatingActionButton? = null
private var downloadAllFab: ExtendedFloatingActionButton? = null
- private var clipboardFab: FloatingActionButton? = null
+ private var clipboardFab: ExtendedFloatingActionButton? = null
private var homeFabs: CoordinatorLayout? = null
private var infoUtil: InfoUtil? = null
private var downloadQueue: ArrayList? = null
private lateinit var resultViewModel : ResultViewModel
private lateinit var downloadViewModel : DownloadViewModel
+ private lateinit var historyViewModel : HistoryViewModel
private var fragmentView: View? = null
private var activity: Activity? = null
@@ -126,7 +130,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
uiHandler = Handler(Looper.getMainLooper())
selectedObjects = ArrayList()
- downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
+ downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
+ historyViewModel = ViewModelProvider(this)[HistoryViewModel::class.java]
downloadQueue = ArrayList()
resultsList = mutableListOf()
@@ -167,7 +172,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
searchSuggestionsRecyclerView = view.findViewById(R.id.search_suggestions_recycler)
searchSuggestionsRecyclerView?.layoutManager = LinearLayoutManager(context)
searchSuggestionsRecyclerView?.adapter = searchSuggestionsAdapter
- searchSuggestionsRecyclerView?.enableFastScroll()
+ searchSuggestionsRecyclerView?.itemAnimator = null
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
@@ -263,6 +268,30 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
}
}
+ lifecycleScope.launch {
+ launch{
+ downloadViewModel.alreadyExistsUiState.collectLatest { res ->
+ if (res.downloadItems.isNotEmpty() || res.historyItems.isNotEmpty()) {
+ withContext(Dispatchers.Main){
+ kotlin.runCatching {
+ UiUtil.handleExistingDownloadsResponse(
+ requireActivity(),
+ requireActivity().lifecycleScope,
+ requireActivity().supportFragmentManager,
+ res,
+ downloadViewModel,
+ historyViewModel)
+ }
+ }
+ downloadViewModel.alreadyExistsUiState.value = DownloadViewModel.AlreadyExistsUIState(
+ mutableListOf(),
+ mutableListOf()
+ )
+ }
+ }
+ }
+ }
+
}
override fun onResume() {
@@ -278,15 +307,11 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
if (arguments?.getBoolean("showDownloadsWithUpdatedFormats") == true){
arguments?.remove("showDownloadsWithUpdatedFormats")
CoroutineScope(Dispatchers.IO).launch {
- val ids = arguments?.getLongArray("downloadIds")
- val items = mutableListOf()
- ids?.forEach {
- items.add(downloadViewModel.getItemByID(it))
- downloadViewModel.deleteDownload(it)
- }
+ val ids = arguments?.getLongArray("downloadIds") ?: return@launch
+ val type = downloadViewModel.updateItemsWithIdsToProcessingStatus(ids.toList())
withContext(Dispatchers.Main){
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
- Pair("downloads", items)
+ Pair("type", type)
))
}
}
@@ -304,6 +329,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
clipboardFab?.isVisible = false
initSearch(searchView!!)
}
+
+ lifecycleScope.launch {
+ clipboardFab?.extend()
+ delay(1000)
+ clipboardFab?.shrink()
+ }
}
}
}
@@ -679,11 +710,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
downloadViewModel.turnResultItemsToDownloadItems(resultsList!!)
}
if (sharedPreferences!!.getBoolean("download_card", true)) {
- findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
- Pair("downloads", downloadList)
- ))
+ CoroutineScope(Dispatchers.IO).launch {
+ downloadViewModel.insertToProcessing(downloadList)
+ }
+
+ findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(Pair("type", downloadList[0].type)))
} else {
- downloadViewModel.queueDownloads(downloadList)
+ downloadList.chunked(100).forEach {
+ downloadViewModel.queueDownloads(it)
+ }
}
}
}
@@ -741,8 +776,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
}
if (sharedPreferences!!.getBoolean("download_card", true)) {
+ CoroutineScope(Dispatchers.IO).launch {
+ downloadViewModel.insertToProcessing(downloadList)
+ }
+
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2, bundleOf(
- Pair("downloads", downloadList)
+ Pair("type", downloadList[0].type)
))
} else {
downloadViewModel.queueDownloads(downloadList)
@@ -827,8 +866,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
chip.text = text
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
chip.setOnClickListener {
+ if (queriesChipGroup!!.childCount == 1) queriesConstraint!!.visibility = View.GONE
queriesChipGroup!!.removeView(chip)
- if (queriesChipGroup!!.childCount == 0) queriesChipGroup!!.visibility = View.GONE
}
queriesChipGroup!!.addView(chip)
}
@@ -849,7 +888,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
resultViewModel.removeSearchQueryFromHistory(text)
- searchSuggestionsAdapter?.notifyItemRemoved(position)
+ updateSearchViewItems(searchView!!.editText.text)
}
deleteDialog.show()
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt
index f3260fd3..8b7ed9c4 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadAdapter.kt
@@ -22,7 +22,9 @@ import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
+import com.deniscerri.ytdlnis.util.Extensions
import com.deniscerri.ytdlnis.util.Extensions.dp
+import com.deniscerri.ytdlnis.util.Extensions.popup
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
@@ -61,6 +63,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.cardView
+ card.popup()
card.tag = "${item!!.id}##card"
val uiHandler = Handler(Looper.getMainLooper())
val thumbnail = card.findViewById(R.id.image_view)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadMinifiedAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadMinifiedAdapter.kt
index 95756b2b..98a43063 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadMinifiedAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/ActiveDownloadMinifiedAdapter.kt
@@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.DownloadItem
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
+import com.deniscerri.ytdlnis.util.Extensions.popup
import com.deniscerri.ytdlnis.util.FileUtil
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
@@ -56,6 +57,8 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
val card = holder.cardView
+ card.popup()
+
val uiHandler = Handler(Looper.getMainLooper())
val thumbnail = card.findViewById(R.id.image_view)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/AlreadyExistsAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/AlreadyExistsAdapter.kt
new file mode 100644
index 00000000..684929c6
--- /dev/null
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/adapter/AlreadyExistsAdapter.kt
@@ -0,0 +1,107 @@
+package com.deniscerri.ytdlnis.ui.adapter
+
+import android.app.Activity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.recyclerview.widget.AsyncDifferConfig
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.deniscerri.ytdlnis.R
+import com.deniscerri.ytdlnis.database.models.DownloadItem
+import com.deniscerri.ytdlnis.database.models.HistoryItem
+import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
+import com.deniscerri.ytdlnis.ui.downloadcard.ConfigureDownloadBottomSheetDialog
+import com.deniscerri.ytdlnis.util.Extensions.popup
+import com.deniscerri.ytdlnis.util.UiUtil
+import com.google.android.material.floatingactionbutton.FloatingActionButton
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class AlreadyExistsAdapter(onItemClickListener: OnItemClickListener, activity: Activity) : ListAdapter, AlreadyExistsAdapter.ViewHolder>(AsyncDifferConfig.Builder(
+ DIFF_CALLBACK
+).build()) {
+ private val onItemClickListener: OnItemClickListener
+ private val activity: Activity
+
+ init {
+ this.onItemClickListener = onItemClickListener
+ this.activity = activity
+ }
+
+ class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val cardView: ConstraintLayout
+
+ init {
+ cardView = itemView as ConstraintLayout
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val cardView = LayoutInflater.from(parent.context)
+ .inflate(R.layout.already_exists_item, parent, false)
+ return ViewHolder(cardView)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = getItem(position) ?: return
+ val alreadyExistsItem = holder.cardView
+ alreadyExistsItem.tag = item.first.id.toString()
+ alreadyExistsItem.popup()
+
+ val editBtn = alreadyExistsItem.findViewById