From c05224336a78bb28dcdd667230c23178df7b8650 Mon Sep 17 00:00:00 2001 From: deniscerri <64997243+deniscerri@users.noreply.github.com> Date: Wed, 27 Sep 2023 19:13:56 +0200 Subject: [PATCH] remove trim filenames command from command type fixed app not showing formats when it has generic formats fixed author getting written as NA for kick videos added separate channel for the worker notification that users can turn off --- README-sq.md | 133 +++++++++++ README.MD | 2 + app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 2 +- .../com/deniscerri/ytdlnis/MainActivity.kt | 6 + .../database/viewmodel/DownloadViewModel.kt | 35 +-- .../com/deniscerri/ytdlnis/ui/BaseActivity.kt | 5 - .../com/deniscerri/ytdlnis/ui/HomeFragment.kt | 11 +- .../downloadcard/CutVideoBottomSheetDialog.kt | 3 +- .../ui/downloadcard/DownloadAudioFragment.kt | 15 +- .../downloadcard/DownloadCommandFragment.kt | 2 - .../DownloadMultipleBottomSheetDialog.kt | 7 +- .../ui/downloadcard/DownloadVideoFragment.kt | 23 +- .../FormatSelectionBottomSheetDialog.kt | 2 +- .../more/settings/GeneralSettingsFragment.kt | 44 +--- .../com/deniscerri/ytdlnis/util/InfoUtil.kt | 210 ++++++++++-------- .../ytdlnis/util/NotificationUtil.kt | 18 +- .../com/deniscerri/ytdlnis/util/UpdateUtil.kt | 11 +- .../deniscerri/ytdlnis/work/DownloadWorker.kt | 14 +- app/src/main/res/values/arrays.xml | 19 +- app/src/main/res/values/strings.xml | 2 +- app/src/main/res/xml/general_preferences.xml | 7 - .../main/res/xml/processing_preferences.xml | 2 +- 23 files changed, 361 insertions(+), 215 deletions(-) create mode 100644 README-sq.md diff --git a/README-sq.md b/README-sq.md new file mode 100644 index 00000000..a680986a --- /dev/null +++ b/README-sq.md @@ -0,0 +1,133 @@ +

+
+ YTDLnis +

+ +
+ English +   |    + Shqip +
+ +

+ YTDLnis është një shkarkues video/audio falas dhe me burim të hapur duke përdorur yt-dlp për Android 6.0 dhe më lart. +

+ +
+ +[![Github Yükləyicisi](https://custom-icon-badges.herokuapp.com/badge/Download-blue?style=for-the-badge&logo=download&logoColor=white)](https://github.com/deniscerri/ytdlnis/releases/latest) +[![IzzyOnDroid Anbarı](https://custom-icon-badges.herokuapp.com/badge/IzzyOnDroid%20Repo-red?style=for-the-badge&logo=download&logoColor=white)](https://android.izzysoft.de/repo/apk/com.deniscerri.ytdl) +[![UpToDown](https://custom-icon-badges.herokuapp.com/badge/UpToDown-green?style=for-the-badge&logo=download&logoColor=white)](https://ytdlnis.en.uptodown.com/android/download) + +![CI](https://github.com/deniscerri/ytdlnis/actions/workflows/android.yml/badge.svg?branch=main&event=pull) +[![önizləmə buraxılışı](https://img.shields.io/github/release/deniscerri/ytdlnis.svg?maxAge=3600&include_prereleases&label=preview)](https://github.com/deniscerri/ytdlnis/releases) +[![yükləyici](https://img.shields.io/github/downloads/deniscerri/ytdlnis/total?style=flat-square)](https://github.com/deniscerri/ytdlnis/releases) +[![Tərcümə vəziyyəti](https://hosted.weblate.org/widgets/ytdlnis/-/svg-badge.svg)](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget) +[![icma](https://img.shields.io/badge/Discord-YTDLnis-blueviolet?style=flat-square&logo=discord)](https://discord.gg/WW3KYWxAPm) +[![icma](https://img.shields.io/badge/Telegram-YTDLnis-blue?style=flat-square&logo=telegram)](https://t.me/ytdlnis) +[![icma](https://img.shields.io/badge/Telegram-Updates-red?style=flat-square&logo=telegram)](https://t.me/ytdlnisupdates) + + + + + +
+ +## 💡 Veçoritë: + +- shkarko skedarë audio/video nga më shumë se 1000 website +- përpunoni lista +- modifikoni çdo artikull të listës veç e veç si me një artikull normal shkarkimi. +- zgjidhni një format të përbashkët për të gjithë artikujt dhe/ose zgjidhni formate të shumta audio në rast se po i shkarkoni si video +- zgjidhni një lloj shkarkimi për të gjithë artikujt +- zgjidhni një shabllon të emrit të skedarit për të gjithë artikujt +- përditëso llojin i shkarkimit në audio/video/komandë pme një klik +- vendosni shkarkimet në radhë dhe planifikoni ato sipas datës dhe orës +- gjithashtu mund të planifikoni shumë artikuj në të njëjtën kohë +- shkarkoni shumë artikuj në të njëjtën kohë +- përdorni komanda dhe shabllone të personalizuara ose shkoni në modalitetin e plotë yt-dlp me një terminal të integruar +- mund të ruani dhe riktheni shabllonet, në mënyrë që të mund t'i ndani me miqtë tuaj +- Mbështetje për BISKOTAT. Hyni me llogaritë tuaja dhe shkarkoni video private/të padisponueshme, zhbllokoni formatet premium etj. +- prit video bazuar sipas kohës dhe kapitujt e videos (Kjo veçori yt-dlp është eksperimentale në projektin origjinal) +- ju mund të bëni prerje të pafundme +- hiqni elementet e SPONSORBLOCK (si pjeset pa muzike) nga artikulli +- futini ato si kapituj në videon tuaj +- fut titrat/metadatat/kapitujt etj +- modifikoni titullin dhe autorin +- ndani artikullin në skedarë të veçantë në varësi të kapitujve të tij +- zgjidhni formate të ndryshme shkarkimi +- shfaq karte direkt nga menyja e shpërndarjes, nuk ka nevojë të hapni aplikacionin +- mund të krijoni një skedar txt dhe ta plotësoni me linke/playliste/fjale kerkimi të ndara nga një rresht i ri dhe aplikacioni do t'i përpunojë ato +- kërkoni ose futni një link nga aplikacioni +- mund të grumbulloni kërkime në mënyrë që t'i përpunoni ato në të njëjtën kohë +- regjistrimet e shkarkimeve në rast problemesh +- rishkarkimi i shkarkimeve të anuluara ose të dështuara +- mund të përdorni gjeste për të rrëshqitur majtas për ta shkarkuar përsëri dhe djathtas për t'i fshirë +- mund të klikoni gjatë butonin e rishkarkimit në fletën e detajeve për të shfaqur kartën e shkarkimit për më shumë funksionalitet +- INCOGNITO kur nuk dëshironi të ruani një histori shkarkimi ose regjistra +- mënyra e shkarkimit të shpejtë +- shkarkoni menjëherë pa pasur nevojë të prisni që të dhënat të përpunohen. Fikni kartën e poshtme dhe ajo do të fillojë menjëherë +- hapni / ndani skedarët e shkarkuar menjëherë nga njoftimi i përfunduar +- shumica e veçorive yt-dlp janë zbatuar, sugjerimet janë të mirëseardhura +- ndërfaqja e MATERIAL U +- opsionet ngjyrash +- rezervoni dhe rivendosni veçoritë e aplikacionit. (Pothuajse gjithçka mund të rezervohet dhe mund te shperndash me shoke) +- Arkitektura MVVM me WorkManager + +## 📲 Fotot + +
+ + + + + + + + + + + + + +
+ +## 💬 Kontakt + +Bashkohuni me [Kanalin tonë Telegram](https://t.me/ytdlnis) ose [Diskord](https://discord.gg/WW3KYWxAPm) për diskutim, njoftime dhe publikime! + +## 🔑 Lidhu me ReVanced + +Emri i paketës së aplikacionit është "com.deniscerri.ytdl" + +## 📝 Ndihmo për të përkthyer në Weblate + +Translation status + + + + +Translation status + + +## 😇 Kontributo + +Nëse dëshironi të kontribuoni. Ju lutemi lexoni seksionin [Kontribimi](CONTRIBUTING.MD). + +## 📄 Liçensa + +[GNU GPL v3.0](https://github.com/deniscerri/ytdlnis/blob/main/LICENSE) + +⚠️ Paralajmërim
+ +Përveç kodit burimor të licencuar sipas licencës GPLv3, të gjitha palët e tjera janë të ndaluara të përdorin emrin 'YTDLnis' si një aplikacion shkarkues dhe e njëjta gjë vlen edhe për derivatet e tij. Derivatet përfshijnë por nuk kufizohen në forks dhe ndërtime jozyrtare. + +## 🙏 Falenderimet + +- [yt-dlp](https://github.com/yt-dlp/yt-dlp) dhe kontribuesit e tij për të bërë të mundur këtë mjet. Pa të ky aplikacion nuk do të ekzistonte. +- [youtubedl-android](https://github.com/yausername/youtubedl-android) për transferimin e yt-dlp në android. +- [dvd](https://github.com/yausername/dvd) për të treguar se si të zbatohet youtubedl-android +- [seal](https://github.com/JunkFood02/Seal) për elementë dhe veçori të caktuara të dizajnit doja të përdorja gjithashtu në këtë aplikacion +- [decipher3114](https://github.com/decipher3114) për ikonën e aplikacionit + +dhe shumë të huaj të tjerë nga interneti. diff --git a/README.MD b/README.MD index 2beee643..5b42c327 100644 --- a/README.MD +++ b/README.MD @@ -6,6 +6,8 @@
English   |    + Shqip +   |    Azərbaycanca   |    Português diff --git a/app/build.gradle b/app/build.gradle index f7029470..f9888e51 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -76,6 +76,9 @@ android { } } + lintOptions{ + disable 'MissingTranslation' + } android.applicationVariants.configureEach { variant -> variant.outputs.each { output -> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ea3e030..a4b577a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -84,7 +84,7 @@ diff --git a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt index 126d0756..35d934ab 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/MainActivity.kt @@ -198,6 +198,12 @@ class MainActivity : BaseActivity() { } true } + //settings button + (navigationView as NavigationView).menu.getItem(8).setOnMenuItemClickListener { + val intent = Intent(context, SettingsActivity::class.java) + startActivity(intent) + true + } (navigationView as NavigationView).getHeaderView(0).findViewById(R.id.title).text = ThemeUtil.getStyledAppName(this) } 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 658f9871..e82b5f2a 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 @@ -122,14 +122,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() } val confTmp = Configuration(application.resources.configuration) - confTmp.locale = Locale(sharedPreferences.getString("app_language", "en")!!) + confTmp.setLocale(Locale(sharedPreferences.getString("app_language", "en")!!)) val metrics = DisplayMetrics() resources = Resources(application.assets, metrics, confTmp) videoContainer = sharedPreferences.getString("video_format", "Default") - defaultVideoFormats = getGenericVideoFormats() - bestVideoFormat = defaultVideoFormats.last() + defaultVideoFormats = infoUtil.getGenericVideoFormats(resources) + bestVideoFormat = defaultVideoFormats.first() audioContainer = sharedPreferences.getString("audio_format", "mp3") bestAudioFormat = if (audioFormatIDPreference.isEmpty()){ @@ -387,10 +387,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application return cloneFormat ( try { try{ - formats.filter { it.format_note.contains("audio", ignoreCase = true)}.sortedBy { it.format_id } + formats.filter { it.format_note.contains("audio", ignoreCase = true)} .firstOrNull { audioFormatIDPreference.indexOfFirst { a -> a.contains(it.format_id) } >= 0 || it.container == audioContainer} ?: throw Exception() }catch (e: Exception){ - formats.last { it.format_note.contains("audio", ignoreCase = true) } + formats.first { it.format_note.contains("audio", ignoreCase = true) } } }catch (e: Exception){ bestAudioFormat @@ -401,18 +401,17 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application Type.video -> { return cloneFormat( try { - val theFormats = formats.filter { !it.format_note.contains("audio", true) }.sortedBy { it.format_id }.ifEmpty { defaultVideoFormats }.sortedByDescending { it.filesize } + val theFormats = formats.filter { !it.format_note.contains("audio", true) }.ifEmpty { defaultVideoFormats.sortedByDescending { it.filesize } } when (videoQualityPreference) { "worst" -> { - theFormats.first() + theFormats.last() } else /*best*/ -> { val requirements: MutableList<(Format) -> Boolean> = mutableListOf() requirements.add { it: Format -> formatIDPreference.sorted().contains(it.format_id) } requirements.add { it: Format -> if (videoContainer == "mp4") it.container.equals("mpeg_4", true) else it.container.equals(videoContainer, true)} - requirements.add { it: Format -> it.format_note.contains(videoQualityPreference.substring(0, videoQualityPreference.length - 1)) } - requirements.add { it: Format -> it.vcodec.startsWith(videoCodec!!, true) } - + requirements.add { it: Format -> it.format_note.contains(videoQualityPreference.split("_")[0].dropLast(1)) } + requirements.add { it: Format -> "^(${videoCodec}).+$".toRegex(RegexOption.IGNORE_CASE).matches(it.vcodec)} theFormats.maxByOrNull { f -> requirements.count{ req -> req(f)} } ?: throw Exception() } } @@ -440,23 +439,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application ) } - fun getGenericAudioFormats() : MutableList{ - val audioFormats = resources.getStringArray(R.array.audio_formats) - val formats = mutableListOf() - val containerPreference = sharedPreferences.getString("audio_format", "") - audioFormats.forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } - audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"","", "",1, it)) } - return formats - } - fun getGenericVideoFormats() : MutableList{ - val videoFormats = resources.getStringArray(R.array.video_formats_values) - val formats = mutableListOf() - val containerPreference = sharedPreferences.getString("video_format", "") - videoFormats.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",0, it.split("_")[0])) } - formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",1, it)) } - return formats - } fun getLatestCommandTemplateAsFormat() : Format { val t = commandTemplateDao.getFirst() diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/BaseActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/BaseActivity.kt index 52b2c011..1b46367a 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/BaseActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/BaseActivity.kt @@ -2,11 +2,6 @@ package com.deniscerri.ytdlnis.ui import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updatePadding -import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.util.ThemeUtil import com.google.android.material.elevation.SurfaceColors diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt index c52b11cf..1c9483ea 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt @@ -435,7 +435,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene } @SuppressLint("InflateParams") - private suspend fun updateSearchViewItems(it: Editable?, linkYouCopied: View?){ + private suspend fun updateSearchViewItems(searchQuery: Editable?, linkYouCopied: View?){ searchSuggestionsLinearLayout!!.visibility = GONE searchHistoryLinearLayout!!.visibility = GONE searchSuggestionsLinearLayout!!.removeAllViews() @@ -449,16 +449,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0) } val suggestions = withContext(Dispatchers.IO){ - if (it!!.isEmpty()) { - resultViewModel.getSearchHistory().map { it.query } - }else if (sharedPreferences!!.getBoolean("search_suggestions", false)){ - infoUtil!!.getSearchSuggestions(it.toString()) + resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) } + + if (sharedPreferences!!.getBoolean("search_suggestions", false)){ + infoUtil!!.getSearchSuggestions(searchQuery.toString()) }else{ emptyList() } } - if (it!!.isEmpty()){ + if (searchQuery!!.isEmpty()){ for (i in suggestions.indices) { val v = LayoutInflater.from(fragmentContext) .inflate(R.layout.search_suggestion_item, null) diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt index f3345bfa..8b18d986 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/CutVideoBottomSheetDialog.kt @@ -335,7 +335,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls if ((event!!.action == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) { - val values = rangeSlider.values var endTimestamp = (rangeSlider.valueTo.toInt() * timeSeconds) / 100 toTextInput.editText!!.clearFocus() @@ -361,7 +360,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls rangeSlider.setValues(startValue, endValue) okBtn.isEnabled = startValue != 0F || endValue != 100F try { - player.seekTo(startSeconds.toLong() * 1000) + player.seekTo((seconds.toLong() - 4L) * 1000) player.play() }catch (ignored: Exception) {} diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt index fa176495..e8a8755d 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadAudioFragment.kt @@ -38,7 +38,6 @@ import java.io.File class DownloadAudioFragment(private var resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync { - private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null private lateinit var downloadViewModel : DownloadViewModel @@ -46,6 +45,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr private lateinit var saveDir : TextInputLayout private lateinit var freeSpace : TextView private lateinit var infoUtil: InfoUtil + private lateinit var genericAudioFormats: MutableList lateinit var downloadItem : DownloadItem lateinit var title : TextInputLayout @@ -55,12 +55,12 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr container: ViewGroup?, savedInstanceState: Bundle? ): View? { - _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false) activity = getActivity() downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] infoUtil = InfoUtil(requireContext()) + genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) return fragmentView } @@ -129,7 +129,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) } .maxByOrNull { it.filesize }!! }.onFailure { - downloadItem.format = downloadViewModel.getGenericAudioFormats().last() + downloadItem.format = genericAudioFormats.last() } } } @@ -142,7 +142,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr val containerAutoCompleteTextView = view.findViewById(R.id.container_textview) - if (formats.isEmpty()) formats = downloadViewModel.getGenericAudioFormats() + if (formats.isEmpty()) formats = genericAudioFormats val formatCard = view.findViewById(R.id.format_card_constraintLayout) val chosenFormat = downloadItem.format @@ -154,16 +154,17 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr lifecycleScope.launch { withContext(Dispatchers.IO){ resultItem.formats.removeAll(formats.toSet()) - resultItem.formats.addAll(allFormats.first()) + resultItem.formats.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) }) resultViewModel.update(resultItem) } } - formats = allFormats.first().toMutableList() + formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList() + formats.removeAll(genericAudioFormats) } } formatCard.setOnClickListener{ if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ - val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats), listener) + val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats.ifEmpty { genericAudioFormats }), listener) bottomSheet.show(parentFragmentManager, "formatSheet") } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt index 94058679..13f77d6b 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadCommandFragment.kt @@ -42,7 +42,6 @@ import kotlinx.coroutines.withContext import java.io.File class DownloadCommandFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment() { - private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null private lateinit var downloadViewModel : DownloadViewModel @@ -57,7 +56,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu container: ViewGroup?, savedInstanceState: Bundle? ): View? { - _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false) activity = getActivity() downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index 1a948230..0c0b9b42 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -50,6 +50,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel import com.deniscerri.ytdlnis.receiver.ShareActivity import com.deniscerri.ytdlnis.util.FileUtil +import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.UiUtil import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll import com.deniscerri.ytdlnis.work.UpdatePlaylistFormatsWorker @@ -78,6 +79,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList private lateinit var bottomAppBar: BottomAppBar private lateinit var filesize : TextView @@ -90,6 +92,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList listOf>(downloadViewModel.getGenericAudioFormats()) - else -> listOf>(downloadViewModel.getGenericVideoFormats()) + DownloadViewModel.Type.audio -> listOf>(infoUtil.getGenericAudioFormats(requireContext().resources)) + else -> listOf>(infoUtil.getGenericVideoFormats(requireContext().resources)) } } val bottomSheet = FormatSelectionBottomSheetDialog(items, formats, formatListener) 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 1be349d0..d0119119 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 @@ -25,7 +25,6 @@ import com.deniscerri.ytdlnis.database.models.ResultItem import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel -import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding import com.deniscerri.ytdlnis.util.FileUtil import com.deniscerri.ytdlnis.util.InfoUtil import com.deniscerri.ytdlnis.util.UiUtil @@ -39,7 +38,6 @@ import java.io.File class DownloadVideoFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync { - private var _binding : FragmentHomeBinding? = null private var fragmentView: View? = null private var activity: Activity? = null private lateinit var downloadViewModel : DownloadViewModel @@ -50,6 +48,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr private lateinit var saveDir : TextInputLayout private lateinit var freeSpace : TextView private lateinit var infoUtil: InfoUtil + private lateinit var genericVideoFormats: MutableList lateinit var downloadItem: DownloadItem @@ -58,12 +57,12 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr container: ViewGroup?, savedInstanceState: Bundle? ): View? { - _binding = FragmentHomeBinding.inflate(inflater, container, false) fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false) activity = getActivity() downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java] infoUtil = InfoUtil(requireContext()) + genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources) return fragmentView } @@ -136,7 +135,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr downloadItem.allFormats.filter { it.vcodec.isNotEmpty() } .maxByOrNull { it.filesize }!! }.onFailure { - downloadItem.format = downloadViewModel.getGenericVideoFormats().last() + downloadItem.format = genericVideoFormats.last() } } } @@ -149,7 +148,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr var containerPreference = sharedPreferences.getString("video_format", "Default") if (containerPreference == "Default") containerPreference = getString(R.string.defaultValue) - if (formats.isEmpty()) formats = downloadViewModel.getGenericVideoFormats() + if (formats.isEmpty()) formats = genericVideoFormats val formatCard = view.findViewById(R.id.format_card_constraintLayout) @@ -158,22 +157,24 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr val listener = object : OnFormatClickListener { override fun onFormatClick(allFormats: List>, item: List) { downloadItem.format = item.first().format - item.first().audioFormats?.map { it.format_id } - ?.let { downloadItem.videoPreferences.audioFormatIDs.addAll(it) } + item.first().audioFormats?.map { it.format_id }?.let { + downloadItem.videoPreferences.audioFormatIDs.clear() + downloadItem.videoPreferences.audioFormatIDs.addAll(it) + } lifecycleScope.launch { withContext(Dispatchers.IO){ - resultItem.formats.removeAll(formats.toSet()) - resultItem.formats.addAll(allFormats.first()) + resultItem.formats.removeAll(formats) + resultItem.formats.addAll(allFormats.first().filter { !genericVideoFormats.contains(it) }) resultViewModel.update(resultItem) } } - formats = allFormats.first().toMutableList() + formats = allFormats.first().filter { !genericVideoFormats.contains(it) }.toMutableList() UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, item.first().audioFormats) } } formatCard.setOnClickListener{ if (parentFragmentManager.findFragmentByTag("formatSheet") == null){ - val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats), listener) + val bottomSheet = FormatSelectionBottomSheetDialog(listOf(downloadItem), listOf(formats.ifEmpty { genericVideoFormats }), listener) bottomSheet.show(parentFragmentManager, "formatSheet") } } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt index a4446f48..77501afb 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloadcard/FormatSelectionBottomSheetDialog.kt @@ -276,7 +276,7 @@ class FormatSelectionBottomSheetDialog(private val items: List, p } } - for (i in finalFormats.lastIndex downTo 0){ + for (i in 0.. finalFormats.lastIndex){ val format = finalFormats[i] val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null) formatItem.tag = "${format.format_id}${format.format_note}" diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt index 518e1fd2..f5ae52b5 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/GeneralSettingsFragment.kt @@ -5,8 +5,6 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.net.Uri -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES import android.os.Bundle import android.os.PowerManager import android.provider.Settings @@ -29,7 +27,6 @@ class GeneralSettingsFragment : BaseSettingsFragment() { override val title: Int = R.string.general private var language: ListPreference? = null - private var language13: Preference? = null private var theme: ListPreference? = null private var accent: ListPreference? = null private var highContrast: SwitchPreferenceCompat? = null @@ -51,45 +48,28 @@ class GeneralSettingsFragment : BaseSettingsFragment() { } language = findPreference("app_language") - language13 = findPreference("app_language_13") theme = findPreference("ytdlnis_theme") accent = findPreference("theme_accent") highContrast = findPreference("high_contrast") locale = findPreference("locale") showTerminalShareIcon = findPreference("show_terminal") - if(VERSION.SDK_INT < VERSION_CODES.TIRAMISU){ - val values = resources.getStringArray(R.array.language_values) - val entries = mutableListOf() - values.forEach { - entries.add(Locale(it).getDisplayName(Locale(it))) - } - language!!.entries = entries.toTypedArray() + val values = resources.getStringArray(R.array.language_values) + val entries = mutableListOf() + values.forEach { + entries.add(Locale(it).getDisplayName(Locale(it))) + } + language!!.entries = entries.toTypedArray() - if(language!!.value == null) language!!.value = Locale.getDefault().language - language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value)) + if(language!!.value == null) language!!.value = Locale.getDefault().language + language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value)) - language!!.onPreferenceChangeListener = - Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> - language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString())) - AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) - true - } - - language13?.isVisible = false - }else{ - language?.isVisible = false - language13?.isVisible = true - language13?.summary = Locale.getDefault().displayLanguage - - language13!!.setOnPreferenceClickListener { - val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS) - intent.data = Uri.fromParts("package", requireActivity().packageName, null) - requireActivity().startActivity(intent) - requireActivity().finishAffinity() + language!!.onPreferenceChangeListener = + Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any -> + language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString())) + AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue.toString())) true } - } theme!!.summary = theme!!.entry 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 8d83519e..53167599 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt @@ -2,11 +2,13 @@ package com.deniscerri.ytdlnis.util import android.content.Context import android.content.SharedPreferences +import android.content.res.Resources import android.os.Looper import android.text.Html import android.util.Log import android.util.Patterns import android.widget.Toast +import androidx.preference.AndroidResources import androidx.preference.PreferenceManager import com.deniscerri.ytdlnis.R import com.deniscerri.ytdlnis.database.models.ChapterItem @@ -32,7 +34,6 @@ import java.util.regex.Pattern class InfoUtil(private val context: Context) { - private var items: ArrayList private lateinit var sharedPreferences: SharedPreferences init { @@ -43,12 +44,10 @@ class InfoUtil(private val context: Context) { } catch (e: Exception) { e.printStackTrace() } - items = ArrayList() } fun search(query: String): ArrayList { - items = ArrayList() return when(sharedPreferences.getString("search_engine", "ytsearch")){ "ytsearch" -> try{ searchFromPiped(query) @@ -68,6 +67,7 @@ class InfoUtil(private val context: Context) { @Throws(JSONException::class) fun searchFromPiped(query: String): ArrayList { + val items = arrayListOf() val data = genericRequest("$pipedURL/search?q=$query&filter=videos®ion=${countryCODE}") val dataArray = data.getJSONArray("items") if (dataArray.length() == 0) return getFromYTDL(query) @@ -86,6 +86,7 @@ class InfoUtil(private val context: Context) { @Throws(JSONException::class) fun searchFromPipedMusic(query: String): ArrayList { + val items = arrayListOf() val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs®ion=${countryCODE}") val dataArray = data.getJSONArray("items") if (dataArray.length() == 0) return getFromYTDL(query) @@ -105,7 +106,7 @@ class InfoUtil(private val context: Context) { @Throws(JSONException::class) fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple { try{ - items = ArrayList() + val items = arrayListOf() // -------------- PIPED API FUNCTION ------------------- var url = "" url = if (nextPageToken.isBlank()) "$pipedURL/playlists/$id" @@ -226,7 +227,6 @@ class InfoUtil(private val context: Context) { } } - formats.sortBy { it.filesize } formats.groupBy { it.format_id }.forEach { if (it.value.count() > 1) { it.value.filter { f-> !f.format_note.contains("original", true) }.forEachIndexed { index, format -> format.format_id = format.format_id.split("-")[0] + "-${index}" } @@ -234,6 +234,7 @@ class InfoUtil(private val context: Context) { engDefault?.format_id = (engDefault?.format_id?.split("-")?.get(0) ?: "") + "-${it.value.size-1}" } } + formats.sortByDescending { it.filesize } } val chapters = ArrayList() @@ -350,7 +351,6 @@ class InfoUtil(private val context: Context) { @Throws(JSONException::class) fun getTrending(): ArrayList { - items = ArrayList() if (sharedPreferences.getString("api_key", "")!!.isNotBlank()){ return getTrendingFromYoutubeAPI() } @@ -359,8 +359,8 @@ class InfoUtil(private val context: Context) { @Throws(JSONException::class) fun getTrendingFromYoutubeAPI(): ArrayList { + val items = arrayListOf() val key = sharedPreferences.getString("api_key", "")!! - val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode=$countryCODE&maxResults=25&key=$key" //short data val res = genericRequest(url) @@ -389,6 +389,7 @@ class InfoUtil(private val context: Context) { } private fun getTrendingFromPiped(): ArrayList { + val items = arrayListOf() val url = "$pipedURL/trending?region=$countryCODE" val res = genericArrayRequest(url) try { @@ -547,7 +548,7 @@ class InfoUtil(private val context: Context) { } fun getFromYTDL(query: String): ArrayList { - items = ArrayList() + val items = arrayListOf() val searchEngine = sharedPreferences.getString("search_engine", "ytsearch") try { val request : YoutubeDLRequest @@ -583,12 +584,12 @@ class InfoUtil(private val context: Context) { } val youtubeDLResponse = YoutubeDL.getInstance().execute(request) - val results: Array = try { + val results: List = try { val lineSeparator = System.getProperty("line.separator") - youtubeDLResponse.out.split(lineSeparator!!).toTypedArray() + youtubeDLResponse.out.split(lineSeparator!!) } catch (e: Exception) { - arrayOf(youtubeDLResponse.out) - } + listOf(youtubeDLResponse.out) + }.filter { it.isNotBlank() } for (result in results) { if (result.isNullOrBlank()) continue val jsonObject = JSONObject(result) @@ -690,7 +691,7 @@ class InfoUtil(private val context: Context) { val formats = arrayListOf() if (formatsInJSON != null) { - for (f in 0 until formatsInJSON.length()){ + for (f in formatsInJSON.length() - 1 downTo 0){ val format = formatsInJSON.getJSONObject(f) if (format.has("filesize")){ if (format.get("filesize") == "None"){ @@ -987,8 +988,9 @@ class InfoUtil(private val context: Context) { val limitRate = sharedPreferences.getString("limit_rate", "") if (limitRate != "") request.addOption("-r", limitRate!!) - request.addOption("--trim-filenames", 120) if(downloadItem.type != DownloadViewModel.Type.command){ + request.addOption("--trim-filenames", 120) + if (downloadItem.SaveThumb) { request.addOption("--write-thumbnail") request.addOption("--convert-thumbnails", "jpg") @@ -1024,6 +1026,7 @@ class InfoUtil(private val context: Context) { request.addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title)) } if (downloadItem.author.isNotBlank()){ + request.addOption("--parse-metadata", "%(artist,uploader,creator,channel|null)s:%(uploader)s") request.addCommands(listOf("--replace-in-metadata", "uploader", ".+", downloadItem.author)) request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$","")) } @@ -1078,7 +1081,7 @@ class InfoUtil(private val context: Context) { } } - + downloadItem.customFileNameTemplate = downloadItem.customFileNameTemplate.replace("%(uploader)s", "%(uploader,channel)s") } if (sharedPreferences.getBoolean("restrict_filenames", true)) { @@ -1166,10 +1169,6 @@ class InfoUtil(private val context: Context) { DownloadViewModel.Type.video -> { val supportedContainers = context.resources.getStringArray(R.array.video_containers) - if (!sharedPreferences.getBoolean("embed_metadata", true)){ - request.addOption("--no-embed-metadata") - } - if (downloadItem.videoPreferences.addChapters) { if (sharedPreferences.getBoolean("use_sponsorblock", true)){ request.addOption("--sponsorblock-mark", "all") @@ -1200,100 +1199,102 @@ class InfoUtil(private val context: Context) { } //format logic - - val defaultFormats = context.resources.getStringArray(R.array.video_formats_values) - var usingGenericFormat = false - var videof = downloadItem.format.format_id - if (videof == context.resources.getString(R.string.best_quality) || videof == "best") { - videof = "bestvideo" - usingGenericFormat = true - }else if (videof == context.resources.getString(R.string.worst_quality) || videof == "worst") { - videof = "worst" - }else if (defaultFormats.contains(videof)) { - videof = "bestvideo[height<="+videof.split("_")[0].dropLast(1)+"]" - } - - val preferredFormatIDs = sharedPreferences.getString("format_id", "").toString() - .split(",") - .filter { it.isNotEmpty() } - .ifEmpty { listOf(videof) } - - var audiof = "bestaudio" + var audiof = "ba" if (downloadItem.videoPreferences.audioFormatIDs.isNotEmpty()){ audiof = downloadItem.videoPreferences.audioFormatIDs.joinToString("+") } if (downloadItem.videoPreferences.removeAudio) audiof = "" - val preferredAudioFormatIDs = sharedPreferences.getString("format_id_audio", "") - .toString() - .split(",") - .filter { it.isNotEmpty() } - .ifEmpty { listOf(audiof) } - - val preferredVideoFormats = if (usingGenericFormat) preferredFormatIDs else listOf(videof) - val preferredAudioFormats = if (usingGenericFormat && downloadItem.videoPreferences.audioFormatIDs.isEmpty()) { - preferredAudioFormatIDs - } else listOf(audiof) - - if (preferredAudioFormats.any{it.contains("+")}){ - request.addOption("--audio-multistreams") - } - - val preferredCodec = sharedPreferences.getString("video_codec", "") - val extPref = if (cont.isNotBlank()) "[ext=$cont]" else "" - val vCodecPref = if (preferredCodec!!.isNotBlank()) "[vcodec^=$preferredCodec]" else "" - + val defaultFormats = context.resources.getStringArray(R.array.video_formats_values) + val usingGenericFormat = defaultFormats.contains(videof) || downloadItem.allFormats.isEmpty() || downloadItem.allFormats == getGenericVideoFormats(context.resources) val f = StringBuilder() - //build format with extension and vcodec - preferredVideoFormats.forEach { v -> - preferredAudioFormats.forEach { a -> - val aa = if (a.isNotBlank()) "+$a" else "" - f.append("$v$extPref$vCodecPref$aa/") - } - } + if(!usingGenericFormat){ + val aa = if (audiof.isNotBlank()) "+$audiof" else "" + f.append("$videof$aa/$videof/best") - //build format with vcodec - if (extPref.isNotBlank()){ - preferredVideoFormats.forEach { v -> + if (audiof.contains("+")){ + request.addOption("--audio-multistreams") + } + }else{ + if (videof == context.resources.getString(R.string.best_quality) || videof == "best") { + videof = "bv" + }else if (videof == context.resources.getString(R.string.worst_quality) || videof == "worst") { + videof = "worst" + }else if (defaultFormats.contains(videof)) { + videof = "bv[height<="+videof.split("_")[0].dropLast(1)+"]" + } + + val preferredFormatIDs = sharedPreferences.getString("format_id", "").toString() + .split(",") + .filter { it.isNotEmpty() } + .ifEmpty { listOf(videof) }.toMutableList() + if (!preferredFormatIDs.contains(videof)){ + preferredFormatIDs.add(0, videof) + } + + val preferredAudioFormatIDs = sharedPreferences.getString("format_id_audio", "") + .toString() + .split(",") + .filter { it.isNotEmpty() } + .ifEmpty { listOf(audiof) } + + val preferredAudioFormats = if (downloadItem.videoPreferences.audioFormatIDs.isEmpty()) { + preferredAudioFormatIDs + } else listOf(audiof) + + if (preferredAudioFormats.any{it.contains("+")}){ + request.addOption("--audio-multistreams") + } + + val preferredCodec = sharedPreferences.getString("video_codec", "") + val extPref = if (cont.isNotBlank()) "[ext=$cont]" else "" + val vCodecPref = if (preferredCodec!!.isNotBlank()) "[vcodec~='^($preferredCodec)']" else "" + + + preferredFormatIDs.forEach { v -> + //build format with extension and vcodec preferredAudioFormats.forEach { a -> val aa = if (a.isNotBlank()) "+$a" else "" - f.append("$videof$vCodecPref$aa/") + f.append("$v$extPref$vCodecPref$aa/") } - } - } - if (vCodecPref.isNotBlank()){ - preferredVideoFormats.forEach { v -> //build format with vcodec - preferredAudioFormats.forEach {a -> - val aa = if (a.isNotBlank()) "+$a" else "" - f.append("$v$aa/") + if (extPref.isNotBlank()){ + preferredAudioFormats.forEach { a -> + val aa = if (a.isNotBlank()) "+$a" else "" + f.append("$v$vCodecPref$aa/") + } } - } - } - if (!downloadItem.videoPreferences.removeAudio){ - //build format with best audio - preferredVideoFormats.forEach { v -> - if(!f.contains("$v+bestaudio/")){ - f.append("$v+bestaudio/") + //build format with audio + if (vCodecPref.isNotBlank()){ + preferredAudioFormats.forEach {a -> + val aa = if (a.isNotBlank()) "+$a" else "" + f.append("$v$aa/") + } + } + + if (!downloadItem.videoPreferences.removeAudio){ + //build format with best audio + if(!f.contains("$v+ba/")){ + f.append("$v+ba/") + } + + //build formats with standalone video + if(!f.contains("/$v/")){ + f.append("$v/") + } } } - //build formats with standalone video - preferredVideoFormats.forEach { v -> - if(!f.contains("/$v/")){ - f.append("$v/") - } + if(!f.endsWith("best/")){ + //last fallback + f.append("best") } } - if(!f.endsWith("best/")){ - //last fallback - f.append("best") - } request.addOption("-f", f.toString().replace("/$".toRegex(), "")) @@ -1302,8 +1303,10 @@ class InfoUtil(private val context: Context) { request.addOption("--write-subs") request.addOption("--write-auto-subs") - request.addOption("--sub-format", "${subFormat}/best") - request.addOption("--convert-subtitles", subFormat ?: "srt") + if(subFormat!!.isNotBlank()){ + request.addOption("--sub-format", "${subFormat}/best") + request.addOption("--convert-subtitles", subFormat ?: "srt") + } if (!downloadItem.videoPreferences.embedSubs) { request.addOption("--sub-langs", downloadItem.videoPreferences.subsLanguages.ifEmpty { "en.*,.*-orig" }) } @@ -1368,6 +1371,27 @@ class InfoUtil(private val context: Context) { return final } + fun getGenericAudioFormats(resources: Resources) : MutableList{ + val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() } + val audioFormats = resources.getStringArray(R.array.audio_formats) + val formats = mutableListOf() + val containerPreference = sharedPreferences.getString("audio_format", "") + audioFormats.reversed().forEach { formats.add(Format(it, containerPreference!!,"","", "",0, it)) } + audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"","", "",1, it)) } + return formats + } + + fun getGenericVideoFormats(resources: Resources) : MutableList{ + val formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() } + + val videoFormats = resources.getStringArray(R.array.video_formats_values) + val formats = mutableListOf() + val containerPreference = sharedPreferences.getString("video_format", "") + videoFormats.reversed().forEach { formats.add(Format(it, containerPreference!!,"Default","", "",0, it.split("_")[0])) } + formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"Default","", "",1, it)) } + return formats + } + private val pipedURL = sharedPreferences.getString("piped_instance", defaultPipedURL)?.ifEmpty { defaultPipedURL }?.removeSuffix("/") class PlaylistTuple internal constructor( diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt index 44d50a81..2876a6ab 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/NotificationUtil.kt @@ -34,11 +34,18 @@ class NotificationUtil(var context: Context) { NotificationManager::class.java ) - //gui downloads - var name: CharSequence = context.getString(R.string.download_notification_channel_name) - var description = context.getString(R.string.download_notification_channel_description) + //downloading worker notification + var name: CharSequence = context.getString(R.string.downloading) + var description = "WorkManager Default Notification" val importance = NotificationManager.IMPORTANCE_LOW - var channel = NotificationChannel(DOWNLOAD_SERVICE_CHANNEL_ID, name, importance) + var channel = NotificationChannel(DOWNLOAD_WORKER_CHANNEL_ID, name, importance) + channel.description = description + notificationManager.createNotificationChannel(channel) + + //gui downloads + name = context.getString(R.string.download_notification_channel_name) + description = context.getString(R.string.download_notification_channel_description) + channel = NotificationChannel(DOWNLOAD_SERVICE_CHANNEL_ID, name, importance) channel.description = description notificationManager.createNotificationChannel(channel) @@ -77,7 +84,7 @@ class NotificationUtil(var context: Context) { } fun createDefaultWorkerNotification() : Notification { - val notificationBuilder = getBuilder(DOWNLOAD_SERVICE_CHANNEL_ID) + val notificationBuilder = getBuilder(DOWNLOAD_WORKER_CHANNEL_ID) return notificationBuilder .setContentTitle(context.getString(R.string.downloading)) @@ -480,6 +487,7 @@ class NotificationUtil(var context: Context) { 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" private const val PROGRESS_MAX = 100 private const val PROGRESS_CURR = 0 } diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/UpdateUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/UpdateUtil.kt index ecf6f52e..19e87b2c 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/util/UpdateUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/util/UpdateUtil.kt @@ -57,16 +57,13 @@ class UpdateUtil(var context: Context) { v = res.first { !it.tag_name.contains("beta", true) } } - val current = BuildConfig.VERSION_NAME.replace("-beta", "").replace(".", "").toInt() - val incoming = v.tag_name.removePrefix("v").replace("-beta", "").replace(".", "").toInt() + val current = BuildConfig.VERSION_NAME.replace("-beta", "").replace(".", "").padEnd(10,'0').toInt() + val incoming = v.tag_name.removePrefix("v").replace("-beta", "").replace(".", "").padEnd(10,'0').toInt() - Log.e("aa", current.toString()) - Log.e("aa", incoming.toString()) var isInLatest = true - if ((BuildConfig.VERSION_NAME.contains("beta", true) && !useBeta) || - (!BuildConfig.VERSION_NAME.contains("beta", true) && useBeta) || - current < incoming ){ + if ((current < incoming) || + (current > incoming && BuildConfig.VERSION_NAME.contains("beta", true) && !useBeta)){ isInLatest = false } 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 e328d647..84a39a53 100644 --- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt +++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt @@ -1,8 +1,11 @@ package com.deniscerri.ytdlnis.work import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources import android.os.Handler import android.os.Looper +import android.util.DisplayMetrics import android.util.Log import android.widget.Toast import androidx.navigation.NavDeepLinkBuilder @@ -35,6 +38,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import java.io.File +import java.util.Locale import kotlin.random.Random @@ -60,6 +64,12 @@ class DownloadWorker( val currentWork = WorkManager.getInstance(context).getWorkInfosByTag("download").await() if (currentWork.count{it.state == WorkInfo.State.RUNNING} > 1) return Result.success() + val confTmp = Configuration(context.resources.configuration) + val currLang = sharedPreferences.getString("app_language", "en")!!.split("-") + confTmp.setLocale(if (currLang.size == 1) Locale(currLang[0]) else Locale(currLang[0], currLang[1])) + val metrics = DisplayMetrics() + val resources = Resources(context.assets, metrics, confTmp) + val pendingIntent = NavDeepLinkBuilder(context) .setGraph(R.navigation.nav_graph) .setDestination(R.id.downloadQueueMainFragment) @@ -162,8 +172,10 @@ class DownloadWorker( .sortedByDescending { it.length() } .map { it.absolutePath } .toList() - FileUtil.scanMedia(finalPaths, context) + if (finalPaths.isEmpty()){ + finalPaths = listOf(context.getString(R.string.unfound_file)) + } }else{ //move file from internal to set download directory setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id)) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index d2e2a5d7..724a05c8 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -48,7 +48,7 @@ worst - 360p + 360p_ytdlnisgeneric 480p_ytdlnisgeneric 720p_ytdlnisgeneric 1080p_ytdlnisgeneric @@ -766,6 +766,15 @@ + @string/defaultValue + srt + ass + lrc + vtt + + + + srt ass lrc @@ -794,9 +803,9 @@ - av0 - vp - avc - hev + av[01] + vp[09] + avc|h264 + hev|h265 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5db9503c..8eee5618 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -292,7 +292,7 @@ Extra Command Use Cookies Embed Metadata - Embed metadata to the video file. Also embeds chapters/infojson if present + Embed and parse metadata to the audio file. Update Formats in Background Auto-Update yt-dlp Use SponsorBlock diff --git a/app/src/main/res/xml/general_preferences.xml b/app/src/main/res/xml/general_preferences.xml index d1d181d5..062acb33 100644 --- a/app/src/main/res/xml/general_preferences.xml +++ b/app/src/main/res/xml/general_preferences.xml @@ -11,13 +11,6 @@ app:summary="en" app:title="@string/language" /> - - -