diff --git a/app/build.gradle b/app/build.gradle index e0aec3d9..5c1a5693 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -95,6 +95,7 @@ android { } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } @@ -126,7 +127,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - + coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4" implementation "com.github.yausername.youtubedl-android:library:$youtubedlAndroidVer" implementation "com.github.yausername.youtubedl-android:ffmpeg:$youtubedlAndroidVer" implementation "com.github.yausername.youtubedl-android:aria2c:$youtubedlAndroidVer" @@ -170,7 +171,7 @@ dependencies { implementation 'androidx.paging:paging-runtime-ktx:3.3.0' implementation "androidx.room:room-paging:$roomVer" androidTestImplementation "androidx.room:room-testing:$roomVer" - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.3' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4' implementation "androidx.compose.runtime:runtime:$composeVer" androidTestImplementation 'com.google.truth:truth:1.1.5' @@ -182,7 +183,7 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0' implementation 'it.xabaras.android:recyclerview-swipedecorator:1.4' - implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.3" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.4" implementation "com.github.Irineu333:Highlight-KT:1.0.4" // For media playback using ExoPlayer @@ -201,5 +202,5 @@ dependencies { implementation 'androidx.compose.material3:material3-android:1.2.1' implementation "io.noties.markwon:core:4.6.2" implementation("org.greenrobot:eventbus:3.3.1") - //implementation("com.github.TeamNewPipe:NewPipeExtractor:0.24.2") + implementation("com.github.TeamNewPipe:NewPipeExtractor:0.24.2") } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index e4c07011..1bcacb01 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -88,11 +88,16 @@ static <1>$Companion Companion; } -### Rules for NewPipeExtractor -#-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; } -#-keep class org.mozilla.javascript.** { *; } -#-keep class org.mozilla.classfile.ClassFileWriter -#-dontwarn org.mozilla.javascript.tools.** +# Rules for NewPipeExtractor +-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; } +-keep class org.mozilla.javascript.** { *; } +-keep class org.mozilla.classfile.ClassFileWriter +-dontwarn org.mozilla.javascript.tools.** +-dontwarn java.beans.BeanDescriptor +-dontwarn java.beans.BeanInfo +-dontwarn java.beans.IntrospectionException +-dontwarn java.beans.Introspector +-dontwarn java.beans.PropertyDescriptor -keepattributes RuntimeVisibleAnnotations,AnnotationDefault -dontobfuscate diff --git a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt index b0aa95f4..fe99d3a7 100644 --- a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt @@ -441,14 +441,30 @@ class MainActivity : BaseActivity() { e.printStackTrace() } }else if (action == Intent.ACTION_VIEW){ - navController.popBackStack(navController.graph.startDestinationId, true) + val navbarItems = NavbarUtil.getNavBarItems(this) when(intent.getStringExtra("destination")){ "Downloads" -> { + if (navbarItems.any { n -> n.itemId == R.id.historyFragment && n.isVisible }) { + navController.popBackStack(navController.graph.startDestinationId, true) + } navController.navigate(R.id.historyFragment) } "Queue" -> { - navController.navigate(R.id.downloadQueueMainFragment) + if (navbarItems.any { n -> n.itemId == R.id.downloadQueueMainFragment && n.isVisible }) { + navController.popBackStack(navController.graph.startDestinationId, true) + } + + val bundle = Bundle() + intent.getStringExtra("tab")?.apply { + bundle.putString("tab", this) + } + intent.getLongExtra("reconfigure", 0L).apply { + if (this != 0L){ + bundle.putLong("reconfigure", this) + } + } + navController.navigate(R.id.downloadQueueMainFragment, bundle) } "Search" -> { val bundle = Bundle() diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/ResultDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/ResultDao.kt index 927cc12c..cbb8f06f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/ResultDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/ResultDao.kt @@ -14,6 +14,9 @@ interface ResultDao { @Query("SELECT * FROM results order by id") fun getResults() : Flow> + @Query("SELECT * FROM results WHERE playlistTitle LIKE '%' || :playlistName || '%' order by id") + fun getResultsWithPlaylistName(playlistName: String) : List + @Query("SELECT COUNT(id) FROM results") fun getCount() : Flow diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt index a6146156..6de60d3b 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/ResultRepository.kt @@ -2,23 +2,49 @@ package com.deniscerri.ytdl.database.repository import android.content.Context import android.util.Patterns +import androidx.preference.PreferenceManager +import com.deniscerri.ytdl.database.DBManager.SORTING import com.deniscerri.ytdl.database.dao.ResultDao +import com.deniscerri.ytdl.database.models.ChapterItem import com.deniscerri.ytdl.database.models.DownloadItem +import com.deniscerri.ytdl.database.models.Format +import com.deniscerri.ytdl.database.models.HistoryItem import com.deniscerri.ytdl.database.models.ResultItem +import com.deniscerri.ytdl.database.repository.HistoryRepository.HistorySortType +import com.deniscerri.ytdl.database.viewmodel.ResultViewModel +import com.deniscerri.ytdl.util.Extensions.isYoutubeChannelURL import com.deniscerri.ytdl.util.Extensions.isYoutubeURL -import com.deniscerri.ytdl.util.InfoUtil -import kotlinx.coroutines.delay +import com.deniscerri.ytdl.util.FileUtil +import com.deniscerri.ytdl.util.extractors.GoogleApiUtil +import com.deniscerri.ytdl.util.extractors.newpipe.NewPipeUtil +import com.deniscerri.ytdl.util.extractors.YTDLPUtil +import com.deniscerri.ytdl.util.extractors.YoutubeApiUtil import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import java.util.regex.Pattern +import kotlinx.coroutines.runBlocking class ResultRepository(private val resultDao: ResultDao, private val context: Context) { val YTDLNIS_SEARCH = "YTDLNIS_SEARCH" val allResults : Flow> = resultDao.getResults() var itemCount = MutableStateFlow(-1) + fun getFiltered(playlistName : String = "") : List { + return resultDao.getResultsWithPlaylistName(playlistName) + } + + private val youtubeApiUtil = YoutubeApiUtil(context) + //private val pipedApiUtil = PipedApiUtil(context) + private val newPipeUtil = NewPipeUtil(context) + private val ytdlpUtil = YTDLPUtil(context) + + private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + enum class SourceType { - YOUTUBE_VIDEO, YOUTUBE_PLAYLIST, SEARCH_QUERY, YT_DLP + YOUTUBE_VIDEO, + YOUTUBE_PLAYLIST, + YOUTUBE_CHANNEL, + SEARCH_QUERY, + YT_DLP } suspend fun insert(it: ResultItem){ @@ -31,17 +57,71 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co suspend fun updateTrending(){ deleteAll() - val infoUtil = InfoUtil(context) - val items = infoUtil.getTrending() + val items = if (sharedPreferences.getString("api_key", "")!!.isNotBlank()) { + youtubeApiUtil.getTrending() + }else{ + newPipeUtil.getTrending() + } + itemCount.value = items.size resultDao.insertMultiple(items) } + fun getSearchSuggestions(searchQuery: String) : ArrayList { + return GoogleApiUtil.getSearchSuggestions(searchQuery) + } + + fun getStreamingUrlAndChapters(url: String) : Pair, List?> { + val newPipeTrial = newPipeUtil.getStreamingUrlAndChapters(url) + if (newPipeTrial.isFailure){ + val res = ytdlpUtil.getStreamingUrlAndChapters(url) + return res.getOrDefault(Pair(listOf(""), null)) + } + + return newPipeTrial.getOrNull()!! + } + suspend fun search(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : ArrayList{ - val infoUtil = InfoUtil(context) if (resetResults) deleteAll() - val res = infoUtil.search(inputQuery) - itemCount.value = res.size + val res = when(sharedPreferences.getString("search_engine", "ytsearch")) { + "ytsearch" -> newPipeUtil.search(inputQuery) + "ytsearchmusic" -> newPipeUtil.searchMusic(inputQuery) + else -> Result.failure(Throwable()) + } + + val items = if (res.isSuccess) { + res.getOrNull()!! + }else{ + //fallback if newpipe failed + ytdlpUtil.getFromYTDL(inputQuery) + } + + itemCount.value = items.size + if (addToResults){ + val ids = resultDao.insertMultiple(items) + ids.forEachIndexed { index, id -> + items[index].id = id + } + } + return items + } + + private suspend fun getYoutubeVideo(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : List{ + val theURL = inputQuery.replace("\\?list.*".toRegex(), "") + val newPipeRes = newPipeUtil.getVideoData(theURL) + + val res = if (newPipeRes.isSuccess) { + newPipeRes.getOrNull()!! + }else{ + ytdlpUtil.getFromYTDL(inputQuery) + } + + if (resetResults) { + deleteAll() + itemCount.value = res.size + }else{ + res.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH } + } if (addToResults){ val ids = resultDao.insertMultiple(res) ids.forEachIndexed { index, id -> @@ -51,53 +131,70 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co return res } - suspend fun getYoutubeVideo(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : ArrayList{ - val infoUtil = InfoUtil(context) - val v = infoUtil.getYoutubeVideo(inputQuery) ?: return arrayListOf() - if (resetResults) { - deleteAll() - itemCount.value = v.size - }else{ - v.filter { it.playlistTitle.isBlank() }.forEach { it.playlistTitle = YTDLNIS_SEARCH } - } - if (addToResults){ - val ids = resultDao.insertMultiple(v) - ids.forEachIndexed { index, id -> - v[index].id = id - } - } - return ArrayList(v) - } - - suspend fun getPlaylist(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : ArrayList{ - val query = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() } - .toTypedArray()[1].split("&").first() - var nextPageToken = "" + private suspend fun getYoutubePlaylist(inputQuery: String, resetResults: Boolean, addToResults: Boolean) : List{ + val id = inputQuery.split("list=".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1].split("&").first() + val playlistURL = "https://youtube.com/playlist?list=${id}" if (resetResults) deleteAll() - val infoUtil = InfoUtil(context) - val items = arrayListOf() - do { - val tmp = infoUtil.getPlaylist(query, nextPageToken, if (items.isNotEmpty()) items[0].playlistTitle else "") + val items = mutableListOf() + val newPipeResult = newPipeUtil.getPlaylistData(playlistURL) { if (addToResults){ - val ids = resultDao.insertMultiple(tmp.videos.toList()) - ids.forEachIndexed { index, id -> - tmp.videos[index].id = id + runBlocking { + val ids = resultDao.insertMultiple(it) + ids.forEachIndexed { index, id -> + it[index].id = id + } } } - items.addAll(tmp.videos) - val tmpToken = tmp.nextPageToken - if (tmpToken.isEmpty()) break - if (tmpToken == nextPageToken) break - nextPageToken = tmpToken - delay(1000) - } while (true) - itemCount.value = items.size - return items + items.addAll(it) + } + + val response = if (newPipeResult.isSuccess){ + newPipeResult.getOrElse { items } + }else{ + val res = ytdlpUtil.getFromYTDL(playlistURL) + val ids = resultDao.insertMultiple(res) + ids.forEachIndexed { index, id -> + res[index].id = id + } + res + } + + itemCount.value = response.size + return response } - suspend fun getDefault(inputQuery: String, resetResults: Boolean, addToResults: Boolean, singleItem: Boolean = false) : ArrayList { - val infoUtil = InfoUtil(context) - val items = infoUtil.getFromYTDL(inputQuery, singleItem) + private suspend fun getYoutubeChannel(url: String, resetResults: Boolean, addToResults: Boolean) : List{ + if (resetResults) deleteAll() + val items = mutableListOf() + val newPipeResult = newPipeUtil.getChannelData(url) { + if (addToResults){ + runBlocking { + val ids = resultDao.insertMultiple(it) + ids.forEachIndexed { index, id -> + it[index].id = id + } + } + } + items.addAll(it) + } + + val response = if (newPipeResult.isSuccess){ + newPipeResult.getOrElse { items } + }else{ + val res = ytdlpUtil.getFromYTDL(url) + val ids = resultDao.insertMultiple(res) + ids.forEachIndexed { index, id -> + res[index].id = id + } + res + } + + itemCount.value = response.size + return response + } + + private suspend fun getFromYTDLP(inputQuery: String, resetResults: Boolean, addToResults: Boolean, singleItem: Boolean = false) : ArrayList { + val items = ytdlpUtil.getFromYTDL(inputQuery, singleItem) if (resetResults) { deleteAll() itemCount.value = items.size @@ -115,6 +212,43 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co return items } + fun getFormats(url: String, source : String? = null) : List { + val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp") + val res = if (url.isYoutubeURL() && formatSource == "piped") { + val tmpRes = newPipeUtil.getFormats(url) + if (tmpRes.isFailure && source != null) { + Result.success(listOf()) + }else{ + tmpRes + } + }else{ + Result.failure(Throwable()) + } + + return if (res.isSuccess){ + res.getOrNull()!! + }else{ + ytdlpUtil.getFormats(url) + } + } + + suspend fun getFormatsMultiple(urls: List, source: String? = null, progress: (progress: ResultViewModel.MultipleFormatProgress) -> Unit) : MutableList> { + val formatSource = source ?: sharedPreferences.getString("formats_source", "yt-dlp") + val allYoutubeLinks = urls.all { it.isYoutubeURL() } + if (allYoutubeLinks && formatSource == "piped") { + val res = newPipeUtil.getFormatsForAll(urls) { + progress(it) + } + return res.getOrElse { mutableListOf() } + } + else { + val res = ytdlpUtil.getFormatsForAll(urls) { + progress(it) + } + return res.getOrElse { mutableListOf() } + } + } + suspend fun delete(item: ResultItem){ resultDao.delete(item.id) } @@ -148,23 +282,30 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co return resultDao.getAllByIDs(ids) } - suspend fun getResultsFromSource(inputQuery: String, resetResults: Boolean, addToResults: Boolean = true, singleItem: Boolean = false) : ArrayList { + suspend fun getResultsFromSource(inputQuery: String, resetResults: Boolean, addToResults: Boolean = true, singleItem: Boolean = false) : List { return when(getQueryType(inputQuery)){ SourceType.YOUTUBE_VIDEO -> { getYoutubeVideo(inputQuery, resetResults, addToResults) } SourceType.YOUTUBE_PLAYLIST -> { if (singleItem){ - getDefault(inputQuery, resetResults, addToResults, true) + getFromYTDLP(inputQuery, resetResults, addToResults, true) }else{ - getPlaylist(inputQuery, resetResults, addToResults) + getYoutubePlaylist(inputQuery, resetResults, addToResults) + } + } + SourceType.YOUTUBE_CHANNEL -> { + if (singleItem) { + getFromYTDLP(inputQuery, resetResults, addToResults, true) + }else{ + getYoutubeChannel(inputQuery, resetResults, addToResults) } } SourceType.SEARCH_QUERY -> { search(inputQuery, resetResults, addToResults) } SourceType.YT_DLP -> { - getDefault(inputQuery, resetResults, addToResults, singleItem) + getFromYTDLP(inputQuery, resetResults, addToResults, singleItem) } } @@ -176,6 +317,8 @@ class ResultRepository(private val resultDao: ResultDao, private val context: Co type = SourceType.YOUTUBE_VIDEO if (inputQuery.contains("playlist?list=")) { type = SourceType.YOUTUBE_PLAYLIST + }else if (inputQuery.isYoutubeChannelURL()) { + type = SourceType.YOUTUBE_CHANNEL } } else if (Patterns.WEB_URL.matcher(inputQuery).matches()) { type = SourceType.YT_DLP diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt index 56a846ff..713987eb 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/DownloadViewModel.kt @@ -5,11 +5,8 @@ import android.content.SharedPreferences import android.content.res.Configuration import android.content.res.Resources import android.os.Build -import android.os.Handler -import android.os.Looper import android.os.Parcelable import android.util.DisplayMetrics -import android.widget.Toast import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData @@ -40,8 +37,8 @@ import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.ui.downloadcard.MultipleItemFormatTuple import com.deniscerri.ytdl.util.Extensions.toListString import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.FormatSorter -import com.deniscerri.ytdl.util.InfoUtil +import com.deniscerri.ytdl.util.FormatUtil +import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.deniscerri.ytdl.work.AlarmScheduler import com.deniscerri.ytdl.work.UpdateMultipleDownloadsFormatsWorker import com.google.gson.Gson @@ -64,7 +61,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val repository : DownloadRepository private val sharedPreferences: SharedPreferences private val commandTemplateDao: CommandTemplateDao - private val infoUtil : InfoUtil + private val formatUtil = FormatUtil(application) + private val ytdlpUtil = YTDLPUtil(application) private val resources : Resources val allDownloads : Flow> @@ -122,7 +120,6 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel resultRepository = ResultRepository(dbManager.resultDao, application) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) commandTemplateDao = DBManager.getInstance(application).commandTemplateDao - infoUtil = InfoUtil(application) activeDownloadsCount = repository.activeDownloadsCount queuedDownloadsCount = repository.queuedDownloadsCount @@ -478,9 +475,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel return cloneFormat ( try { val theFormats = formats.filter { it.vcodec.isBlank() || it.vcodec == "none" } - FormatSorter(application).sortAudioFormats(theFormats).first() + FormatUtil(application).sortAudioFormats(theFormats).first() }catch (e: Exception){ - infoUtil.getGenericAudioFormats(resources).first() + formatUtil.getGenericAudioFormats(resources).first() } ) @@ -489,12 +486,12 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel return cloneFormat( try { val theFormats = formats.filter { it.vcodec.isNotBlank() && it.vcodec != "none" }.ifEmpty { - infoUtil.getGenericVideoFormats(resources).sortedByDescending { it.filesize } + formatUtil.getGenericVideoFormats(resources).sortedByDescending { it.filesize } } - FormatSorter(application).sortVideoFormats(theFormats).first() + FormatUtil(application).sortVideoFormats(theFormats).first() }catch (e: Exception){ - infoUtil.getGenericVideoFormats(resources).first() + formatUtil.getGenericVideoFormats(resources).first() } ) } @@ -529,7 +526,7 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } if (preferredAudioFormats.isEmpty()){ val audioF = getFormat(formats, Type.audio) - if (!infoUtil.getGenericAudioFormats(resources).contains(audioF)){ + if (!formatUtil.getGenericAudioFormats(resources).contains(audioF)){ preferredAudioFormats.add(audioF.format_id) } } @@ -873,8 +870,8 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } } "config" -> { - val currentCommand = infoUtil.buildYoutubeDLRequest(it) - val parsedCurrentCommand = infoUtil.parseYTDLRequestString(currentCommand) + val currentCommand = ytdlpUtil.buildYoutubeDLRequest(it) + val parsedCurrentCommand = ytdlpUtil.parseYTDLRequestString(currentCommand) val existingDownload = activeAndQueuedDownloads.firstOrNull{d -> d.id = 0 d.logID = null diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt index c0b074a1..a8a7b0da 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/ResultViewModel.kt @@ -7,6 +7,8 @@ import android.content.SharedPreferences import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import androidx.preference.PreferenceManager @@ -14,18 +16,17 @@ import com.deniscerri.ytdl.App import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.DBManager import com.deniscerri.ytdl.database.dao.ResultDao +import com.deniscerri.ytdl.database.models.ChapterItem import com.deniscerri.ytdl.database.models.Format +import com.deniscerri.ytdl.database.models.HistoryItem import com.deniscerri.ytdl.database.models.ResultItem import com.deniscerri.ytdl.database.models.SearchHistoryItem import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.database.repository.SearchHistoryRepository -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.NotificationUtil import com.yausername.youtubedl_android.YoutubeDLException -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.joinAll @@ -40,7 +41,9 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a val repository : ResultRepository private val searchHistoryRepository : SearchHistoryRepository val items : LiveData> - private val infoUtil: InfoUtil + private var _items = MediatorLiveData>() + val playlistFilter = MutableLiveData("") + private val notificationUtil: NotificationUtil private val dao: ResultDao data class ResultsUiState( @@ -72,12 +75,31 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a dao = DBManager.getInstance(application).resultDao repository = ResultRepository(dao, getApplication().applicationContext) searchHistoryRepository = SearchHistoryRepository(DBManager.getInstance(application).searchHistoryDao) + items = repository.allResults.asLiveData() + _items.addSource(items){ + filter() + } + _items.addSource(playlistFilter) { + filter() + } + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(application) - infoUtil = InfoUtil(application) notificationUtil = NotificationUtil(application) } + fun filter() = viewModelScope.launch(Dispatchers.IO){ + _items.postValue(repository.getFiltered(playlistFilter.value ?: "")) + } + + fun setPlaylistFilter(p: String){ + playlistFilter.value = p + } + + fun getFilteredList() : LiveData> { + return _items + } + fun checkTrending() = viewModelScope.launch(Dispatchers.IO){ try { val item = repository.getFirstResult() @@ -271,7 +293,7 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a updateFormatsResultDataJob = viewModelScope.launch(Dispatchers.IO) { updatingFormats.emit(true) try { - val formats = infoUtil.getFormats(result.url) + val formats = getFormats(result.url) updatingFormats.emit(false) formats.apply { if (formats.isNotEmpty() && updateFormatsResultDataJob?.isCancelled == false) { @@ -309,7 +331,34 @@ class ResultViewModel(private val application: Application) : AndroidViewModel(a } } + data class MultipleFormatProgress( + val url: String, + val formats: List, + val unavailable : Boolean = false, + val unavailableMessage: String = "" + ) + + + fun getFormats(url: String, source: String? = null) : List { + return repository.getFormats(url, source) + } + + suspend fun getFormatsMultiple(urls: List, source: String? = null, progress: (progress: MultipleFormatProgress) -> Unit) : MutableList> { + val res = repository.getFormatsMultiple(urls, source) { + progress(it) + } + return res + } + fun getResultsBetweenTwoItems(item1: Long, item2: Long) : List{ return dao.getResultsBetweenTwoItems(item1, item2) } + + fun getSearchSuggestions(searchQuery: String) : ArrayList { + return repository.getSearchSuggestions(searchQuery) + } + + fun getStreamingUrlAndChapters(url: String) : Pair, List?> { + return repository.getStreamingUrlAndChapters(url) + } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt index e296ceaa..3273cd66 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/HomeFragment.kt @@ -9,7 +9,6 @@ import android.graphics.Color import android.os.Bundle import android.os.Handler import android.os.Looper -import android.text.Editable import android.util.Log import android.util.Patterns import android.view.* @@ -19,7 +18,6 @@ import androidx.activity.addCallback import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode import androidx.constraintlayout.widget.ConstraintLayout -import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.os.bundleOf import androidx.core.view.children import androidx.core.view.forEach @@ -38,13 +36,15 @@ import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.ResultItem import com.deniscerri.ytdl.database.models.SearchSuggestionItem import com.deniscerri.ytdl.database.models.SearchSuggestionType +import com.deniscerri.ytdl.database.repository.ResultRepository import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.ui.adapter.HomeAdapter import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter +import com.deniscerri.ytdl.ui.downloads.HistoryFragment +import com.deniscerri.ytdl.ui.downloads.HistoryFragment.Companion import com.deniscerri.ytdl.util.Extensions.enableFastScroll -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.ThemeUtil import com.deniscerri.ytdl.util.UiUtil @@ -52,19 +52,15 @@ import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.button.MaterialButton -import com.google.android.material.card.MaterialCardView import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton -import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.search.SearchBar import com.google.android.material.search.SearchView -import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update @@ -84,10 +80,12 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti private var downloadAllFab: ExtendedFloatingActionButton? = null private var clipboardFab: ExtendedFloatingActionButton? = null private var homeFabs: LinearLayout? = null - private var infoUtil: InfoUtil? = null private var notificationUtil: NotificationUtil? = null private var downloadQueue: ArrayList? = null + private lateinit var playlistNameFilterScrollView: HorizontalScrollView + private lateinit var playlistNameFilterChipGroup: ChipGroup + private lateinit var resultViewModel : ResultViewModel private lateinit var downloadViewModel : DownloadViewModel private lateinit var historyViewModel : HistoryViewModel @@ -123,7 +121,6 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti activity = getActivity() mainActivity = activity as MainActivity? quickLaunchSheet = false - infoUtil = InfoUtil(requireContext()) notificationUtil = NotificationUtil(requireContext()) selectedObjects = arrayListOf() return fragmentView @@ -157,6 +154,8 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti downloadSelectedFab = homeFabs!!.findViewById(R.id.download_selected_fab) downloadAllFab = homeFabs!!.findViewById(R.id.download_all_fab) clipboardFab = homeFabs!!.findViewById(R.id.copied_url_fab) + playlistNameFilterScrollView = view.findViewById(R.id.playlist_selection_chips_scrollview) + playlistNameFilterChipGroup = view.findViewById(R.id.playlist_selection_chips) runCatching { materialToolbar!!.title = ThemeUtil.getStyledAppName(requireContext()) } @@ -186,7 +185,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti val progressBar = view.findViewById(R.id.progress) resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] - resultViewModel.items.observe(requireActivity()) { + resultViewModel.getFilteredList().observe(requireActivity()) { kotlin.runCatching { homeAdapter!!.submitList(it) resultsList = it @@ -197,6 +196,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti }else{ downloadAllFab!!.visibility = GONE } + }else if (resultViewModel.repository.itemCount.value == 1){ if (sharedPreferences!!.getBoolean("download_card", true)){ if(it.size == 1 && quickLaunchSheet && parentFragmentManager.findFragmentByTag("downloadSingleSheet") == null){ @@ -213,6 +213,14 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti } } + resultViewModel.items.observe(requireActivity()) { + updateMultiplePlaylistResults(it + .filter { it2 -> it2.playlistTitle != "" && it2.playlistTitle != "YTDLNIS_SEARCH" } + .map { it.playlistTitle } + .distinct() + ) + } + initMenu() downloadSelectedFab?.tag = "downloadSelected" downloadSelectedFab?.setOnClickListener(this) @@ -523,13 +531,13 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti val combinedList = mutableListOf() val history = withContext(Dispatchers.IO){ - resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery) } + resultViewModel.getSearchHistory().map { it.query }.filter { it.contains(searchQuery, ignoreCase = true) } }.map { SearchSuggestionItem(it, SearchSuggestionType.HISTORY) } val suggestions = if (sharedPreferences!!.getBoolean("search_suggestions", false)){ withContext(Dispatchers.IO){ - infoUtil!!.getSearchSuggestions(searchQuery) + resultViewModel.getSearchSuggestions(searchQuery) } }else{ emptyList() @@ -780,6 +788,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti mode!!.menuInflater.inflate(R.menu.main_menu_context, menu) mode.title = "${selectedObjects.size} ${getString(R.string.selected)}" searchBar!!.isEnabled = false + playlistNameFilterChipGroup.children.forEach { it.isEnabled = false } searchBar!!.menu.forEach { it.isEnabled = false } (activity as MainActivity).disableBottomNavigation() return true @@ -877,6 +886,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti (activity as MainActivity).enableBottomNavigation() clearCheckedItems() searchBar!!.isEnabled = true + playlistNameFilterChipGroup.children.forEach { it.isEnabled = true } searchBar!!.menu.forEach { it.isEnabled = true } searchBar?.expand(appBarLayout!!) } @@ -970,4 +980,34 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti searchView!!.editText.setText(text) searchView!!.editText.setSelection(searchView!!.editText.length()) } + + private fun updateMultiplePlaylistResults(playlistTitles: List) { + if (playlistTitles.isEmpty() || playlistTitles.size == 1) { + playlistNameFilterScrollView.isVisible = false + playlistNameFilterChipGroup.children.filter { it.tag != "all" }.forEach { + playlistNameFilterChipGroup.removeView(it) + } + return + } + + playlistNameFilterChipGroup.children.first().setOnClickListener { + resultViewModel.setPlaylistFilter("") + } + + for (t in playlistTitles) { + val exists = playlistNameFilterChipGroup.children.any { it.tag == t } + if (exists) continue + + val tmp = layoutinflater!!.inflate(R.layout.filter_chip, playlistNameFilterChipGroup, false) as Chip + tmp.text = t + tmp.tag = t + tmp.setOnClickListener { + resultViewModel.setPlaylistFilter(t) + } + + playlistNameFilterChipGroup.addView(tmp) + } + + playlistNameFilterScrollView.isVisible = true + } } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt index 83eab791..f71c56e8 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/adapter/ConfigureMultipleDownloadsAdapter.kt @@ -11,7 +11,6 @@ import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.core.view.isVisible -import androidx.paging.PagingDataAdapter import androidx.preference.PreferenceManager import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil @@ -19,12 +18,10 @@ import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.DownloadItemConfigureMultiple -import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.util.Extensions.loadThumbnail import com.deniscerri.ytdl.util.Extensions.popup import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.InfoUtil import com.google.android.material.button.MaterialButton import java.util.Locale diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/AddExtraCommandsDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/AddExtraCommandsDialog.kt index 6877d5e1..aeb13c85 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/AddExtraCommandsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/AddExtraCommandsDialog.kt @@ -15,14 +15,13 @@ import android.view.inputmethod.InputMethodManager import android.widget.* import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceManager import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.DownloadItem import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel import com.deniscerri.ytdl.util.Extensions.enableTextHighlight -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.UiUtil +import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.elevation.SurfaceColors @@ -32,13 +31,13 @@ import kotlinx.coroutines.withContext class AddExtraCommandsDialog(private val item: DownloadItem? = null, private val callback: ExtraCommandsListener? = null) : BottomSheetDialogFragment() { - private lateinit var infoUtil: InfoUtil + private lateinit var ytdlpUtil: YTDLPUtil private lateinit var sharedPreferences: SharedPreferences private lateinit var commandTemplateViewModel: CommandTemplateViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - infoUtil = InfoUtil(requireActivity().applicationContext) + ytdlpUtil = YTDLPUtil(requireActivity().applicationContext) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) commandTemplateViewModel = ViewModelProvider(this)[CommandTemplateViewModel::class.java] } @@ -87,7 +86,7 @@ class AddExtraCommandsDialog(private val item: DownloadItem? = null, private val val currentText = view.findViewById(R.id.currentText) if (item != null){ - val currentCommand = infoUtil.parseYTDLRequestString(infoUtil.buildYoutubeDLRequest(item)) + val currentCommand = ytdlpUtil.parseYTDLRequestString(ytdlpUtil.buildYoutubeDLRequest(item)) currentText?.text = currentCommand }else{ view.findViewById(R.id.current).visibility = View.GONE diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/CutVideoBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/CutVideoBottomSheetDialog.kt index aa25770b..a2f365d2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/CutVideoBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/CutVideoBottomSheetDialog.kt @@ -13,12 +13,11 @@ import android.view.KeyEvent import android.view.LayoutInflater import android.view.MotionEvent import android.view.View -import android.view.Window -import android.view.WindowManager import android.widget.* import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.children import androidx.core.view.isVisible +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.media3.common.MediaItem.fromUri import androidx.media3.common.Player @@ -31,17 +30,15 @@ import androidx.preference.PreferenceManager import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.ChapterItem import com.deniscerri.ytdl.database.models.DownloadItem -import com.deniscerri.ytdl.util.Extensions +import com.deniscerri.ytdl.database.viewmodel.CommandTemplateViewModel +import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.util.Extensions.convertToTimestamp import com.deniscerri.ytdl.util.Extensions.setTextAndRecalculateWidth import com.deniscerri.ytdl.util.Extensions.toStringDuration import com.deniscerri.ytdl.util.Extensions.toStringTimeStamp -import com.deniscerri.ytdl.util.Extensions.toTimePeriodsArray -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.VideoPlayerUtil import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView @@ -52,26 +49,19 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.elevation.SurfaceColors import com.google.android.material.materialswitch.MaterialSwitch import com.google.android.material.slider.RangeSlider -import com.google.android.material.textfield.TextInputLayout -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.lang.reflect.Type import java.util.* -import kotlin.math.min -import kotlin.properties.Delegates class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private val urls : String? = null, private var chapters: List? = null, private val listener: VideoCutListener? = null) : BottomSheetDialogFragment() { private lateinit var behavior: BottomSheetBehavior - private lateinit var infoUtil: InfoUtil private lateinit var player: ExoPlayer - + private lateinit var resultViewModel: ResultViewModel private lateinit var cutSection : ConstraintLayout private lateinit var durationText: TextView private lateinit var progress : ProgressBar @@ -99,8 +89,8 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private private lateinit var selectedCuts: MutableList override fun onCreate(savedInstanceState: Bundle?) { + resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] super.onCreate(savedInstanceState) - infoUtil = InfoUtil(requireActivity().applicationContext) } @@ -182,7 +172,7 @@ class CutVideoBottomSheetDialog(private val _item: DownloadItem? = null, private try { val data = withContext(Dispatchers.IO) { if (urls.isNullOrEmpty()) { - infoUtil.getStreamingUrlAndChapters(item.url) + resultViewModel.getStreamingUrlAndChapters(item.url) }else{ Pair(urls.split("\n"), chapters) } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt index 08d7a142..151803e2 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadAudioFragment.kt @@ -6,7 +6,6 @@ import android.content.Intent import android.content.SharedPreferences import android.os.Bundle import android.text.Editable -import android.text.TextUtils import android.text.TextWatcher import android.view.LayoutInflater import android.view.View @@ -16,10 +15,8 @@ import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.LinearLayout import android.widget.TextView -import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources -import androidx.core.view.ViewCompat import androidx.core.view.isVisible import androidx.core.view.setPadding import androidx.fragment.app.Fragment @@ -35,7 +32,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.InfoUtil +import com.deniscerri.ytdl.util.FormatUtil import com.deniscerri.ytdl.util.UiUtil import com.google.android.material.card.MaterialCardView import com.google.android.material.snackbar.Snackbar @@ -48,7 +45,6 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.Locale class DownloadAudioFragment(private var resultItem: ResultItem? = null, private var currentDownloadItem: DownloadItem? = null, private var url: String = "", private var nonSpecific: Boolean = false) : Fragment(), GUISync { @@ -58,7 +54,6 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private private lateinit var resultViewModel : ResultViewModel private lateinit var saveDir : TextInputLayout private lateinit var freeSpace : TextView - private lateinit var infoUtil: InfoUtil private lateinit var genericAudioFormats: MutableList lateinit var downloadItem : DownloadItem @@ -76,8 +71,7 @@ class DownloadAudioFragment(private var resultItem: ResultItem? = null, private activity = getActivity() downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] - infoUtil = InfoUtil(requireContext()) - genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) + genericAudioFormats = FormatUtil(requireContext()).getGenericAudioFormats(requireContext().resources) preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) shownFields = preferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList() return fragmentView diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt index 65dd89f9..ec38bb37 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadBottomSheetDialog.kt @@ -7,7 +7,6 @@ import android.content.SharedPreferences import android.content.res.Configuration import android.os.Build import android.os.Bundle -import android.text.format.DateFormat import android.util.DisplayMetrics import android.util.Patterns import android.view.LayoutInflater @@ -40,7 +39,6 @@ import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.receiver.ShareActivity import com.deniscerri.ytdl.ui.BaseActivity -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.bottomsheet.BottomSheetBehavior @@ -60,8 +58,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext -import java.text.SimpleDateFormat -import java.util.Locale class DownloadBottomSheetDialog : BottomSheetDialogFragment() { @@ -73,7 +69,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { private lateinit var resultViewModel: ResultViewModel private lateinit var behavior: BottomSheetBehavior private lateinit var commandTemplateViewModel : CommandTemplateViewModel - private lateinit var infoUtil: InfoUtil private lateinit var sharedPreferences : SharedPreferences private lateinit var updateItem : Button private lateinit var view: View @@ -96,7 +91,6 @@ class DownloadBottomSheetDialog : BottomSheetDialogFragment() { historyViewModel = ViewModelProvider(requireActivity())[HistoryViewModel::class.java] resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java] commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java] - infoUtil = InfoUtil(requireContext()) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) val res: ResultItem? val dwl: DownloadItem? diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt index a0a9c1ef..f96ce041 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadMultipleBottomSheetDialog.kt @@ -44,7 +44,6 @@ import com.deniscerri.ytdl.ui.BaseActivity import com.deniscerri.ytdl.ui.adapter.ConfigureMultipleDownloadsAdapter import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.bottomappbar.BottomAppBar @@ -73,7 +72,6 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure 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 private lateinit var bottomAppBar: BottomAppBar private lateinit var filesize : TextView @@ -100,7 +98,6 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java] commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java] sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) - infoUtil = InfoUtil(requireContext()) currentDownloadIDs = arguments?.getLongArray("currentDownloadIDs")?.toList() ?: listOf() processingItemsCount = currentDownloadIDs.size diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt index 7bd9573d..6bfe829d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/DownloadVideoFragment.kt @@ -15,7 +15,6 @@ import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import android.widget.LinearLayout import android.widget.TextView -import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.isVisible @@ -33,7 +32,7 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.InfoUtil +import com.deniscerri.ytdl.util.FormatUtil import com.deniscerri.ytdl.util.UiUtil import com.google.android.material.card.MaterialCardView import com.google.android.material.snackbar.Snackbar @@ -60,7 +59,6 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private lateinit var author : TextInputLayout private lateinit var saveDir : TextInputLayout private lateinit var freeSpace : TextView - private lateinit var infoUtil: InfoUtil private lateinit var genericVideoFormats: MutableList private lateinit var genericAudioFormats: MutableList @@ -78,9 +76,9 @@ class DownloadVideoFragment(private var resultItem: ResultItem? = null, private activity = getActivity() downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] - infoUtil = InfoUtil(requireContext()) - genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources) - genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) + val formatUtil = FormatUtil(requireContext()) + genericVideoFormats = formatUtil.getGenericVideoFormats(requireContext().resources) + genericAudioFormats = formatUtil.getGenericAudioFormats(requireContext().resources) preferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) shownFields = preferences.getStringSet("modify_download_card", requireContext().getStringArray(R.array.modify_download_card_values).toSet())!!.toList() return fragmentView diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/FormatSelectionBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/FormatSelectionBottomSheetDialog.kt index 53a60945..e72361ef 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/FormatSelectionBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/FormatSelectionBottomSheetDialog.kt @@ -7,10 +7,8 @@ import android.os.Bundle import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.view.Window import android.widget.* -import androidx.compose.foundation.layout.PaddingValues import androidx.core.view.children import androidx.core.view.forEach import androidx.core.view.isVisible @@ -24,25 +22,20 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.util.Extensions.isYoutubeURL -import com.deniscerri.ytdl.util.FormatSorter -import com.deniscerri.ytdl.util.InfoUtil +import com.deniscerri.ytdl.util.FormatUtil import com.deniscerri.ytdl.util.UiUtil import com.facebook.shimmer.ShimmerFrameLayout import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.card.MaterialCardView -import com.google.android.material.chip.Chip import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.internal.format -import java.util.regex.Pattern class FormatSelectionBottomSheetDialog( @@ -52,10 +45,11 @@ class FormatSelectionBottomSheetDialog( ) : BottomSheetDialogFragment() { private lateinit var behavior: BottomSheetBehavior - private lateinit var infoUtil: InfoUtil + private lateinit var formatUtil: FormatUtil private lateinit var view: View private lateinit var continueInBackgroundSnackBar : Snackbar private lateinit var downloadViewModel: DownloadViewModel + private lateinit var resultViewModel: ResultViewModel private lateinit var sharedPreferences: SharedPreferences private lateinit var videoFormatList : LinearLayout private lateinit var audioFormatList : LinearLayout @@ -95,11 +89,12 @@ class FormatSelectionBottomSheetDialog( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - infoUtil = InfoUtil(requireActivity().applicationContext) + formatUtil = FormatUtil(requireContext()) chosenFormats = listOf() selectedAudios = mutableListOf() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] + resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] } @@ -133,8 +128,8 @@ class FormatSelectionBottomSheetDialog( multipleFormatsListener = this } - genericAudioFormats = infoUtil.getGenericAudioFormats(requireContext().resources) - genericVideoFormats = infoUtil.getGenericVideoFormats(requireContext().resources) + genericAudioFormats = formatUtil.getGenericAudioFormats(requireContext().resources) + genericVideoFormats = formatUtil.getGenericVideoFormats(requireContext().resources) sortBy = FormatSorting.valueOf(sharedPreferences.getString("format_order", "filesize")!!) filterBy = FormatCategory.valueOf(sharedPreferences.getString("format_filter", "ALL")!!) @@ -221,7 +216,7 @@ class FormatSelectionBottomSheetDialog( //simple download if (items.size == 1) { kotlin.runCatching { - val res = infoUtil.getFormats(items.first()!!.url, currentFormatSource) + val res = resultViewModel.getFormats(items.first()!!.url, currentFormatSource) if (!isActive) return@launch res.filter { it.format_note != "storyboard" } chosenFormats = if (items.first()?.type == Type.audio) { @@ -255,7 +250,7 @@ class FormatSelectionBottomSheetDialog( refreshBtn.text = progress } - val res = infoUtil.getFormatsMultiple(itemsWithMissingFormats.map { it!!.url }, currentFormatSource) { + val res = resultViewModel.getFormatsMultiple(itemsWithMissingFormats.map { it!!.url }, currentFormatSource) { if (!isActive) return@getFormatsMultiple if (it.unavailable) { @@ -493,7 +488,7 @@ class FormatSelectionBottomSheetDialog( FormatSorting.filesize -> chosenFormats } - val formatSorter = FormatSorter(requireContext()) + val formatSorter = FormatUtil(requireContext()) //filter category when(filterBy){ diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ObserveSourcesBottomSheetDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ObserveSourcesBottomSheetDialog.kt index 64f98cc6..a398736b 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ObserveSourcesBottomSheetDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ObserveSourcesBottomSheetDialog.kt @@ -40,8 +40,6 @@ import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel.Type import com.deniscerri.ytdl.database.viewmodel.HistoryViewModel import com.deniscerri.ytdl.database.viewmodel.ObserveSourcesViewModel import com.deniscerri.ytdl.database.viewmodel.ResultViewModel -import com.deniscerri.ytdl.util.Extensions.TextWithSubtitle -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.UiUtil import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialogFragment @@ -56,9 +54,7 @@ import com.google.android.material.textfield.TextInputLayout import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.hamcrest.core.Every import java.text.SimpleDateFormat -import java.time.Month import java.util.Calendar import java.util.Locale @@ -73,7 +69,6 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() { private lateinit var resultViewModel: ResultViewModel private lateinit var behavior: BottomSheetBehavior private lateinit var commandTemplateViewModel : CommandTemplateViewModel - private lateinit var infoUtil: InfoUtil private lateinit var sharedPreferences : SharedPreferences private lateinit var view: View @@ -110,7 +105,6 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() { historyViewModel = ViewModelProvider(requireActivity())[HistoryViewModel::class.java] resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java] commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java] - infoUtil = InfoUtil(requireContext()) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) currentItem = if (Build.VERSION.SDK_INT >= 33){ diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt index 904508a5..8cbb1f98 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloadcard/ResultCardDetailsDialog.kt @@ -13,7 +13,6 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Environment -import android.text.format.DateFormat import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View @@ -21,8 +20,6 @@ import android.view.ViewGroup import android.view.Window import android.widget.Button import android.widget.TextView -import android.widget.Toast -import androidx.core.content.ContextCompat import androidx.core.net.toUri import androidx.core.os.bundleOf import androidx.lifecycle.ViewModelProvider @@ -48,8 +45,6 @@ import com.deniscerri.ytdl.database.viewmodel.ResultViewModel import com.deniscerri.ytdl.ui.adapter.ActiveDownloadMinifiedAdapter import com.deniscerri.ytdl.ui.adapter.GenericDownloadAdapter import com.deniscerri.ytdl.util.Extensions.setFullScreen -import com.deniscerri.ytdl.util.FileUtil -import com.deniscerri.ytdl.util.InfoUtil import com.deniscerri.ytdl.util.NotificationUtil import com.deniscerri.ytdl.util.UiUtil import com.deniscerri.ytdl.util.VideoPlayerUtil @@ -58,7 +53,6 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.button.MaterialButton -import com.google.android.material.chip.Chip import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.elevation.SurfaceColors @@ -76,13 +70,9 @@ import kotlinx.coroutines.withContext import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Locale class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdapter.OnItemClickListener, ActiveDownloadMinifiedAdapter.OnItemClickListener { - private lateinit var infoUtil: InfoUtil private lateinit var notificationUtil: NotificationUtil private lateinit var videoView: PlayerView private lateinit var downloadViewModel: DownloadViewModel @@ -99,7 +89,6 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - infoUtil = InfoUtil(requireActivity()) notificationUtil = NotificationUtil(requireActivity()) downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java] resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java] @@ -283,7 +272,7 @@ class ResultCardDetailsDialog : BottomSheetDialogFragment(), GenericDownloadAdap try { val data = withContext(Dispatchers.IO) { if (item.urls.isEmpty()) { - infoUtil.getStreamingUrlAndChapters(item.url) + resultViewModel.getStreamingUrlAndChapters(item.url) }else{ Pair(item.urls.split("\n"), null) } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/DownloadQueueMainFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/DownloadQueueMainFragment.kt index a832e4d4..66f3383f 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/DownloadQueueMainFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/DownloadQueueMainFragment.kt @@ -30,6 +30,7 @@ import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator +import com.google.gson.Gson import com.yausername.youtubedl_android.YoutubeDL import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest @@ -71,7 +72,9 @@ class DownloadQueueMainFragment : Fragment(){ }else{ mainActivity.hideBottomNavigation() } - topAppBar.setNavigationOnClickListener { mainActivity.onBackPressedDispatcher.onBackPressed() } + topAppBar.setNavigationOnClickListener { + mainActivity.onBackPressedDispatcher.onBackPressed() + } tabLayout = view.findViewById(R.id.download_tablayout) viewPager2 = view.findViewById(R.id.download_viewpager) @@ -148,6 +151,8 @@ class DownloadQueueMainFragment : Fragment(){ }, 200) } + arguments?.clear() + if (sharedPreferences.getBoolean("show_count_downloads", false)){ lifecycleScope.launch { downloadViewModel.activeDownloadsCount.collectLatest { diff --git a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt index 2ed10798..703999eb 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/Extensions.kt @@ -45,6 +45,7 @@ import com.deniscerri.ytdl.database.models.Format import com.deniscerri.ytdl.database.models.observeSources.ObserveSourcesItem import com.deniscerri.ytdl.database.repository.DownloadRepository import com.deniscerri.ytdl.database.repository.ObserveSourcesRepository.EveryCategory +import com.deniscerri.ytdl.util.Extensions.isYoutubeURL import com.deniscerri.ytdl.util.Extensions.toTimePeriodsArray import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -498,4 +499,8 @@ object Extensions { return Pattern.compile("((^(https?)://)?(www.)?(m.)?youtu(.be)?)|(^(https?)://(www.)?piped.video)").matcher(this).find() } + fun String.isYoutubeChannelURL() : Boolean { + return Pattern.compile("((^(https?)://)?(www.)?(m.)?youtu(.be)?(be.com))/@[a-zA-Z]+").matcher(this).find() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/util/FormatSorter.kt b/app/src/main/java/com/deniscerri/ytdl/util/FormatUtil.kt similarity index 80% rename from app/src/main/java/com/deniscerri/ytdl/util/FormatSorter.kt rename to app/src/main/java/com/deniscerri/ytdl/util/FormatUtil.kt index e84b79d7..45b08a0d 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/FormatSorter.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/FormatUtil.kt @@ -3,20 +3,13 @@ package com.deniscerri.ytdl.util import android.annotation.SuppressLint import android.content.Context import android.content.SharedPreferences -import androidx.core.content.ContextCompat -import androidx.core.content.contentValuesOf +import android.content.res.Resources import androidx.preference.PreferenceManager import com.afollestad.materialdialogs.utils.MDUtil.getStringArray import com.deniscerri.ytdl.R import com.deniscerri.ytdl.database.models.Format -import com.deniscerri.ytdl.database.viewmodel.DownloadViewModel -import com.google.gson.Gson -import com.google.gson.JsonObject -import org.json.JSONObject -import java.text.Normalizer.Form -import java.util.regex.Pattern -class FormatSorter(private var context: Context) { +class FormatUtil(private var context: Context) { private val sharedPreferences : SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) private val videoFormatIDPreference : List = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() } private val audioFormatIDPreference : List = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() } @@ -170,6 +163,53 @@ class FormatSorter(private var context: Context) { return formats.sortedWith(fieldSorter) } + fun getGenericAudioFormats(resources: Resources) : MutableList{ + val audioFormatIDPreference = sharedPreferences.getString("format_id_audio", "").toString().split(",").filter { it.isNotEmpty() } + val audioFormats = resources.getStringArray(R.array.audio_formats) + val audioFormatsValues = resources.getStringArray(R.array.audio_formats_values) + val formats = mutableListOf() + val containerPreference = sharedPreferences.getString("audio_format", "") + val acodecPreference = sharedPreferences.getString("audio_codec", "")!!.run { + if (this.isEmpty()){ + resources.getString(R.string.defaultValue) + }else{ + val audioCodecs = resources.getStringArray(R.array.audio_codec) + val audioCodecsValues = resources.getStringArray(R.array.audio_codec_values) + audioCodecs[audioCodecsValues.indexOf(this)] + } + } + audioFormats.forEachIndexed { idx, it -> formats.add(Format(audioFormatsValues[idx], containerPreference!!,"",acodecPreference!!, "",0, it)) } + audioFormatIDPreference.forEach { formats.add(Format(it, containerPreference!!,"",resources.getString(R.string.preferred_format_id), "",1, it)) } + return formats + } + + fun getGenericVideoFormats(resources: Resources) : MutableList{ + val formatIDPreference = sharedPreferences.getString("format_id", "").toString().split(",").filter { it.isNotEmpty() } + val videoFormatsValues = resources.getStringArray(R.array.video_formats_values) + val videoFormats = resources.getStringArray(R.array.video_formats) + val formats = mutableListOf() + val containerPreference = sharedPreferences.getString("video_format", "") + val audioCodecPreference = sharedPreferences.getString("audio_codec", "")!!.run { + if (this.isNotEmpty()){ + val audioCodecs = resources.getStringArray(R.array.audio_codec) + val audioCodecsValues = resources.getStringArray(R.array.audio_codec_values) + audioCodecs[audioCodecsValues.indexOf(this)] + }else this + } + val videoCodecPreference = sharedPreferences.getString("video_codec", "")!!.run { + if (this.isEmpty()){ + resources.getString(R.string.defaultValue) + }else{ + val videoCodecs = resources.getStringArray(R.array.video_codec) + val videoCodecsValues = resources.getStringArray(R.array.video_codec_values) + videoCodecs[videoCodecsValues.indexOf(this)] + } + } + videoFormatsValues.forEachIndexed { index, it -> formats.add(Format(it, containerPreference!!,videoCodecPreference,audioCodecPreference, "",0, videoFormats[index])) } + formatIDPreference.forEach { formats.add(Format(it, containerPreference!!,resources.getString(R.string.preferred_format_id),"", "",1, it)) } + return formats + } + //OLD CODE JUST FOR STORAGE / REFERENCE /* diff --git a/app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt index 8e2d1088..5010f2ac 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/NavbarUtil.kt @@ -35,7 +35,6 @@ object NavbarUtil { val pref = settings.getString("start_destination", "")!! val items = getDefaultNavBarItems(context) val navBarHasPref = getNavBarPrefs().contains(items.indexOfFirst { it.itemId == navItems[pref] }.toString()) - println("START FRAGMENT: $pref") return if (pref == "") { R.id.homeFragment }else { diff --git a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt index 232adddc..43d74b45 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/NotificationUtil.kt @@ -9,7 +9,6 @@ import android.app.TaskStackBuilder import android.content.Context import android.content.Intent import android.content.res.Resources -import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri import android.os.Build @@ -17,7 +16,6 @@ import android.os.Bundle import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.content.FileProvider -import androidx.core.os.bundleOf import androidx.documentfile.provider.DocumentFile import androidx.navigation.NavDeepLinkBuilder import com.deniscerri.ytdl.MainActivity @@ -29,6 +27,7 @@ import com.deniscerri.ytdl.receiver.PauseDownloadNotificationReceiver import com.deniscerri.ytdl.receiver.ResumeActivity import com.deniscerri.ytdl.util.Extensions.toBitmap import java.io.File +import kotlin.random.Random class NotificationUtil(var context: Context) { @@ -339,22 +338,28 @@ class NotificationUtil(var context: Context) { .setArguments(bundle) .createPendingIntent() - val errorTabPendingIntent = NavDeepLinkBuilder(context) - .setGraph(R.navigation.nav_graph) - .setDestination(R.id.downloadQueueMainFragment) - .setArguments(bundleOf(Pair("tab", "error"))) - .createPendingIntent() + val intent = Intent(context, MainActivity::class.java) + intent.setAction(Intent.ACTION_VIEW) + intent.putExtra("destination", "Queue") + intent.putExtra("tab", "error") + val errorTabPendingIntent = PendingIntent.getActivity( + context, + Random.nextInt(), + intent, + PendingIntent.FLAG_IMMUTABLE + ) - val reconfigurePendingItent = NavDeepLinkBuilder(context) - .setGraph(R.navigation.nav_graph) - .setDestination(R.id.downloadQueueMainFragment) - .setArguments( - bundleOf( - Pair("tab", "error"), - Pair("reconfigure", id) - ) - ) - .createPendingIntent() + val intent2 = Intent(context, MainActivity::class.java) + intent2.setAction(Intent.ACTION_VIEW) + intent2.putExtra("reconfigure", id) + intent2.putExtra("tab", "error") + intent2.putExtra("destination", "Queue") + val reconfigurePendingItent = PendingIntent.getActivity( + context, + Random.nextInt(), + intent2, + PendingIntent.FLAG_IMMUTABLE + ) notificationBuilder .setContentTitle("${res.getString(R.string.failed_download)}: $title") diff --git a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt index 61d90220..05f2b5c9 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/UiUtil.kt @@ -70,6 +70,8 @@ import com.deniscerri.ytdl.util.Extensions.enableFastScroll import com.deniscerri.ytdl.util.Extensions.enableTextHighlight import com.deniscerri.ytdl.util.Extensions.getMediaDuration import com.deniscerri.ytdl.util.Extensions.toStringDuration +import com.deniscerri.ytdl.util.extractors.PipedApiUtil +import com.deniscerri.ytdl.util.extractors.YTDLPUtil import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeUtils import com.google.android.material.badge.ExperimentalBadgeUtils @@ -567,10 +569,10 @@ object UiUtil { if (fileSizeReadable == "?") fileSize!!.visibility = View.GONE else fileSize!!.text = fileSizeReadable - val infoUtil = InfoUtil(context) + val ytdlpUtil = YTDLPUtil(context) command?.setOnClickListener { - showGeneratedCommand(context, infoUtil.parseYTDLRequestString(infoUtil.buildYoutubeDLRequest(item))) + showGeneratedCommand(context, ytdlpUtil.parseYTDLRequestString(ytdlpUtil.buildYoutubeDLRequest(item))) } val link = bottomSheet.findViewById