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" /> - - -