add video embed thumbnail toggle in adjust video

This commit is contained in:
deniscerri 2025-10-04 14:48:00 +02:00
parent 6756ad847c
commit 10b17337b3
No known key found for this signature in database
GPG key ID: 95C43D517D830350
12 changed files with 208 additions and 80 deletions

View file

@ -18,5 +18,6 @@ data class VideoPreferences (
var recodeVideo: Boolean = false, var recodeVideo: Boolean = false,
var liveFromStart: Boolean = false, var liveFromStart: Boolean = false,
var waitForVideoMinutes: Int = 0, var waitForVideoMinutes: Int = 0,
var compatibilityMode: Boolean = false var compatibilityMode: Boolean = false,
var embedThumbnail: Boolean = false,
) : Parcelable ) : Parcelable

View file

@ -270,6 +270,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
val addChapters = sharedPreferences.getBoolean("add_chapters", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false)
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
val videoEmbedThumb = sharedPreferences.getBoolean("video_embed_thumbnail", false)
val cropThumb = sharedPreferences.getBoolean("crop_thumbnail", false) val cropThumb = sharedPreferences.getBoolean("crop_thumbnail", false)
var type = getDownloadType(givenType, resultItem.url) var type = getDownloadType(givenType, resultItem.url)
@ -310,7 +311,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
saveAutoSubs, saveAutoSubs,
subsLanguages, subsLanguages,
audioFormatIDs = preferredAudioFormats, audioFormatIDs = preferredAudioFormats,
recodeVideo = recodeVideo recodeVideo = recodeVideo,
embedThumbnail = videoEmbedThumb
) )
val extraCommands = when(type){ val extraCommands = when(type){

View file

@ -644,6 +644,10 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
items.forEach { it.videoPreferences.splitByChapters = checked } items.forEach { it.videoPreferences.splitByChapters = checked }
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }
}, },
embedThumbnailClicked = {checked ->
items.forEach { it.videoPreferences.embedThumbnail = checked }
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }
},
saveThumbnailClicked = {checked -> saveThumbnailClicked = {checked ->
items.forEach { it.SaveThumb = checked } items.forEach { it.SaveThumb = checked }
CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } } CoroutineScope(Dispatchers.IO).launch { items.forEach { downloadViewModel.updateDownload(it) } }

View file

@ -363,6 +363,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private
splitByChaptersClicked = { splitByChaptersClicked = {
downloadItem.videoPreferences.splitByChapters = it downloadItem.videoPreferences.splitByChapters = it
}, },
embedThumbnailClicked = {
downloadItem.videoPreferences.embedThumbnail = it
},
saveThumbnailClicked = { saveThumbnailClicked = {
downloadItem.SaveThumb = it downloadItem.SaveThumb = it
}, },

View file

@ -297,9 +297,9 @@ object Extensions {
val badge = BadgeDrawable.create(context).apply { val badge = BadgeDrawable.create(context).apply {
number = nr number = nr
backgroundColor = context.getColor(R.color.white) backgroundColor = context.getColor(R.color.white)
verticalOffset = 25 verticalOffset = 30
horizontalOffset = horizontalOffset =
if (nr < 10) dp(App.instance.resources, 17f) if (nr < 10) dp(App.instance.resources, 16f)
else if (nr < 100) dp(App.instance.resources, 19f) else if (nr < 100) dp(App.instance.resources, 19f)
else dp(App.instance.resources, 22f) else dp(App.instance.resources, 22f)
} }

View file

@ -1306,6 +1306,7 @@ object UiUtil {
embedSubsClicked : (Boolean) -> Unit, embedSubsClicked : (Boolean) -> Unit,
addChaptersClicked: (Boolean) -> Unit, addChaptersClicked: (Boolean) -> Unit,
splitByChaptersClicked: (Boolean) -> Unit, splitByChaptersClicked: (Boolean) -> Unit,
embedThumbnailClicked: (Boolean) -> Unit,
saveThumbnailClicked: (Boolean) -> Unit, saveThumbnailClicked: (Boolean) -> Unit,
sponsorBlockItemsSet: (values: Array<String>, checkedItems: List<Boolean>) -> Unit, sponsorBlockItemsSet: (values: Array<String>, checkedItems: List<Boolean>) -> Unit,
cutClicked: (VideoCutListener) -> Unit, cutClicked: (VideoCutListener) -> Unit,
@ -1325,42 +1326,109 @@ object UiUtil {
){ ){
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
val addChapters = view.findViewById<Chip>(R.id.add_chapters) val adjustThumbnail = view.findViewById<Chip>(R.id.thumbnail)
addChapters!!.isChecked = items.all { it.videoPreferences.addChapters } fun calculateAdjustThumbnailChangeCount() {
addChapters.setOnClickListener{ if (items.size == 1) {
addChaptersClicked(addChapters.isChecked) val firstItem = items.first()
} val count = listOf(
firstItem.SaveThumb,
val splitByChapters = view.findViewById<Chip>(R.id.split_by_chapters) firstItem.videoPreferences.embedThumbnail
if(items.size == 1 && items[0].downloadSections.isNotBlank()){ )
splitByChapters.isEnabled = false adjustThumbnail.createBadge(context, count.filter { it }.size)
splitByChapters.isChecked = false
}else{
splitByChapters!!.isChecked = items.all { it.videoPreferences.splitByChapters }
}
if (splitByChapters.isChecked){
items.forEach { it.videoPreferences.addChapters = false }
addChapters.isChecked = false
addChapters.isEnabled = false
addChaptersClicked(false)
}
splitByChapters.setOnClickListener {
if (splitByChapters.isChecked){
addChapters.isEnabled = false
addChapters.isChecked = false
addChaptersClicked(false)
}else{
addChapters.isEnabled = true
} }
splitByChaptersClicked(splitByChapters.isChecked) }
calculateAdjustThumbnailChangeCount()
adjustThumbnail.setOnClickListener {
val adjustThumbnailView = context.layoutInflater.inflate(R.layout.video_thumbnail_download_preferences_dialog, null)
val embedThumb = adjustThumbnailView.findViewById<MaterialSwitch>(R.id.embed_thumbnail)
val saveThumb = adjustThumbnailView.findViewById<MaterialSwitch>(R.id.save_thumbnail)
embedThumb!!.isChecked = items.all { it.videoPreferences.embedThumbnail }
embedThumb.setOnClickListener {
embedThumbnailClicked(embedThumb.isChecked)
items.forEach { it.videoPreferences.embedThumbnail = embedThumb.isChecked }
calculateAdjustThumbnailChangeCount()
}
saveThumb!!.isChecked = items.all { it.SaveThumb }
saveThumb.setOnClickListener {
saveThumbnailClicked(saveThumb.isChecked)
items.forEach { it.SaveThumb = saveThumb.isChecked }
calculateAdjustThumbnailChangeCount()
}
val adjustThumbnailDialog = MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.thumbnail))
.setView(adjustThumbnailView)
.setIcon(R.drawable.ic_image)
.setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> }
adjustThumbnailDialog.show()
} }
val saveThumbnail = view.findViewById<Chip>(R.id.save_thumbnail)
saveThumbnail!!.isChecked = items.all { it.SaveThumb } val adjustChapters = view.findViewById<Chip>(R.id.chapters)
saveThumbnail.setOnClickListener { fun calculateAdjustChaptersChangeCount() {
saveThumbnailClicked(saveThumbnail.isChecked) if (items.size == 1) {
val firstItem = items.first()
val count = listOf(
firstItem.videoPreferences.addChapters,
firstItem.videoPreferences.splitByChapters
)
adjustChapters.createBadge(context, count.filter { it }.size)
}
} }
if(items.size == 1 && items[0].downloadSections.isNotBlank()){
items.forEach { it.videoPreferences.splitByChapters = false }
}
calculateAdjustChaptersChangeCount()
adjustChapters.setOnClickListener {
val adjustChapterView = context.layoutInflater.inflate(R.layout.video_chapter_download_preferences_dialog, null)
val addChapters = adjustChapterView.findViewById<MaterialSwitch>(R.id.add_chapters)
addChapters!!.isChecked = items.all { it.videoPreferences.addChapters }
addChapters.setOnClickListener{
addChaptersClicked(addChapters.isChecked)
items.forEach { it.videoPreferences.addChapters = addChapters.isChecked }
calculateAdjustChaptersChangeCount()
}
val splitByChapters = adjustChapterView.findViewById<MaterialSwitch>(R.id.split_by_chapters)
if(items.size == 1 && items[0].downloadSections.isNotBlank()){
splitByChapters.isEnabled = false
splitByChapters.isChecked = false
}else{
splitByChapters!!.isChecked = items.all { it.videoPreferences.splitByChapters }
}
if (splitByChapters.isChecked){
items.forEach { it.videoPreferences.addChapters = false }
addChaptersClicked(false)
addChapters.isChecked = false
addChapters.isEnabled = false
}
splitByChapters.setOnClickListener {
if (splitByChapters.isChecked){
addChapters.isEnabled = false
addChapters.isChecked = false
items.forEach { it.videoPreferences.addChapters = false }
addChaptersClicked(false)
}else{
addChapters.isEnabled = true
}
splitByChaptersClicked(splitByChapters.isChecked)
calculateAdjustChaptersChangeCount()
}
val adjustChapterDialog = MaterialAlertDialogBuilder(context)
.setTitle(context.getString(R.string.thumbnail))
.setView(adjustChapterView)
.setIcon(R.drawable.ic_chapters)
.setNegativeButton(context.resources.getString(R.string.dismiss)) { _: DialogInterface?, _: Int -> }
adjustChapterDialog.show()
}
val adjustSubtitles = view.findViewById<Chip>(R.id.adjust_subtitles) val adjustSubtitles = view.findViewById<Chip>(R.id.adjust_subtitles)
fun calculateAdjustSubtitlesChangeCount() { fun calculateAdjustSubtitlesChangeCount() {
@ -1538,6 +1606,13 @@ object UiUtil {
val adjustLiveStream = view.findViewById<Chip>(R.id.adjust_live_stream) val adjustLiveStream = view.findViewById<Chip>(R.id.adjust_live_stream)
fun calculateLiveStreamChanges() {
if (items.size == 1) {
val count = listOf(items.first().videoPreferences.waitForVideoMinutes > 0, items.first().videoPreferences.liveFromStart)
adjustLiveStream.createBadge(context, count.filter { it }.size)
}
}
calculateLiveStreamChanges()
if (items.size == 1) { if (items.size == 1) {
adjustLiveStream.setOnClickListener { adjustLiveStream.setOnClickListener {
val adjustLiveStreamView = context.layoutInflater.inflate(R.layout.livestream_download_preferences_dialog, null) val adjustLiveStreamView = context.layoutInflater.inflate(R.layout.livestream_download_preferences_dialog, null)
@ -1566,6 +1641,8 @@ object UiUtil {
liveFromStartSwitch.setOnCheckedChangeListener { compoundButton, b -> liveFromStartSwitch.setOnCheckedChangeListener { compoundButton, b ->
liveFromStart(b) liveFromStart(b)
items.forEach { it.videoPreferences.liveFromStart = b }
calculateLiveStreamChanges()
} }
waitForVideoSwitch.setOnCheckedChangeListener { compoundButton, b -> waitForVideoSwitch.setOnCheckedChangeListener { compoundButton, b ->
@ -1577,6 +1654,8 @@ object UiUtil {
} }
waitForVideo(b, number) waitForVideo(b, number)
items.forEach { it.videoPreferences.waitForVideoMinutes = if (b) number else 0 }
calculateLiveStreamChanges()
} }
liveFromStartSwitch.isChecked = item.videoPreferences.liveFromStart liveFromStartSwitch.isChecked = item.videoPreferences.liveFromStart
@ -1647,33 +1726,22 @@ object UiUtil {
if(duration.isNotEmpty() && duration != "-1" && duration != "0:00"){ if(duration.isNotEmpty() && duration != "-1" && duration != "0:00"){
val downloadItem = items[0] val downloadItem = items[0]
cut.alpha = 1f cut.alpha = 1f
if (downloadItem.downloadSections.isNotBlank()) cut.text = downloadItem.downloadSections if (downloadItem.downloadSections.isNotBlank()) cut.createBadge(context, downloadItem.downloadSections.count())
val cutVideoListener = object : VideoCutListener { val cutVideoListener = object : VideoCutListener {
override fun onChangeCut(list: List<String>) { override fun onChangeCut(list: List<String>) {
cut.createBadge(context, list.size)
if (list.isEmpty()){ if (list.isEmpty()){
cutValueChanged("") cutValueChanged("")
cut.text = context.getString(R.string.cut)
splitByChapters.isEnabled = true
splitByChapters.isChecked = downloadItem.videoPreferences.splitByChapters
if (splitByChapters.isChecked){
addChapters.isEnabled = false
addChapters.isChecked = false
}else{
addChapters.isEnabled = true
}
}else{ }else{
var value = "" var value = ""
list.forEach { list.forEach {
value += "$it;" value += "$it;"
} }
cutValueChanged(value) cutValueChanged(value)
cut.text = value.dropLast(1)
splitByChapters.isEnabled = false items.forEach { it.videoPreferences.splitByChapters = false }
splitByChapters.isChecked = false calculateAdjustChaptersChangeCount()
addChapters.isEnabled = true
} }
} }
@ -1856,12 +1924,15 @@ object UiUtil {
val duration = downloadItem.duration val duration = downloadItem.duration
if (duration.isNotEmpty() && duration != "-1" && duration != "0:00"){ if (duration.isNotEmpty() && duration != "-1" && duration != "0:00"){
cut.alpha = 1f cut.alpha = 1f
if (downloadItem.downloadSections.isNotBlank()) cut.text = downloadItem.downloadSections if (downloadItem.downloadSections.isNotBlank()) {
cut.createBadge(context, downloadItem.downloadSections.count())
}
val cutVideoListener = object : VideoCutListener { val cutVideoListener = object : VideoCutListener {
override fun onChangeCut(list: List<String>) { override fun onChangeCut(list: List<String>) {
cut.createBadge(context, list.size)
if (list.isEmpty()){ if (list.isEmpty()){
cutValueChanged("") cutValueChanged("")
cut.text = context.getString(R.string.cut)
splitByChapters.isEnabled = true splitByChapters.isEnabled = true
splitByChapters.isChecked = downloadItem.audioPreferences.splitByChapters splitByChapters.isChecked = downloadItem.audioPreferences.splitByChapters
@ -1871,7 +1942,6 @@ object UiUtil {
value += "$it;" value += "$it;"
} }
cutValueChanged(value) cutValueChanged(value)
cut.text = value.dropLast(1)
splitByChapters.isEnabled = false splitByChapters.isEnabled = false
splitByChapters.isChecked = false splitByChapters.isChecked = false

View file

@ -1155,8 +1155,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
} }
if (!listOf("webm", "avi", "flv", "gif").contains(outputContainer.lowercase())) { if (!listOf("webm", "avi", "flv", "gif").contains(outputContainer.lowercase())) {
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) if (downloadItem.videoPreferences.embedThumbnail) {
if (embedThumb) {
metadataCommands.addOption("--embed-thumbnail") metadataCommands.addOption("--embed-thumbnail")
if (!request.toString().contains("--convert-thumbnails")) request.addOption("--convert-thumbnails", thumbnailFormat!!) if (!request.toString().contains("--convert-thumbnails")) request.addOption("--convert-thumbnails", thumbnailFormat!!)
} }

View file

@ -26,31 +26,40 @@
app:singleLine="true"> app:singleLine="true">
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/save_thumbnail" android:id="@+id/thumbnail"
style="@style/Widget.Material3.Chip.Filter.Elevated" style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="false" android:checked="false"
app:chipIcon="@drawable/ic_image"
android:outlineProvider="none" android:outlineProvider="none"
android:text="@string/save_thumb" /> android:text="@string/thumbnail" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/add_chapters" android:id="@+id/chapters"
style="@style/Widget.Material3.Chip.Filter.Elevated" style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="false" android:checked="false"
app:chipIcon="@drawable/ic_chapters"
android:outlineProvider="none" android:outlineProvider="none"
android:text="@string/add_chapter" /> android:text="@string/chapters" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/split_by_chapters" android:id="@+id/adjust_subtitles"
style="@style/Widget.Material3.Chip.Filter.Elevated" style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="false" android:gravity="center"
android:text="@string/subtitles"
app:chipIconVisible="true"
app:chipIcon="@drawable/ic_subtitles"
android:minWidth="30dp"
app:cornerRadius="10dp"
android:outlineProvider="none" android:outlineProvider="none"
android:text="@string/split_by_chapters"/> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</com.google.android.material.chip.ChipGroup> </com.google.android.material.chip.ChipGroup>
@ -68,21 +77,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:singleLine="true"> app:singleLine="true">
<com.google.android.material.chip.Chip
android:id="@+id/adjust_subtitles"
style="@style/Widget.Material3.Chip.Assist.Elevated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/subtitles"
app:chipIconVisible="true"
app:chipIcon="@drawable/ic_subtitles"
android:minWidth="30dp"
app:cornerRadius="10dp"
android:outlineProvider="none"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
android:id="@+id/remove_audio" android:id="@+id/remove_audio"

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="wrap_content">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/add_chapters"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:text="@string/add_chapters" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/split_by_chapters"
android:layout_width="match_parent"
android:layout_marginHorizontal="20dp"
android:text="@string/split_by_chapters"
android:layout_height="wrap_content" />
</LinearLayout>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_height="wrap_content">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/embed_thumbnail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:text="@string/embed_thumb" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/save_thumbnail"
android:layout_width="match_parent"
android:layout_marginHorizontal="20dp"
android:text="@string/save_thumb"
android:layout_height="wrap_content" />
</LinearLayout>

View file

@ -499,4 +499,5 @@
<string name="url_regex">URL Regex</string> <string name="url_regex">URL Regex</string>
<string name="disable_flat_playlist">Disable flat playlist</string> <string name="disable_flat_playlist">Disable flat playlist</string>
<string name="disable_flat_playlist_summary">Don\'t use --flat-playlist. Slower data fetching, but more data rich</string> <string name="disable_flat_playlist_summary">Don\'t use --flat-playlist. Slower data fetching, but more data rich</string>
<string name="chapters">Chapters</string>
</resources> </resources>

View file

@ -214,6 +214,14 @@
app:summary="@string/save_thumb_summary" app:summary="@string/save_thumb_summary"
app:title="@string/save_thumb" /> app:title="@string/save_thumb" />
<SwitchPreferenceCompat
android:widgetLayout="@layout/preferece_material_switch"
app:defaultValue="false"
app:icon="@drawable/ic_image"
app:key="video_embed_thumbnail"
app:summary="@string/embed_thumb_summary"
app:title="@string/embed_thumb" />
<ListPreference <ListPreference
android:defaultValue="jpg" android:defaultValue="jpg"
android:entries="@array/thumbnail_containers" android:entries="@array/thumbnail_containers"