diff --git a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt index aac61368..0e6d5e79 100644 --- a/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt +++ b/app/src/main/java/com/deniscerri/ytdl/MainActivity.kt @@ -156,7 +156,7 @@ class MainActivity : BaseActivity() { navController.navigate(R.id.downloadQueueMainFragment) }else{ kotlin.runCatching { - (navHostFragment.childFragmentManager.primaryNavigationFragment!! as HistoryFragment).scrollToTop() + (navHostFragment.childFragmentManager.primaryNavigationFragment!! as HistoryFragment).openSearchView() } } } diff --git a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt index 8abcfb76..52d06f45 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/dao/LogDao.kt @@ -19,7 +19,7 @@ interface LogDao { fun getAllLogsFlow() : Flow> @Query("SELECT * FROM logs WHERE id=:id LIMIT 1") - fun getLogFlowByID(id: Long) : Flow + fun getLogFlowByID(id: Long) : Flow @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insert(item: LogItem) : Long @@ -56,5 +56,5 @@ interface LogDao { suspend fun update(item: LogItem) @Query("SELECT * FROM logs WHERE id=:id LIMIT 1") - fun getByID(id: Long) : LogItem + fun getByID(id: Long) : LogItem? } \ No newline at end of file diff --git a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt index 0a40a872..3afa30fa 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/repository/LogRepository.kt @@ -11,7 +11,7 @@ class LogRepository(private val logDao: LogDao) { return logDao.getAllLogs() } - fun getLogFlowByID(id: Long) : Flow { + fun getLogFlowByID(id: Long) : Flow { return logDao.getLogFlowByID(id) } @@ -33,7 +33,7 @@ class LogRepository(private val logDao: LogDao) { logDao.deleteAll() } - fun getItem(id: Long) : LogItem{ + fun getItem(id: Long) : LogItem? { return logDao.getByID(id) } 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 2cc749e8..7d09875b 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 @@ -264,6 +264,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val saveSubs = sharedPreferences.getBoolean("write_subtitles", false) val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false) val recodeVideo = sharedPreferences.getBoolean("recode_video", false) + val compatibilityMode = sharedPreferences.getBoolean("compatible_video", false) + val removeAudio = sharedPreferences.getBoolean("remove_audio", false) + val alsoDownloadAudio = sharedPreferences.getBoolean("also_download_audio", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) @@ -309,7 +312,10 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel subsLanguages, audioFormatIDs = preferredAudioFormats, recodeVideo = recodeVideo, - embedThumbnail = videoEmbedThumb + compatibilityMode = compatibilityMode, + embedThumbnail = videoEmbedThumb, + removeAudio = removeAudio, + alsoDownloadAsAudio = alsoDownloadAudio ) val extraCommands = when(type){ @@ -448,6 +454,9 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel val saveSubs = sharedPreferences.getBoolean("write_subtitles", false) val saveAutoSubs = sharedPreferences.getBoolean("write_auto_subtitles", false) val recodeVideo = sharedPreferences.getBoolean("recode_video", false) + val removeAudio = sharedPreferences.getBoolean("remove_audio", false) + val compatibilityMode = sharedPreferences.getBoolean("compatible_video", false) + val alsoDownloadAudio = sharedPreferences.getBoolean("also_download_audio", false) val addChapters = sharedPreferences.getBoolean("add_chapters", false) val saveThumb = sharedPreferences.getBoolean("write_thumbnail", false) val embedThumb = sharedPreferences.getBoolean("embed_thumbnail", false) @@ -486,8 +495,25 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel } }.joinToString(" ") { it.content } - val audioPreferences = AudioPreferences(embedThumb, cropThumb,false, ArrayList(sponsorblock!!), audioBitrate!!) - val videoPreferences = VideoPreferences(embedSubs, addChapters, false, ArrayList(sponsorblock), saveSubs, saveAutoSubs, subsLanguages, recodeVideo = recodeVideo) + val audioPreferences = AudioPreferences( + embedThumb = embedThumb, + cropThumb = cropThumb, + splitByChapters = false, + sponsorBlockFilters = ArrayList(sponsorblock!!), + bitrate = audioBitrate!! + ) + val videoPreferences = VideoPreferences( + embedSubs = embedSubs, + addChapters = addChapters, + splitByChapters = false, + sponsorBlockFilters = ArrayList(sponsorblock), + writeSubs = saveSubs, + writeAutoSubs = saveAutoSubs, + subsLanguages = subsLanguages, + recodeVideo = recodeVideo, + compatibilityMode = compatibilityMode, + removeAudio = removeAudio, + alsoDownloadAsAudio = alsoDownloadAudio) var path = defaultPath historyItem.downloadPath.first().apply { File(this).parent?.apply { diff --git a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/LogViewModel.kt b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/LogViewModel.kt index fd14cf26..ea40dfec 100644 --- a/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/LogViewModel.kt +++ b/app/src/main/java/com/deniscerri/ytdl/database/viewmodel/LogViewModel.kt @@ -28,11 +28,11 @@ class LogViewModel(private val application: Application) : AndroidViewModel(appl } - fun getLogFlowByID(id: Long) : LiveData { + fun getLogFlowByID(id: Long) : LiveData { return repository.getLogFlowByID(id).asLiveData() } - fun getItemById(id: Long): LogItem{ + fun getItemById(id: Long): LogItem? { return repository.getItem(id) } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt index 74d5d719..811b672e 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/downloads/HistoryFragment.kt @@ -218,10 +218,17 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ } } + fun openSearchView() { + topAppBar.menu.performIdentifierAction(R.id.search_history, 0) + } + private fun initMenu() { + val searchView = topAppBar.menu.findItem(R.id.search_history).actionView as SearchView? + val onActionExpandListener: MenuItem.OnActionExpandListener = object : MenuItem.OnActionExpandListener { override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean { + searchView?.requestFocus() return true } @@ -231,7 +238,7 @@ class HistoryFragment : Fragment(), HistoryPaginatedAdapter.OnItemClickListener{ } topAppBar.menu.findItem(R.id.search_history) .setOnActionExpandListener(onActionExpandListener) - val searchView = topAppBar.menu.findItem(R.id.search_history).actionView as SearchView? + searchView!!.inputType = InputType.TYPE_CLASS_TEXT searchView.queryHint = getString(R.string.search_history_hint) searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/cookies/CookiesFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/cookies/CookiesFragment.kt index 663038e9..fd058e52 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/cookies/CookiesFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/cookies/CookiesFragment.kt @@ -125,7 +125,6 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener { } private fun initMenu() { - topAppBar.menu.getItem(1).isChecked = preferences.getBoolean("use_header", false) topAppBar.setOnMenuItemClickListener { m: MenuItem -> when (m.itemId) { R.id.delete_cookies -> { @@ -143,10 +142,6 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener { } deleteDialog.show() } - R.id.use_header -> { - m.isChecked = !m.isChecked - preferences.edit().putBoolean("use_header", m.isChecked).apply() - } R.id.import_clipboard -> { lifecycleScope.launch(Dispatchers.IO){ cookiesViewModel.importFromClipboard() diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt index 88214bc7..9ba15f7e 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogFragment.kt @@ -113,7 +113,7 @@ class DownloadLogFragment : Fragment() { CoroutineScope(Dispatchers.IO).launch { runCatching { - val logItem = logViewModel.getItemById(logID!!) + val logItem = logViewModel.getItemById(logID!!) ?: throw Exception() withContext(Dispatchers.Main){ topAppBar.title = logItem.title } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogListFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogListFragment.kt index 633de570..88de9638 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogListFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/downloadLogs/DownloadLogListFragment.kt @@ -236,16 +236,19 @@ class DownloadLogListFragment : Fragment(), DownloadLogsAdapter.OnItemClickListe when (direction) { ItemTouchHelper.LEFT -> { lifecycleScope.launch { - val deletedItem = withContext(Dispatchers.IO){ + val toDelete = withContext(Dispatchers.IO){ logViewModel.getItemById(items[position].id) } - logViewModel.delete(deletedItem) - Snackbar.make(recyclerView, getString(R.string.you_are_going_to_delete) + ": " + deletedItem.title, Snackbar.LENGTH_INDEFINITE) - .setAction(getString(R.string.undo)) { - lifecycleScope.launch(Dispatchers.IO){ - logViewModel.insert(deletedItem) - } - }.show() + + toDelete?.apply { + logViewModel.delete(this) + Snackbar.make(recyclerView, getString(R.string.you_are_going_to_delete) + ": " + this.title, Snackbar.LENGTH_INDEFINITE) + .setAction(getString(R.string.undo)) { + lifecycleScope.launch(Dispatchers.IO){ + logViewModel.insert(this@apply) + } + }.show() + } } } diff --git a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/advanced/generateyoutubepotokens/GenerateYoutubePoTokensFragment.kt b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/advanced/generateyoutubepotokens/GenerateYoutubePoTokensFragment.kt index 40cfa663..139264de 100644 --- a/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/advanced/generateyoutubepotokens/GenerateYoutubePoTokensFragment.kt +++ b/app/src/main/java/com/deniscerri/ytdl/ui/more/settings/advanced/generateyoutubepotokens/GenerateYoutubePoTokensFragment.kt @@ -216,7 +216,7 @@ class GenerateYoutubePoTokensFragment : Fragment() { regenerateBtn.isEnabled = false editText.doOnTextChanged { text, start, before, count -> - regenerateBtn.isEnabled = editText.text.toString().getIDFromYoutubeURL() != null + regenerateBtn.isEnabled = editText.text.toString().isYoutubeURL() } regenerateBtn.setOnClickListener { @@ -224,7 +224,7 @@ class GenerateYoutubePoTokensFragment : Fragment() { val intent = Intent(requireContext(), PoTokenWebViewLoginActivity::class.java) intent.putExtra("url", "https://www.youtube.com") - intent.putExtra("redirect_url", "https://www.youtube.com/embed/${editText.text.toString().getIDFromYoutubeURL()}") + //intent.putExtra("redirect_url", "https://www.youtube.com/embed/${editText.text.toString().getIDFromYoutubeURL()}") intent.putExtra("no_auth", true) webPoTokenResultLauncher.launch(intent) } diff --git a/app/src/main/java/com/deniscerri/ytdl/util/extractors/ytdlp/YTDLPUtil.kt b/app/src/main/java/com/deniscerri/ytdl/util/extractors/ytdlp/YTDLPUtil.kt index 4574c30d..75abe99a 100644 --- a/app/src/main/java/com/deniscerri/ytdl/util/extractors/ytdlp/YTDLPUtil.kt +++ b/app/src/main/java/com/deniscerri/ytdl/util/extractors/ytdlp/YTDLPUtil.kt @@ -472,6 +472,19 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co } if (!request.hasOption("--no-check-certificates")) request.addOption("--no-check-certificates") + val canUseWriteInfoJson = + !sharedPreferences.getBoolean("disable_write_info_json", false) && + !request.toString().contains("--download-sections") + if (canUseWriteInfoJson) { + val infoJsonFile = getInfoJsonFile(url) + //ignore info file if its older than 5 hours. puny measure to prevent expired formats in some cases + if (infoJsonFile != null && System.currentTimeMillis() - infoJsonFile.lastModified() <= (1000 * 60 * 60 * 5)) { + request.addOption("--load-info-json", infoJsonFile.absolutePath) + }else { + request.addWriteInfoJson(url) + } + } + val res = YoutubeDL.getInstance().execute(request) val results: Array = try { res.out.split(System.lineSeparator()).toTypedArray() @@ -610,6 +623,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co val infoJsonName = hash(id) //yt-dlp doesnt overwrite info json, so delete manually File("${cachePath}/${infoJsonName}video.info.json").delete() + this.addOption("--no-clean-info-json") this.addCommands(listOf("--print-to-file", "video:%()j", "${cachePath}/${infoJsonName}${System.currentTimeMillis()}video.info.json")) } @@ -645,7 +659,6 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co request.addOption("--print", "%(filename)s") request.addOption("--skip-download") request.addOption("--simulate") - request.addOption("--no-check-certificates") request.addOption("--no-check-formats") request.addOption("--quiet") @@ -1161,8 +1174,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co metadataCommands.addOption("--parse-metadata", "%(album_artist,first_artist|)s:%(album_artist)s") - metadataCommands.addOption("--parse-metadata", "description:(?:.+?Released\\ on\\s*:\\s*(?P\\d{4}))?") - metadataCommands.addOption("--parse-metadata", "%(dscrptn_year,release_year,release_date>%Y,upload_date>%Y)s:(?P\\d+)") + metadataCommands.addOption("--parse-metadata", "%(release_year,release_date>%Y,upload_date>%Y)s:(?P\\d+)") if (isPlaylistItem) { metadataCommands.addOption("--parse-metadata", "%(track_number,playlist_index)d:(?P\\d+)") @@ -1214,6 +1226,12 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co var cont = "" val outputContainer = downloadItem.container + if (downloadItem.videoPreferences.compatibilityMode) { + request.addOption("--recode-video", "mp4") + request.addOption("--merge-output-format", "mp4/mkv") + request.addOption("--ppa", "VideoConvertor+ffmpeg_o:-c:v libx264 -c:a aac -profile:v baseline") + } + if(outputContainer.isNotEmpty() && outputContainer != "Default" && outputContainer != context.getString( R.string.defaultValue) && supportedContainers.contains(outputContainer)){ cont = outputContainer @@ -1221,14 +1239,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co val cantRecode = listOf("avi") if (downloadItem.videoPreferences.recodeVideo && !cantRecode.contains(cont)) { request.addOption("--recode-video", outputContainer.lowercase()) - }else{ - if (downloadItem.videoPreferences.compatibilityMode) { - request.addOption("--recode-video", "mp4") - request.addOption("--merge-output-format", "mp4/mkv") - } - else { - request.addOption("--merge-output-format", outputContainer.lowercase()) - } + }else if (!downloadItem.videoPreferences.compatibilityMode){ + request.addOption("--merge-output-format", outputContainer.lowercase()) } if (!listOf("webm", "avi", "flv", "gif").contains(outputContainer.lowercase())) { @@ -1258,6 +1270,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co } ?: f }.ifBlank { "ba" } val preferredAudioLanguage = sharedPreferences.getString("audio_language", "")!! + val preferDRCAudio = sharedPreferences.getBoolean("prefer_drc_audio", false) if (downloadItem.videoPreferences.removeAudio) audioF = "" var abrSort = "" @@ -1293,6 +1306,8 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co if (!f.contains("$videoF+ba")) { if (preferredAudioLanguage.isNotEmpty()) { f.append("$videoF+ba[language^=$preferredAudioLanguage]/") + } else if (preferDRCAudio) { + f.append("$videoF+ba[format_id$=-drc]/") }else { f.append("$videoF+ba/") } @@ -1328,6 +1343,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co .filter { it.isNotBlank() } .ifEmpty { val list = mutableListOf() + if (preferDRCAudio) list.add("ba[format_id$=-drc]") if (preferredAudioLanguage.isNotEmpty() && !downloadItem.videoPreferences.removeAudio) list.add("ba[language^=$preferredAudioLanguage]") list.add(audioF) list diff --git a/app/src/main/res/drawable/outline_arrow_forward_24.xml b/app/src/main/res/drawable/outline_arrow_forward_24.xml new file mode 100644 index 00000000..03d7ed4f --- /dev/null +++ b/app/src/main/res/drawable/outline_arrow_forward_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_generate_youtube_po_token.xml b/app/src/main/res/layout/fragment_generate_youtube_po_token.xml index b0c85612..04f25d51 100644 --- a/app/src/main/res/layout/fragment_generate_youtube_po_token.xml +++ b/app/src/main/res/layout/fragment_generate_youtube_po_token.xml @@ -276,8 +276,7 @@ android:checked="true" android:textSize="16sp" android:textStyle="bold" - android:text="Use Visitor Data" - tools:ignore="HardcodedText" /> + android:text="@string/visitor_data" /> diff --git a/app/src/main/res/menu/cookies_menu.xml b/app/src/main/res/menu/cookies_menu.xml index 60440432..af72d17b 100644 --- a/app/src/main/res/menu/cookies_menu.xml +++ b/app/src/main/res/menu/cookies_menu.xml @@ -6,11 +6,6 @@ android:title="@string/delete_all_cookies" app:showAsAction="never" /> - - System + @string/system English العربية Azərbaycanca @@ -1591,6 +1591,4 @@ smallest largest - - Hook \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c57cba2b..e8862f0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -509,4 +509,5 @@ App Icon Show option to download immediately in the share menu Don\'t fetch data + Hook diff --git a/app/src/main/res/xml/advanced_preferences.xml b/app/src/main/res/xml/advanced_preferences.xml index 161e6b9a..3e16e754 100644 --- a/app/src/main/res/xml/advanced_preferences.xml +++ b/app/src/main/res/xml/advanced_preferences.xml @@ -84,6 +84,14 @@ android:icon="@drawable/baseline_note_24" android:key="no_check_certificates" app:title="@string/no_check_certificates" /> + + + diff --git a/app/src/main/res/xml/downloading_preferences.xml b/app/src/main/res/xml/downloading_preferences.xml index d7fea784..a77323e4 100644 --- a/app/src/main/res/xml/downloading_preferences.xml +++ b/app/src/main/res/xml/downloading_preferences.xml @@ -211,11 +211,6 @@ /> - - + + + +