diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5cccc364..aac7de73 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,6 +13,8 @@
+
+
(R.id.content)
- content.visibility = View.GONE
+ content.text = item?.content
card.setOnClickListener {
onItemClickListener.onItemClick(item!!)
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryAdapter.kt
index 172c8203..7c2d3f0e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HistoryAdapter.kt
@@ -21,6 +21,7 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.HistoryItem
+import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
@@ -172,6 +173,16 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
checkedItems.clear()
}
+ fun invertSelected(items: List?){
+ val invertedList = mutableListOf()
+ items?.forEach {
+ if (!checkedItems.contains(it!!.id)) invertedList.add(it.id)
+ }
+ checkedItems.clear()
+ checkedItems.addAll(invertedList)
+ notifyDataSetChanged()
+ }
+
companion object {
private val DIFF_CALLBACK: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: HistoryItem, newItem: HistoryItem): Boolean {
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt
index 11205185..2061e678 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/HomeAdapter.kt
@@ -173,6 +173,16 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
notifyDataSetChanged()
}
+ fun invertSelected(items: List?){
+ val invertedList = mutableListOf()
+ items?.forEach {
+ if (!checkedVideos.contains(it!!.url)) invertedList.add(it.url)
+ }
+ checkedVideos.clear()
+ checkedVideos.addAll(invertedList)
+ notifyDataSetChanged()
+ }
+
fun clearCheckedItems(){
checkedVideos.clear()
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/adapter/TemplatesAdapter.kt b/app/src/main/java/com/deniscerri/ytdlnis/adapter/TemplatesAdapter.kt
index 9bce5544..c753f382 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/adapter/TemplatesAdapter.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/adapter/TemplatesAdapter.kt
@@ -74,7 +74,7 @@ class TemplatesAdapter(onItemClickListener: OnItemClickListener, activity: Activ
}
override fun areContentsTheSame(oldItem: CommandTemplate, newItem: CommandTemplate): Boolean {
- return oldItem.content == newItem.content
+ return oldItem.title == newItem.title && oldItem.content == newItem.content
}
}
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
index 855e4902..46091c2b 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/dao/CookieDao.kt
@@ -13,11 +13,11 @@ interface CookieDao {
@Query("SELECT * FROM cookies ORDER BY id DESC")
fun getAllCookiesLiveData() : LiveData>
- @Query("SELECT * FROM cookies WHERE url=:url")
- fun checkIfExistsWithSameURL(url: String) : CookieItem
+ @Query("SELECT EXISTS(SELECT * FROM cookies WHERE url=:url LIMIT 1)")
+ fun checkIfExistsWithSameURL(url: String) : Boolean
@Insert(onConflict = OnConflictStrategy.IGNORE)
- suspend fun insert(item: CookieItem)
+ suspend fun insert(item: CookieItem) : Long
@Query("DELETE FROM cookies")
suspend fun deleteAll()
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
index bff6c098..1bc0b44a 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/repository/CookieRepository.kt
@@ -12,12 +12,11 @@ class CookieRepository(private val cookieDao: CookieDao) {
}
- suspend fun insert(item: CookieItem){
- try{
- cookieDao.checkIfExistsWithSameURL(item.url)
- }catch(e: Exception){
- cookieDao.insert(item)
+ suspend fun insert(item: CookieItem) : Long{
+ if (! cookieDao.checkIfExistsWithSameURL(item.url)){
+ return cookieDao.insert(item)
}
+ return -1
}
suspend fun delete(item: CookieItem){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
index 41aa1a1b..a80df524 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/CookieViewModel.kt
@@ -6,26 +6,27 @@ import android.content.ClipboardManager
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
+import android.util.Log
import android.webkit.CookieManager
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import com.deniscerri.ytdlnis.database.DBManager
-import com.deniscerri.ytdlnis.database.models.CommandTemplateExport
import com.deniscerri.ytdlnis.database.models.CookieItem
import com.deniscerri.ytdlnis.database.repository.CookieRepository
import com.deniscerri.ytdlnis.ui.more.WebViewActivity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
+import java.io.File
class CookieViewModel(private val application: Application) : AndroidViewModel(application) {
private val repository: CookieRepository
val items: LiveData>
private val jsonFormat = Json { prettyPrint = true }
+ private val cookieHeader = "# Netscape HTTP Cookie File\n" +
+ "# WebView Generated by the YTDLnis app\n"
init {
val dao = DBManager.getInstance(application).cookieDao
@@ -37,12 +38,13 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
return repository.getAll()
}
- fun insert(item: CookieItem) = viewModelScope.launch(Dispatchers.IO) {
- repository.insert(item)
+ suspend fun insert(item: CookieItem) : Long {
+ return repository.insert(item)
}
fun delete(item: CookieItem) = viewModelScope.launch(Dispatchers.IO) {
repository.delete(item)
+ updateCookiesFile()
}
fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
@@ -106,20 +108,53 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
close()
}
close()
- val header = "# Netscape HTTP Cookie File\n" +
- "# WebView Generated by the YTDLnis app\n"
- cookieList.fold(StringBuilder(header)) { acc, cookie ->
+ cookieList.fold(StringBuilder("")) { acc, cookie ->
acc.append(cookie.toNetscapeFormat()).append("\n")
}.toString()
}
}
+ fun updateCookiesFile() = viewModelScope.launch(Dispatchers.IO) {
+ val cookies = repository.getAll()
+ val cookieTXT = StringBuilder(cookieHeader)
+ val cookieFile = File(application.cacheDir, "cookies.txt")
+ if (cookies.isEmpty()) cookieFile.apply { writeText("") }
+ cookies.forEach {
+ it.content.lines().forEach {line ->
+ if (! cookieTXT.contains(line)) cookieTXT.append(it.content)
+ }
+ }
+ cookieFile.apply { writeText(cookieTXT.toString()) }
+ }
+
+ suspend fun importFromClipboard() {
+ try{
+ val clipboard: ClipboardManager =
+ application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
+ var clip = clipboard.primaryClip!!.getItemAt(0).text
+ Log.e("Aaa", clip.toString())
+ if (clip.startsWith(cookieHeader)){
+ clip = clip.removePrefix(cookieHeader)
+ val cookie = CookieItem(
+ 0,
+ "Cookie Import [${System.currentTimeMillis()}]",
+ clip.toString()
+ )
+ insert(cookie)
+ updateCookiesFile()
+ }
+ }catch (e: Exception){
+ e.printStackTrace()
+ }
+ }
+
fun exportToClipboard() = viewModelScope.launch {
try{
val clipboard: ClipboardManager =
application.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
-
- getCookiesFromDB().getOrNull()?.let {
+ val cookieFile = File(application.cacheDir, "cookies.txt")
+ if (! cookieFile.exists()) updateCookiesFile()
+ cookieFile.readText().let {
clipboard.setText(it)
}
}catch (e: Exception){
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
index 55b1b83c..9aebda3c 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/database/viewmodel/DownloadViewModel.kt
@@ -2,7 +2,10 @@ package com.deniscerri.ytdlnis.database.viewmodel
import android.app.Activity
import android.app.Application
+import android.content.Context
import android.content.SharedPreferences
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
import android.text.format.DateFormat
import android.util.Log
import android.widget.Toast
@@ -10,10 +13,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import androidx.work.Data
-import androidx.work.ExistingWorkPolicy
-import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.WorkManager
+import androidx.work.*
import com.deniscerri.ytdlnis.App
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.DBManager
@@ -287,6 +287,7 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
suspend fun queueDownloads(items: List) {
val context = getApplication().applicationContext
+ val allowMeteredNetworks = sharedPreferences.getBoolean("metered_networks", true)
items.forEach { it.status = DownloadRepository.Status.Queued.toString() }
val ids = repository.insertAll(items)
for (i in ids.indices){
@@ -301,6 +302,11 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
val workRequest = OneTimeWorkRequestBuilder()
.setInputData(Data.Builder().putLong("id", it.id).build())
.addTag("download")
+ .setConstraints(
+ Constraints.Builder()
+ .setRequiredNetworkType(if (allowMeteredNetworks) NetworkType.CONNECTED else NetworkType.UNMETERED)
+ .build()
+ )
.setInitialDelay(delay, TimeUnit.MILLISECONDS)
.build()
@@ -318,6 +324,10 @@ class DownloadViewModel(application: Application) : AndroidViewModel(application
repository.delete(it)
}
}
+ val isCurrentNetworkMetered = (context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).isActiveNetworkMetered
+ if (!allowMeteredNetworks && isCurrentNetworkMetered){
+ Toast.makeText(context, context.getString(R.string.metered_network_download_start_info), Toast.LENGTH_LONG).show()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
index 7c98c073..3e14190c 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/receiver/ShareActivity.kt
@@ -78,10 +78,7 @@ class ShareActivity : AppCompatActivity() {
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
sharedPreferences = getSharedPreferences("root_preferences", Activity.MODE_PRIVATE)
- cookieViewModel.getCookiesFromDB().getOrNull()?.let{
- File(cacheDir, "cookies.txt").apply { writeText(it) }
- }
-
+ cookieViewModel.updateCookiesFile()
val intent = intent
handleIntents(intent)
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
index 034a22e1..43d3dc6e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/HomeFragment.kt
@@ -52,6 +52,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
+import kotlin.collections.ArrayList
class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickListener {
@@ -595,6 +596,17 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, View.OnClickLi
mode?.title = getString(R.string.all_items_selected)
true
}
+ R.id.invert_selected -> {
+ homeAdapter?.invertSelected(resultsList)
+ val invertedList = arrayListOf()
+ resultsList?.forEach {
+ if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
+ }
+ selectedObjects?.clear()
+ selectedObjects?.addAll(invertedList)
+ actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
+ true
+ }
else -> false
}
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt
index 9d1962a1..b14e9e25 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/downloads/HistoryFragment.kt
@@ -3,6 +3,7 @@ package com.deniscerri.ytdlnis.ui.downloads
import android.app.Activity
import android.content.Context
import android.content.DialogInterface
+import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.os.Handler
@@ -32,6 +33,7 @@ import com.deniscerri.ytdlnis.MainActivity
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.adapter.HistoryAdapter
import com.deniscerri.ytdlnis.database.models.HistoryItem
+import com.deniscerri.ytdlnis.database.models.ResultItem
import com.deniscerri.ytdlnis.database.repository.HistoryRepository
import com.deniscerri.ytdlnis.database.repository.HistoryRepository.HistorySort
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
@@ -213,6 +215,10 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
deleteDialog.show()
}
}
+ R.id.download_queue -> {
+ val intent = Intent(context, DownloadQueueActivity::class.java)
+ startActivity(intent)
+ }
R.id.remove_deleted_history -> {
if(allhistoryList!!.isEmpty()){
Toast.makeText(context, R.string.history_is_empty, Toast.LENGTH_SHORT).show()
@@ -565,6 +571,17 @@ class HistoryFragment : Fragment(), HistoryAdapter.OnItemClickListener{
actionMode?.finish()
true
}
+ R.id.invert_selected -> {
+ historyAdapter?.invertSelected(historyList)
+ val invertedList = arrayListOf()
+ historyList?.forEach {
+ if (!selectedObjects?.contains(it)!!) invertedList.add(it!!)
+ }
+ selectedObjects?.clear()
+ selectedObjects?.addAll(invertedList)
+ actionMode!!.title = "${selectedObjects!!.size} ${getString(R.string.selected)}"
+ true
+ }
else -> false
}
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesActivity.kt
index c2c74203..fda0d30e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/CookiesActivity.kt
@@ -4,13 +4,16 @@ import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
+import android.util.Log
import android.view.MenuItem
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.deniscerri.ytdlnis.R
@@ -22,6 +25,8 @@ import com.deniscerri.ytdlnis.util.UiUtil
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
import java.io.File
import java.net.CookieManager
@@ -70,13 +75,18 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
deleteDialog.setMessage(getString(R.string.confirm_delete_cookies_desc))
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
- android.webkit.CookieManager.getInstance().removeAllCookies(null)
+ cookiesViewModel.deleteAll()
kotlin.runCatching {
File(cacheDir, "cookies.txt").apply { writeText("") }
}
}
deleteDialog.show()
}
+ R.id.import_clipboard -> {
+ lifecycleScope.launch(Dispatchers.IO){
+ cookiesViewModel.importFromClipboard()
+ }
+ }
R.id.export_clipboard -> {
cookiesViewModel.exportToClipboard()
}
@@ -97,7 +107,9 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
builder.setTitle(getString(R.string.cookies))
val inputLayout = layoutInflater.inflate(R.layout.textinput, null)
val editText = inputLayout.findViewById(R.id.url_edittext)
- editText.setText(url)
+ val text = if (url.isNullOrBlank()) "https://" else url
+ editText.setText(text)
+ editText.setSelection(editText.text.length)
builder.setView(inputLayout)
builder.setPositiveButton(
getString(R.string.get_cookies)
@@ -112,9 +124,8 @@ class CookiesActivity : AppCompatActivity(), CookieAdapter.OnItemClickListener {
getString(R.string.cancel)
) { dialog: DialogInterface?, which: Int -> }
val dialog = builder.create()
- editText.setOnKeyListener { view, i, keyEvent ->
+ editText.doOnTextChanged { text, start, before, count ->
dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = editText.text.isNotEmpty()
- false
}
dialog.show()
val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
index be582bcc..8736028e 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/WebViewActivity.kt
@@ -1,5 +1,6 @@
package com.deniscerri.ytdlnis.ui.more
+import android.annotation.SuppressLint
import android.os.Bundle
import android.webkit.CookieManager
import android.webkit.WebView
@@ -7,12 +8,16 @@ import android.webkit.WebViewClient
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.CookieItem
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.io.File
@@ -25,6 +30,7 @@ class WebViewActivity : AppCompatActivity() {
private lateinit var url: String
private lateinit var cookies: String
+ @SuppressLint("SetJavaScriptEnabled")
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.webview_activity)
@@ -35,6 +41,7 @@ class WebViewActivity : AppCompatActivity() {
toolbar = appbar.findViewById(R.id.webviewToolbar)
generateBtn = toolbar.findViewById(R.id.generate)
cookieManager = CookieManager.getInstance()
+ cookieManager.removeAllCookies(null)
webView = findViewById(R.id.webview)
webView.settings.javaScriptEnabled = true
@@ -62,20 +69,24 @@ class WebViewActivity : AppCompatActivity() {
generateBtn.setOnClickListener {
cookiesViewModel.getCookiesFromDB().getOrNull()?.let {
- File(cacheDir , "/cookies.txt").apply { writeText(it) }
+ kotlin.runCatching {
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO){
+ cookiesViewModel.insert(
+ CookieItem(
+ 0,
+ url,
+ it
+ )
+ )
+ }
+ cookiesViewModel.updateCookiesFile()
+ }
+ }.onFailure {
+ Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
+ }
}
- kotlin.runCatching {
- cookiesViewModel.insert(
- CookieItem(
- 0,
- url,
- ""
- )
- )
- }.onFailure {
- Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
- }
- cookieManager.flush()
+ cookieManager.removeAllCookies(null)
onBackPressedDispatcher.onBackPressed()
}
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/SettingsFragment.kt b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/SettingsFragment.kt
index b0b19753..c578baa3 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/ui/more/settings/SettingsFragment.kt
@@ -43,6 +43,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var incognito: SwitchPreferenceCompat? = null
private var preferredDownloadType : ListPreference? = null
private var downloadCard: SwitchPreferenceCompat? = null
+ private var meteredNetwork: SwitchPreferenceCompat? = null
private var apiKey: EditTextPreference? = null
private var concurrentFragments: SeekBarPreference? = null
private var concurrentDownloads: SeekBarPreference? = null
@@ -106,6 +107,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
incognito = findPreference("incognito")
preferredDownloadType = findPreference("preferred_download_type")
downloadCard = findPreference("download_card")
+ meteredNetwork = findPreference("metered_networks")
apiKey = findPreference("api_key")
concurrentFragments = findPreference("concurrent_fragments")
concurrentDownloads = findPreference("concurrent_downloads")
@@ -168,6 +170,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.putBoolean("incognito", incognito!!.isChecked)
editor.putString("preferred_download_type", preferredDownloadType!!.value)
editor.putBoolean("download_card", downloadCard!!.isChecked)
+ editor.putBoolean("metered_networks", meteredNetwork!!.isChecked)
editor.putString("api_key", apiKey!!.text)
editor.putInt("concurrent_fragments", concurrentFragments!!.value)
editor.putInt("concurrent_downloads", concurrentDownloads!!.value)
@@ -282,8 +285,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.apply()
true
}
-
-
downloadCard!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
val enable = newValue as Boolean
@@ -291,6 +292,13 @@ class SettingsFragment : PreferenceFragmentCompat() {
editor.apply()
true
}
+ meteredNetwork!!.onPreferenceChangeListener =
+ Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
+ val enable = newValue as Boolean
+ editor.putBoolean("metered_networks", enable)
+ editor.apply()
+ true
+ }
apiKey!!.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
editor.putString("api_key", newValue.toString())
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
index ecda6225..9011600c 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/util/InfoUtil.kt
@@ -3,7 +3,9 @@ package com.deniscerri.ytdlnis.util
import android.app.Activity
import android.content.Context
import android.os.Environment
+import android.os.Looper
import android.util.Log
+import android.widget.Toast
import com.deniscerri.ytdlnis.R
import com.deniscerri.ytdlnis.database.models.Format
import com.deniscerri.ytdlnis.database.models.ResultItem
@@ -138,63 +140,70 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun getPlaylist(id: String, nextPageToken: String): PlaylistTuple {
- init()
- items = ArrayList()
- if (key!!.isEmpty()) {
- return if (useInvidous) getPlaylistFromInvidous(id) else PlaylistTuple(
+ try{
+ init()
+ items = ArrayList()
+ if (key!!.isEmpty()) {
+ return if (useInvidous) getPlaylistFromInvidous(id) else PlaylistTuple(
+ "",
+ getFromYTDL("https://www.youtube.com/playlist?list=$id")
+ )
+ }
+ val url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken=$nextPageToken&maxResults=50®ionCode=$countryCODE&playlistId=$id&key=$key"
+ //short data
+ val res = genericRequest(url)
+ if (!res.has("items")) return PlaylistTuple(
+ "",
+ getFromYTDL("https://www.youtube.com/playlist?list=$id")
+ )
+ val dataArray = res.getJSONArray("items")
+
+ //extra data
+ var url2 = "https://www.googleapis.com/youtube/v3/videos?id="
+ //getting all ids, for the extra data request
+ for (i in 0 until dataArray.length()) {
+ val element = dataArray.getJSONObject(i)
+ val snippet = element.getJSONObject("snippet")
+ val videoID = snippet.getJSONObject("resourceId").getString("videoId")
+ url2 = "$url2$videoID,"
+ snippet.put("videoID", videoID)
+ }
+ url2 = url2.substring(
+ 0,
+ url2.length - 1
+ ) + "&part=contentDetails®ionCode=" + countryCODE + "&key=" + key
+ val extra = genericRequest(url2)
+ val extraArray = extra.getJSONArray("items")
+ var j = 0
+ var i = 0
+ while (i < extraArray.length()) {
+ val element = dataArray.getJSONObject(i)
+ val snippet = element.getJSONObject("snippet")
+ var duration =
+ extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails")
+ .getString("duration")
+ duration = formatDuration(duration)
+ snippet.put("duration", duration)
+ fixThumbnail(snippet)
+ val v = createVideofromJSON(snippet)
+ if (v == null || v.thumb.isEmpty()) {
+ i++
+ continue
+ } else {
+ j++
+ }
+ v.playlistTitle = "YTDLnis"
+ items.add(v)
+ i++
+ }
+ val next = res.optString("nextPageToken")
+ return PlaylistTuple(next, items)
+ }catch (e: Exception){
+ return PlaylistTuple(
"",
getFromYTDL("https://www.youtube.com/playlist?list=$id")
)
}
- val url = "https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&pageToken=$nextPageToken&maxResults=50®ionCode=$countryCODE&playlistId=$id&key=$key"
- //short data
- val res = genericRequest(url)
- if (!res.has("items")) return PlaylistTuple(
- "",
- getFromYTDL("https://www.youtube.com/playlist?list=$id")
- )
- val dataArray = res.getJSONArray("items")
-
- //extra data
- var url2 = "https://www.googleapis.com/youtube/v3/videos?id="
- //getting all ids, for the extra data request
- for (i in 0 until dataArray.length()) {
- val element = dataArray.getJSONObject(i)
- val snippet = element.getJSONObject("snippet")
- val videoID = snippet.getJSONObject("resourceId").getString("videoId")
- url2 = "$url2$videoID,"
- snippet.put("videoID", videoID)
- }
- url2 = url2.substring(
- 0,
- url2.length - 1
- ) + "&part=contentDetails®ionCode=" + countryCODE + "&key=" + key
- val extra = genericRequest(url2)
- val extraArray = extra.getJSONArray("items")
- var j = 0
- var i = 0
- while (i < extraArray.length()) {
- val element = dataArray.getJSONObject(i)
- val snippet = element.getJSONObject("snippet")
- var duration =
- extra.getJSONArray("items").getJSONObject(j).getJSONObject("contentDetails")
- .getString("duration")
- duration = formatDuration(duration)
- snippet.put("duration", duration)
- fixThumbnail(snippet)
- val v = createVideofromJSON(snippet)
- if (v == null || v.thumb.isEmpty()) {
- i++
- continue
- } else {
- j++
- }
- v.playlistTitle = "YTDLnis"
- items.add(v)
- i++
- }
- val next = res.optString("nextPageToken")
- return PlaylistTuple(next, items)
}
private fun getPlaylistFromInvidous(id: String): PlaylistTuple {
@@ -221,30 +230,34 @@ class InfoUtil(private val context: Context) {
@Throws(JSONException::class)
fun getVideo(id: String): ResultItem? {
- init()
- if (key!!.isEmpty()) {
- return if (useInvidous) {
- val res = genericRequest(invidousURL + "videos/" + id)
- if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideofromInvidiousJSON(
- res
- )
- } else {
- getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
+ try {
+ init()
+ if (key!!.isEmpty()) {
+ return if (useInvidous) {
+ val res = genericRequest(invidousURL + "videos/" + id)
+ if (res.length() == 0) getFromYTDL("https://www.youtube.com/watch?v=$id")[0] else createVideofromInvidiousJSON(
+ res
+ )
+ } else {
+ getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
+ }
}
- }
- //short data
- var res =
- genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=$id&key=$key")
- if (!res.has("items")) return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
- var duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails")
- .getString("duration")
- duration = formatDuration(duration)
- res = res.getJSONArray("items").getJSONObject(0).getJSONObject("snippet")
- res.put("videoID", id)
- res.put("duration", duration)
- fixThumbnail(res)
- return createVideofromJSON(res)
+ //short data
+ var res =
+ genericRequest("https://youtube.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=$id&key=$key")
+ if (!res.has("items")) return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
+ var duration = res.getJSONArray("items").getJSONObject(0).getJSONObject("contentDetails")
+ .getString("duration")
+ duration = formatDuration(duration)
+ res = res.getJSONArray("items").getJSONObject(0).getJSONObject("snippet")
+ res.put("videoID", id)
+ res.put("duration", duration)
+ fixThumbnail(res)
+ return createVideofromJSON(res)
+ }catch (e: Exception){
+ return getFromYTDL("https://www.youtube.com/watch?v=$id")[0]
+ }
}
private fun createVideofromJSON(obj: JSONObject): ResultItem? {
@@ -510,7 +523,8 @@ class InfoUtil(private val context: Context) {
arrayOf(youtubeDLResponse.out)
}
for (result in results) {
- val jsonObject = JSONObject(result!!)
+ if (result.isNullOrBlank()) continue
+ val jsonObject = JSONObject(result)
val title = if (jsonObject.has("title")) {
if (jsonObject.getString("title") == "[Private video]") continue
jsonObject.getString("title")
@@ -531,7 +545,9 @@ class InfoUtil(private val context: Context) {
thumb = jsonObject.getString("thumbnail")
} else if (jsonObject.has("thumbnails")) {
val thumbs = jsonObject.getJSONArray("thumbnails")
- thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
+ if (thumbs.length() > 0){
+ thumb = thumbs.getJSONObject(thumbs.length() - 1).getString("url")
+ }
}
val website = if (jsonObject.has("ie_key")) jsonObject.getString("ie_key") else jsonObject.getString("extractor")
var playlistTitle: String? = ""
@@ -560,6 +576,9 @@ class InfoUtil(private val context: Context) {
)
}
} catch (e: Exception) {
+ Looper.prepare().run {
+ Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
+ }
e.printStackTrace()
}
return items
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
index 51fcec0e..8a4c77a3 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/DownloadWorker.kt
@@ -145,25 +145,27 @@ class DownloadWorker(
if (audioQualityId.isNotBlank()){
request.addOption("-f", audioQualityId)
- }
- val ext = downloadItem.format.container
- if(ext != context.getString(R.string.defaultValue) && ext != "webm"){
- val codec = when(downloadItem.format.container){
- "m4a" -> "aac"
- "aac" -> "aac"
- "mp3" -> "libmp3lame"
- "flac" -> "flac"
- "opus" -> "libopus"
- else -> ""
- }
- Log.e("aa", codec)
- if (codec.isEmpty()){
- request.addOption("-x")
- }else{
- request.addOption("--remux-video", ext)
- request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec")
+ val ext = downloadItem.format.container
+ if(ext != context.getString(R.string.defaultValue) && ext != "webm"){
+ val codec = when(downloadItem.format.container){
+ "aac" -> "aac"
+ "mp3" -> "libmp3lame"
+ "flac" -> "flac"
+ "opus" -> "libopus"
+ else -> ""
+ }
+ Log.e("aa", codec)
+ if (codec.isEmpty()){
+ request.addOption("-x")
+ }else{
+ request.addOption("--remux-video", ext)
+ request.addOption("--ppa", "VideoRemuxer:-vn -c:a $codec")
+ }
}
+
+ }else{
+ request.addOption("-x")
}
request.addOption("--embed-metadata")
diff --git a/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt b/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
index 26c5480f..fec492e3 100644
--- a/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
+++ b/app/src/main/java/com/deniscerri/ytdlnis/work/TerminalDownloadWorker.kt
@@ -103,6 +103,15 @@ class TerminalDownloadWorker(
}, 1000)
}
+ val chunks = it.out.chunked(5000)
+ val dataBuilder = Data.Builder();
+ dataBuilder.putString("output", chunks[0])
+
+
+ return Result.success(
+ dataBuilder.build()
+ )
+
}.onFailure {
if (it is YoutubeDL.CanceledException) {
return Result.failure()
diff --git a/app/src/main/res/drawable/baseline_downloading_24.xml b/app/src/main/res/drawable/baseline_downloading_24.xml
new file mode 100644
index 00000000..a4bfc0a6
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_downloading_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/metered_networks.xml b/app/src/main/res/drawable/metered_networks.xml
new file mode 100644
index 00000000..2ae0b9f1
--- /dev/null
+++ b/app/src/main/res/drawable/metered_networks.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/active_download_card.xml b/app/src/main/res/layout/active_download_card.xml
index 287d371f..bcd1ffe5 100644
--- a/app/src/main/res/layout/active_download_card.xml
+++ b/app/src/main/res/layout/active_download_card.xml
@@ -24,7 +24,7 @@
android:checkable="true"
app:strokeWidth="0dp"
app:cardPreventCornerOverlap="true"
- android:layout_margin="20dp">
+ android:layout_margin="10dp">
@@ -37,6 +36,7 @@
android:id="@+id/chips_command"
android:layout_width="wrap_content"
android:scrollbars="none"
+ android:layout_margin="10dp"
android:layout_height="wrap_content">
-
-
+ android:layout_height="match_parent"
+ android:layout_margin="10dp">
-
+ android:focusableInTouchMode="true"
+ android:orientation="vertical">
-
+
-
+
-
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_download_audio.xml b/app/src/main/res/layout/fragment_download_audio.xml
index 90434456..31e851d3 100644
--- a/app/src/main/res/layout/fragment_download_audio.xml
+++ b/app/src/main/res/layout/fragment_download_audio.xml
@@ -19,7 +19,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
- android:padding="10dp"
+ android:paddingHorizontal="10dp"
+ android:paddingBottom="10dp"
android:hint="@string/title"
style="@style/Widget.Material3.TextInputLayout.FilledBox">
@@ -34,7 +35,7 @@
diff --git a/app/src/main/res/layout/fragment_download_video.xml b/app/src/main/res/layout/fragment_download_video.xml
index 2fa91180..109c213e 100644
--- a/app/src/main/res/layout/fragment_download_video.xml
+++ b/app/src/main/res/layout/fragment_download_video.xml
@@ -19,7 +19,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:hint="@string/title"
- android:padding="10dp">
+ android:paddingBottom="10dp"
+ android:paddingHorizontal="10dp">
+ android:paddingHorizontal="10dp">
+ android:paddingHorizontal="10dp">
-
+ android:layout_height="match_parent">
-
-
-
+ android:gravity="top"
+ android:orientation="vertical"
+ tools:ignore="MissingConstraints">
-
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
+
+
diff --git a/app/src/main/res/layout/history_sort_sheet.xml b/app/src/main/res/layout/history_sort_sheet.xml
index 1d6b8d5d..d3df3298 100644
--- a/app/src/main/res/layout/history_sort_sheet.xml
+++ b/app/src/main/res/layout/history_sort_sheet.xml
@@ -10,15 +10,13 @@
-
diff --git a/app/src/main/res/layout/template_shortcuts_list.xml b/app/src/main/res/layout/template_shortcuts_list.xml
index 921d2052..5b0ac847 100644
--- a/app/src/main/res/layout/template_shortcuts_list.xml
+++ b/app/src/main/res/layout/template_shortcuts_list.xml
@@ -57,7 +57,8 @@
diff --git a/app/src/main/res/layout/textinput.xml b/app/src/main/res/layout/textinput.xml
index a2b99785..745e4006 100644
--- a/app/src/main/res/layout/textinput.xml
+++ b/app/src/main/res/layout/textinput.xml
@@ -2,6 +2,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/main_menu_context.xml b/app/src/main/res/menu/main_menu_context.xml
index 3b227bea..eecfffeb 100644
--- a/app/src/main/res/menu/main_menu_context.xml
+++ b/app/src/main/res/menu/main_menu_context.xml
@@ -20,5 +20,10 @@
android:title="@string/select_all"
app:showAsAction="never" />
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 429a423b..63da7497 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -204,4 +204,8 @@
Get Cookies
Delete All Cookies
Delete all the cookies, permanently
+ Download over Metered Networks
+ Let the download worker work on any network type. If disabled, it will wait for an unmetered network to start the downloads
+ Download worker will start after you are connected to an unmetered network
+ Invert Selected
\ No newline at end of file
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index cb6e080b..ce5ad3ba 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -65,6 +65,14 @@
app:summary="@string/download_card_summary"
app:title="@string/show_download_card" />
+
+