From 0df5a9e1940a678d3ca97df482c2c4fd5490ef5b Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Sat, 23 Nov 2024 19:40:08 +0100 Subject: [PATCH] more features --- app/build.gradle | 8 +- .../ytdl/database/dao/DownloadDao.kt | 3 + .../database/repository/DownloadRepository.kt | 4 + .../database/viewmodel/DownloadViewModel.kt | 4 +- .../ytdl/ui/adapter/TemplatesAdapter.kt | 7 +- .../com/deniscerri/ytdl/util/Extensions.kt | 42 +++---- .../java/com/deniscerri/ytdl/util/FileUtil.kt | 2 +- .../ytdl/util/extractors/YTDLPUtil.kt | 12 ++ .../main/res/layout/command_template_item.xml | 107 ++++++++++-------- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/advanced_preferences.xml | 8 ++ build.gradle | 8 +- gradle.properties | 2 +- 13 files changed, 127 insertions(+), 82 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 1200f98f..8cc6b21b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,8 +3,9 @@ plugins { id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' id 'org.jetbrains.kotlin.android' id 'com.google.devtools.ksp' - id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10" - id "org.jetbrains.kotlin.plugin.parcelize" version "1.8.10" + id "org.jetbrains.kotlin.plugin.serialization" version "2.0.21" + id "org.jetbrains.kotlin.plugin.parcelize" version "2.0.21" + id "org.jetbrains.kotlin.plugin.compose" version "2.0.21" } def properties = new Properties() @@ -101,6 +102,7 @@ android { buildFeatures { viewBinding true + buildConfig true compose true } @@ -195,7 +197,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0' implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4' implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.6" - implementation "com.github.Irineu333:Highlight-KT:1.0.4" + implementation "com.neoutils.highlight:highlight-view:2.2.0" // For media playback using ExoPlayer implementation("androidx.media3:media3-exoplayer:$media3_version") diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt index 2694ef41..b98153cd 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/DownloadDao.kt @@ -228,6 +228,9 @@ interface DownloadDao { @Query("UPDATE downloads set status=:status where id=:id") suspend fun setStatus(id: Long, status: String) + @Query("UPDATE downloads set status=:status where id IN (:ids)") + suspend fun setStatusMultiple(ids: List, status: String) + @Update suspend fun updateWithoutUpsert(item: DownloadItem) diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt index 602d2395..63441716 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/DownloadRepository.kt @@ -123,6 +123,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) { downloadDao.setStatus(id, status.toString()) } + suspend fun setDownloadStatusMultiple(ids: List, status: Status) { + downloadDao.setStatusMultiple(ids, status.toString()) + } + fun getItemByID(id: Long) : DownloadItem { return downloadDao.getDownloadById(id) } diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index 02593234..ba11a052 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -1268,9 +1268,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } delay(1000) isPausingResuming = false - activeDownloadsList.forEach { - repository.setDownloadStatus(it.id, DownloadRepository.Status.Paused) - } + repository.setDownloadStatusMultiple(activeDownloadsList.map { it.id }, DownloadRepository.Status.Paused) pausedAllDownloads.value = PausedAllDownloadsState.RESUME } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/TemplatesAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/TemplatesAdapter.kt index fbee6208..ca44b3cc 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/TemplatesAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/TemplatesAdapter.kt @@ -71,6 +71,10 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ text = finalText } + card.findViewById(R.id.dataFetchingExtraCommands).apply { + isVisible = item.useAsExtraCommandDataFetching + } + card.findViewById(R.id.preferredTemplate).apply { val preferred = sharedPreferences.getString("preferred_command_template", "") isVisible = preferred == item.content @@ -165,7 +169,8 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ oldItem.content == newItem.content && oldItem.useAsExtraCommand == newItem.useAsExtraCommand && oldItem.useAsExtraCommandAudio == newItem.useAsExtraCommandAudio && - oldItem.useAsExtraCommandVideo == newItem.useAsExtraCommandVideo + oldItem.useAsExtraCommandVideo == newItem.useAsExtraCommandVideo && + oldItem.useAsExtraCommandDataFetching == newItem.useAsExtraCommandDataFetching } } } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt index a541ba61..7be6db1f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt @@ -46,11 +46,12 @@ import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository.EveryCat import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.tabs.TabLayout -import com.google.gson.Gson -import com.google.gson.JsonArray -import com.neo.highlight.core.Highlight -import com.neo.highlight.util.listener.HighlightTextWatcher -import com.neo.highlight.util.scheme.ColorScheme +import com.neoutils.highlight.core.Highlight +import com.neoutils.highlight.core.scheme.TextColorScheme +import com.neoutils.highlight.core.util.Match +import com.neoutils.highlight.core.util.UiColor +import com.neoutils.highlight.view.extension.toSpannedString +import com.neoutils.highlight.view.text.HighlightTextWatcher import com.squareup.picasso.Picasso import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -72,28 +73,27 @@ object Extensions { return (metrics.density * px).toInt() } - private var textHighLightSchemes = listOf( - ColorScheme(Pattern.compile("([\"'])(?:\\\\1|.)*?\\1"), Color.parseColor("#FC8500")), - ColorScheme(Pattern.compile("yt-dlp"), Color.parseColor("#77eb09")), - ColorScheme(Pattern.compile("(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})"), Color.parseColor("#b5942f")), - ColorScheme(Pattern.compile("\\d+(\\.\\d)?%"), Color.parseColor("#43a564")) + private var textHighlightSchemes = listOf( + TextColorScheme(regex = "([\"'])(?:\\\\1|.)*?\\1".toRegex(), match = Match.fully(UiColor.Hex("#FC8500"))), + TextColorScheme(regex = "yt-dlp".toRegex(), match = Match.fully(UiColor.Hex("#77eb09"))), + TextColorScheme(regex = "(https?://(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?://(?:www\\.|(?!www))[a-zA-Z0-9]+\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]+\\.[^\\s]{2,})".toRegex(), match = Match.fully(UiColor.Hex("#b5942f"))), + TextColorScheme(regex = "\\d+(\\.\\d)?%".toRegex(), match = Match.fully(UiColor.Hex("#43a564"))), ) + fun View.enableTextHighlight(){ if (this is EditText || this is TextView){ //init syntax highlighter - val highlight = Highlight() - val highlightWatcher = HighlightTextWatcher() + val highlight = Highlight(textHighlightSchemes) + val highlightWatcher = HighlightTextWatcher(highlight) - highlight.addScheme( - *textHighLightSchemes.map { it }.toTypedArray() - ) - highlightWatcher.addScheme( - *textHighLightSchemes.map { it }.toTypedArray() - ) - - highlight.setSpan(this as TextView) - this.addTextChangedListener(highlightWatcher) + if (this is EditText) { + this.addTextChangedListener(highlightWatcher) + this.setText(highlight.toSpannedString(this.text.toString())) + }else if (this is TextView) { + this.addTextChangedListener(highlightWatcher) + this.text = highlight.toSpannedString(this.text.toString()) + } } } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt index 14fe4710..4294726a 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/FileUtil.kt @@ -108,7 +108,7 @@ object FileUtil { if (it.isDirectory && it.absolutePath == originDir.absolutePath) return@forEach var destFile: DocumentFile try { - if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(.*.part-Frag.*)|(.*.live_chat)|(.*.ytdl)".toRegex())){ + if (it.name.matches("(^config.*.\\.txt\$)|(rList)|(.*.part-Frag.*)|(.*.live_chat)|(.*.ytdl)|(.*.info.json)".toRegex())){ return@forEach } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt index 3f90ac1e..a7fbf06b 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/extractors/YTDLPUtil.kt @@ -725,6 +725,18 @@ class YTDLPUtil(private val context: Context) { request.addOption("--download-archive", FileUtil.getDownloadArchivePath(context)) } + if (!sharedPreferences.getBoolean("disable_write_info_json", false)) { + val infoJsonFile = downDir.walkTopDown().firstOrNull { it.name == "${downloadItem.id}.info.json" } + //ignore info file if its older than 5 hours. puny measure to prevent expired formats in some cases + if (infoJsonFile == null || System.currentTimeMillis() - infoJsonFile.lastModified() > (1000 * 60 * 60 * 5)) { + request.addOption("--write-info-json") + request.addOption("--no-clean-info-json") + request.addOption("-o", "infojson:${downloadItem.id}") + }else { + request.addOption("--load-info-json", infoJsonFile.absolutePath) + } + } + val preferredAudioCodec = sharedPreferences.getString("audio_codec", "")!! val aCodecPrefIndex = context.getStringArray(R.array.audio_codec_values).indexOf(preferredAudioCodec) val aCodecPref = runCatching { context.getStringArray(R.array.audio_codec_values_ytdlp)[aCodecPrefIndex] }.getOrElse { "" } diff --git a/app/src/main/res/layout/command_template_item.xml b/app/src/main/res/layout/command_template_item.xml index 240104eb..72c8fec0 100644 --- a/app/src/main/res/layout/command_template_item.xml +++ b/app/src/main/res/layout/command_template_item.xml @@ -41,64 +41,75 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/title" /> - - + android:layout_height="wrap_content" + android:background="@drawable/rounded_corner" + android:backgroundTint="?attr/colorPrimaryContainer" + android:clickable="false" + android:gravity="center" + android:minWidth="30dp" + android:paddingHorizontal="5dp" + android:textSize="12sp" + android:textStyle="bold" + app:cornerRadius="10dp" + android:text="@string/preferred_command_template" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"/> - + - + - - - + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 277408bd..b2725e59 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -437,4 +437,6 @@ Data Fetching Extra Command Thumbnail Enable command templates to be used for data fetching as extra commands + Disable Write Info Json + (Not Recommended) Every time you restart / resume the download, yt-dlp will re-download json data from the servers. \ No newline at end of file diff --git a/app/src/main/res/xml/advanced_preferences.xml b/app/src/main/res/xml/advanced_preferences.xml index 90dc8917..ed9f9c00 100644 --- a/app/src/main/res/xml/advanced_preferences.xml +++ b/app/src/main/res/xml/advanced_preferences.xml @@ -33,4 +33,12 @@ android:title="@string/data_fetching_extra_command" /> + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index afac371c..3e6fef71 100644 --- a/build.gradle +++ b/build.gradle @@ -41,11 +41,11 @@ buildscript { plugins { id 'com.android.application' version '8.5.2' apply false id 'com.android.library' version '8.5.2' apply false - id 'org.jetbrains.kotlin.android' version '1.8.10' apply false - id "org.jetbrains.kotlin.plugin.serialization" version "1.8.10" apply false - id "org.jetbrains.kotlin.plugin.parcelize" version "1.8.10" apply false + id 'org.jetbrains.kotlin.android' version '2.0.21' apply false + id "org.jetbrains.kotlin.plugin.serialization" version "2.0.21" apply false + id "org.jetbrains.kotlin.plugin.parcelize" version "2.0.21" apply false id 'com.android.test' version '8.5.2' apply false - id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false + id 'com.google.devtools.ksp' version '2.0.21-1.0.28' apply false } tasks.register('clean', Delete) { diff --git a/gradle.properties b/gradle.properties index 427acc09..81da9f84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,9 +11,9 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Sun Oct 08 20:53:48 CEST 2023 -android.defaults.buildfeatures.buildconfig=true android.enableJetifier=true android.nonFinalResIds=false android.nonTransitiveRClass=false +android.suppressUnsupportedCompileSdk=35 android.useAndroidX=true org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1024M" -Dfile.encoding\=UTF-8