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:
parent
2964f7da75
commit
c05224336a
23 changed files with 361 additions and 215 deletions
133
README-sq.md
Normal file
133
README-sq.md
Normal 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>
|
||||||
|
|
|
||||||
|
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">
|
||||||
|
|
||||||
|
[](https://github.com/deniscerri/ytdlnis/releases/latest)
|
||||||
|
[](https://android.izzysoft.de/repo/apk/com.deniscerri.ytdl)
|
||||||
|
[](https://ytdlnis.en.uptodown.com/android/download)
|
||||||
|
|
||||||
|

|
||||||
|
[](https://github.com/deniscerri/ytdlnis/releases)
|
||||||
|
[](https://github.com/deniscerri/ytdlnis/releases)
|
||||||
|
[](https://hosted.weblate.org/engage/ytdlnis/?utm_source=widget)
|
||||||
|
[](https://discord.gg/WW3KYWxAPm)
|
||||||
|
[](https://t.me/ytdlnis)
|
||||||
|
[](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.
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
English
|
English
|
||||||
|
|
|
|
||||||
|
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-sq.md">Shqip</a>
|
||||||
|
|
|
||||||
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-az.md">Azərbaycanca</a>
|
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-az.md">Azərbaycanca</a>
|
||||||
|
|
|
|
||||||
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-pt.md">Português</a>
|
<a href="https://github.com/deniscerri/ytdlnis/blob/main/README-pt.md">Português</a>
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lintOptions{
|
||||||
|
disable 'MissingTranslation'
|
||||||
|
}
|
||||||
|
|
||||||
android.applicationVariants.configureEach { variant ->
|
android.applicationVariants.configureEach { variant ->
|
||||||
variant.outputs.each { output ->
|
variant.outputs.each { output ->
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.more.settings.SettingsActivity"
|
android:name=".ui.more.settings.SettingsActivity"
|
||||||
android:configChanges="smallestScreenSize|layoutDirection|locale|orientation|screenSize"
|
android:configChanges="smallestScreenSize|layoutDirection|orientation|screenSize"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:parentActivityName=".MainActivity">
|
android:parentActivityName=".MainActivity">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,12 @@ class MainActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
true
|
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)
|
(navigationView as NavigationView).getHeaderView(0).findViewById<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -122,14 +122,14 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() }
|
||||||
|
|
||||||
val confTmp = Configuration(application.resources.configuration)
|
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()
|
val metrics = DisplayMetrics()
|
||||||
resources = Resources(application.assets, metrics, confTmp)
|
resources = Resources(application.assets, metrics, confTmp)
|
||||||
|
|
||||||
|
|
||||||
videoContainer = sharedPreferences.getString("video_format", "Default")
|
videoContainer = sharedPreferences.getString("video_format", "Default")
|
||||||
defaultVideoFormats = getGenericVideoFormats()
|
defaultVideoFormats = infoUtil.getGenericVideoFormats(resources)
|
||||||
bestVideoFormat = defaultVideoFormats.last()
|
bestVideoFormat = defaultVideoFormats.first()
|
||||||
|
|
||||||
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
audioContainer = sharedPreferences.getString("audio_format", "mp3")
|
||||||
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
bestAudioFormat = if (audioFormatIDPreference.isEmpty()){
|
||||||
|
|
@ -387,10 +387,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
return cloneFormat (
|
return cloneFormat (
|
||||||
try {
|
try {
|
||||||
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()
|
.firstOrNull { audioFormatIDPreference.indexOfFirst { a -> a.contains(it.format_id) } >= 0 || it.container == audioContainer} ?: throw Exception()
|
||||||
}catch (e: 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){
|
}catch (e: Exception){
|
||||||
bestAudioFormat
|
bestAudioFormat
|
||||||
|
|
@ -401,18 +401,17 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
|
||||||
Type.video -> {
|
Type.video -> {
|
||||||
return cloneFormat(
|
return cloneFormat(
|
||||||
try {
|
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) {
|
when (videoQualityPreference) {
|
||||||
"worst" -> {
|
"worst" -> {
|
||||||
theFormats.first()
|
theFormats.last()
|
||||||
}
|
}
|
||||||
else /*best*/ -> {
|
else /*best*/ -> {
|
||||||
val requirements: MutableList<(Format) -> Boolean> = mutableListOf()
|
val requirements: MutableList<(Format) -> Boolean> = mutableListOf()
|
||||||
requirements.add { it: Format -> formatIDPreference.sorted().contains(it.format_id) }
|
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 -> 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.format_note.contains(videoQualityPreference.split("_")[0].dropLast(1)) }
|
||||||
requirements.add { it: Format -> it.vcodec.startsWith(videoCodec!!, true) }
|
requirements.add { it: Format -> "^(${videoCodec}).+$".toRegex(RegexOption.IGNORE_CASE).matches(it.vcodec)}
|
||||||
|
|
||||||
theFormats.maxByOrNull { f -> requirements.count{ req -> req(f)} } ?: throw Exception()
|
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 {
|
fun getLatestCommandTemplateAsFormat() : Format {
|
||||||
val t = commandTemplateDao.getFirst()
|
val t = commandTemplateDao.getFirst()
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ package com.deniscerri.ytdlnis.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
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.deniscerri.ytdlnis.util.ThemeUtil
|
||||||
import com.google.android.material.elevation.SurfaceColors
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -435,7 +435,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InflateParams")
|
@SuppressLint("InflateParams")
|
||||||
private suspend fun updateSearchViewItems(it: Editable?, linkYouCopied: View?){
|
private suspend fun updateSearchViewItems(searchQuery: Editable?, linkYouCopied: View?){
|
||||||
searchSuggestionsLinearLayout!!.visibility = GONE
|
searchSuggestionsLinearLayout!!.visibility = GONE
|
||||||
searchHistoryLinearLayout!!.visibility = GONE
|
searchHistoryLinearLayout!!.visibility = GONE
|
||||||
searchSuggestionsLinearLayout!!.removeAllViews()
|
searchSuggestionsLinearLayout!!.removeAllViews()
|
||||||
|
|
@ -449,16 +449,15 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, OnClickListene
|
||||||
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0)
|
searchView!!.editText.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_plus, 0)
|
||||||
}
|
}
|
||||||
val suggestions = withContext(Dispatchers.IO){
|
val suggestions = withContext(Dispatchers.IO){
|
||||||
if (it!!.isEmpty()) {
|
resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery!!) } +
|
||||||
resultViewModel.getSearchHistory().map { it.query }
|
if (sharedPreferences!!.getBoolean("search_suggestions", false)){
|
||||||
}else if (sharedPreferences!!.getBoolean("search_suggestions", false)){
|
infoUtil!!.getSearchSuggestions(searchQuery.toString())
|
||||||
infoUtil!!.getSearchSuggestions(it.toString())
|
|
||||||
}else{
|
}else{
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (it!!.isEmpty()){
|
if (searchQuery!!.isEmpty()){
|
||||||
for (i in suggestions.indices) {
|
for (i in suggestions.indices) {
|
||||||
val v = LayoutInflater.from(fragmentContext)
|
val v = LayoutInflater.from(fragmentContext)
|
||||||
.inflate(R.layout.search_suggestion_item, null)
|
.inflate(R.layout.search_suggestion_item, null)
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,6 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
|
||||||
if ((event!!.action == KeyEvent.ACTION_DOWN) &&
|
if ((event!!.action == KeyEvent.ACTION_DOWN) &&
|
||||||
(keyCode == KeyEvent.KEYCODE_ENTER)) {
|
(keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||||
|
|
||||||
val values = rangeSlider.values
|
|
||||||
var endTimestamp = (rangeSlider.valueTo.toInt() * timeSeconds) / 100
|
var endTimestamp = (rangeSlider.valueTo.toInt() * timeSeconds) / 100
|
||||||
|
|
||||||
toTextInput.editText!!.clearFocus()
|
toTextInput.editText!!.clearFocus()
|
||||||
|
|
@ -361,7 +360,7 @@ class CutVideoBottomSheetDialog(private val item: DownloadItem, private val urls
|
||||||
rangeSlider.setValues(startValue, endValue)
|
rangeSlider.setValues(startValue, endValue)
|
||||||
okBtn.isEnabled = startValue != 0F || endValue != 100F
|
okBtn.isEnabled = startValue != 0F || endValue != 100F
|
||||||
try {
|
try {
|
||||||
player.seekTo(startSeconds.toLong() * 1000)
|
player.seekTo((seconds.toLong() - 4L) * 1000)
|
||||||
player.play()
|
player.play()
|
||||||
}catch (ignored: Exception) {}
|
}catch (ignored: Exception) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ import java.io.File
|
||||||
|
|
||||||
|
|
||||||
class DownloadAudioFragment(private var resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync {
|
class DownloadAudioFragment(private var resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync {
|
||||||
private var _binding : FragmentHomeBinding? = null
|
|
||||||
private var fragmentView: View? = null
|
private var fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
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 saveDir : TextInputLayout
|
||||||
private lateinit var freeSpace : TextView
|
private lateinit var freeSpace : TextView
|
||||||
private lateinit var infoUtil: InfoUtil
|
private lateinit var infoUtil: InfoUtil
|
||||||
|
private lateinit var genericAudioFormats: MutableList<Format>
|
||||||
|
|
||||||
lateinit var downloadItem : DownloadItem
|
lateinit var downloadItem : DownloadItem
|
||||||
lateinit var title : TextInputLayout
|
lateinit var title : TextInputLayout
|
||||||
|
|
@ -55,12 +55,12 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_download_audio, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||||
infoUtil = InfoUtil(requireContext())
|
infoUtil = InfoUtil(requireContext())
|
||||||
|
genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources)
|
||||||
return fragmentView
|
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) }
|
downloadItem.allFormats.filter { it.format_note.contains("audio", ignoreCase = true) }
|
||||||
.maxByOrNull { it.filesize }!!
|
.maxByOrNull { it.filesize }!!
|
||||||
}.onFailure {
|
}.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 =
|
val containerAutoCompleteTextView =
|
||||||
view.findViewById<AutoCompleteTextView>(R.id.container_textview)
|
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 formatCard = view.findViewById<MaterialCardView>(R.id.format_card_constraintLayout)
|
||||||
val chosenFormat = downloadItem.format
|
val chosenFormat = downloadItem.format
|
||||||
|
|
@ -154,16 +154,17 @@ class DownloadAudioFragment(private var resultItem: ResultItem, private var curr
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
resultItem.formats.removeAll(formats.toSet())
|
resultItem.formats.removeAll(formats.toSet())
|
||||||
resultItem.formats.addAll(allFormats.first())
|
resultItem.formats.addAll(allFormats.first().filter { !genericAudioFormats.contains(it) })
|
||||||
resultViewModel.update(resultItem)
|
resultViewModel.update(resultItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formats = allFormats.first().toMutableList()
|
formats = allFormats.first().filter { !genericAudioFormats.contains(it) }.toMutableList()
|
||||||
|
formats.removeAll(genericAudioFormats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formatCard.setOnClickListener{
|
formatCard.setOnClickListener{
|
||||||
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
|
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")
|
bottomSheet.show(parentFragmentManager, "formatSheet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class DownloadCommandFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment() {
|
class DownloadCommandFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment() {
|
||||||
private var _binding : FragmentHomeBinding? = null
|
|
||||||
private var fragmentView: View? = null
|
private var fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
private lateinit var downloadViewModel : DownloadViewModel
|
||||||
|
|
@ -57,7 +56,6 @@ class DownloadCommandFragment(private val resultItem: ResultItem, private var cu
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_download_command, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.receiver.ShareActivity
|
import com.deniscerri.ytdlnis.receiver.ShareActivity
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll
|
import com.deniscerri.ytdlnis.util.UiUtil.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.work.UpdatePlaylistFormatsWorker
|
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 resultViewModel: ResultViewModel
|
||||||
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
|
private lateinit var listAdapter : ConfigureMultipleDownloadsAdapter
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
|
private lateinit var infoUtil: InfoUtil
|
||||||
private lateinit var behavior: BottomSheetBehavior<View>
|
private lateinit var behavior: BottomSheetBehavior<View>
|
||||||
private lateinit var bottomAppBar: BottomAppBar
|
private lateinit var bottomAppBar: BottomAppBar
|
||||||
private lateinit var filesize : TextView
|
private lateinit var filesize : TextView
|
||||||
|
|
@ -90,6 +92,7 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
||||||
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
|
commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java]
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
infoUtil = InfoUtil(requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
||||||
|
|
@ -357,8 +360,8 @@ class DownloadMultipleBottomSheetDialog(private var items: MutableList<DownloadI
|
||||||
items.map { it.allFormats }
|
items.map { it.allFormats }
|
||||||
}else{
|
}else{
|
||||||
when(items.first().type){
|
when(items.first().type){
|
||||||
DownloadViewModel.Type.audio -> listOf<List<Format>>(downloadViewModel.getGenericAudioFormats())
|
DownloadViewModel.Type.audio -> listOf<List<Format>>(infoUtil.getGenericAudioFormats(requireContext().resources))
|
||||||
else -> listOf<List<Format>>(downloadViewModel.getGenericVideoFormats())
|
else -> listOf<List<Format>>(infoUtil.getGenericVideoFormats(requireContext().resources))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val bottomSheet = FormatSelectionBottomSheetDialog(items, formats, formatListener)
|
val bottomSheet = FormatSelectionBottomSheetDialog(items, formats, formatListener)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel.Type
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
||||||
import com.deniscerri.ytdlnis.databinding.FragmentHomeBinding
|
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.InfoUtil
|
import com.deniscerri.ytdlnis.util.InfoUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
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 {
|
class DownloadVideoFragment(private val resultItem: ResultItem, private var currentDownloadItem: DownloadItem?) : Fragment(), TitleAuthorSync {
|
||||||
private var _binding : FragmentHomeBinding? = null
|
|
||||||
private var fragmentView: View? = null
|
private var fragmentView: View? = null
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private lateinit var downloadViewModel : DownloadViewModel
|
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 saveDir : TextInputLayout
|
||||||
private lateinit var freeSpace : TextView
|
private lateinit var freeSpace : TextView
|
||||||
private lateinit var infoUtil: InfoUtil
|
private lateinit var infoUtil: InfoUtil
|
||||||
|
private lateinit var genericVideoFormats: MutableList<Format>
|
||||||
|
|
||||||
lateinit var downloadItem: DownloadItem
|
lateinit var downloadItem: DownloadItem
|
||||||
|
|
||||||
|
|
@ -58,12 +57,12 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
|
||||||
fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false)
|
fragmentView = inflater.inflate(R.layout.fragment_download_video, container, false)
|
||||||
activity = getActivity()
|
activity = getActivity()
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(this@DownloadVideoFragment)[ResultViewModel::class.java]
|
||||||
infoUtil = InfoUtil(requireContext())
|
infoUtil = InfoUtil(requireContext())
|
||||||
|
genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources)
|
||||||
return fragmentView
|
return fragmentView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,7 +135,7 @@ class DownloadVideoFragment(private val resultItem: ResultItem, private var curr
|
||||||
downloadItem.allFormats.filter { it.vcodec.isNotEmpty() }
|
downloadItem.allFormats.filter { it.vcodec.isNotEmpty() }
|
||||||
.maxByOrNull { it.filesize }!!
|
.maxByOrNull { it.filesize }!!
|
||||||
}.onFailure {
|
}.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")
|
var containerPreference = sharedPreferences.getString("video_format", "Default")
|
||||||
if (containerPreference == "Default") containerPreference = getString(R.string.defaultValue)
|
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)
|
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 {
|
val listener = object : OnFormatClickListener {
|
||||||
override fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>) {
|
override fun onFormatClick(allFormats: List<List<Format>>, item: List<FormatTuple>) {
|
||||||
downloadItem.format = item.first().format
|
downloadItem.format = item.first().format
|
||||||
item.first().audioFormats?.map { it.format_id }
|
item.first().audioFormats?.map { it.format_id }?.let {
|
||||||
?.let { downloadItem.videoPreferences.audioFormatIDs.addAll(it) }
|
downloadItem.videoPreferences.audioFormatIDs.clear()
|
||||||
|
downloadItem.videoPreferences.audioFormatIDs.addAll(it)
|
||||||
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
resultItem.formats.removeAll(formats.toSet())
|
resultItem.formats.removeAll(formats)
|
||||||
resultItem.formats.addAll(allFormats.first())
|
resultItem.formats.addAll(allFormats.first().filter { !genericVideoFormats.contains(it) })
|
||||||
resultViewModel.update(resultItem)
|
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)
|
UiUtil.populateFormatCard(requireContext(), formatCard, item.first().format, item.first().audioFormats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formatCard.setOnClickListener{
|
formatCard.setOnClickListener{
|
||||||
if (parentFragmentManager.findFragmentByTag("formatSheet") == null){
|
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")
|
bottomSheet.show(parentFragmentManager, "formatSheet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 format = finalFormats[i]
|
||||||
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
|
val formatItem = LayoutInflater.from(context).inflate(R.layout.format_item, null)
|
||||||
formatItem.tag = "${format.format_id}${format.format_note}"
|
formatItem.tag = "${format.format_id}${format.format_note}"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build.VERSION
|
|
||||||
import android.os.Build.VERSION_CODES
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
|
|
@ -29,7 +27,6 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
override val title: Int = R.string.general
|
override val title: Int = R.string.general
|
||||||
|
|
||||||
private var language: ListPreference? = null
|
private var language: ListPreference? = null
|
||||||
private var language13: Preference? = null
|
|
||||||
private var theme: ListPreference? = null
|
private var theme: ListPreference? = null
|
||||||
private var accent: ListPreference? = null
|
private var accent: ListPreference? = null
|
||||||
private var highContrast: SwitchPreferenceCompat? = null
|
private var highContrast: SwitchPreferenceCompat? = null
|
||||||
|
|
@ -51,45 +48,28 @@ class GeneralSettingsFragment : BaseSettingsFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
language = findPreference("app_language")
|
language = findPreference("app_language")
|
||||||
language13 = findPreference("app_language_13")
|
|
||||||
theme = findPreference("ytdlnis_theme")
|
theme = findPreference("ytdlnis_theme")
|
||||||
accent = findPreference("theme_accent")
|
accent = findPreference("theme_accent")
|
||||||
highContrast = findPreference("high_contrast")
|
highContrast = findPreference("high_contrast")
|
||||||
locale = findPreference("locale")
|
locale = findPreference("locale")
|
||||||
showTerminalShareIcon = findPreference("show_terminal")
|
showTerminalShareIcon = findPreference("show_terminal")
|
||||||
|
|
||||||
if(VERSION.SDK_INT < VERSION_CODES.TIRAMISU){
|
val values = resources.getStringArray(R.array.language_values)
|
||||||
val values = resources.getStringArray(R.array.language_values)
|
val entries = mutableListOf<String>()
|
||||||
val entries = mutableListOf<String>()
|
values.forEach {
|
||||||
values.forEach {
|
entries.add(Locale(it).getDisplayName(Locale(it)))
|
||||||
entries.add(Locale(it).getDisplayName(Locale(it)))
|
}
|
||||||
}
|
language!!.entries = entries.toTypedArray()
|
||||||
language!!.entries = entries.toTypedArray()
|
|
||||||
|
|
||||||
if(language!!.value == null) language!!.value = Locale.getDefault().language
|
if(language!!.value == null) language!!.value = Locale.getDefault().language
|
||||||
language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value))
|
language!!.summary = Locale(language!!.value).getDisplayLanguage(Locale(language!!.value))
|
||||||
|
|
||||||
language!!.onPreferenceChangeListener =
|
language!!.onPreferenceChangeListener =
|
||||||
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
|
||||||
language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString()))
|
language!!.summary = Locale(newValue.toString()).getDisplayLanguage(Locale(newValue.toString()))
|
||||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(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()
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
theme!!.summary = theme!!.entry
|
theme!!.summary = theme!!.entry
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ package com.deniscerri.ytdlnis.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.content.res.Resources
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.preference.AndroidResources
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ChapterItem
|
import com.deniscerri.ytdlnis.database.models.ChapterItem
|
||||||
|
|
@ -32,7 +34,6 @@ import java.util.regex.Pattern
|
||||||
|
|
||||||
|
|
||||||
class InfoUtil(private val context: Context) {
|
class InfoUtil(private val context: Context) {
|
||||||
private var items: ArrayList<ResultItem?>
|
|
||||||
private lateinit var sharedPreferences: SharedPreferences
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
@ -43,12 +44,10 @@ class InfoUtil(private val context: Context) {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
items = ArrayList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun search(query: String): ArrayList<ResultItem?> {
|
fun search(query: String): ArrayList<ResultItem?> {
|
||||||
items = ArrayList()
|
|
||||||
return when(sharedPreferences.getString("search_engine", "ytsearch")){
|
return when(sharedPreferences.getString("search_engine", "ytsearch")){
|
||||||
"ytsearch" -> try{
|
"ytsearch" -> try{
|
||||||
searchFromPiped(query)
|
searchFromPiped(query)
|
||||||
|
|
@ -68,6 +67,7 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromPiped(query: String): ArrayList<ResultItem?> {
|
fun searchFromPiped(query: String): ArrayList<ResultItem?> {
|
||||||
|
val items = arrayListOf<ResultItem?>()
|
||||||
val data = genericRequest("$pipedURL/search?q=$query&filter=videos®ion=${countryCODE}")
|
val data = genericRequest("$pipedURL/search?q=$query&filter=videos®ion=${countryCODE}")
|
||||||
val dataArray = data.getJSONArray("items")
|
val dataArray = data.getJSONArray("items")
|
||||||
if (dataArray.length() == 0) return getFromYTDL(query)
|
if (dataArray.length() == 0) return getFromYTDL(query)
|
||||||
|
|
@ -86,6 +86,7 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun searchFromPipedMusic(query: String): ArrayList<ResultItem?> {
|
fun searchFromPipedMusic(query: String): ArrayList<ResultItem?> {
|
||||||
|
val items = arrayListOf<ResultItem?>()
|
||||||
val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs®ion=${countryCODE}")
|
val data = genericRequest("$pipedURL/search?q=$query=&filter=music_songs®ion=${countryCODE}")
|
||||||
val dataArray = data.getJSONArray("items")
|
val dataArray = data.getJSONArray("items")
|
||||||
if (dataArray.length() == 0) return getFromYTDL(query)
|
if (dataArray.length() == 0) return getFromYTDL(query)
|
||||||
|
|
@ -105,7 +106,7 @@ class InfoUtil(private val context: Context) {
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
|
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
|
||||||
try{
|
try{
|
||||||
items = ArrayList()
|
val items = arrayListOf<ResultItem?>()
|
||||||
// -------------- PIPED API FUNCTION -------------------
|
// -------------- PIPED API FUNCTION -------------------
|
||||||
var url = ""
|
var url = ""
|
||||||
url = if (nextPageToken.isBlank()) "$pipedURL/playlists/$id"
|
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 {
|
formats.groupBy { it.format_id }.forEach {
|
||||||
if (it.value.count() > 1) {
|
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}" }
|
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}"
|
engDefault?.format_id = (engDefault?.format_id?.split("-")?.get(0) ?: "") + "-${it.value.size-1}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
formats.sortByDescending { it.filesize }
|
||||||
}
|
}
|
||||||
|
|
||||||
val chapters = ArrayList<ChapterItem>()
|
val chapters = ArrayList<ChapterItem>()
|
||||||
|
|
@ -350,7 +351,6 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getTrending(): ArrayList<ResultItem?> {
|
fun getTrending(): ArrayList<ResultItem?> {
|
||||||
items = ArrayList()
|
|
||||||
if (sharedPreferences.getString("api_key", "")!!.isNotBlank()){
|
if (sharedPreferences.getString("api_key", "")!!.isNotBlank()){
|
||||||
return getTrendingFromYoutubeAPI()
|
return getTrendingFromYoutubeAPI()
|
||||||
}
|
}
|
||||||
|
|
@ -359,8 +359,8 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
@Throws(JSONException::class)
|
@Throws(JSONException::class)
|
||||||
fun getTrendingFromYoutubeAPI(): ArrayList<ResultItem?> {
|
fun getTrendingFromYoutubeAPI(): ArrayList<ResultItem?> {
|
||||||
|
val items = arrayListOf<ResultItem?>()
|
||||||
val key = sharedPreferences.getString("api_key", "")!!
|
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"
|
val url = "https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&videoCategoryId=10®ionCode=$countryCODE&maxResults=25&key=$key"
|
||||||
//short data
|
//short data
|
||||||
val res = genericRequest(url)
|
val res = genericRequest(url)
|
||||||
|
|
@ -389,6 +389,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getTrendingFromPiped(): ArrayList<ResultItem?> {
|
private fun getTrendingFromPiped(): ArrayList<ResultItem?> {
|
||||||
|
val items = arrayListOf<ResultItem?>()
|
||||||
val url = "$pipedURL/trending?region=$countryCODE"
|
val url = "$pipedURL/trending?region=$countryCODE"
|
||||||
val res = genericArrayRequest(url)
|
val res = genericArrayRequest(url)
|
||||||
try {
|
try {
|
||||||
|
|
@ -547,7 +548,7 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFromYTDL(query: String): ArrayList<ResultItem?> {
|
fun getFromYTDL(query: String): ArrayList<ResultItem?> {
|
||||||
items = ArrayList()
|
val items = arrayListOf<ResultItem?>()
|
||||||
val searchEngine = sharedPreferences.getString("search_engine", "ytsearch")
|
val searchEngine = sharedPreferences.getString("search_engine", "ytsearch")
|
||||||
try {
|
try {
|
||||||
val request : YoutubeDLRequest
|
val request : YoutubeDLRequest
|
||||||
|
|
@ -583,12 +584,12 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
val youtubeDLResponse = YoutubeDL.getInstance().execute(request)
|
||||||
val results: Array<String?> = try {
|
val results: List<String?> = try {
|
||||||
val lineSeparator = System.getProperty("line.separator")
|
val lineSeparator = System.getProperty("line.separator")
|
||||||
youtubeDLResponse.out.split(lineSeparator!!).toTypedArray()
|
youtubeDLResponse.out.split(lineSeparator!!)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
arrayOf(youtubeDLResponse.out)
|
listOf(youtubeDLResponse.out)
|
||||||
}
|
}.filter { it.isNotBlank() }
|
||||||
for (result in results) {
|
for (result in results) {
|
||||||
if (result.isNullOrBlank()) continue
|
if (result.isNullOrBlank()) continue
|
||||||
val jsonObject = JSONObject(result)
|
val jsonObject = JSONObject(result)
|
||||||
|
|
@ -690,7 +691,7 @@ class InfoUtil(private val context: Context) {
|
||||||
val formats = arrayListOf<Format>()
|
val formats = arrayListOf<Format>()
|
||||||
|
|
||||||
if (formatsInJSON != null) {
|
if (formatsInJSON != null) {
|
||||||
for (f in 0 until formatsInJSON.length()){
|
for (f in formatsInJSON.length() - 1 downTo 0){
|
||||||
val format = formatsInJSON.getJSONObject(f)
|
val format = formatsInJSON.getJSONObject(f)
|
||||||
if (format.has("filesize")){
|
if (format.has("filesize")){
|
||||||
if (format.get("filesize") == "None"){
|
if (format.get("filesize") == "None"){
|
||||||
|
|
@ -987,8 +988,9 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
val limitRate = sharedPreferences.getString("limit_rate", "")
|
val limitRate = sharedPreferences.getString("limit_rate", "")
|
||||||
if (limitRate != "") request.addOption("-r", limitRate!!)
|
if (limitRate != "") request.addOption("-r", limitRate!!)
|
||||||
request.addOption("--trim-filenames", 120)
|
|
||||||
if(downloadItem.type != DownloadViewModel.Type.command){
|
if(downloadItem.type != DownloadViewModel.Type.command){
|
||||||
|
request.addOption("--trim-filenames", 120)
|
||||||
|
|
||||||
if (downloadItem.SaveThumb) {
|
if (downloadItem.SaveThumb) {
|
||||||
request.addOption("--write-thumbnail")
|
request.addOption("--write-thumbnail")
|
||||||
request.addOption("--convert-thumbnails", "jpg")
|
request.addOption("--convert-thumbnails", "jpg")
|
||||||
|
|
@ -1024,6 +1026,7 @@ class InfoUtil(private val context: Context) {
|
||||||
request.addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
|
request.addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
|
||||||
}
|
}
|
||||||
if (downloadItem.author.isNotBlank()){
|
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", ".+", downloadItem.author))
|
||||||
request.addCommands(listOf("--replace-in-metadata","uploader"," - Topic$",""))
|
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)) {
|
if (sharedPreferences.getBoolean("restrict_filenames", true)) {
|
||||||
|
|
@ -1166,10 +1169,6 @@ class InfoUtil(private val context: Context) {
|
||||||
DownloadViewModel.Type.video -> {
|
DownloadViewModel.Type.video -> {
|
||||||
val supportedContainers = context.resources.getStringArray(R.array.video_containers)
|
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 (downloadItem.videoPreferences.addChapters) {
|
||||||
if (sharedPreferences.getBoolean("use_sponsorblock", true)){
|
if (sharedPreferences.getBoolean("use_sponsorblock", true)){
|
||||||
request.addOption("--sponsorblock-mark", "all")
|
request.addOption("--sponsorblock-mark", "all")
|
||||||
|
|
@ -1200,100 +1199,102 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//format logic
|
//format logic
|
||||||
|
|
||||||
val defaultFormats = context.resources.getStringArray(R.array.video_formats_values)
|
|
||||||
var usingGenericFormat = false
|
|
||||||
|
|
||||||
var videof = downloadItem.format.format_id
|
var videof = downloadItem.format.format_id
|
||||||
if (videof == context.resources.getString(R.string.best_quality) || videof == "best") {
|
var audiof = "ba"
|
||||||
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"
|
|
||||||
if (downloadItem.videoPreferences.audioFormatIDs.isNotEmpty()){
|
if (downloadItem.videoPreferences.audioFormatIDs.isNotEmpty()){
|
||||||
audiof = downloadItem.videoPreferences.audioFormatIDs.joinToString("+")
|
audiof = downloadItem.videoPreferences.audioFormatIDs.joinToString("+")
|
||||||
}
|
}
|
||||||
if (downloadItem.videoPreferences.removeAudio) audiof = ""
|
if (downloadItem.videoPreferences.removeAudio) audiof = ""
|
||||||
|
|
||||||
val preferredAudioFormatIDs = sharedPreferences.getString("format_id_audio", "")
|
val defaultFormats = context.resources.getStringArray(R.array.video_formats_values)
|
||||||
.toString()
|
val usingGenericFormat = defaultFormats.contains(videof) || downloadItem.allFormats.isEmpty() || downloadItem.allFormats == getGenericVideoFormats(context.resources)
|
||||||
.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 f = StringBuilder()
|
val f = StringBuilder()
|
||||||
|
|
||||||
//build format with extension and vcodec
|
if(!usingGenericFormat){
|
||||||
preferredVideoFormats.forEach { v ->
|
val aa = if (audiof.isNotBlank()) "+$audiof" else ""
|
||||||
preferredAudioFormats.forEach { a ->
|
f.append("$videof$aa/$videof/best")
|
||||||
val aa = if (a.isNotBlank()) "+$a" else ""
|
|
||||||
f.append("$v$extPref$vCodecPref$aa/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//build format with vcodec
|
if (audiof.contains("+")){
|
||||||
if (extPref.isNotBlank()){
|
request.addOption("--audio-multistreams")
|
||||||
preferredVideoFormats.forEach { v ->
|
}
|
||||||
|
}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 ->
|
preferredAudioFormats.forEach { a ->
|
||||||
val aa = if (a.isNotBlank()) "+$a" else ""
|
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
|
//build format with vcodec
|
||||||
preferredAudioFormats.forEach {a ->
|
if (extPref.isNotBlank()){
|
||||||
val aa = if (a.isNotBlank()) "+$a" else ""
|
preferredAudioFormats.forEach { a ->
|
||||||
f.append("$v$aa/")
|
val aa = if (a.isNotBlank()) "+$a" else ""
|
||||||
|
f.append("$v$vCodecPref$aa/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!downloadItem.videoPreferences.removeAudio){
|
//build format with audio
|
||||||
//build format with best audio
|
if (vCodecPref.isNotBlank()){
|
||||||
preferredVideoFormats.forEach { v ->
|
preferredAudioFormats.forEach {a ->
|
||||||
if(!f.contains("$v+bestaudio/")){
|
val aa = if (a.isNotBlank()) "+$a" else ""
|
||||||
f.append("$v+bestaudio/")
|
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
|
if(!f.endsWith("best/")){
|
||||||
preferredVideoFormats.forEach { v ->
|
//last fallback
|
||||||
if(!f.contains("/$v/")){
|
f.append("best")
|
||||||
f.append("$v/")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!f.endsWith("best/")){
|
|
||||||
//last fallback
|
|
||||||
f.append("best")
|
|
||||||
}
|
|
||||||
|
|
||||||
request.addOption("-f", f.toString().replace("/$".toRegex(), ""))
|
request.addOption("-f", f.toString().replace("/$".toRegex(), ""))
|
||||||
|
|
||||||
|
|
@ -1302,8 +1303,10 @@ class InfoUtil(private val context: Context) {
|
||||||
|
|
||||||
request.addOption("--write-subs")
|
request.addOption("--write-subs")
|
||||||
request.addOption("--write-auto-subs")
|
request.addOption("--write-auto-subs")
|
||||||
request.addOption("--sub-format", "${subFormat}/best")
|
if(subFormat!!.isNotBlank()){
|
||||||
request.addOption("--convert-subtitles", subFormat ?: "srt")
|
request.addOption("--sub-format", "${subFormat}/best")
|
||||||
|
request.addOption("--convert-subtitles", subFormat ?: "srt")
|
||||||
|
}
|
||||||
if (!downloadItem.videoPreferences.embedSubs) {
|
if (!downloadItem.videoPreferences.embedSubs) {
|
||||||
request.addOption("--sub-langs", downloadItem.videoPreferences.subsLanguages.ifEmpty { "en.*,.*-orig" })
|
request.addOption("--sub-langs", downloadItem.videoPreferences.subsLanguages.ifEmpty { "en.*,.*-orig" })
|
||||||
}
|
}
|
||||||
|
|
@ -1368,6 +1371,27 @@ class InfoUtil(private val context: Context) {
|
||||||
return final
|
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("/")
|
private val pipedURL = sharedPreferences.getString("piped_instance", defaultPipedURL)?.ifEmpty { defaultPipedURL }?.removeSuffix("/")
|
||||||
|
|
||||||
class PlaylistTuple internal constructor(
|
class PlaylistTuple internal constructor(
|
||||||
|
|
|
||||||
|
|
@ -34,11 +34,18 @@ class NotificationUtil(var context: Context) {
|
||||||
NotificationManager::class.java
|
NotificationManager::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
//gui downloads
|
//downloading worker notification
|
||||||
var name: CharSequence = context.getString(R.string.download_notification_channel_name)
|
var name: CharSequence = context.getString(R.string.downloading)
|
||||||
var description = context.getString(R.string.download_notification_channel_description)
|
var description = "WorkManager Default Notification"
|
||||||
val importance = NotificationManager.IMPORTANCE_LOW
|
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
|
channel.description = description
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
|
||||||
|
|
@ -77,7 +84,7 @@ class NotificationUtil(var context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDefaultWorkerNotification() : Notification {
|
fun createDefaultWorkerNotification() : Notification {
|
||||||
val notificationBuilder = getBuilder(DOWNLOAD_SERVICE_CHANNEL_ID)
|
val notificationBuilder = getBuilder(DOWNLOAD_WORKER_CHANNEL_ID)
|
||||||
|
|
||||||
return notificationBuilder
|
return notificationBuilder
|
||||||
.setContentTitle(context.getString(R.string.downloading))
|
.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 FORMAT_UPDATING_FINISHED_NOTIFICATION_ID = 7
|
||||||
const val DOWNLOAD_MISC_CHANNEL_ID = "4"
|
const val DOWNLOAD_MISC_CHANNEL_ID = "4"
|
||||||
const val DOWNLOAD_MISC_NOTIFICATION_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_MAX = 100
|
||||||
private const val PROGRESS_CURR = 0
|
private const val PROGRESS_CURR = 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,13 @@ class UpdateUtil(var context: Context) {
|
||||||
v = res.first { !it.tag_name.contains("beta", true) }
|
v = res.first { !it.tag_name.contains("beta", true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val current = BuildConfig.VERSION_NAME.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(".", "").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
|
var isInLatest = true
|
||||||
if ((BuildConfig.VERSION_NAME.contains("beta", true) && !useBeta) ||
|
if ((current < incoming) ||
|
||||||
(!BuildConfig.VERSION_NAME.contains("beta", true) && useBeta) ||
|
(current > incoming && BuildConfig.VERSION_NAME.contains("beta", true) && !useBeta)){
|
||||||
current < incoming ){
|
|
||||||
isInLatest = false
|
isInLatest = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.deniscerri.ytdlnis.work
|
package com.deniscerri.ytdlnis.work
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.content.res.Resources
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.util.DisplayMetrics
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.navigation.NavDeepLinkBuilder
|
import androidx.navigation.NavDeepLinkBuilder
|
||||||
|
|
@ -35,6 +38,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -60,6 +64,12 @@ class DownloadWorker(
|
||||||
val currentWork = WorkManager.getInstance(context).getWorkInfosByTag("download").await()
|
val currentWork = WorkManager.getInstance(context).getWorkInfosByTag("download").await()
|
||||||
if (currentWork.count{it.state == WorkInfo.State.RUNNING} > 1) return Result.success()
|
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)
|
val pendingIntent = NavDeepLinkBuilder(context)
|
||||||
.setGraph(R.navigation.nav_graph)
|
.setGraph(R.navigation.nav_graph)
|
||||||
.setDestination(R.id.downloadQueueMainFragment)
|
.setDestination(R.id.downloadQueueMainFragment)
|
||||||
|
|
@ -162,8 +172,10 @@ class DownloadWorker(
|
||||||
.sortedByDescending { it.length() }
|
.sortedByDescending { it.length() }
|
||||||
.map { it.absolutePath }
|
.map { it.absolutePath }
|
||||||
.toList()
|
.toList()
|
||||||
|
|
||||||
FileUtil.scanMedia(finalPaths, context)
|
FileUtil.scanMedia(finalPaths, context)
|
||||||
|
if (finalPaths.isEmpty()){
|
||||||
|
finalPaths = listOf(context.getString(R.string.unfound_file))
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
//move file from internal to set download directory
|
//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))
|
setProgressAsync(workDataOf("progress" to 100, "output" to "Moving file to ${FileUtil.formatPath(downloadLocation)}", "id" to downloadItem.id))
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
|
|
||||||
<string-array name="video_formats_values">
|
<string-array name="video_formats_values">
|
||||||
<item>worst</item>
|
<item>worst</item>
|
||||||
<item>360p</item>
|
<item>360p_ytdlnisgeneric</item>
|
||||||
<item>480p_ytdlnisgeneric</item>
|
<item>480p_ytdlnisgeneric</item>
|
||||||
<item>720p_ytdlnisgeneric</item>
|
<item>720p_ytdlnisgeneric</item>
|
||||||
<item>1080p_ytdlnisgeneric</item>
|
<item>1080p_ytdlnisgeneric</item>
|
||||||
|
|
@ -766,6 +766,15 @@
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<string-array name="sub_formats">
|
<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>srt</item>
|
||||||
<item>ass</item>
|
<item>ass</item>
|
||||||
<item>lrc</item>
|
<item>lrc</item>
|
||||||
|
|
@ -794,9 +803,9 @@
|
||||||
|
|
||||||
<string-array name="video_codec_values">
|
<string-array name="video_codec_values">
|
||||||
<item></item>
|
<item></item>
|
||||||
<item>av0</item>
|
<item>av[01]</item>
|
||||||
<item>vp</item>
|
<item>vp[09]</item>
|
||||||
<item>avc</item>
|
<item>avc|h264</item>
|
||||||
<item>hev</item>
|
<item>hev|h265</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -292,7 +292,7 @@
|
||||||
<string name="extra_command">Extra Command</string>
|
<string name="extra_command">Extra Command</string>
|
||||||
<string name="use_cookies">Use Cookies</string>
|
<string name="use_cookies">Use Cookies</string>
|
||||||
<string name="embed_metadata">Embed Metadata</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="update_formats_background">Update Formats in Background</string>
|
||||||
<string name="auto_update_ytdlp">Auto-Update yt-dlp</string>
|
<string name="auto_update_ytdlp">Auto-Update yt-dlp</string>
|
||||||
<string name="use_sponsorblock">Use SponsorBlock</string>
|
<string name="use_sponsorblock">Use SponsorBlock</string>
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,6 @@
|
||||||
app:summary="en"
|
app:summary="en"
|
||||||
app:title="@string/language" />
|
app:title="@string/language" />
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:icon="@drawable/baseline_translate_24"
|
|
||||||
app:key="app_language_13"
|
|
||||||
app:summary="en"
|
|
||||||
app:title="@string/language" />
|
|
||||||
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:entries="@array/themes"
|
android:entries="@array/themes"
|
||||||
android:entryValues="@array/themes_values"
|
android:entryValues="@array/themes_values"
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:defaultValue="srt"
|
android:defaultValue="srt"
|
||||||
android:entries="@array/sub_formats"
|
android:entries="@array/sub_formats"
|
||||||
android:entryValues="@array/sub_formats"
|
android:entryValues="@array/sub_formats_values"
|
||||||
android:icon="@drawable/baseline_format_quote_24"
|
android:icon="@drawable/baseline_format_quote_24"
|
||||||
app:key="sub_format"
|
app:key="sub_format"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue