1.7.3.1
This commit is contained in:
parent
a5bd545f52
commit
b43bf26131
42 changed files with 615 additions and 407 deletions
|
|
@ -11,12 +11,12 @@ def properties = new Properties()
|
||||||
def versionMajor = 1
|
def versionMajor = 1
|
||||||
def versionMinor = 7
|
def versionMinor = 7
|
||||||
def versionPatch = 3
|
def versionPatch = 3
|
||||||
def versionBuild = 0 // bump for dogfood builds, public betas, etc.
|
def versionBuild = 1 // bump for dogfood builds, public betas, etc.
|
||||||
def versionExt = ""
|
def versionExt = ".${versionBuild}"
|
||||||
|
|
||||||
if (versionBuild > 0){
|
//if (versionBuild > 0){
|
||||||
versionExt = ".${versionBuild}-beta"
|
// versionExt = ".${versionBuild}-beta"
|
||||||
}
|
//}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.deniscerri.ytdlnis'
|
namespace 'com.deniscerri.ytdlnis'
|
||||||
|
|
@ -117,6 +117,13 @@ android {
|
||||||
composeOptions {
|
composeOptions {
|
||||||
kotlinCompilerExtensionVersion composeVer
|
kotlinCompilerExtensionVersion composeVer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependenciesInfo {
|
||||||
|
// Disables dependency metadata when building APKs.
|
||||||
|
includeInApk = false
|
||||||
|
// Disables dependency metadata when building Android App Bundles.
|
||||||
|
includeInBundle = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,9 @@ interface DownloadDao {
|
||||||
@Query("Select url from downloads where status in (:status)")
|
@Query("Select url from downloads where status in (:status)")
|
||||||
fun getURLsByStatus(status: List<String>) : List<String>
|
fun getURLsByStatus(status: List<String>) : List<String>
|
||||||
|
|
||||||
|
@Query("Select url from downloads where id in (:ids)")
|
||||||
|
fun getURLsByID(ids: List<Long>) : List<String>
|
||||||
|
|
||||||
@Query("UPDATE downloads SET downloadStartTime=0 where id in (:list)")
|
@Query("UPDATE downloads SET downloadStartTime=0 where id in (:list)")
|
||||||
suspend fun resetScheduleTimeForItems(list: List<Long>)
|
suspend fun resetScheduleTimeForItems(list: List<Long>)
|
||||||
|
|
||||||
|
|
@ -243,4 +246,8 @@ interface DownloadDao {
|
||||||
|
|
||||||
@Query("Update downloads set id=:newId where id=:id")
|
@Query("Update downloads set id=:newId where id=:id")
|
||||||
suspend fun updateDownloadID(id: Long, newId: Long)
|
suspend fun updateDownloadID(id: Long, newId: Long)
|
||||||
|
|
||||||
|
|
||||||
|
@Query("SELECT id from downloads WHERE id > :item1 AND id < :item2 AND status in (:statuses) ORDER BY id DESC")
|
||||||
|
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long>
|
||||||
}
|
}
|
||||||
|
|
@ -21,6 +21,7 @@ import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
import com.deniscerri.ytdlnis.database.dao.DownloadDao
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.work.DownloadWorker
|
import com.deniscerri.ytdlnis.work.DownloadWorker
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -63,10 +64,6 @@ class DownloadRepository(private val downloadDao: DownloadDao) {
|
||||||
Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved, Processing
|
Active, ActivePaused, PausedReQueued, Queued, QueuedPaused, Error, Cancelled, Saved, Processing
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Status>.toListString() : List<String>{
|
|
||||||
return this.map { it.toString() }
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun insert(item: DownloadItem) : Long {
|
suspend fun insert(item: DownloadItem) : Long {
|
||||||
return downloadDao.insert(item)
|
return downloadDao.insert(item)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.deniscerri.ytdlnis.database.dao.ObserveSourcesDao
|
||||||
import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
|
import com.deniscerri.ytdlnis.database.models.ObserveSourcesItem
|
||||||
import com.deniscerri.ytdlnis.receiver.ObserveAlarmReceiver
|
import com.deniscerri.ytdlnis.receiver.ObserveAlarmReceiver
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao) {
|
class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao) {
|
||||||
val items : Flow<List<ObserveSourcesItem>> = observeSourcesDao.getAllSourcesFlow()
|
val items : Flow<List<ObserveSourcesItem>> = observeSourcesDao.getAllSourcesFlow()
|
||||||
|
|
@ -74,4 +75,47 @@ class ObserveSourcesRepository(private val observeSourcesDao: ObserveSourcesDao)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateNextTime(item: ObserveSourcesItem) : Long {
|
||||||
|
val c = Calendar.getInstance()
|
||||||
|
|
||||||
|
val hourMin = Calendar.getInstance()
|
||||||
|
hourMin.timeInMillis = item.everyTime
|
||||||
|
|
||||||
|
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||||
|
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||||
|
|
||||||
|
if (item.everyNr == 0) item.everyNr = 1
|
||||||
|
|
||||||
|
when(item.everyCategory){
|
||||||
|
EveryCategory.DAY -> {
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, item.everyNr)
|
||||||
|
}
|
||||||
|
EveryCategory.WEEK -> {
|
||||||
|
if(item.everyWeekDay.isEmpty()){
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||||
|
}else{
|
||||||
|
val weekDayNr = c.get(Calendar.DAY_OF_WEEK)
|
||||||
|
val followingWeekDay = item.everyWeekDay.firstOrNull { it.toInt() > weekDayNr }
|
||||||
|
if (followingWeekDay == null){
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, item.everyWeekDay.minBy { it.toInt() }.toInt() + (7 - weekDayNr))
|
||||||
|
item.everyNr--
|
||||||
|
}else{
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, followingWeekDay.toInt() - weekDayNr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.everyNr > 1){
|
||||||
|
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EveryCategory.MONTH -> {
|
||||||
|
c.add(Calendar.MONTH, item.everyNr)
|
||||||
|
c.set(Calendar.DAY_OF_MONTH, item.everyMonthDay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return c.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ class CookieViewModel(private val application: Application) : AndroidViewModel(a
|
||||||
private val repository: CookieRepository
|
private val repository: CookieRepository
|
||||||
val items: LiveData<List<CookieItem>>
|
val items: LiveData<List<CookieItem>>
|
||||||
|
|
||||||
private val cookieHeader =
|
val cookieHeader =
|
||||||
"# Netscape HTTP Cookie File\n" +
|
"# Netscape HTTP Cookie File\n" +
|
||||||
"# WebView Generated by the YTDLnis app\n" +
|
"# WebView Generated by the YTDLnis app\n" +
|
||||||
"# This is a generated file! Do not edit."
|
"# This is a generated file! Do not edit."
|
||||||
|
|
|
||||||
|
|
@ -940,5 +940,13 @@ class DownloadViewModel(private val application: Application) : AndroidViewModel
|
||||||
return dao.getURLsByStatus(list.map { it.toString() })
|
return dao.getURLsByStatus(list.map { it.toString() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getURLsByIds(list: List<Long>) : List<String> {
|
||||||
|
return dao.getURLsByID(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIDsBetweenTwoItems(item1: Long, item2: Long, statuses: List<String>) : List<Long> {
|
||||||
|
return dao.getIDsBetweenTwoItems(item1, item2, statuses)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -75,68 +75,59 @@ class ObserveSourcesViewModel(private val application: Application) : AndroidVie
|
||||||
repository.update(item)
|
repository.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancelObservationTaskByID(id : Long){
|
||||||
|
repository.cancelObservationTaskByID(application, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun observeTask(it: ObserveSourcesItem){
|
private fun observeTask(it: ObserveSourcesItem){
|
||||||
|
cancelObservationTaskByID(it.id)
|
||||||
|
|
||||||
val id = it.id
|
val id = it.id
|
||||||
val c = Calendar.getInstance()
|
val c = Calendar.getInstance()
|
||||||
val date = Calendar.getInstance()
|
|
||||||
date.timeInMillis = it.startsTime
|
val date = Calendar.getInstance()
|
||||||
val hourMin = Calendar.getInstance()
|
date.timeInMillis = it.startsTime
|
||||||
hourMin.timeInMillis = it.everyTime
|
val hourMin = Calendar.getInstance()
|
||||||
|
hourMin.timeInMillis = it.everyTime
|
||||||
c.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH))
|
c.set(Calendar.DAY_OF_MONTH, date.get(Calendar.DAY_OF_MONTH))
|
||||||
c.set(Calendar.MONTH, date.get(Calendar.MONTH))
|
c.set(Calendar.MONTH, date.get(Calendar.MONTH))
|
||||||
c.set(Calendar.YEAR, date.get(Calendar.YEAR))
|
c.set(Calendar.YEAR, date.get(Calendar.YEAR))
|
||||||
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
||||||
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
||||||
|
|
||||||
|
|
||||||
repository.cancelObservationTaskByID(application, id)
|
|
||||||
|
|
||||||
val intent = Intent(application, ObserveAlarmReceiver::class.java)
|
val intent = Intent(application, ObserveAlarmReceiver::class.java)
|
||||||
intent.putExtra("id", id)
|
intent.putExtra("id", id)
|
||||||
if (it.everyNr == 0) it.everyNr = 1
|
|
||||||
|
|
||||||
when(it.everyCategory){
|
when(it.everyCategory){
|
||||||
ObserveSourcesRepository.EveryCategory.DAY -> {
|
ObserveSourcesRepository.EveryCategory.DAY -> {}
|
||||||
alarmManager.setExact(
|
|
||||||
AlarmManager.RTC,
|
|
||||||
c.timeInMillis,
|
|
||||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
||||||
if (it.everyWeekDay.isNotEmpty()){
|
val weekDayNr = c.get(Calendar.DAY_OF_WEEK)
|
||||||
val weekDayID = c.get(Calendar.DAY_OF_WEEK).toString()
|
val followingWeekDay = it.everyWeekDay.firstOrNull { it.toInt() >= weekDayNr }
|
||||||
val followingWeekDay = (it.everyWeekDay.firstOrNull { it.toInt() > weekDayID.toInt() } ?: it.everyWeekDay.minBy { it.toInt() }).toInt()
|
if (followingWeekDay == null){
|
||||||
c.set(Calendar.DAY_OF_WEEK, followingWeekDay)
|
c.add(Calendar.DAY_OF_MONTH, it.everyWeekDay.minBy { it.toInt() }.toInt() + (7 - weekDayNr))
|
||||||
if(c.timeInMillis < System.currentTimeMillis()){
|
}else{
|
||||||
c.add(Calendar.DAY_OF_MONTH, 7)
|
c.add(Calendar.DAY_OF_MONTH, followingWeekDay.toInt() - weekDayNr)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
alarmManager.setExact(
|
|
||||||
AlarmManager.RTC,
|
|
||||||
c.timeInMillis,
|
|
||||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
||||||
val theMonthIndex = Month.values().indexOf(it.startsMonth)
|
val theMonthIndex = Month.values().indexOf(it.startsMonth)
|
||||||
val currentMonthIndex = c.get(Calendar.MONTH)
|
val currentMonthIndex = c.get(Calendar.MONTH)
|
||||||
if (theMonthIndex != currentMonthIndex){
|
if (theMonthIndex != currentMonthIndex){
|
||||||
c.set(Calendar.MONTH, theMonthIndex)
|
c.set(Calendar.MONTH, theMonthIndex)
|
||||||
if (c.timeInMillis < System.currentTimeMillis()){
|
if (c.timeInMillis < Calendar.getInstance().timeInMillis){
|
||||||
c.add(Calendar.YEAR, 1)
|
c.add(Calendar.YEAR, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
alarmManager.setExact(
|
|
||||||
AlarmManager.RTC,
|
|
||||||
c.timeInMillis,
|
|
||||||
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alarmManager.setExact(
|
||||||
|
AlarmManager.RTC,
|
||||||
|
c.timeInMillis,
|
||||||
|
PendingIntent.getBroadcast(application, it.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class ObserveAlarmReceiver : BroadcastReceiver() {
|
||||||
.setInputData(Data.Builder().putLong("id", sourceID).build())
|
.setInputData(Data.Builder().putLong("id", sourceID).build())
|
||||||
|
|
||||||
WorkManager.getInstance(p0!!).enqueueUniqueWork(
|
WorkManager.getInstance(p0!!).enqueueUniqueWork(
|
||||||
sourceID.toString(),
|
"OBSERVE$sourceID",
|
||||||
ExistingWorkPolicy.REPLACE,
|
ExistingWorkPolicy.REPLACE,
|
||||||
workRequest.build()
|
workRequest.build()
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -500,7 +500,9 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
|
|
||||||
val url = checkClipboard()
|
val url = checkClipboard()
|
||||||
url?.apply {
|
url?.apply {
|
||||||
combinedList.add(0, SearchSuggestionItem(this.joinToString("\n"), SearchSuggestionType.CLIPBOARD))
|
if (this.isNotEmpty()){
|
||||||
|
combinedList.add(0, SearchSuggestionItem(this.joinToString("\n"), SearchSuggestionType.CLIPBOARD))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -845,7 +847,7 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
return kotlin.runCatching {
|
return kotlin.runCatching {
|
||||||
val clipboard = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = requireContext().getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clip = clipboard.primaryClip!!.getItemAt(0).text
|
val clip = clipboard.primaryClip!!.getItemAt(0).text
|
||||||
return clip.split("\r\n").filter { Patterns.WEB_URL.matcher(it).matches() }
|
return clip.split("\r","\n").map { it.trim() }.filter { Patterns.WEB_URL.matcher(it).matches() }
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -873,27 +875,36 @@ class HomeFragment : Fragment(), HomeAdapter.OnItemClickListener, SearchSuggesti
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSearchSuggestionAdd(text: String) {
|
override fun onSearchSuggestionAdd(t: String) {
|
||||||
val present = queriesChipGroup!!.children.firstOrNull { (it as Chip).text.toString() == text }
|
val items = t.split("\n")
|
||||||
if (present == null) {
|
|
||||||
val chip = layoutinflater!!.inflate(R.layout.input_chip, queriesChipGroup, false) as Chip
|
items.forEach {text ->
|
||||||
chip.text = text
|
val present = queriesChipGroup!!.children.firstOrNull { (it as Chip).text.toString() == text }
|
||||||
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
if (present == null) {
|
||||||
chip.setOnClickListener {
|
val chip = layoutinflater!!.inflate(R.layout.input_chip, queriesChipGroup, false) as Chip
|
||||||
if (queriesChipGroup!!.childCount == 1) queriesConstraint!!.visibility = View.GONE
|
chip.text = text
|
||||||
queriesChipGroup!!.removeView(chip)
|
chip.chipBackgroundColor = ColorStateList.valueOf(MaterialColors.getColor(requireContext(), R.attr.colorSecondaryContainer, Color.BLACK))
|
||||||
|
chip.setOnClickListener {
|
||||||
|
if (queriesChipGroup!!.childCount == 1) queriesConstraint!!.visibility = View.GONE
|
||||||
|
queriesChipGroup!!.removeView(chip)
|
||||||
|
}
|
||||||
|
queriesChipGroup!!.addView(chip)
|
||||||
}
|
}
|
||||||
queriesChipGroup!!.addView(chip)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
searchView!!.editText.setText("")
|
||||||
if (queriesChipGroup!!.childCount == 0) queriesConstraint!!.visibility = View.GONE
|
if (queriesChipGroup!!.childCount == 0) queriesConstraint!!.visibility = View.GONE
|
||||||
else queriesConstraint!!.visibility = View.VISIBLE
|
else queriesConstraint!!.visibility = View.VISIBLE
|
||||||
searchView!!.editText.setText("")
|
|
||||||
val clipBoardItem = searchSuggestionsRecyclerView?.layoutManager?.findViewByPosition(0)
|
val clipBoardItem = searchSuggestionsRecyclerView?.layoutManager?.findViewByPosition(0)
|
||||||
clipBoardItem?.apply {
|
clipBoardItem?.apply {
|
||||||
if ((this as ConstraintLayout).findViewById<TextView>(R.id.suggestion_text).text == getString(R.string.link_you_copied)){
|
if ((this as ConstraintLayout).findViewById<TextView>(R.id.suggestion_text).text == getString(R.string.link_you_copied)){
|
||||||
searchSuggestionsAdapter?.notifyItemRemoved(0)
|
searchSuggestionsAdapter?.notifyItemRemoved(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSearchSuggestionLongClick(text: String, position: Int) {
|
override fun onSearchSuggestionLongClick(text: String, position: Int) {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.dp
|
import com.deniscerri.ytdlnis.util.Extensions.dp
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
@ -65,17 +66,8 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||||
val imageURL = item.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROGRESS BAR ----------------------------------------------------
|
// PROGRESS BAR ----------------------------------------------------
|
||||||
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
||||||
|
|
@ -168,7 +160,7 @@ class ActiveDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
val value = animation.animatedValue as Int
|
val value = animation.animatedValue as Int
|
||||||
pauseButton.cornerRadius = value
|
pauseButton.cornerRadius = value
|
||||||
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
|
pauseButton.icon = ContextCompat.getDrawable(activity, R.drawable.exomedia_ic_play_arrow_white)
|
||||||
pauseButton.contentDescription = activity.getString(R.string.start)
|
pauseButton.contentDescription = activity.getString(R.string.resume)
|
||||||
pauseButton.tag = ActiveDownloadAction.Resume
|
pauseButton.tag = ActiveDownloadAction.Resume
|
||||||
pauseButton.isEnabled = true
|
pauseButton.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
|
@ -62,17 +63,8 @@ class ActiveDownloadMinifiedAdapter(onItemClickListener: OnItemClickListener, ac
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||||
val imageURL = item!!.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// PROGRESS BAR ----------------------------------------------------
|
// PROGRESS BAR ----------------------------------------------------
|
||||||
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
val progressBar = card.findViewById<LinearProgressIndicator>(R.id.progress)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
|
@ -63,17 +64,8 @@ class ConfigureMultipleDownloadsAdapter(onItemClickListener: OnItemClickListener
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||||
val imageURL = item.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||||
if (!imageURL.isNullOrBlank()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val duration = card.findViewById<TextView>(R.id.duration)
|
val duration = card.findViewById<TextView>(R.id.duration)
|
||||||
duration.text = item.duration
|
duration.text = item.duration
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
import com.deniscerri.ytdlnis.database.models.DownloadItemSimple
|
||||||
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
|
|
@ -28,12 +29,12 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
) {
|
) {
|
||||||
private val onItemClickListener: OnItemClickListener
|
private val onItemClickListener: OnItemClickListener
|
||||||
private val activity: Activity
|
private val activity: Activity
|
||||||
val checkedItems: ArrayList<Long>
|
val checkedItems: MutableSet<Long>
|
||||||
var inverted: Boolean
|
var inverted: Boolean
|
||||||
private val sharedPreferences: SharedPreferences
|
private val sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
init {
|
init {
|
||||||
checkedItems = ArrayList()
|
checkedItems = mutableSetOf()
|
||||||
this.onItemClickListener = onItemClickListener
|
this.onItemClickListener = onItemClickListener
|
||||||
this.activity = activity
|
this.activity = activity
|
||||||
this.inverted = false
|
this.inverted = false
|
||||||
|
|
@ -65,16 +66,8 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("queue")
|
||||||
val imageURL = item.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val duration = card.findViewById<TextView>(R.id.duration)
|
val duration = card.findViewById<TextView>(R.id.duration)
|
||||||
duration.text = item.duration
|
duration.text = item.duration
|
||||||
|
|
@ -180,6 +173,14 @@ class GenericDownloadAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun checkMultipleItems(list: List<Long>){
|
||||||
|
checkedItems.clear()
|
||||||
|
inverted = false
|
||||||
|
checkedItems.addAll(list)
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
fun invertSelected() {
|
fun invertSelected() {
|
||||||
inverted = !inverted
|
inverted = !inverted
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
import com.deniscerri.ytdlnis.database.models.HistoryItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
|
@ -89,17 +90,8 @@ class HistoryAdapter(onItemClickListener: OnItemClickListener, activity: Activit
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("downloads")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("downloads")
|
||||||
val imageURL = item!!.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
thumbnail.setColorFilter(Color.argb(20, 0, 0, 0))
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TITLE ----------------------------------
|
// TITLE ----------------------------------
|
||||||
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
|
val itemTitle = card.findViewById<TextView>(R.id.downloads_title)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.card.MaterialCardView
|
import com.google.android.material.card.MaterialCardView
|
||||||
|
|
@ -62,16 +63,8 @@ class HomeAdapter(onItemClickListener: OnItemClickListener, activity: Activity)
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||||
val imageURL = video!!.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, video!!.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TITLE ----------------------------------
|
// TITLE ----------------------------------
|
||||||
val videoTitle = card.findViewById<TextView>(R.id.result_title)
|
val videoTitle = card.findViewById<TextView>(R.id.result_title)
|
||||||
|
|
|
||||||
|
|
@ -57,14 +57,12 @@ class ObserveSourcesAdapter(onItemClickListener: OnItemClickListener, activity:
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
val card = holder.cardView
|
val card = holder.cardView
|
||||||
val uiHandler = Handler(Looper.getMainLooper())
|
|
||||||
card.popup()
|
card.popup()
|
||||||
|
|
||||||
if (item == null) return
|
if (item == null) return
|
||||||
card.tag = item.url
|
card.tag = item.url
|
||||||
holder.itemView.tag = item.url
|
holder.itemView.tag = item.url
|
||||||
|
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.result_image_view)
|
|
||||||
|
|
||||||
// TITLE ----------------------------------
|
// TITLE ----------------------------------
|
||||||
val itemTitle = card.findViewById<TextView>(R.id.title)
|
val itemTitle = card.findViewById<TextView>(R.id.title)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.ResultItem
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.loadThumbnail
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.popup
|
import com.deniscerri.ytdlnis.util.Extensions.popup
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -62,17 +63,8 @@ class PlaylistAdapter(onItemClickListener: OnItemClickListener, activity: Activi
|
||||||
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
val thumbnail = card.findViewById<ImageView>(R.id.downloads_image_view)
|
||||||
|
|
||||||
// THUMBNAIL ----------------------------------
|
// THUMBNAIL ----------------------------------
|
||||||
if (!sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")){
|
val hideThumb = sharedPreferences.getStringSet("hide_thumbnails", emptySet())!!.contains("home")
|
||||||
val imageURL = item!!.thumb
|
uiHandler.post { thumbnail.loadThumbnail(hideThumb, item!!.thumb) }
|
||||||
if (imageURL.isNotEmpty()) {
|
|
||||||
uiHandler.post { Picasso.get().load(imageURL).into(thumbnail) }
|
|
||||||
} else {
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
thumbnail.setColorFilter(Color.argb(95, 0, 0, 0))
|
|
||||||
}else{
|
|
||||||
uiHandler.post { Picasso.get().load(R.color.black).into(thumbnail) }
|
|
||||||
}
|
|
||||||
|
|
||||||
card.findViewById<TextView>(R.id.title).text = item!!.title
|
card.findViewById<TextView>(R.id.title).text = item!!.title
|
||||||
card.findViewById<TextView>(R.id.author).text = item.author
|
card.findViewById<TextView>(R.id.author).text = item.author
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
|
|
@ -32,6 +33,7 @@ import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
|
||||||
import com.deniscerri.ytdlnis.R
|
import com.deniscerri.ytdlnis.R
|
||||||
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
import com.deniscerri.ytdlnis.database.models.DownloadItem
|
||||||
import com.deniscerri.ytdlnis.database.models.Format
|
import com.deniscerri.ytdlnis.database.models.Format
|
||||||
|
import com.deniscerri.ytdlnis.database.models.ResultItem
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.CommandTemplateViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
import com.deniscerri.ytdlnis.database.viewmodel.HistoryViewModel
|
||||||
|
|
@ -73,6 +75,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
private lateinit var count : TextView
|
private lateinit var count : TextView
|
||||||
private lateinit var sharedPreferences: SharedPreferences
|
private lateinit var sharedPreferences: SharedPreferences
|
||||||
|
|
||||||
|
private lateinit var currentDownloadIDs: List<Long>
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||||
|
|
@ -81,6 +85,8 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java]
|
commandTemplateViewModel = ViewModelProvider(requireActivity())[CommandTemplateViewModel::class.java]
|
||||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
infoUtil = InfoUtil(requireContext())
|
infoUtil = InfoUtil(requireContext())
|
||||||
|
|
||||||
|
currentDownloadIDs = arguments?.getLongArray("currentDownloadIDs")?.toList() ?: listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
@SuppressLint("RestrictedApi", "NotifyDataSetChanged")
|
||||||
|
|
@ -137,6 +143,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
downloadViewModel.downloadProcessingDownloads(cal.timeInMillis)
|
downloadViewModel.downloadProcessingDownloads(cal.timeInMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +160,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
download.isEnabled = false
|
download.isEnabled = false
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
downloadViewModel.downloadProcessingDownloads()
|
downloadViewModel.downloadProcessingDownloads()
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
@ -166,6 +174,7 @@ class DownloadMultipleBottomSheetDialog : BottomSheetDialogFragment(), Configure
|
||||||
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
dd.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch{
|
lifecycleScope.launch{
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.deleteAllWithID(currentDownloadIDs)
|
||||||
downloadViewModel.moveProcessingToSavedCategory()
|
downloadViewModel.moveProcessingToSavedCategory()
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
private lateinit var endsAfterNr: TextInputLayout
|
private lateinit var endsAfterNr: TextInputLayout
|
||||||
private lateinit var retryMissingDownloads: MaterialSwitch
|
private lateinit var retryMissingDownloads: MaterialSwitch
|
||||||
private lateinit var getOnlyNewUploads: MaterialSwitch
|
private lateinit var getOnlyNewUploads: MaterialSwitch
|
||||||
|
private lateinit var resetProcessedLinks: MaterialSwitch
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
@ -252,6 +253,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
endsAfterNr = view.findViewById(R.id.after_nr)
|
endsAfterNr = view.findViewById(R.id.after_nr)
|
||||||
retryMissingDownloads = view.findViewById(R.id.retry_missing_downloads)
|
retryMissingDownloads = view.findViewById(R.id.retry_missing_downloads)
|
||||||
getOnlyNewUploads = view.findViewById(R.id.get_new_uploads)
|
getOnlyNewUploads = view.findViewById(R.id.get_new_uploads)
|
||||||
|
resetProcessedLinks = view.findViewById(R.id.reset_processed_links)
|
||||||
okButton = view.findViewById(R.id.okButton)
|
okButton = view.findViewById(R.id.okButton)
|
||||||
|
|
||||||
title.editText!!.setText(currentItem?.name ?: "")
|
title.editText!!.setText(currentItem?.name ?: "")
|
||||||
|
|
@ -446,6 +448,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
|
|
||||||
retryMissingDownloads.isChecked = currentItem?.retryMissingDownloads ?: false
|
retryMissingDownloads.isChecked = currentItem?.retryMissingDownloads ?: false
|
||||||
getOnlyNewUploads.isChecked = currentItem?.getOnlyNewUploads ?: false
|
getOnlyNewUploads.isChecked = currentItem?.getOnlyNewUploads ?: false
|
||||||
|
resetProcessedLinks.isVisible = currentItem != null
|
||||||
|
|
||||||
if (currentItem != null) okButton.text = getString(R.string.update)
|
if (currentItem != null) okButton.text = getString(R.string.update)
|
||||||
okButton.setOnClickListener {
|
okButton.setOnClickListener {
|
||||||
|
|
@ -481,7 +484,7 @@ class ObserveSourcesBottomSheetDialog : BottomSheetDialogFragment() {
|
||||||
0,
|
0,
|
||||||
getOnlyNewUploads.isChecked,
|
getOnlyNewUploads.isChecked,
|
||||||
retryMissingDownloads.isChecked,
|
retryMissingDownloads.isChecked,
|
||||||
mutableListOf()
|
if (resetProcessedLinks.isChecked) mutableListOf() else currentItem?.alreadyProcessedLinks ?: mutableListOf()
|
||||||
)
|
)
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
observeSourcesViewModel.insert(observeItem)
|
observeSourcesViewModel.insert(observeItem)
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme)
|
setStyle(STYLE_NORMAL, R.style.FullScreenDialogTheme)
|
||||||
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
downloadViewModel = ViewModelProvider(requireActivity())[DownloadViewModel::class.java]
|
||||||
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
resultViewModel = ViewModelProvider(requireActivity())[ResultViewModel::class.java]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
@ -173,6 +173,7 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
ok = toolbar.menu.getItem(0)
|
ok = toolbar.menu.getItem(0)
|
||||||
ok.isEnabled = false
|
ok.isEnabled = false
|
||||||
ok.setOnMenuItemClickListener {
|
ok.setOnMenuItemClickListener {
|
||||||
|
ok.isEnabled = false
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
val checkedItems = listAdapter.getCheckedItems()
|
val checkedItems = listAdapter.getCheckedItems()
|
||||||
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
val checkedResultItems = items.filter { item -> checkedItems.contains(item!!.url) }
|
||||||
|
|
@ -196,11 +197,11 @@ class SelectPlaylistItemsDialog : DialogFragment(), PlaylistAdapter.OnItemClickL
|
||||||
downloadItems.add(i)
|
downloadItems.add(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
downloadViewModel.insertToProcessing(downloadItems)
|
||||||
downloadViewModel.insertToProcessing(downloadItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
withContext(Dispatchers.Main){
|
||||||
|
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
|
import androidx.core.view.children
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
@ -33,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
|
@ -152,6 +154,22 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||||
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
|
actionMode?.apply {
|
||||||
|
if (selectedObjects == 0){
|
||||||
|
this.finish()
|
||||||
|
}else{
|
||||||
|
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
if (selectedObjects == 2){
|
||||||
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||||
|
}
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
if (actionMode == null){
|
if (actionMode == null){
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
|
|
@ -200,21 +218,28 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||||
|
}.toMutableList()
|
||||||
|
idsInMiddle.addAll(selectedIDs)
|
||||||
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
adapter.checkMultipleItems(idsInMiddle)
|
||||||
|
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Cancelled))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
downloadViewModel.deleteAllWithID(selectedObjects)
|
downloadViewModel.deleteAllWithID(selectedObjects)
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
|
|
@ -225,21 +250,15 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
}
|
}
|
||||||
R.id.redownload -> {
|
R.id.redownload -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Cancelled))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferences.getBoolean("download_card", true)){
|
if (preferences.getBoolean("download_card", true)){
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
val bundle = Bundle()
|
||||||
|
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||||
|
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
@ -266,6 +285,17 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
if (selectedObjects == 0) actionMode?.finish()
|
if (selectedObjects == 0) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.copy_urls -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
val urls = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getURLsByIds(selectedObjects)
|
||||||
|
}
|
||||||
|
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,6 +304,17 @@ class CancelledDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClic
|
||||||
actionMode = null
|
actionMode = null
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSelectedIDs() : List<Long>{
|
||||||
|
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||||
|
DownloadRepository.Status.Cancelled))
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
adapter.checkedItems.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
|
|
|
||||||
|
|
@ -123,40 +123,36 @@ class DownloadQueueMainFragment : Fragment(){
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.activeDownloadsCount.collectLatest {
|
downloadViewModel.activeDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(0)?.apply {
|
tabLayout.getTabAt(0)?.apply {
|
||||||
if (it == 0) removeBadge()
|
createBadge(it)
|
||||||
else createBadge(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.queuedDownloadsCount.collectLatest {
|
downloadViewModel.queuedDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(1)?.apply {
|
tabLayout.getTabAt(1)?.apply {
|
||||||
if (it == 0) removeBadge()
|
createBadge(it)
|
||||||
else createBadge(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.cancelledDownloadsCount.collectLatest {
|
downloadViewModel.cancelledDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(2)?.apply {
|
tabLayout.getTabAt(2)?.apply {
|
||||||
if (it == 0) removeBadge()
|
createBadge(it)
|
||||||
else createBadge(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.erroredDownloadsCount.collectLatest {
|
downloadViewModel.erroredDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(3)?.apply {
|
tabLayout.getTabAt(3)?.apply {
|
||||||
if (it == 0) removeBadge()
|
createBadge(it)
|
||||||
else createBadge(it)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
downloadViewModel.savedDownloadsCount.collectLatest {
|
downloadViewModel.savedDownloadsCount.collectLatest {
|
||||||
tabLayout.getTabAt(4)?.apply {
|
tabLayout.getTabAt(4)?.apply {
|
||||||
if (it == 0) removeBadge()
|
removeBadge()
|
||||||
else createBadge(it)
|
if (it > 0) createBadge(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
|
@ -154,18 +155,19 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||||
if (isChecked) {
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
if (actionMode == null){
|
actionMode?.apply {
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
|
||||||
|
|
||||||
}else{
|
|
||||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
if (selectedObjects == 0){
|
if (selectedObjects == 0){
|
||||||
actionMode?.finish()
|
this.finish()
|
||||||
|
}else{
|
||||||
|
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
if(selectedObjects == 2){
|
||||||
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Error).toListString())
|
||||||
|
}
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -207,20 +209,28 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||||
|
}.toMutableList()
|
||||||
|
idsInMiddle.addAll(selectedIDs)
|
||||||
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
adapter.checkMultipleItems(idsInMiddle)
|
||||||
|
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Error))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
downloadViewModel.deleteAllWithID(selectedObjects)
|
downloadViewModel.deleteAllWithID(selectedObjects)
|
||||||
actionMode?.finish()
|
actionMode?.finish()
|
||||||
|
|
@ -231,21 +241,16 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
}
|
}
|
||||||
R.id.redownload -> {
|
R.id.redownload -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Error))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferences.getBoolean("download_card", true)){
|
if (preferences.getBoolean("download_card", true)){
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
val bundle = Bundle()
|
||||||
|
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||||
|
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
@ -270,6 +275,17 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
if (selectedObjects == 0) actionMode?.finish()
|
if (selectedObjects == 0) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.copy_urls -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
val urls = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getURLsByIds(selectedObjects)
|
||||||
|
}
|
||||||
|
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +294,17 @@ class ErroredDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickL
|
||||||
actionMode = null
|
actionMode = null
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSelectedIDs() : List<Long>{
|
||||||
|
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||||
|
DownloadRepository.Status.Error))
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
adapter.checkedItems.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
private var simpleCallback: ItemTouchHelper.SimpleCallback =
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.FileUtil
|
import com.deniscerri.ytdlnis.util.FileUtil
|
||||||
import com.deniscerri.ytdlnis.util.NotificationUtil
|
import com.deniscerri.ytdlnis.util.NotificationUtil
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
|
|
@ -180,27 +181,26 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||||
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (isChecked) {
|
actionMode?.apply {
|
||||||
if (actionMode == null){
|
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
|
||||||
}else{
|
|
||||||
|
|
||||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
if (selectedObjects == 0){
|
if (selectedObjects == 0){
|
||||||
actionMode?.finish()
|
this.finish()
|
||||||
}
|
}else{
|
||||||
}
|
this.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
if (actionMode != null){
|
this.menu.findItem(R.id.download).isVisible = withContext(Dispatchers.IO){
|
||||||
actionMode!!.menu.getItem(2).isVisible = withContext(Dispatchers.IO){
|
downloadViewModel.checkAllQueuedItemsAreScheduledAfterNow(adapter.checkedItems.toList(), adapter.inverted, now)
|
||||||
downloadViewModel.checkAllQueuedItemsAreScheduledAfterNow(adapter.checkedItems, adapter.inverted, now)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
actionMode!!.menu.getItem(1).isVisible = position > 0
|
this.menu.findItem(R.id.up).isVisible = position > 0
|
||||||
|
if(selectedObjects == 2){
|
||||||
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused).toListString())
|
||||||
|
}
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -250,20 +250,28 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||||
|
}.toMutableList()
|
||||||
|
idsInMiddle.addAll(selectedIDs)
|
||||||
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
adapter.checkMultipleItems(idsInMiddle)
|
||||||
|
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
for (id in selectedObjects){
|
for (id in selectedObjects){
|
||||||
YoutubeDL.getInstance().destroyProcessById(id.toInt().toString())
|
YoutubeDL.getInstance().destroyProcessById(id.toInt().toString())
|
||||||
|
|
@ -280,14 +288,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
}
|
}
|
||||||
R.id.download -> {
|
R.id.download -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
for (id in selectedObjects){
|
for (id in selectedObjects){
|
||||||
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString())
|
WorkManager.getInstance(requireContext()).cancelAllWorkByTag(id.toInt().toString())
|
||||||
|
|
@ -313,15 +314,7 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
}
|
}
|
||||||
R.id.up -> {
|
R.id.up -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.putAtTopOfQueue(selectedObjects)
|
downloadViewModel.putAtTopOfQueue(selectedObjects)
|
||||||
|
|
@ -330,6 +323,17 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.copy_urls -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
val urls = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getURLsByIds(selectedObjects)
|
||||||
|
}
|
||||||
|
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -338,6 +342,17 @@ class QueuedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLi
|
||||||
actionMode = null
|
actionMode = null
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSelectedIDs() : List<Long>{
|
||||||
|
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||||
|
DownloadRepository.Status.Queued, DownloadRepository.Status.QueuedPaused))
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
adapter.checkedItems.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
||||||
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
import com.deniscerri.ytdlnis.ui.adapter.GenericDownloadAdapter
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
import com.deniscerri.ytdlnis.util.Extensions.enableFastScroll
|
||||||
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
import com.deniscerri.ytdlnis.util.Extensions.forceFastScrollMode
|
||||||
|
import com.deniscerri.ytdlnis.util.Extensions.toListString
|
||||||
import com.deniscerri.ytdlnis.util.UiUtil
|
import com.deniscerri.ytdlnis.util.UiUtil
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
|
|
@ -152,18 +153,19 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
override fun onCardSelect(isChecked: Boolean, position: Int) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
val selectedObjects = adapter.getSelectedObjectsCount(totalSize)
|
||||||
if (isChecked) {
|
if (actionMode == null) actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
||||||
if (actionMode == null){
|
actionMode?.apply {
|
||||||
actionMode = (getActivity() as AppCompatActivity?)!!.startSupportActionMode(contextualActionBar)
|
|
||||||
|
|
||||||
}else{
|
|
||||||
actionMode!!.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
|
||||||
if (selectedObjects == 0){
|
if (selectedObjects == 0){
|
||||||
actionMode?.finish()
|
this.finish()
|
||||||
|
}else{
|
||||||
|
actionMode?.title = "$selectedObjects ${getString(R.string.selected)}"
|
||||||
|
if(selectedObjects == 2){
|
||||||
|
val selectedIDs = contextualActionBar.getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Saved).toListString())
|
||||||
|
}
|
||||||
|
this.menu.findItem(R.id.select_between).isVisible = idsInMiddle.isNotEmpty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -201,20 +203,28 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
item: MenuItem?
|
item: MenuItem?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return when (item!!.itemId) {
|
return when (item!!.itemId) {
|
||||||
|
R.id.select_between -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedIDs = getSelectedIDs().sortedBy { it }
|
||||||
|
val idsInMiddle = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getIDsBetweenTwoItems(selectedIDs.first(), selectedIDs.last(), listOf(DownloadRepository.Status.Cancelled).toListString())
|
||||||
|
}.toMutableList()
|
||||||
|
idsInMiddle.addAll(selectedIDs)
|
||||||
|
if (idsInMiddle.isNotEmpty()){
|
||||||
|
adapter.checkMultipleItems(idsInMiddle)
|
||||||
|
actionMode?.title = "${idsInMiddle.count()} ${getString(R.string.selected)}"
|
||||||
|
}
|
||||||
|
mode?.menu?.findItem(R.id.select_between)?.isVisible = false
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
R.id.delete_results -> {
|
R.id.delete_results -> {
|
||||||
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
val deleteDialog = MaterialAlertDialogBuilder(requireContext())
|
||||||
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
deleteDialog.setTitle(getString(R.string.you_are_going_to_delete_multiple_items))
|
||||||
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
deleteDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
||||||
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
deleteDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Saved))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
for (id in selectedObjects){
|
for (id in selectedObjects){
|
||||||
downloadViewModel.deleteDownload(id)
|
downloadViewModel.deleteDownload(id)
|
||||||
|
|
@ -227,21 +237,16 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
}
|
}
|
||||||
R.id.download -> {
|
R.id.download -> {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val selectedObjects = if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
val selectedObjects = getSelectedIDs()
|
||||||
withContext(Dispatchers.IO){
|
|
||||||
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems, listOf(
|
|
||||||
DownloadRepository.Status.Saved))
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
adapter.checkedItems.toList()
|
|
||||||
}
|
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
if (preferences.getBoolean("download_card", true)){
|
if (preferences.getBoolean("download_card", true)){
|
||||||
withContext(Dispatchers.IO){
|
withContext(Dispatchers.IO){
|
||||||
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
downloadViewModel.addDownloadsToProcessing(selectedObjects)
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.Main){
|
withContext(Dispatchers.Main){
|
||||||
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2)
|
val bundle = Bundle()
|
||||||
|
bundle.putLongArray("currentDownloadIDs", selectedObjects.toLongArray())
|
||||||
|
findNavController().navigate(R.id.downloadMultipleBottomSheetDialog2,bundle)
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
@ -265,6 +270,17 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
if (selectedObjects == 0) actionMode?.finish()
|
if (selectedObjects == 0) actionMode?.finish()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
R.id.copy_urls -> {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
val selectedObjects = getSelectedIDs()
|
||||||
|
val urls = withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getURLsByIds(selectedObjects)
|
||||||
|
}
|
||||||
|
UiUtil.copyToClipboard(urls.joinToString("\n"), requireActivity())
|
||||||
|
actionMode?.finish()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +289,17 @@ class SavedDownloadsFragment : Fragment(), GenericDownloadAdapter.OnItemClickLis
|
||||||
actionMode = null
|
actionMode = null
|
||||||
adapter.clearCheckedItems()
|
adapter.clearCheckedItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSelectedIDs() : List<Long>{
|
||||||
|
return if (adapter.inverted || adapter.checkedItems.isEmpty()){
|
||||||
|
withContext(Dispatchers.IO){
|
||||||
|
downloadViewModel.getItemIDsNotPresentIn(adapter.checkedItems.toList(), listOf(
|
||||||
|
DownloadRepository.Status.Saved))
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
adapter.checkedItems.toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ class CookiesFragment : Fragment(), CookieAdapter.OnItemClickListener {
|
||||||
builder.setNeutralButton(
|
builder.setNeutralButton(
|
||||||
getString(android.R.string.copy)
|
getString(android.R.string.copy)
|
||||||
) { dialog: DialogInterface?, which: Int ->
|
) { dialog: DialogInterface?, which: Int ->
|
||||||
UiUtil.copyToClipboard(item.content, requireActivity())
|
UiUtil.copyToClipboard(cookiesViewModel.cookieHeader + "\n" + item.content, requireActivity())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ class ObserveSourcesFragment : Fragment(), ObserveSourcesAdapter.OnItemClickList
|
||||||
}
|
}
|
||||||
override fun onItemSearch(item: ObserveSourcesItem) {
|
override fun onItemSearch(item: ObserveSourcesItem) {
|
||||||
runCatching {
|
runCatching {
|
||||||
|
observeSourcesViewModel.cancelObservationTaskByID(item.id)
|
||||||
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
||||||
intent.putExtra("id", item.id)
|
intent.putExtra("id", item.id)
|
||||||
requireActivity().sendBroadcast(intent)
|
requireActivity().sendBroadcast(intent)
|
||||||
|
|
|
||||||
|
|
@ -20,18 +20,22 @@ import android.view.ViewGroup
|
||||||
import android.view.ViewOutlineProvider
|
import android.view.ViewOutlineProvider
|
||||||
import android.view.animation.Interpolator
|
import android.view.animation.Interpolator
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.annotation.Px
|
import androidx.annotation.Px
|
||||||
import androidx.core.view.updateLayoutParams
|
import androidx.core.view.updateLayoutParams
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.withStarted
|
import androidx.lifecycle.withStarted
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.deniscerri.ytdlnis.R
|
||||||
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
import com.neo.highlight.core.Highlight
|
import com.neo.highlight.core.Highlight
|
||||||
import com.neo.highlight.util.listener.HighlightTextWatcher
|
import com.neo.highlight.util.listener.HighlightTextWatcher
|
||||||
import com.neo.highlight.util.scheme.ColorScheme
|
import com.neo.highlight.util.scheme.ColorScheme
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
import me.zhanghai.android.fastscroll.FastScrollerBuilder
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
@ -215,10 +219,16 @@ object Extensions {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TabLayout.Tab.createBadge(nr: Int){
|
fun TabLayout.Tab.createBadge(nr: Int){
|
||||||
this.orCreateBadge.apply {
|
removeBadge()
|
||||||
number = nr
|
if (nr > 0) {
|
||||||
verticalOffset = 5
|
orCreateBadge.apply {
|
||||||
horizontalOffset = 10
|
number = nr
|
||||||
|
verticalOffset = 3
|
||||||
|
horizontalOffset =
|
||||||
|
if (nr < 10) 7
|
||||||
|
else if (nr < 100) 10
|
||||||
|
else 20
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +243,26 @@ object Extensions {
|
||||||
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
return lines.dropLastWhile { it.contains("[download") }.joinToString("\n") + "\n${newLine}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ImageView.loadThumbnail(hideThumb: Boolean, imageURL: String){
|
||||||
|
if(!hideThumb){
|
||||||
|
if (imageURL.isNotEmpty()) {
|
||||||
|
Picasso.get()
|
||||||
|
.load(imageURL)
|
||||||
|
.resize(1280, 0)
|
||||||
|
.onlyScaleDown()
|
||||||
|
.into(this)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Picasso.get().load(R.color.black).into(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun List<DownloadRepository.Status>.toListString() : List<String>{
|
||||||
|
return this.map { it.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
fun List<String>.closestValue(value: String) = minBy { abs(value.toInt() - it.toInt()) }
|
fun List<String>.closestValue(value: String) = minBy { abs(value.toInt() - it.toInt()) }
|
||||||
|
|
||||||
class CustomInterpolator : Interpolator {
|
class CustomInterpolator : Interpolator {
|
||||||
|
|
|
||||||
|
|
@ -761,7 +761,7 @@ class InfoUtil(private val context: Context) {
|
||||||
formatProper.format_note = format.getString("format_note")
|
formatProper.format_note = format.getString("format_note")
|
||||||
}else{
|
}else{
|
||||||
if (!formatProper.format_note.endsWith("audio", true)){
|
if (!formatProper.format_note.endsWith("audio", true)){
|
||||||
formatProper.format_note = "${format.getString("format_note")} audio"
|
formatProper.format_note = format.getString("format_note").uppercase().removeSuffix("AUDIO") + " AUDIO"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -772,8 +772,8 @@ class InfoUtil(private val context: Context) {
|
||||||
formatProper.tbr = formatProper.tbr + "k"
|
formatProper.tbr = formatProper.tbr + "k"
|
||||||
}
|
}
|
||||||
|
|
||||||
if(formatProper.vcodec.isEmpty() || formatProper.vcodec == "null"){
|
if(formatProper.vcodec.isNullOrEmpty() || formatProper.vcodec == "null"){
|
||||||
if(formatProper.acodec.isEmpty() || formatProper.acodec == "null"){
|
if(formatProper.acodec.isNullOrEmpty() || formatProper.acodec == "null"){
|
||||||
formatProper.vcodec = format.getStringByAny("video_ext", "ext").ifEmpty { "unknown" }
|
formatProper.vcodec = format.getStringByAny("video_ext", "ext").ifEmpty { "unknown" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1032,36 +1032,22 @@ class InfoUtil(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (downloadItem.playlistTitle.isNotBlank()){
|
||||||
|
request.addCommands(listOf("--replace-in-metadata", "video:playlist", ".+", downloadItem.playlistTitle))
|
||||||
try {
|
runCatching {
|
||||||
val config = File(context.cacheDir.absolutePath + "/config" + downloadItem.id + "##replaceMetadata.txt")
|
if (downloadItem.playlistIndex != null){
|
||||||
YoutubeDLRequest("").apply {
|
request.addOption("--parse-metadata", downloadItem.playlistIndex.toString() + ":%(playlist_index)s")
|
||||||
|
|
||||||
if (downloadItem.playlistTitle.isNotBlank()){
|
|
||||||
addCommands(listOf("--replace-in-metadata", "playlist", ".+", downloadItem.playlistTitle))
|
|
||||||
runCatching {
|
|
||||||
if (downloadItem.playlistIndex != null){
|
|
||||||
addOption("--parse-metadata", downloadItem.playlistIndex.toString() + ":%(playlist_index)s")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(downloadItem.title.isNotBlank()){
|
|
||||||
addCommands(listOf("--replace-in-metadata", "title", ".+", downloadItem.title))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (downloadItem.author.isNotBlank()){
|
|
||||||
addCommands(listOf("--replace-in-metadata", "uploader", ".+", downloadItem.author))
|
|
||||||
}
|
|
||||||
|
|
||||||
val configData = parseYTDLRequestString(this).replace(" \" \"", "")
|
|
||||||
config.writeText(configData)
|
|
||||||
request.addOption("--config", config.absolutePath)
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
}
|
||||||
e.printStackTrace()
|
|
||||||
|
if(downloadItem.title.isNotBlank()){
|
||||||
|
request.addCommands(listOf("--replace-in-metadata", "video:title", ".+", downloadItem.title.take(150)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (downloadItem.author.isNotBlank()){
|
||||||
|
request.addCommands(listOf("--replace-in-metadata", "video:uploader", ".+", downloadItem.author.take(30)))
|
||||||
}
|
}
|
||||||
|
|
||||||
request.addOption("--parse-metadata", "uploader:(?P<uploader>.+)(?: - Topic)$")
|
request.addOption("--parse-metadata", "uploader:(?P<uploader>.+)(?: - Topic)$")
|
||||||
|
|
@ -1111,10 +1097,6 @@ class InfoUtil(private val context: Context) {
|
||||||
if (sharedPreferences.getBoolean("write_description", false)){
|
if (sharedPreferences.getBoolean("write_description", false)){
|
||||||
request.addOption("--write-description")
|
request.addOption("--write-description")
|
||||||
}
|
}
|
||||||
|
|
||||||
filenameTemplate = filenameTemplate
|
|
||||||
.replace("%(uploader)s", "%(uploader,channel).30B")
|
|
||||||
.replace("%(title)s", "%(title).170B")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sharedPreferences.getBoolean("download_archive", false)){
|
if (sharedPreferences.getBoolean("download_archive", false)){
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,7 @@ class NotificationUtil(var context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDownloadFinished(
|
fun createDownloadFinished(
|
||||||
|
id: Long,
|
||||||
title: String?,
|
title: String?,
|
||||||
filepath: List<String>?,
|
filepath: List<String>?,
|
||||||
res: Resources
|
res: Resources
|
||||||
|
|
@ -234,64 +235,75 @@ class NotificationUtil(var context: Context) {
|
||||||
R.drawable.ic_launcher_foreground_large
|
R.drawable.ic_launcher_foreground_large
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.setGroup(DOWNLOAD_FINISHED_NOTIFICATION_ID.toString())
|
||||||
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
|
||||||
.setContentText("")
|
.setContentText("")
|
||||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
.clearActions()
|
.clearActions()
|
||||||
if (filepath != null){
|
if (filepath != null){
|
||||||
try{
|
try{
|
||||||
val uri = filepath.first().runCatching {
|
val uris = filepath.mapNotNull {
|
||||||
DocumentFile.fromSingleUri(context, Uri.parse(filepath.first())).run{
|
runCatching {
|
||||||
if (this?.exists() == true){
|
DocumentFile.fromSingleUri(context, Uri.parse(it)).run{
|
||||||
this.uri
|
if (this?.exists() == true){
|
||||||
}else if (File(this@runCatching).exists()){
|
this.uri
|
||||||
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
}else if (File(it).exists()){
|
||||||
File(this@runCatching))
|
FileProvider.getUriForFile(context, context.packageName + ".fileprovider",
|
||||||
}else null
|
File(it))
|
||||||
}
|
}else null
|
||||||
}.getOrNull()
|
}
|
||||||
|
}.getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
val openFileIntent = Intent()
|
val openFileIntent = Intent()
|
||||||
|
val shareFileIntent = Intent()
|
||||||
|
|
||||||
if (uri != null){
|
if (uris.isNotEmpty()){
|
||||||
openFileIntent.apply {
|
openFileIntent.apply {
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
action = Intent.ACTION_VIEW
|
action = Intent.ACTION_VIEW
|
||||||
data = uri
|
data = uris.first()
|
||||||
|
}
|
||||||
|
|
||||||
|
shareFileIntent.apply {
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||||
|
type = if (uris.size == 1) uris[0].let { context.contentResolver.getType(it) } ?: "media/*" else "*/*"
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val openNotificationPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
|
val openNotificationPendingIntent: PendingIntent = TaskStackBuilder.create(context).run {
|
||||||
addNextIntentWithParentStack(openFileIntent)
|
addNextIntentWithParentStack(openFileIntent)
|
||||||
getPendingIntent(0,
|
getPendingIntent(id.toInt(),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//share intent
|
//share intent
|
||||||
val shareIntent = Intent(context, ShareFileActivity::class.java)
|
|
||||||
shareIntent.putExtra("path", filepath.toTypedArray())
|
|
||||||
shareIntent.putExtra("notificationID", DOWNLOAD_FINISHED_NOTIFICATION_ID)
|
|
||||||
val shareNotificationPendingIntent: PendingIntent = PendingIntent.getActivity(
|
val shareNotificationPendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
0,
|
id.toInt(),
|
||||||
shareIntent,
|
Intent.createChooser(shareFileIntent, res.getString(R.string.share)),
|
||||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
notificationBuilder.addAction(0, res.getString(R.string.Open_File), openNotificationPendingIntent)
|
notificationBuilder.addAction(0, res.getString(R.string.Open_File), openNotificationPendingIntent)
|
||||||
notificationBuilder.addAction(0, res.getString(R.string.share), shareNotificationPendingIntent)
|
notificationBuilder.addAction(0, res.getString(R.string.share), shareNotificationPendingIntent)
|
||||||
}catch (_: Exception){}
|
}catch (_: Exception){}
|
||||||
}
|
}
|
||||||
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID, notificationBuilder.build())
|
notificationManager.notify(DOWNLOAD_FINISHED_NOTIFICATION_ID + id.toInt(), notificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createDownloadErrored(title: String?,
|
fun createDownloadErrored(
|
||||||
error: String?,
|
id: Long,
|
||||||
logID: Long?,
|
title: String?,
|
||||||
res: Resources
|
error: String?,
|
||||||
|
logID: Long?,
|
||||||
|
res: Resources
|
||||||
) {
|
) {
|
||||||
val notificationBuilder = getBuilder(DOWNLOAD_ERRORED_CHANNEL_ID)
|
val notificationBuilder = getBuilder(DOWNLOAD_ERRORED_CHANNEL_ID)
|
||||||
|
|
||||||
|
|
@ -333,7 +345,7 @@ class NotificationUtil(var context: Context) {
|
||||||
notificationBuilder.setContentIntent(errorPendingIntent)
|
notificationBuilder.setContentIntent(errorPendingIntent)
|
||||||
notificationBuilder.addAction(0, res.getString(R.string.logs), errorPendingIntent)
|
notificationBuilder.addAction(0, res.getString(R.string.logs), errorPendingIntent)
|
||||||
}
|
}
|
||||||
notificationManager.notify(DOWNLOAD_ERRORED_NOTIFICATION_ID, notificationBuilder.build())
|
notificationManager.notify(DOWNLOAD_ERRORED_NOTIFICATION_ID + id.toInt(), notificationBuilder.build())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -422,7 +434,6 @@ class NotificationUtil(var context: Context) {
|
||||||
fun cancelDownloadNotification(id: Int) {
|
fun cancelDownloadNotification(id: Int) {
|
||||||
notificationManager.cancel(id)
|
notificationManager.cancel(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createMoveCacheFilesNotification(pendingIntent: PendingIntent?, downloadMiscChannelId: String): Notification {
|
fun createMoveCacheFilesNotification(pendingIntent: PendingIntent?, downloadMiscChannelId: String): Notification {
|
||||||
val notificationBuilder = getBuilder(downloadMiscChannelId)
|
val notificationBuilder = getBuilder(downloadMiscChannelId)
|
||||||
|
|
||||||
|
|
@ -574,7 +585,7 @@ class NotificationUtil(var context: Context) {
|
||||||
.createPendingIntent()
|
.createPendingIntent()
|
||||||
|
|
||||||
notificationBuilder
|
notificationBuilder
|
||||||
.setContentTitle(resources.getString(R.string.finished_download_notification_channel_name))
|
.setContentTitle(resources.getString(R.string.all_queries_finished))
|
||||||
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
|
.setSmallIcon(R.drawable.ic_launcher_foreground_large)
|
||||||
.setLargeIcon(
|
.setLargeIcon(
|
||||||
BitmapFactory.decodeResource(
|
BitmapFactory.decodeResource(
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ class UpdateUtil(var context: Context) {
|
||||||
d?.dismiss()
|
d?.dismiss()
|
||||||
}
|
}
|
||||||
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
.setNegativeButton(context.resources.getString(R.string.cancel)) { _: DialogInterface?, _: Int -> }
|
||||||
.setPositiveButton(context.resources.getString(R.string.update)) { _: DialogInterface?, _: Int ->
|
.setPositiveButton(context.resources.getString(R.string.update)) { d: DialogInterface?, _: Int ->
|
||||||
runCatching {
|
runCatching {
|
||||||
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
|
val releaseVersion = v.assets.firstOrNull { it.name.contains(Build.SUPPORTED_ABIS[0]) }
|
||||||
if (releaseVersion == null){
|
if (releaseVersion == null){
|
||||||
|
|
@ -140,8 +140,8 @@ class UpdateUtil(var context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
context.registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
context.registerReceiver(onDownloadComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||||
|
d?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
val view = updateDialog.show()
|
val view = updateDialog.show()
|
||||||
val textView = view.findViewById<TextView>(android.R.id.message)
|
val textView = view.findViewById<TextView>(android.R.id.message)
|
||||||
|
|
@ -176,7 +176,7 @@ class UpdateUtil(var context: Context) {
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
|
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
)
|
)
|
||||||
|
|
||||||
layoutParams.setMargins(5, 5, 5, 0)
|
layoutParams.setMargins(10, 10, 10, 0)
|
||||||
scrollView.layoutParams = layoutParams
|
scrollView.layoutParams = layoutParams
|
||||||
scrollView.addView(linearLayout)
|
scrollView.addView(linearLayout)
|
||||||
val releases = getGithubReleases()
|
val releases = getGithubReleases()
|
||||||
|
|
|
||||||
|
|
@ -267,7 +267,7 @@ class DownloadWorker(
|
||||||
|
|
||||||
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
notificationUtil.cancelDownloadNotification(downloadItem.id.toInt())
|
||||||
notificationUtil.createDownloadFinished(
|
notificationUtil.createDownloadFinished(
|
||||||
downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
downloadItem.id, downloadItem.title, if (finalPaths?.first().equals(context.getString(R.string.unfound_file))) null else finalPaths, resources
|
||||||
)
|
)
|
||||||
|
|
||||||
if (wasQuickDownloaded && createResultItem){
|
if (wasQuickDownloaded && createResultItem){
|
||||||
|
|
@ -326,7 +326,9 @@ class DownloadWorker(
|
||||||
}
|
}
|
||||||
|
|
||||||
notificationUtil.createDownloadErrored(
|
notificationUtil.createDownloadErrored(
|
||||||
downloadItem.title.ifEmpty { downloadItem.url }, it.message,
|
downloadItem.id,
|
||||||
|
downloadItem.title.ifEmpty { downloadItem.url },
|
||||||
|
it.message,
|
||||||
downloadItem.logID,
|
downloadItem.logID,
|
||||||
resources
|
resources
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -65,22 +65,20 @@ class ObserveSourceWorker(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filter { result ->
|
.filter { result ->
|
||||||
|
val history = historyRepo.getAllByURL(result.url)
|
||||||
val history = historyRepo.getAllByURL(result.url)
|
if (item.retryMissingDownloads){
|
||||||
if (!item.retryMissingDownloads){
|
//all items that are not present in history
|
||||||
//all items that are not present in history
|
history.none { hi -> hi.downloadPath.any { path -> FileUtil.exists(path) } }
|
||||||
history.none { hi -> hi.downloadPath.any { path -> FileUtil.exists(path) } }
|
|
||||||
}else{
|
|
||||||
//all items that are not already processed
|
|
||||||
if(item.alreadyProcessedLinks.isEmpty()){
|
|
||||||
!history.map { it.url }.contains(result.url) && !item.alreadyProcessedLinks.contains(result.url)
|
|
||||||
}else{
|
}else{
|
||||||
!item.alreadyProcessedLinks.contains(result.url)
|
//all items that are not already processed
|
||||||
|
if(item.alreadyProcessedLinks.isEmpty()){
|
||||||
|
!history.map { it.url }.contains(result.url)
|
||||||
|
}else{
|
||||||
|
!item.alreadyProcessedLinks.contains(result.url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
val items = mutableListOf<DownloadItem>()
|
val items = mutableListOf<DownloadItem>()
|
||||||
|
|
||||||
res.forEach {
|
res.forEach {
|
||||||
|
|
@ -208,47 +206,18 @@ class ObserveSourceWorker(
|
||||||
repo.update(item)
|
repo.update(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduleForNextTime(item)
|
//schedule for next time
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun scheduleForNextTime(item: ObserveSourcesItem){
|
|
||||||
val alarmManager = context.getSystemService(AlarmManager::class.java)
|
val alarmManager = context.getSystemService(AlarmManager::class.java)
|
||||||
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
val intent = Intent(context, ObserveAlarmReceiver::class.java)
|
||||||
intent.putExtra("id", item.id)
|
intent.putExtra("id", item.id)
|
||||||
|
|
||||||
val c = Calendar.getInstance()
|
|
||||||
val hourMin = Calendar.getInstance()
|
|
||||||
hourMin.timeInMillis = item.everyTime
|
|
||||||
c.set(Calendar.HOUR_OF_DAY, hourMin.get(Calendar.HOUR_OF_DAY))
|
|
||||||
c.set(Calendar.MINUTE, hourMin.get(Calendar.MINUTE))
|
|
||||||
|
|
||||||
when(item.everyCategory){
|
|
||||||
ObserveSourcesRepository.EveryCategory.DAY -> {
|
|
||||||
c.add(Calendar.DAY_OF_MONTH, item.everyNr)
|
|
||||||
}
|
|
||||||
ObserveSourcesRepository.EveryCategory.WEEK -> {
|
|
||||||
if(item.everyWeekDay.isEmpty()){
|
|
||||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
|
||||||
}else{
|
|
||||||
val weekDayID = c.get(Calendar.DAY_OF_WEEK).toString()
|
|
||||||
val followingWeekDay = (item.everyWeekDay.firstOrNull { it.toInt() > weekDayID.toInt() } ?: item.everyWeekDay.minBy { it.toInt() }).toInt()
|
|
||||||
c.set(Calendar.DAY_OF_WEEK, followingWeekDay)
|
|
||||||
if (item.everyNr > 1){
|
|
||||||
c.add(Calendar.DAY_OF_MONTH, 7 * item.everyNr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ObserveSourcesRepository.EveryCategory.MONTH -> {
|
|
||||||
c.add(Calendar.MONTH, item.everyNr)
|
|
||||||
c.set(Calendar.DAY_OF_MONTH, item.everyMonthDay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
alarmManager.setExact(
|
alarmManager.setExact(
|
||||||
AlarmManager.RTC,
|
AlarmManager.RTC_WAKEUP,
|
||||||
c.timeInMillis,
|
repo.calculateNextTime(item),
|
||||||
PendingIntent.getBroadcast(context, item.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getBroadcast(context, item.id.toInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
10
app/src/main/res/drawable/ic_select_between.xml
Normal file
10
app/src/main/res/drawable/ic_select_between.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?android:textColorPrimary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M440,880L440,712L376,776L320,720L480,560L640,720L584,776L520,712L520,880L440,880ZM160,520L160,440L800,440L800,520L160,520ZM480,400L320,240L376,184L440,248L440,80L520,80L520,248L584,184L640,240L480,400Z"/>
|
||||||
|
</vector>
|
||||||
|
|
@ -171,6 +171,7 @@
|
||||||
style="?attr/materialIconButtonFilledStyle"
|
style="?attr/materialIconButtonFilledStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/pause"
|
||||||
android:layoutAnimation="@anim/popup_enter"
|
android:layoutAnimation="@anim/popup_enter"
|
||||||
app:backgroundTint="?attr/colorSurface"
|
app:backgroundTint="?attr/colorSurface"
|
||||||
app:cornerRadius="15dp"
|
app:cornerRadius="15dp"
|
||||||
|
|
|
||||||
|
|
@ -63,35 +63,38 @@
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/format_note"
|
android:id="@+id/format_note"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="10dp"
|
android:textDirection="locale"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
|
android:paddingHorizontal="10dp"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toStartOf="@+id/format_id"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="0dp"
|
android:id="@+id/relativeLayout"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/format_id"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/format_note">
|
app:layout_constraintTop_toBottomOf="@id/format_note">
|
||||||
|
|
||||||
<HorizontalScrollView
|
<HorizontalScrollView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_marginEnd="10dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
|
android:layout_marginEnd="10dp"
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:layout_marginStart="10dp"
|
|
||||||
android:scrollbars="none">
|
android:scrollbars="none">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|
@ -111,14 +114,14 @@
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:maxLength="10"
|
||||||
android:minWidth="30dp"
|
android:minWidth="30dp"
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
|
android:textCursorDrawable="@string/audio_format"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
android:maxLength="10"
|
|
||||||
app:drawableStartCompat="@drawable/ic_music_formatcard"
|
app:drawableStartCompat="@drawable/ic_music_formatcard"
|
||||||
android:textCursorDrawable="@string/audio_format"
|
|
||||||
app:drawableTint="@color/white"
|
app:drawableTint="@color/white"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|
@ -175,16 +178,14 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_marginBottom="3dp"
|
|
||||||
android:clickable="false"
|
android:clickable="false"
|
||||||
|
android:ellipsize="end"
|
||||||
android:gravity="bottom|end"
|
android:gravity="bottom|end"
|
||||||
android:maxLength="10"
|
android:maxLength="10"
|
||||||
android:ellipsize="end"
|
|
||||||
app:cornerRadius="10dp"
|
app:cornerRadius="10dp"
|
||||||
app:layout_constraintVertical_bias="1.0"
|
app:layout_constraintBottom_toBottomOf="@+id/format_note"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/format_note" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
|
|
@ -500,6 +500,13 @@
|
||||||
android:checked="false"
|
android:checked="false"
|
||||||
android:text="@string/retry_missing_downloads"/>
|
android:text="@string/retry_missing_downloads"/>
|
||||||
|
|
||||||
|
<com.google.android.material.materialswitch.MaterialSwitch
|
||||||
|
android:id="@+id/reset_processed_links"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checked="false"
|
||||||
|
android:text="@string/reset_processed_links"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
@ -25,5 +32,11 @@
|
||||||
android:title="@string/invert_selected"
|
android:title="@string/invert_selected"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy_urls"
|
||||||
|
android:icon="@drawable/ic_delete_all"
|
||||||
|
android:title="@string/copy_urls"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,13 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
@ -32,5 +39,11 @@
|
||||||
android:title="@string/invert_selected"
|
android:title="@string/invert_selected"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy_urls"
|
||||||
|
android:icon="@drawable/ic_delete_all"
|
||||||
|
android:title="@string/copy_urls"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,14 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".MainActivity" >
|
tools:context=".MainActivity" >
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/select_between"
|
||||||
|
android:visible="false"
|
||||||
|
android:title="@string/select_between"
|
||||||
|
android:icon="@drawable/ic_select_between"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/delete_results"
|
android:id="@+id/delete_results"
|
||||||
android:title="@string/remove_results"
|
android:title="@string/remove_results"
|
||||||
|
|
@ -25,5 +33,11 @@
|
||||||
android:title="@string/invert_selected"
|
android:title="@string/invert_selected"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/copy_urls"
|
||||||
|
android:icon="@drawable/ic_delete_all"
|
||||||
|
android:title="@string/copy_urls"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -372,4 +372,7 @@
|
||||||
<string name="text_size">Text Size</string>
|
<string name="text_size">Text Size</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
<string name="back">Back</string>
|
<string name="back">Back</string>
|
||||||
|
<string name="all_queries_finished">All Queries have been processed</string>
|
||||||
|
<string name="select_between">Select Items Between</string>
|
||||||
|
<string name="reset_processed_links">Reset Already Processed Links</string>
|
||||||
</resources>
|
</resources>
|
||||||
15
fastlane/metadata/android/en-US/changelogs/10731.txt
Normal file
15
fastlane/metadata/android/en-US/changelogs/10731.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
## What's New
|
||||||
|
|
||||||
|
- Added ability to select all items between two selected items in download queue
|
||||||
|
- Fixed app only getting one URL when tapping "Link you copied" in search view even if you copied multiple links
|
||||||
|
- Fixed app crashing on playlist selector when sharing a playlist. Forgor to configure it with the new changes
|
||||||
|
- Fixed app not removing errored, cancelled, saved downloads after you queued them for re-download
|
||||||
|
- Added option to copy URLs of selected downloads in the download queue screen
|
||||||
|
- Made app show all finished and errored notifications, instead of the latest one
|
||||||
|
- Kinda fixed the app not giving you the correct path when sharing a file from the notification
|
||||||
|
- Fixed app crashing when loading soundcloud results. They had stupidly large thumbnails, they are resized now
|
||||||
|
- Added option to reset the recorded links in observe resources when you are trying to update it. From now on they wont get reset like before
|
||||||
|
- Fixed major bug of app adding extra quotes in the yt-dlp config, making it fail on titles with quotes in them
|
||||||
|
|
||||||
|
Had to push this soon because of mostly the last error
|
||||||
|
|
||||||
Loading…
Reference in a new issue