From f259d31b7fd737a1e17d983b2861fe4438a05685 Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Fri, 26 May 2023 20:54:34 +0200 Subject: [PATCH] more stuff Added crop thumbnail as a preference Added ability to backup errored downloads Format cards will show collected filesize when multiple audio formats are chosen --- .../ytdlnis/database/dao/DownloadDao.kt | 3 + .../database/repository/DownloadRepository.kt | 4 ++ .../database/viewmodel/DownloadViewModel.kt | 63 +++++++++++++------ .../ui/downloadcard/DownloadVideoFragment.kt | 2 +- .../ui/more/settings/MainSettingsFragment.kt | 32 ++++++++++ .../com/deniscerri/ytdlnis/util/InfoUtil.kt | 10 ++- .../com/deniscerri/ytdlnis/util/UiUtil.kt | 8 ++- .../deniscerri/ytdlnis/work/DownloadWorker.kt | 17 ++--- .../res/layout-sw600dp-land/activity_main.xml | 1 + .../main/res/layout-sw600dp/activity_main.xml | 1 + app/src/main/res/layout/activity_main.xml | 1 + app/src/main/res/values/arrays.xml | 2 + app/src/main/res/values/strings.xml | 2 + .../main/res/xml/processing_preferences.xml | 9 +++ 14 files changed, 117 insertions(+), 38 deletions(-) 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 895a2c25..ae049e66 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 @@ -37,6 +37,9 @@ interface DownloadDao { @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") fun getErroredDownloads() : Flow> + @Query("SELECT * FROM downloads WHERE status='Error' ORDER BY id DESC") + fun getErroredDownloadsList() : List + @Query("SELECT * FROM downloads WHERE status='Processing' ORDER BY id DESC") fun getProcessingDownloads() : Flow> 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 95265329..21a488f3 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 @@ -60,6 +60,10 @@ class DownloadRepository(private val downloadDao: DownloadDao) { return downloadDao.getCancelledDownloadsList() } + fun getErroredDownloads() : List { + return downloadDao.getErroredDownloadsList() + } + suspend fun deleteCancelled(){ downloadDao.deleteCancelled() } 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 fdb3b098..a141b471 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 @@ -3,7 +3,11 @@ package com.deniscerri.ytdlnis.database.viewmodel import android.app.Application import android.content.Context import android.content.SharedPreferences +import android.content.res.Configuration +import android.content.res.Resources import android.net.ConnectivityManager +import android.os.Environment +import android.util.DisplayMetrics import android.util.Log import android.widget.Toast import androidx.lifecycle.AndroidViewModel @@ -37,8 +41,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.io.File +import java.util.Locale import java.util.concurrent.TimeUnit + class DownloadViewModel(private val application: Application) : AndroidViewModel(application) { private val repository : DownloadRepository private val sharedPreferences: SharedPreferences @@ -58,6 +65,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel private val videoQualityPreference: String private val formatIDPreference: String + private val resources : Resources enum class Type { audio, video, command } @@ -80,7 +88,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel videoQualityPreference = sharedPreferences.getString("video_quality", application.getString(R.string.best_quality)).toString() formatIDPreference = sharedPreferences.getString("format_id", "").toString() - val videoFormat = App.instance.resources.getStringArray(R.array.video_formats) + val confTmp = Configuration(application.resources.configuration) + confTmp.locale = Locale(sharedPreferences.getString("app_language", "en")!!) + val metrics = DisplayMetrics() + resources = Resources(application.assets, metrics, confTmp) + + + val videoFormat = resources.getStringArray(R.array.video_formats) var videoContainer = sharedPreferences.getString("video_format", "Default") if (videoContainer == "Default") videoContainer = App.instance.getString(R.string.defaultValue) @@ -103,13 +117,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel var audioContainer = sharedPreferences.getString("audio_format", "mp3") if (audioContainer == "Default") audioContainer = App.instance.getString(R.string.defaultValue) bestAudioFormat = Format( - getApplication().resources.getString(R.string.best_quality), + resources.getString(R.string.best_quality), audioContainer!!, "", "", "", 0, - getApplication().resources.getString(R.string.best_quality) + resources.getString(R.string.best_quality) ) } @@ -138,9 +152,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } val downloadPath = when(type){ - Type.audio -> sharedPreferences.getString("music_path", getApplication().resources.getString(R.string.music_path)) - Type.video -> sharedPreferences.getString("video_path", getApplication().resources.getString(R.string.video_path)) - else -> sharedPreferences.getString("command_path", getApplication().resources.getString(R.string.command_path)) + Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath) + Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath) + else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath) } val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) @@ -233,9 +247,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } val downloadPath = when(historyItem.type){ - Type.audio -> sharedPreferences.getString("music_path", getApplication().resources.getString(R.string.music_path)) - Type.video -> sharedPreferences.getString("video_path", getApplication().resources.getString(R.string.video_path)) - else -> sharedPreferences.getString("command_path", getApplication().resources.getString(R.string.command_path)) + Type.audio -> sharedPreferences.getString("music_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Audio").absolutePath) + Type.video -> sharedPreferences.getString("video_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Video").absolutePath) + else -> sharedPreferences.getString("command_path", File(Environment.DIRECTORY_DOWNLOADS, "YTDLnis/Command").absolutePath) } val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet()) @@ -283,10 +297,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel formats.first { !it.format_note.contains("audio", ignoreCase = true) && it.format_id == formatIDPreference } }catch (e: Exception){ when (videoQualityPreference) { - getApplication().resources.getString(R.string.worst_quality) -> { + "worst" -> { theFormats.first {!it.format_note.contains("audio", ignoreCase = true) } } - getApplication().resources.getString(R.string.best_quality) -> { + "best" -> { theFormats.last {!it.format_note.contains("audio", ignoreCase = true) } } else -> { @@ -323,20 +337,20 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } fun getGenericAudioFormats() : MutableList{ - val audioFormats = App.instance.resources.getStringArray(R.array.audio_formats) + val audioFormats = resources.getStringArray(R.array.audio_formats) val formats = mutableListOf() var containerPreference = sharedPreferences.getString("audio_format", "Default") - if (containerPreference == "Default") containerPreference = App.instance.getString(R.string.defaultValue) + if (containerPreference == "Default") containerPreference = resources.getString(R.string.defaultValue) audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } return formats } fun getGenericVideoFormats() : MutableList{ - val videoFormats = App.instance.resources.getStringArray(R.array.video_formats) + val videoFormats = resources.getStringArray(R.array.video_formats) val formats = mutableListOf() var containerPreference = sharedPreferences.getString("video_format", "Default") - if (containerPreference == "Default") containerPreference = application.getString(R.string.defaultValue) - videoFormats.forEach { formats.add(Format(it, containerPreference!!,application.getString(R.string.defaultValue),"", "",0, it)) } + if (containerPreference == "Default") containerPreference = resources.getString(R.string.defaultValue) + videoFormats.forEach { formats.add(Format(it, containerPreference!!,resources.getString(R.string.defaultValue),"", "",0, it)) } return formats } @@ -378,6 +392,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel return repository.getCancelledDownloads() } + fun getErrored() : List { + return repository.getErroredDownloads() + } + private fun cloneFormat(item: Format) : Format { val string = Gson().toJson(item, Format::class.java) return Gson().fromJson(string, Format::class.java) @@ -407,10 +425,15 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } != null) { Toast.makeText(context, context.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show() }else{ - it.id = lastDownloadId - val insert = async {repository.insert(it)} - val id = insert.await() - it.id = id + if (it.id == 0L){ + it.id = lastDownloadId + val insert = async {repository.insert(it)} + val id = insert.await() + it.id = id + }else{ + repository.update(it) + } + queuedItems.add(it) } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt index 88cc71f9..2b0bb8fc 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadVideoFragment.kt @@ -161,7 +161,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr } } formats = allFormats.first().toMutableList() - uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1).map { it.format_note }) + uiUtil.populateFormatCard(formatCard, item.first(), item.drop(1)) downloadItem.format.container = container.editText?.text.toString() } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/MainSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/MainSettingsFragment.kt index 6537e23b..fd6d9289 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/MainSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/MainSettingsFragment.kt @@ -155,6 +155,7 @@ class MainSettingsFragment : PreferenceFragmentCompat() { "downloads" -> json.add("downloads", backupHistory()) "queued" -> json.add("queued", backupQueuedDownloads() ) "cancelled" -> json.add("cancelled", backupCancelledDownloads() ) + "errored" -> json.add("errored", backupErroredDownloads() ) "cookies" -> json.add("cookies", backupCookies() ) "templates" -> json.add("templates", backupCommandTemplates() ) "shortcuts" -> json.add("shortcuts", backupShortcuts() ) @@ -273,6 +274,20 @@ class MainSettingsFragment : PreferenceFragmentCompat() { return JsonArray() } + private suspend fun backupErroredDownloads() : JsonArray { + runCatching { + val items = withContext(Dispatchers.IO) { + downloadViewModel.getErrored() + } + val arr = JsonArray() + items.forEach { + arr.add(JsonParser().parse(Gson().toJson(it)).asJsonObject) + } + return arr + } + return JsonArray() + } + private suspend fun backupCookies() : JsonArray { runCatching { val items = withContext(Dispatchers.IO) { @@ -436,6 +451,23 @@ class MainSettingsFragment : PreferenceFragmentCompat() { } } + //erorred downloads restore + if(json.has("errored")){ + val items = json.getAsJsonArray("errored") + val errored = mutableListOf() + items.forEach { + val item = Gson().fromJson(it.toString().replace("^\"|\"$", ""), DownloadItem::class.java) + item.id = 0L + errored.add(item) + withContext(Dispatchers.IO){ + downloadViewModel.insert(item) + } + } + if(errored.isNotEmpty()){ + finalMessage.append("${getString(R.string.errored)}: ${errored.count()}\n") + } + } + //cookies restore if(json.has("cookies")){ val items = json.getAsJsonArray("cookies") diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt index b735f628..72a8ec52 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt @@ -3,8 +3,10 @@ package com.deniscerri.ytdlnis.util import android.content.Context import android.content.SharedPreferences import android.os.Looper +import android.text.Html import android.util.Log import android.widget.Toast +import androidx.core.text.HtmlCompat import androidx.preference.PreferenceManager import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.database.models.Format @@ -282,12 +284,8 @@ class InfoUtil(private val context: Context) { var video: ResultItem? = null try { val id = obj.getString("videoId") - val title = obj.getString("title").toString() - title.replace("'s", "'") - title.replace("&", "&") - val author = obj.getString("author").toString() - author.replace("'s", "'") - author.replace("&", "&") + val title = Html.fromHtml(obj.getString("title").toString()).toString() + val author = Html.fromHtml(obj.getString("author").toString()).toString() if (author.isBlank()) throw Exception() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt index 799cb559..69c404af 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/UiUtil.kt @@ -54,12 +54,12 @@ import java.util.Calendar class UiUtil(private val fileUtil: FileUtil) { @SuppressLint("SetTextI18n") - fun populateFormatCard(formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List?){ + fun populateFormatCard(formatCard : MaterialCardView, chosenFormat: Format, audioFormats: List?){ formatCard.findViewById(R.id.container).text = chosenFormat.container.uppercase() if (audioFormats.isNullOrEmpty()){ formatCard.findViewById(R.id.format_note).text = chosenFormat.format_note.uppercase() }else{ - val title = "${chosenFormat.format_note.uppercase()} + [${audioFormats.joinToString("/")}]" + val title = "${chosenFormat.format_note.uppercase()} + [${audioFormats.joinToString("/") { it.format_note }}]" formatCard.findViewById(R.id.format_note).text = title } formatCard.findViewById(R.id.format_id).text = "id: ${chosenFormat.format_id}" @@ -77,7 +77,9 @@ class UiUtil(private val fileUtil: FileUtil) { formatCard.findViewById(R.id.codec).visibility = View.VISIBLE formatCard.findViewById(R.id.codec).text = codec } - formatCard.findViewById(R.id.file_size).text = fileUtil.convertFileSize(chosenFormat.filesize) + var filesize = chosenFormat.filesize + if (!audioFormats.isNullOrEmpty()) filesize += audioFormats.sumOf { it.filesize } + formatCard.findViewById(R.id.file_size).text = fileUtil.convertFileSize(filesize) } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt index 117d0afe..6acfcb4f 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -184,19 +184,20 @@ class DownloadWorker( } } - request.addOption("--embed-metadata") if (downloadItem.audioPreferences.embedThumb) { request.addOption("--embed-thumbnail") request.addOption("--convert-thumbnails", "jpg") - try { - val config = File(context.cacheDir.absolutePath + "/downloads/${downloadItem.id}/config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt") - val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\"" - config.writeText(configData) - request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1") - request.addOption("--config", config.absolutePath) - } catch (ignored: Exception) {} + if (sharedPreferences.getBoolean("crop_thumbnail", true)){ + try { + val config = File(context.cacheDir.absolutePath + "/downloads/${downloadItem.id}/config" + downloadItem.title + "##" + downloadItem.format.format_id + ".txt") + val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\"" + config.writeText(configData) + request.addOption("--ppa", "ThumbnailsConvertor:-qmin 1 -q:v 1") + request.addOption("--config", config.absolutePath) + } catch (ignored: Exception) {} + } } request.addOption("--parse-metadata", "%(release_year,upload_date)s:%(meta_date)s") diff --git a/app/src/main/res/layout-sw600dp-land/activity_main.xml b/app/src/main/res/layout-sw600dp-land/activity_main.xml index e063d53a..c67d916c 100644 --- a/app/src/main/res/layout-sw600dp-land/activity_main.xml +++ b/app/src/main/res/layout-sw600dp-land/activity_main.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" tools:context=".MainActivity"> @string/downloads @string/in_queue @string/cancelled + @string/errored @string/cookies @string/command_templates @string/shortcuts @@ -713,6 +714,7 @@ downloads queued cancelled + errored cookies templates shortcuts diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 29457bd9..bcf28306 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -269,4 +269,6 @@ General Swipe Gestures Swipe items for certain actions + Crop Thumbnail + Crop the thumbnail into a square for audio downloads \ No newline at end of file diff --git a/app/src/main/res/xml/processing_preferences.xml b/app/src/main/res/xml/processing_preferences.xml index 084f3af3..ecaa26a1 100644 --- a/app/src/main/res/xml/processing_preferences.xml +++ b/app/src/main/res/xml/processing_preferences.xml @@ -74,6 +74,15 @@ app:summary="@string/embed_thumb_summary" app:title="@string/embed_thumb" /> + +