some changes
- fixed log removing some lines - added MASTER channel in yt-dlp updates - made errored downloads as a separate notification channel - fix notification language conflict for portugal brasil in worker notification - kept state of download card when going in landscape, even while updating data - add crop thumbnail to adjust audio preferences - fix command templates creation showing extra command checkboxes even though extra command is disabled - fix preferred audio codec disrupting preferred audio id
This commit is contained in:
parent
6eeb662a2c
commit
3e4d89577a
26 changed files with 316 additions and 170 deletions
|
|
@ -10,8 +10,8 @@ plugins {
|
|||
def properties = new Properties()
|
||||
def versionMajor = 1
|
||||
def versionMinor = 6
|
||||
def versionPatch = 8
|
||||
def versionBuild = 1 // bump for dogfood builds, public betas, etc.
|
||||
def versionPatch = 9
|
||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
||||
def versionExt = ""
|
||||
|
||||
if (versionBuild > 0){
|
||||
|
|
|
|||
|
|
@ -7,20 +7,22 @@ import android.content.DialogInterface
|
|||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.WindowInsets
|
||||
import android.widget.CheckBox
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.forEach
|
||||
|
|
@ -30,7 +32,6 @@ import androidx.fragment.app.FragmentContainerView
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
|
|
@ -42,7 +43,6 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||
import com.deniscerri.ytdlnis.ui.BaseActivity
|
||||
import com.deniscerri.ytdlnis.ui.HomeFragment
|
||||
import com.deniscerri.ytdlnis.ui.downloadcard.DownloadBottomSheetDialog
|
||||
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
|
||||
import com.deniscerri.ytdlnis.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.ThemeUtil
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import kotlinx.parcelize.Parcelize
|
|||
@Parcelize
|
||||
data class AudioPreferences(
|
||||
var embedThumb: Boolean = true,
|
||||
var cropThumb: Boolean? = null,
|
||||
var splitByChapters: Boolean = false,
|
||||
var sponsorBlockFilters: ArrayList<String> = arrayListOf()
|
||||
) : Parcelable
|
||||
|
|
|
|||
|
|
@ -41,25 +41,17 @@ class LogRepository(private val logDao: LogDao) {
|
|||
|
||||
suspend fun update(line: String, id: Long){
|
||||
runCatching {
|
||||
val item = getItem(id)
|
||||
val log = item.content
|
||||
//clean duplicate progress + add newline
|
||||
val lines = log.split("\n").toMutableList()
|
||||
run loop@ {
|
||||
for(i in lines.size - 1 downTo 0){
|
||||
val l = lines[i]
|
||||
if(l.contains("\\[download]( *?)(\\d)(.*?)".toRegex())){
|
||||
lines[i] = ""
|
||||
return@loop
|
||||
}
|
||||
}
|
||||
val item = getItem(id) ?: return
|
||||
val log = item.content ?: ""
|
||||
val lines = log.split("\n")
|
||||
//clean dublicate progress + add newline
|
||||
var newLine = line
|
||||
if (newLine.contains("[download")){
|
||||
newLine = "[download]" + line.split("[download]").last()
|
||||
}
|
||||
val l = if (line.contains("[download]")) {
|
||||
"[download]" + line.split("[download]").last()
|
||||
}else {
|
||||
line
|
||||
}
|
||||
item.content = lines.filter { it.isNotBlank() }.joinToString("\n") + "\n${l}"
|
||||
|
||||
val l = lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||
item.content = l
|
||||
logDao.update(item)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||
val cropThumb = sharedPreferences.getBoolean("crop_thumbnail", false)
|
||||
|
||||
var type = getDownloadType(givenType, resultItem.url)
|
||||
if(type == Type.command && commandTemplateDao.getTotalNumber() == 0) type = Type.video
|
||||
|
|
@ -257,7 +258,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
|
||||
val sponsorblock = sharedPreferences.getStringSet("sponsorblock_filters", emptySet())
|
||||
|
||||
val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!))
|
||||
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!))
|
||||
|
||||
val preferredAudioFormats = arrayListOf<String>()
|
||||
for (f in resultItem.formats.sortedBy { it.format_id }){
|
||||
|
|
@ -391,6 +392,8 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
val addChapters = sharedPreferences.getBoolean("add_chapters", false)
|
||||
val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false)
|
||||
val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false)
|
||||
val cropThumb = sharedPreferences.getBoolean("crop_thumbnail", false)
|
||||
|
||||
val customFileNameTemplate = when(historyItem.type) {
|
||||
Type.audio -> sharedPreferences.getString("file_name_template_audio", "%(uploader)s - %(title)s")
|
||||
Type.video -> sharedPreferences.getString("file_name_template", "%(uploader)s - %(title)s")
|
||||
|
|
@ -418,7 +421,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
else -> ""
|
||||
}
|
||||
|
||||
val audioPreferences = AudioPreferences(embedThumb, false, ArrayList(sponsorblock!!))
|
||||
val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!))
|
||||
val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs)
|
||||
val downloadPath = File(historyItem.downloadPath)
|
||||
val path = if (downloadPath.exists()) downloadPath.parent else defaultPath
|
||||
|
|
@ -625,6 +628,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
alarmScheduler.schedule()
|
||||
}
|
||||
|
||||
if (items.any { it.playlistTitle.isEmpty() } && items.size > 1){
|
||||
items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
|
||||
}
|
||||
|
||||
items.forEach {
|
||||
if (it.status != DownloadRepository.Status.ActivePaused.toString()) it.status = DownloadRepository.Status.Queued.toString()
|
||||
val currentCommand = infoUtil.buildYoutubeDLRequest(it)
|
||||
|
|
@ -679,14 +686,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
|||
}
|
||||
}
|
||||
|
||||
if (items.any { it.playlistTitle.isEmpty() }){
|
||||
items.forEachIndexed { index, it -> it.playlistTitle = "Various[${index+1}]" }
|
||||
withContext(Dispatchers.IO){
|
||||
dao.updateMultiple(items)
|
||||
}
|
||||
}
|
||||
|
||||
if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || items.any { it.downloadStartTime > 0L } ){
|
||||
if (!useScheduler || alarmScheduler.isDuringTheScheduledTime() || queuedItems.any { it.downloadStartTime > 0L } ){
|
||||
startDownloadWorker(queuedItems)
|
||||
|
||||
if(!useScheduler){
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.regex.Pattern
|
||||
|
|
@ -195,50 +196,58 @@ class ResultViewModel(application: Application) : AndroidViewModel(application)
|
|||
suspend fun updateItemData(res: ResultItem){
|
||||
if (updateResultDataJob == null || updateResultDataJob?.isCancelled == true || updateResultDataJob?.isCompleted == true){
|
||||
updateResultDataJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
kotlin.runCatching {
|
||||
updatingData.emit(true)
|
||||
val result = parseQueries(listOf(res.url))
|
||||
updatingData.emit(false)
|
||||
updateResultData.emit(result)
|
||||
}.onFailure {
|
||||
updatingData.emit(true)
|
||||
val result = parseQueries(listOf(res.url))
|
||||
updatingData.emit(false)
|
||||
updateResultData.emit(result)
|
||||
}
|
||||
}
|
||||
|
||||
updateResultDataJob?.start()
|
||||
updateResultDataJob?.invokeOnCompletion {
|
||||
if (it != null){
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
updatingData.emit(false)
|
||||
updateResultData.emit(mutableListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateResultDataJob?.start()
|
||||
}
|
||||
|
||||
suspend fun cancelUpdateItemData(){
|
||||
updateResultDataJob?.cancel()
|
||||
updatingData.emit(false)
|
||||
updateResultData.emit(null)
|
||||
updateResultDataJob?.cancel()
|
||||
}
|
||||
|
||||
suspend fun cancelUpdateFormatsItemData(){
|
||||
updateFormatsResultDataJob?.cancel()
|
||||
updatingFormats.emit(false)
|
||||
updateFormatsResultData.emit(null)
|
||||
updateFormatsResultDataJob?.cancel()
|
||||
}
|
||||
|
||||
suspend fun updateFormatItemData(result: ResultItem){
|
||||
updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
kotlin.runCatching {
|
||||
if (updateFormatsResultDataJob == null || updateFormatsResultDataJob?.isCancelled == true || updateFormatsResultDataJob?.isCompleted == true) {
|
||||
updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) {
|
||||
updatingFormats.emit(true)
|
||||
val formats = infoUtil.getFormats(result.url)
|
||||
updatingFormats.emit(false)
|
||||
if (formats.isNotEmpty()){
|
||||
if (formats.isNotEmpty() && isActive) {
|
||||
val res = getItemByURL(result.url)
|
||||
res.formats = formats.toMutableList()
|
||||
update(res)
|
||||
}
|
||||
updateFormatsResultData.emit(formats.toMutableList())
|
||||
}.onFailure {
|
||||
updatingFormats.emit(false)
|
||||
updateFormatsResultData.emit(mutableListOf())
|
||||
}
|
||||
updateFormatsResultDataJob?.start()
|
||||
updateFormatsResultDataJob?.invokeOnCompletion {
|
||||
if (it != null){
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
updatingFormats.emit(false)
|
||||
updateFormatsResultData.emit(mutableListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateFormatsResultDataJob?.start()
|
||||
}
|
||||
}
|
||||
|
|
@ -33,10 +33,13 @@ import com.deniscerri.ytdlnis.util.FileUtil
|
|||
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.android.material.textfield.TextInputLayout.END_ICON_NONE
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
|
@ -258,6 +261,9 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private
|
|||
embedThumbClicked = {
|
||||
downloadItem.audioPreferences.embedThumb = it
|
||||
},
|
||||
cropThumbClicked = {
|
||||
downloadItem.audioPreferences.cropThumb = it
|
||||
},
|
||||
splitByChaptersClicked = {
|
||||
downloadItem.audioPreferences.splitByChapters = it
|
||||
},
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import com.google.android.material.tabs.TabLayout
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -90,6 +91,33 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
commandTemplateDao = DBManager.getInstance(requireContext()).commandTemplateDao
|
||||
infoUtil = InfoUtil(requireContext())
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
|
||||
val res: ResultItem?
|
||||
val dwl: DownloadItem?
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 33){
|
||||
res = arguments?.getParcelable("result", ResultItem::class.java)
|
||||
dwl = arguments?.getParcelable("downloadItem", DownloadItem::class.java)
|
||||
}else{
|
||||
res = arguments?.getParcelable<ResultItem>("result")
|
||||
dwl = arguments?.getParcelable<DownloadItem>("downloadItem")
|
||||
}
|
||||
type = arguments?.getSerializable("type") as Type
|
||||
|
||||
if (res == null){
|
||||
dismiss()
|
||||
return
|
||||
}
|
||||
result = res
|
||||
currentDownloadItem = dwl
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
val downloadItem = getDownloadItem()
|
||||
arguments?.putParcelable("result", result)
|
||||
arguments?.putParcelable("downloadItem", downloadItem)
|
||||
arguments?.putSerializable("type", downloadItem.type)
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi", "InflateParams")
|
||||
|
|
@ -98,26 +126,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
view = LayoutInflater.from(context).inflate(R.layout.download_bottom_sheet, null)
|
||||
dialog.setContentView(view)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 33){
|
||||
arguments?.getParcelable("result", ResultItem::class.java)
|
||||
}else{
|
||||
arguments?.getParcelable<ResultItem>("result")
|
||||
}.apply {
|
||||
if (this == null){
|
||||
dismiss()
|
||||
return
|
||||
}else{
|
||||
result = this
|
||||
}
|
||||
}
|
||||
|
||||
type = arguments?.getSerializable("type") as Type
|
||||
currentDownloadItem = if (Build.VERSION.SDK_INT >= 33){
|
||||
arguments?.getParcelable("downloadItem", DownloadItem::class.java)
|
||||
}else{
|
||||
arguments?.getParcelable<DownloadItem>("downloadItem")
|
||||
}
|
||||
|
||||
dialog.setOnShowListener {
|
||||
behavior = BottomSheetBehavior.from(view.parent as View)
|
||||
val displayMetrics = DisplayMetrics()
|
||||
|
|
@ -297,16 +305,18 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
download!!.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
resultViewModel.cancelUpdateItemData()
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
withContext(Dispatchers.IO){
|
||||
resultViewModel.cancelUpdateItemData()
|
||||
resultViewModel.cancelUpdateFormatsItemData()
|
||||
}
|
||||
scheduleBtn.isEnabled = false
|
||||
download.isEnabled = false
|
||||
val item: DownloadItem = getDownloadItem()
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
scheduleBtn.isEnabled = false
|
||||
download.isEnabled = false
|
||||
val item: DownloadItem = getDownloadItem()
|
||||
runBlocking {
|
||||
downloadViewModel.queueDownloads(listOf(item))
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
||||
download.setOnLongClickListener {
|
||||
|
|
@ -371,7 +381,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch{
|
||||
lifecycleScope.launch{
|
||||
downloadViewModel.uiState.collectLatest { res ->
|
||||
if (res.errorMessage != null) {
|
||||
withContext(Dispatchers.Main){
|
||||
|
|
@ -427,6 +437,7 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
lifecycleScope.launch {
|
||||
resultViewModel.updatingFormats.collectLatest {
|
||||
if (it){
|
||||
delay(500)
|
||||
runCatching {
|
||||
val f1 = fragmentManager.findFragmentByTag("f0") as DownloadAudioFragment
|
||||
f1.view?.findViewById<LinearProgressIndicator>(R.id.format_loading_progress)?.visibility = View.VISIBLE
|
||||
|
|
@ -528,6 +539,11 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
}
|
||||
|
||||
private fun getDownloadItem(selectedTabPosition: Int = tabLayout.selectedTabPosition) : DownloadItem{
|
||||
return when(selectedTabPosition){
|
||||
0 -> {
|
||||
|
|
|
|||
|
|
@ -412,6 +412,11 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
|||
it.audioPreferences.embedThumb = enabled
|
||||
}
|
||||
},
|
||||
cropThumbClicked = {enabled ->
|
||||
items.forEach {
|
||||
it.audioPreferences.cropThumb = enabled
|
||||
}
|
||||
},
|
||||
splitByChaptersClicked = {enabled ->
|
||||
items.forEach {
|
||||
it.audioPreferences.splitByChapters = enabled
|
||||
|
|
|
|||
|
|
@ -34,12 +34,15 @@ import com.deniscerri.ytdlnis.util.FileUtil
|
|||
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||
import com.deniscerri.ytdlnis.util.UiUtil
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.android.material.textfield.TextInputLayout.END_ICON_CUSTOM
|
||||
import com.google.android.material.textfield.TextInputLayout.END_ICON_NONE
|
||||
import com.google.android.material.textfield.TextInputLayout.EndIconMode
|
||||
import com.google.gson.Gson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import androidx.core.os.bundleOf
|
|||
import androidx.core.view.forEach
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
|
|
@ -57,7 +58,11 @@ import com.google.android.material.chip.ChipGroup
|
|||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* A fragment representing a list of Items.
|
||||
|
|
@ -172,8 +177,32 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||
|
||||
|
||||
lifecycleScope.launch{
|
||||
downloadViewModel.uiState.collectLatest { res ->
|
||||
if (res.errorMessage != null) {
|
||||
withContext(Dispatchers.Main){
|
||||
kotlin.runCatching {
|
||||
UiUtil.handleDownloadsResponse(
|
||||
requireActivity(),
|
||||
requireActivity().lifecycleScope,
|
||||
requireActivity().supportFragmentManager,
|
||||
res,
|
||||
downloadViewModel,
|
||||
historyViewModel)
|
||||
}
|
||||
}
|
||||
downloadViewModel.uiState.value = DownloadViewModel.DownloadsUiState(
|
||||
errorMessage = null,
|
||||
actions = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
initMenu()
|
||||
initChips()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
|||
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.util.FileUtil
|
||||
import com.deniscerri.ytdlnis.util.UpdateUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
|
|
|||
|
|
@ -437,47 +437,51 @@ class InfoUtil(private val context: Context) {
|
|||
}
|
||||
|
||||
private fun getFormatsFromYTDL(url: String) : List<Format> {
|
||||
val request = YoutubeDLRequest(url)
|
||||
request.addOption("--print", "%(formats)s")
|
||||
request.addOption("--print", "%(duration)s")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
try {
|
||||
val request = YoutubeDLRequest(url)
|
||||
request.addOption("--print", "%(formats)s")
|
||||
request.addOption("--print", "%(duration)s")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--compat-options", "manifest-filesize-approx")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
FileUtil.getCookieFile(context){
|
||||
request.addOption("--cookies", it)
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
FileUtil.getCookieFile(context){
|
||||
request.addOption("--cookies", it)
|
||||
}
|
||||
|
||||
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
||||
val header = sharedPreferences.getString("useragent_header", "")
|
||||
if (useHeader && !header.isNullOrBlank()){
|
||||
request.addOption("--add-header","User-Agent:${header}")
|
||||
}
|
||||
}
|
||||
|
||||
val useHeader = sharedPreferences.getBoolean("use_header", false)
|
||||
val header = sharedPreferences.getString("useragent_header", "")
|
||||
if (useHeader && !header.isNullOrBlank()){
|
||||
request.addOption("--add-header","User-Agent:${header}")
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()) {
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val res = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
res.out.split(lineSeparator!!).toTypedArray()
|
||||
} catch (e: Exception) {
|
||||
arrayOf(res.out)
|
||||
}
|
||||
val json = results[0]
|
||||
val jsonArray = JSONArray(json)
|
||||
|
||||
return parseYTDLFormats(jsonArray)
|
||||
}catch (e: Exception){
|
||||
return listOf()
|
||||
}
|
||||
|
||||
val proxy = sharedPreferences.getString("proxy", "")
|
||||
if (proxy!!.isNotBlank()) {
|
||||
request.addOption("--proxy", proxy)
|
||||
}
|
||||
|
||||
|
||||
|
||||
val res = YoutubeDL.getInstance().execute(request)
|
||||
val results: Array<String?> = try {
|
||||
val lineSeparator = System.getProperty("line.separator")
|
||||
res.out.split(lineSeparator!!).toTypedArray()
|
||||
} catch (e: Exception) {
|
||||
arrayOf(res.out)
|
||||
}
|
||||
val json = results[0]
|
||||
val jsonArray = JSONArray(json)
|
||||
|
||||
return parseYTDLFormats(jsonArray)
|
||||
}
|
||||
|
||||
fun getFormatsMultiple(urls: List<String>, progress: (progress: List<Format>) -> Unit){
|
||||
|
||||
val urlsFile = File(context.cacheDir, "urls.txt")
|
||||
urlsFile.delete()
|
||||
urlsFile.createNewFile()
|
||||
|
|
@ -575,6 +579,7 @@ class InfoUtil(private val context: Context) {
|
|||
request.addOption("-j")
|
||||
request.addOption("--skip-download")
|
||||
request.addOption("-R", "1")
|
||||
request.addOption("--compat-options", "manifest-filesize-approx")
|
||||
request.addOption("--socket-timeout", "5")
|
||||
|
||||
if (sharedPreferences.getBoolean("use_cookies", false)){
|
||||
|
|
@ -1016,7 +1021,7 @@ class InfoUtil(private val context: Context) {
|
|||
val aCodecPref = "ba[acodec~='^($preferredAudioCodec)']"
|
||||
|
||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||
request.addOption("--trim-filenames", downDir.absolutePath.length + 120)
|
||||
request.addOption("--trim-filenames", downDir.absolutePath.length + 117)
|
||||
|
||||
if (downloadItem.SaveThumb) {
|
||||
request.addOption("--write-thumbnail")
|
||||
|
|
@ -1161,7 +1166,8 @@ class InfoUtil(private val context: Context) {
|
|||
if (downloadItem.audioPreferences.embedThumb) {
|
||||
request.addOption("--embed-thumbnail")
|
||||
if (! request.hasOption("--convert-thumbnails")) request.addOption("--convert-thumbnails", "jpg")
|
||||
if (sharedPreferences.getBoolean("crop_thumbnail", true)){
|
||||
val cropThumb = downloadItem.audioPreferences.cropThumb ?: sharedPreferences.getBoolean("crop_thumbnail", true)
|
||||
if (cropThumb){
|
||||
try {
|
||||
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##ffmpegCrop.txt")
|
||||
val configData = "--ppa \"ffmpeg: -c:v mjpeg -vf crop=\\\"'if(gt(ih,iw),iw,ih)':'if(gt(iw,ih),ih,iw)'\\\"\""
|
||||
|
|
@ -1222,11 +1228,14 @@ class InfoUtil(private val context: Context) {
|
|||
val f = StringBuilder()
|
||||
|
||||
if(!usingGenericFormat){
|
||||
if (preferredAudioCodec.isNotBlank()){
|
||||
f.append("$videof+$aCodecPref/")
|
||||
if (audiof.isBlank()){
|
||||
f.append("$videof/bv/best")
|
||||
}else{
|
||||
f.append("$videof+$audiof/")
|
||||
if (preferredAudioCodec.isNotBlank())
|
||||
f.append("$videof+$aCodecPref/")
|
||||
f.append("$videof/best")
|
||||
}
|
||||
val aa = if (audiof.isNotBlank()) "+$audiof" else ""
|
||||
f.append("$videof$aa/$videof/best")
|
||||
|
||||
if (audiof.contains("+")){
|
||||
request.addOption("--audio-multistreams")
|
||||
|
|
|
|||
|
|
@ -75,6 +75,14 @@ class NotificationUtil(var context: Context) {
|
|||
channel.description = description
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
|
||||
//finished or errored downloads
|
||||
name = resources.getString(R.string.errored_downloads)
|
||||
description =
|
||||
resources.getString(R.string.errored_download_notification_channel_description)
|
||||
channel = NotificationChannel(DOWNLOAD_ERRORED_CHANNEL_ID, name, NotificationManager.IMPORTANCE_HIGH)
|
||||
channel.description = description
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
|
||||
//misc
|
||||
name = resources.getString(R.string.misc)
|
||||
description = ""
|
||||
|
|
@ -264,7 +272,7 @@ class NotificationUtil(var context: Context) {
|
|||
logID: Long?,
|
||||
res: Resources
|
||||
) {
|
||||
val notificationBuilder = getBuilder(DOWNLOAD_FINISHED_CHANNEL_ID)
|
||||
val notificationBuilder = getBuilder(DOWNLOAD_ERRORED_CHANNEL_ID)
|
||||
|
||||
val bundle = Bundle()
|
||||
if (logID != null){
|
||||
|
|
@ -304,7 +312,7 @@ class NotificationUtil(var context: Context) {
|
|||
notificationBuilder.setContentIntent(errorPendingIntent)
|
||||
notificationBuilder.addAction(0, res.getString(R.string.logs), errorPendingIntent)
|
||||
}
|
||||
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
|
||||
notificationManager.notify(DOWNLOAD_ERRORED_NOTIFICATION_ID, notificationBuilder.build())
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -540,14 +548,16 @@ class NotificationUtil(var context: Context) {
|
|||
const val DOWNLOAD_SERVICE_CHANNEL_ID = "1"
|
||||
const val COMMAND_DOWNLOAD_SERVICE_CHANNEL_ID = "2"
|
||||
const val DOWNLOAD_FINISHED_CHANNEL_ID = "3"
|
||||
const val DOWNLOAD_WORKER_CHANNEL_ID = "5"
|
||||
const val DOWNLOAD_MISC_CHANNEL_ID = "4"
|
||||
const val DOWNLOAD_ERRORED_CHANNEL_ID = "6"
|
||||
|
||||
const val DOWNLOAD_FINISHED_NOTIFICATION_ID = 3
|
||||
const val DOWNLOAD_RESUME_NOTIFICATION_ID = 40000
|
||||
const val DOWNLOAD_UPDATING_NOTIFICATION_ID = 5
|
||||
const val FORMAT_UPDATING_NOTIFICATION_ID = 6
|
||||
const val FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 7
|
||||
const val DOWNLOAD_MISC_CHANNEL_ID = "4"
|
||||
const val DOWNLOAD_MISC_NOTIFICATION_ID = 4
|
||||
const val DOWNLOAD_WORKER_CHANNEL_ID = "5"
|
||||
const val DOWNLOAD_ERRORED_NOTIFICATION_ID = 6
|
||||
|
||||
private const val PROGRESS_MAX = 100
|
||||
private const val PROGRESS_CURR = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import androidx.appcompat.content.res.AppCompatResources
|
|||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isNotEmpty
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.fragment.app.FragmentManager
|
||||
|
|
@ -189,8 +190,13 @@ object UiUtil {
|
|||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
ok.isEnabled =
|
||||
!(title.editText!!.text.isEmpty() || content.editText!!.text.isEmpty())
|
||||
ok.isEnabled = title.editText!!.text.isNotEmpty() &&
|
||||
content.editText!!.text.isNotEmpty() &&
|
||||
if (extraCommandsSwitch.isChecked){
|
||||
((extraCommandsAudio.isChecked || extraCommandsVideo.isChecked))
|
||||
}else{
|
||||
true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -198,8 +204,14 @@ object UiUtil {
|
|||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
ok.isEnabled =
|
||||
(title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty())
|
||||
ok.isEnabled = title.editText!!.text.isNotEmpty() &&
|
||||
content.editText!!.text.isNotEmpty() &&
|
||||
if (extraCommandsSwitch.isChecked){
|
||||
((extraCommandsAudio.isChecked || extraCommandsVideo.isChecked))
|
||||
}else{
|
||||
true
|
||||
}
|
||||
|
||||
if (content.editText!!.text.isNotEmpty()){
|
||||
content.endIconDrawable = AppCompatResources.getDrawable(context, R.drawable.ic_delete_all)
|
||||
}else{
|
||||
|
|
@ -241,11 +253,11 @@ object UiUtil {
|
|||
}
|
||||
|
||||
extraCommandsAudio.setOnCheckedChangeListener { compoundButton, b ->
|
||||
ok.isEnabled = extraCommandsAudio.isChecked || extraCommandsVideo.isChecked
|
||||
ok.isEnabled = (extraCommandsAudio.isChecked || extraCommandsVideo.isChecked) && title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty()
|
||||
}
|
||||
|
||||
extraCommandsVideo.setOnCheckedChangeListener { compoundButton, b ->
|
||||
ok.isEnabled = extraCommandsAudio.isChecked || extraCommandsVideo.isChecked
|
||||
ok.isEnabled = (extraCommandsAudio.isChecked || extraCommandsVideo.isChecked) && title.editText!!.text.isNotEmpty() && content.editText!!.text.isNotEmpty()
|
||||
}
|
||||
|
||||
commandTemplateViewModel.shortcuts.observe(lifeCycle){
|
||||
|
|
@ -1210,6 +1222,7 @@ object UiUtil {
|
|||
context: Activity,
|
||||
items: List<DownloadItem>,
|
||||
embedThumbClicked: (Boolean) -> Unit,
|
||||
cropThumbClicked: (Boolean) -> Unit,
|
||||
splitByChaptersClicked: (Boolean) -> Unit,
|
||||
filenameTemplateSet: (String) -> Unit,
|
||||
sponsorBlockItemsSet: (Array<String>, List<Boolean>) -> Unit,
|
||||
|
|
@ -1218,9 +1231,18 @@ object UiUtil {
|
|||
|
||||
){
|
||||
val embedThumb = view.findViewById<Chip>(R.id.embed_thumb)
|
||||
val cropThumb = view.findViewById<Chip>(R.id.crop_thumb)
|
||||
|
||||
embedThumb!!.isChecked = items.all { it.audioPreferences.embedThumb }
|
||||
cropThumb.isVisible = embedThumb.isChecked
|
||||
embedThumb.setOnClickListener {
|
||||
embedThumbClicked(embedThumb.isChecked)
|
||||
cropThumb.isVisible = embedThumb.isChecked
|
||||
}
|
||||
|
||||
cropThumb!!.isChecked = items.all { it.audioPreferences.cropThumb == true }
|
||||
cropThumb.setOnClickListener {
|
||||
cropThumbClicked(cropThumb.isChecked)
|
||||
}
|
||||
|
||||
val splitByChapters = view.findViewById<Chip>(R.id.split_by_chapters)
|
||||
|
|
|
|||
|
|
@ -182,11 +182,15 @@ class UpdateUtil(var context: Context) {
|
|||
}
|
||||
updatingYTDL = true
|
||||
|
||||
val channelMap = mapOf(
|
||||
"stable" to YoutubeDL.UpdateChannel._STABLE,
|
||||
"nightly" to YoutubeDL.UpdateChannel._NIGHTLY,
|
||||
"master" to YoutubeDL.UpdateChannel._MASTER,
|
||||
)
|
||||
val channel = sharedPreferences.getString("ytdlp_source", "nightly")
|
||||
|
||||
try {
|
||||
YoutubeDL.getInstance().updateYoutubeDL(
|
||||
context, if (sharedPreferences.getBoolean("nightly_ytdl", false) ) YoutubeDL.UpdateChannel._NIGHTLY else YoutubeDL.UpdateChannel._STABLE
|
||||
).apply {
|
||||
YoutubeDL.getInstance().updateYoutubeDL(context, channelMap[channel] ?: YoutubeDL.UpdateChannel._NIGHTLY).apply {
|
||||
updatingYTDL = false
|
||||
}
|
||||
}catch (e: Exception){
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ class DownloadWorker(
|
|||
notificationUtil.notify(downloadItem.id.toInt(), notification)
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()
|
||||
|
||||
val request = infoUtil.buildYoutubeDLRequest(downloadItem)
|
||||
downloadItem.status = DownloadRepository.Status.Active.toString()
|
||||
|
|
@ -142,8 +143,6 @@ class DownloadWorker(
|
|||
dao.update(downloadItem)
|
||||
}
|
||||
|
||||
val noCache = !sharedPreferences.getBoolean("cache_downloads", true) && File(FileUtil.formatPath(downloadItem.downloadPath)).canWrite()
|
||||
|
||||
runCatching {
|
||||
YoutubeDL.getInstance().execute(request, downloadItem.id.toString()){ progress, _, line ->
|
||||
setProgressAsync(workDataOf("progress" to progress.toInt(), "output" to line.chunked(5000).first().toString(), "id" to downloadItem.id))
|
||||
|
|
@ -212,11 +211,17 @@ class DownloadWorker(
|
|||
//put download in history
|
||||
val incognito = sharedPreferences.getBoolean("incognito", false)
|
||||
if (!incognito) {
|
||||
val unixtime = System.currentTimeMillis() / 1000
|
||||
val file = File(finalPaths?.first()!!)
|
||||
downloadItem.format.filesize = file.length()
|
||||
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixtime, finalPaths.first() , downloadItem.website, downloadItem.format, downloadItem.id, commandString)
|
||||
historyDao.insert(historyItem)
|
||||
if (request.hasOption("--download-archive") && finalPaths == listOf(context.getString(R.string.unfound_file))) {
|
||||
Looper.prepare().run {
|
||||
Toast.makeText(context, resources.getString(R.string.download_already_exists), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}else{
|
||||
val unixTime = System.currentTimeMillis() / 1000
|
||||
val file = File(finalPaths?.first()!!)
|
||||
downloadItem.format.filesize = file.length()
|
||||
val historyItem = HistoryItem(0, downloadItem.url, downloadItem.title, downloadItem.author, downloadItem.duration, downloadItem.thumb, downloadItem.type, unixTime, finalPaths.first() , downloadItem.website, downloadItem.format, downloadItem.id, commandString)
|
||||
historyDao.insert(historyItem)
|
||||
}
|
||||
}
|
||||
|
||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||
|
|
|
|||
5
app/src/main/res/drawable/baseline_source_24.xml
Normal file
5
app/src/main/res/drawable/baseline_source_24.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?android:colorAccent" android:pathData="M20,6h-8l-2,-2H4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8C22,6.9 21.1,6 20,6zM14,16H6v-2h8V16zM18,12H6v-2h12V12z"/>
|
||||
</vector>
|
||||
|
|
@ -33,6 +33,15 @@
|
|||
android:checked="false"
|
||||
android:text="@string/embed_thumb"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/crop_thumb"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="false"
|
||||
android:visibility="gone"
|
||||
android:text="@string/crop_thumb"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/split_by_chapters"
|
||||
style="@style/Widget.Material3.Chip.Filter.Elevated"
|
||||
|
|
@ -41,22 +50,6 @@
|
|||
android:checked="false"
|
||||
android:text="@string/split_by_chapters"/>
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/cut"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_cut"
|
||||
android:text="@string/cut"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
|
@ -103,6 +96,21 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/cut"
|
||||
style="@style/Widget.Material3.Chip.Assist.Elevated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
app:chipIconVisible="true"
|
||||
app:chipIcon="@drawable/ic_cut"
|
||||
android:text="@string/cut"
|
||||
android:minWidth="30dp"
|
||||
app:cornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
android:layout_marginBottom="10dp"
|
||||
android:checkable="true"
|
||||
android:paddingHorizontal="20dp"
|
||||
android:paddingVertical="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
|
|
@ -18,8 +18,9 @@
|
|||
style="@style/Widget.Material3.Button.TonalButton.Icon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:cornerRadius="10dp"
|
||||
android:autoLink="all"
|
||||
android:gravity="start"
|
||||
android:gravity="center_vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginEnd="10dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/already_exists_edit"
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:visibility="gone"
|
||||
android:checked="true"
|
||||
android:text="@string/audio" />
|
||||
|
||||
|
|
@ -141,6 +142,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:visibility="gone"
|
||||
android:text="@string/video" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
|||
|
|
@ -308,7 +308,6 @@
|
|||
<string name="export_file">ファイルのエクスポート</string>
|
||||
<string name="schedule">スケジュール</string>
|
||||
<string name="user_agent_header">User-Agent ヘッダー</string>
|
||||
<string name="skip">スキップ</string>
|
||||
<string name="license">ライセンス</string>
|
||||
<string name="download_already_exists_summary">再度ダウンロードをしますか\?</string>
|
||||
<string name="modify_download_card">ダウンロードカードの変更</string>
|
||||
|
|
|
|||
|
|
@ -1081,4 +1081,17 @@
|
|||
<item>cookies</item>
|
||||
</array>
|
||||
|
||||
<array name="ytdlp_source">
|
||||
<item>@string/defaultValue</item>
|
||||
<item>@string/update_ytdl_nightly</item>
|
||||
<item>@string/update_ytdl_master</item>
|
||||
</array>
|
||||
|
||||
|
||||
<array name="ytdlp_source_values">
|
||||
<item>stable</item>
|
||||
<item>nightly</item>
|
||||
<item>master</item>
|
||||
</array>
|
||||
|
||||
</resources>
|
||||
|
|
@ -232,7 +232,9 @@
|
|||
<string name="monochrome">Monochrome</string>
|
||||
<string name="downloaded">"Downloaded: "</string>
|
||||
<string name="finished_download_notification_channel_name">Finished Downloads</string>
|
||||
<string name="finished_download_notification_channel_description">Notification for finished or faulty downloads</string>
|
||||
<string name="finished_download_notification_channel_description">Notification for finished downloads</string>
|
||||
<string name="errored_downloads">Errored Downloads</string>
|
||||
<string name="errored_download_notification_channel_description">Notification for faulty downloads</string>
|
||||
<string name="quick_download">Quick Download</string>
|
||||
<string name="quick_download_summary">Don\'t fetch data in the download card</string>
|
||||
<string name="remove_audio">Remove Audio</string>
|
||||
|
|
@ -319,4 +321,6 @@
|
|||
<string name="ignore">Ignore</string>
|
||||
<string name="modify_download_card">Modify Download Card</string>
|
||||
<string name="export_file">Export File</string>
|
||||
<string name="ytdl_source">yt-dlp Source</string>
|
||||
<string name="update_ytdl_master">Master Version of yt-dlp</string>
|
||||
</resources>
|
||||
|
|
@ -21,13 +21,15 @@
|
|||
app:key="ytdl-version"
|
||||
app:title="@string/ytdl_version"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
android:defaultValue="true"
|
||||
app:icon="@drawable/ic_nightly"
|
||||
app:key="nightly_ytdl"
|
||||
android:title="@string/update_ytdl_nightly"
|
||||
/>
|
||||
<ListPreference
|
||||
android:defaultValue="nightly"
|
||||
android:entries="@array/ytdlp_source"
|
||||
android:entryValues="@array/ytdlp_source_values"
|
||||
android:icon="@drawable/baseline_source_24"
|
||||
app:key="ytdlp_source"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
app:title="@string/ytdl_source" />
|
||||
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:widgetLayout="@layout/preferece_material_switch"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ buildscript {
|
|||
commonsIoVer = '2.5'
|
||||
// supports java 1.6
|
||||
commonsCompressVer = '1.12'
|
||||
youtubedlAndroidVer = "23b26d55f8"
|
||||
youtubedlAndroidVer = "-SNAPSHOT"
|
||||
workVer = "2.8.1"
|
||||
composeVer = '1.4.2'
|
||||
kotlinVer = "1.7.21"
|
||||
|
|
|
|||
Loading…
Reference in a new issue