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
This commit is contained in:
deniscerri 2023-09-27 19:13:56 +02:00
parent 2964f7da75
commit c05224336a
No known key found for this signature in database
GPG key ID: 95C43D517D830350
23 changed files with 361 additions and 215 deletions

133
README-sq.md Normal file
View file

@ -0,0 +1,133 @@
<h1 align="center">
<img src="fastlane/metadata/android/en-US/images/icon.png" width="25%" /> <br>
YTDLnis
</h1>
<div align="center">
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README.MD">English</a>
&nbsp;&nbsp;| &nbsp;&nbsp;
Shqip
</div>
<h3 align="center">
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.
</h3>
<div align="center">
[![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)
</div>
## 💡 Veçoritë:
- shkarko skedarë audio/video nga më shumë se <a href="https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md">1000 website</a>
- 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
<div>
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/01.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/02.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/03.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/04.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/05.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/06.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/07.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/08.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/09.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/10.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/11.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/12.png" width="30%" />
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/13.png" width="90%" />
</div>
## 💬 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
<a href="https://hosted.weblate.org/engage/ytdlnis/">
<img src="https://hosted.weblate.org/widgets/ytdlnis/-/strings/open-graph.png" alt="Translation status" />
</a>
<a href="https://hosted.weblate.org/engage/ytdlnis/">
<img src="https://hosted.weblate.org/widgets/ytdlnis/-/multi-auto.svg" alt="Translation status" />
</a>
## 😇 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 <br>
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.

View file

@ -6,6 +6,8 @@
<div align="center">
English
&nbsp;&nbsp;| &nbsp;&nbsp;
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-sq.md">Shqip</a>
&nbsp;&nbsp;| &nbsp;&nbsp;
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-az.md">Azərbaycanca</a>
&nbsp;&nbsp;| &nbsp;&nbsp;
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-pt.md">Português</a>

View file

@ -76,6 +76,9 @@ android {
}
}
lintOptions{
disable 'MissingTranslation'
}
android.applicationVariants.configureEach { variant ->
variant.outputs.each { output ->

View file

@ -84,7 +84,7 @@
</activity>
<activity
android:name=".ui.more.settings.SettingsActivity"
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
android:configChanges="smallestScreenSize|layoutDirection|orientation|screenSize"
android:exported="true"
android:parentActivityName=".MainActivity">
<intent-filter>

View file

@ -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<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
}

View file

@ -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<Format>{
val audioFormats = resources.getStringArray(R.array.audio_formats)
val formats = mutableListOf<Format>()
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<Format>{
val videoFormats = resources.getStringArray(R.array.video_formats_values)
val formats = mutableListOf<Format>()
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()

View file

@ -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

View file

@ -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)

View file

@ -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) {}

View file

@ -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<Format>
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<AutoCompleteTextView>(R.id.container_textview)
if (formats.isEmpty()) formats = downloadViewModel.getGenericAudioFormats()
if (formats.isEmpty()) formats = genericAudioFormats
val formatCard = view.findViewById<MaterialCardView>(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")
}
}

View file

@ -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]

View file

@ -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<DownloadI
private lateinit var resultViewModel: ResultViewModel
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
private lateinit var recyclerView: RecyclerView
private lateinit var infoUtil: InfoUtil
private lateinit var behavior: BottomSheetBehavior<View>
private lateinit var bottomAppBar: BottomAppBar
private lateinit var filesize : TextView
@ -90,6 +92,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
infoUtil = InfoUtil(requireContext())
}
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
@ -357,8 +360,8 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
items.map { it.allFormats }
}else{
when(items.first().type){
DownloadViewModel.Type.audio -> listOf<List<Format>>(downloadViewModel.getGenericAudioFormats())
else -> listOf<List<Format>>(downloadViewModel.getGenericVideoFormats())
DownloadViewModel.Type.audio -> listOf<List<Format>>(infoUtil.getGenericAudioFormats(requireContext().resources))
else -> listOf<List<Format>>(infoUtil.getGenericVideoFormats(requireContext().resources))
}
}
val bottomSheet = FormatSelectionBottomSheetDialog(items, formats, formatListener)

View file

@ -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<Format>
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<MaterialCardView>(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<List<Format>>, item: List<FormatTuple>) {
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")
}
}

View file

@ -276,7 +276,7 @@ class FormatSelectionBottomSheetDialog(private val items: List<DownloadItem?>, 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}"

View file

@ -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<String>()
values.forEach {
entries.add(Locale(it).getDisplayName(Locale(it)))
}
language!!.entries = entries.toTypedArray()
val values = resources.getStringArray(R.array.language_values)
val entries = mutableListOf<String>()
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

View file

@ -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<ResultItem?>
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<ResultItem?> {
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<ResultItem?> {
val items = arrayListOf<ResultItem?>()
val data = genericRequest("$pipedURL/search?q=$query&filter=videos&region=${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<ResultItem?> {
val items = arrayListOf<ResultItem?>()
val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs&region=${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<ResultItem?>()
// -------------- 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<ChapterItem>()
@ -350,7 +351,6 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun getTrending(): ArrayList<ResultItem?> {
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<ResultItem?> {
val items = arrayListOf<ResultItem?>()
val key = sharedPreferences.getString("api_key", "")!!
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10&regionCode=$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<ResultItem?> {
val items = arrayListOf<ResultItem?>()
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<ResultItem?> {
items = ArrayList()
val items = arrayListOf<ResultItem?>()
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<String?> = try {
val results: List<String?> = 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<Format>()
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<Format>{
val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
val audioFormats = resources.getStringArray(R.array.audio_formats)
val formats = mutableListOf<Format>()
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<Format>{
val formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() }
val videoFormats = resources.getStringArray(R.array.video_formats_values)
val formats = mutableListOf<Format>()
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(

View file

@ -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
}

View file

@ -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
}

View file

@ -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))

View file

@ -48,7 +48,7 @@
<string-array name="video_formats_values">
<item>worst</item>
<item>360p</item>
<item>360p_ytdlnisgeneric</item>
<item>480p_ytdlnisgeneric</item>
<item>720p_ytdlnisgeneric</item>
<item>1080p_ytdlnisgeneric</item>
@ -766,6 +766,15 @@
</array>
<string-array name="sub_formats">
<item>@string/defaultValue</item>
<item>srt</item>
<item>ass</item>
<item>lrc</item>
<item>vtt</item>
</string-array>
<string-array name="sub_formats_values">
<item></item>
<item>srt</item>
<item>ass</item>
<item>lrc</item>
@ -794,9 +803,9 @@
<string-array name="video_codec_values">
<item></item>
<item>av0</item>
<item>vp</item>
<item>avc</item>
<item>hev</item>
<item>av[01]</item>
<item>vp[09]</item>
<item>avc|h264</item>
<item>hev|h265</item>
</string-array>
</resources>

View file

@ -292,7 +292,7 @@
<string name="extra_command">Extra Command</string>
<string name="use_cookies">Use Cookies</string>
<string name="embed_metadata">Embed Metadata</string>
<string name="embed_metadata_summary">Embed metadata to the video file. Also embeds chapters/infojson if present</string>
<string name="embed_metadata_summary">Embed and parse metadata to the audio file.</string>
<string name="update_formats_background">Update Formats in Background</string>
<string name="auto_update_ytdlp">Auto-Update yt-dlp</string>
<string name="use_sponsorblock">Use SponsorBlock</string>

View file

@ -11,13 +11,6 @@
app:summary="en"
app:title="@string/language" />
<Preference
android:icon="@drawable/baseline_translate_24"
app:key="app_language_13"
app:summary="en"
app:title="@string/language" />
<ListPreference
android:entries="@array/themes"
android:entryValues="@array/themes_values"

View file

@ -63,7 +63,7 @@
<ListPreference
android:defaultValue="srt"
android:entries="@array/sub_formats"
android:entryValues="@array/sub_formats"
android:entryValues="@array/sub_formats_values"
android:icon="@drawable/baseline_format_quote_24"
app:key="sub_format"
app:useSimpleSummaryProvider="true"