mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Support multiple duplicates, cleanup, and other fixes
This commit is contained in:
parent
01ce26003d
commit
d77cc8f98d
3 changed files with 107 additions and 58 deletions
|
@ -7,6 +7,7 @@ import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.MainThread
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
@ -130,6 +131,18 @@ data class ResultData(
|
||||||
val plotHeaderText: UiText,
|
val plotHeaderText: UiText,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class CheckDuplicateData(
|
||||||
|
val name: String,
|
||||||
|
val year: Int?,
|
||||||
|
val imdbId: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class LibraryListType {
|
||||||
|
BOOKMARKS,
|
||||||
|
FAVORITES,
|
||||||
|
SUBSCRIPTIONS
|
||||||
|
}
|
||||||
|
|
||||||
fun txt(status: DubStatus?): UiText? {
|
fun txt(status: DubStatus?): UiText? {
|
||||||
return txt(
|
return txt(
|
||||||
when (status) {
|
when (status) {
|
||||||
|
@ -824,20 +837,29 @@ class ResultViewModel2 : ViewModel() {
|
||||||
|
|
||||||
val currentStatus = getResultWatchState(currentId)
|
val currentStatus = getResultWatchState(currentId)
|
||||||
|
|
||||||
if (status == currentStatus) return
|
// If the status is not actually changing, set this to an empty
|
||||||
|
// list so that we don't show the duplicate warning dialog, but we
|
||||||
|
// still want to refresh the data anyway
|
||||||
|
val bookmarkedData = if (status == currentStatus) {
|
||||||
|
emptyList()
|
||||||
|
} else getBookmarkedDataByWatchType(status)
|
||||||
|
|
||||||
checkAndWarnDuplicates(
|
checkAndWarnDuplicates(
|
||||||
context,
|
context,
|
||||||
R.string.duplicate_message_bookmarks,
|
LibraryListType.BOOKMARKS,
|
||||||
response.name,
|
CheckDuplicateData(
|
||||||
response.year,
|
name = response.name,
|
||||||
response.getImdbId(),
|
year = response.year,
|
||||||
getBookmarkedDataByWatchType(status)
|
imdbId = response.getImdbId(),
|
||||||
) { shouldContinue: Boolean, duplicateId: Int? ->
|
),
|
||||||
|
bookmarkedData
|
||||||
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
if (!shouldContinue) return@checkAndWarnDuplicates
|
if (!shouldContinue) return@checkAndWarnDuplicates
|
||||||
|
|
||||||
if (duplicateId != null) {
|
if (duplicateIds.isNotEmpty()) {
|
||||||
deleteBookmarkedData(duplicateId)
|
duplicateIds.forEach { duplicateId ->
|
||||||
|
deleteBookmarkedData(duplicateId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setResultWatchState(currentId, status.internalId)
|
setResultWatchState(currentId, status.internalId)
|
||||||
|
@ -900,19 +922,23 @@ class ResultViewModel2 : ViewModel() {
|
||||||
} else {
|
} else {
|
||||||
checkAndWarnDuplicates(
|
checkAndWarnDuplicates(
|
||||||
context,
|
context,
|
||||||
R.string.duplicate_message_subscriptions,
|
LibraryListType.SUBSCRIPTIONS,
|
||||||
response.name,
|
CheckDuplicateData(
|
||||||
response.year,
|
name = response.name,
|
||||||
response.getImdbId(),
|
year = response.year,
|
||||||
|
imdbId = response.getImdbId(),
|
||||||
|
),
|
||||||
getAllSubscriptions(),
|
getAllSubscriptions(),
|
||||||
) { shouldContinue: Boolean, duplicateId: Int? ->
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
if (!shouldContinue) {
|
if (!shouldContinue) {
|
||||||
statusChangedCallback?.invoke(null)
|
statusChangedCallback?.invoke(null)
|
||||||
return@checkAndWarnDuplicates
|
return@checkAndWarnDuplicates
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicateId != null) {
|
if (duplicateIds.isNotEmpty()) {
|
||||||
removeSubscribedData(duplicateId)
|
duplicateIds.forEach { duplicateId ->
|
||||||
|
removeSubscribedData(duplicateId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val current = getSubscribedData(currentId)
|
val current = getSubscribedData(currentId)
|
||||||
|
@ -963,19 +989,23 @@ class ResultViewModel2 : ViewModel() {
|
||||||
} else {
|
} else {
|
||||||
checkAndWarnDuplicates(
|
checkAndWarnDuplicates(
|
||||||
context,
|
context,
|
||||||
R.string.duplicate_message_favorites,
|
LibraryListType.FAVORITES,
|
||||||
response.name,
|
CheckDuplicateData(
|
||||||
response.year,
|
name = response.name,
|
||||||
response.getImdbId(),
|
year = response.year,
|
||||||
|
imdbId = response.getImdbId(),
|
||||||
|
),
|
||||||
getAllFavorites(),
|
getAllFavorites(),
|
||||||
) { shouldContinue: Boolean, duplicateId: Int? ->
|
) { shouldContinue: Boolean, duplicateIds: List<Int?> ->
|
||||||
if (!shouldContinue) {
|
if (!shouldContinue) {
|
||||||
statusChangedCallback?.invoke(null)
|
statusChangedCallback?.invoke(null)
|
||||||
return@checkAndWarnDuplicates
|
return@checkAndWarnDuplicates
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicateId != null) {
|
if (duplicateIds.isNotEmpty()) {
|
||||||
removeFavoritesData(duplicateId)
|
duplicateIds.forEach { duplicateId ->
|
||||||
|
removeFavoritesData(duplicateId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val current = getFavoritesData(currentId)
|
val current = getFavoritesData(currentId)
|
||||||
|
@ -1005,14 +1035,13 @@ class ResultViewModel2 : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
private fun checkAndWarnDuplicates(
|
private fun checkAndWarnDuplicates(
|
||||||
context: Context?,
|
context: Context?,
|
||||||
message: Int,
|
listType: LibraryListType,
|
||||||
name: String,
|
checkDuplicateData: CheckDuplicateData,
|
||||||
year: Int?,
|
|
||||||
imdbId: String?,
|
|
||||||
data: List<DataStoreHelper.LibrarySearchResponse>,
|
data: List<DataStoreHelper.LibrarySearchResponse>,
|
||||||
checkDuplicatesCallback: (shouldContinue: Boolean, duplicateId: Int?) -> Unit
|
checkDuplicatesCallback: (shouldContinue: Boolean, duplicateIds: List<Int?>) -> Unit
|
||||||
) {
|
) {
|
||||||
fun normalizeString(input: String): String {
|
fun normalizeString(input: String): String {
|
||||||
/**
|
/**
|
||||||
|
@ -1021,48 +1050,70 @@ class ResultViewModel2 : ViewModel() {
|
||||||
* and one provider has the title with an extra whitespace. This is minor enough that
|
* and one provider has the title with an extra whitespace. This is minor enough that
|
||||||
* it should still match in this case.
|
* it should still match in this case.
|
||||||
*/
|
*/
|
||||||
return input.trim().replace("\\s+".toRegex(), " ")
|
val regex = "\\s+".toRegex()
|
||||||
|
return input.trim().replace(regex, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
val duplicateEntry = data.find {
|
val duplicateEntries = data.filter {
|
||||||
imdbId != null && it.imdbId == imdbId ||
|
checkDuplicateData.imdbId != null && it.imdbId == checkDuplicateData.imdbId ||
|
||||||
normalizeString(it.name) == normalizeString(name) && it.year == year
|
normalizeString(it.name) == normalizeString(checkDuplicateData.name) && it.year == checkDuplicateData.year
|
||||||
}
|
}
|
||||||
|
|
||||||
if (duplicateEntry == null || context == null) {
|
if (duplicateEntries.isEmpty() || context == null) {
|
||||||
checkDuplicatesCallback.invoke(true, null)
|
checkDuplicatesCallback.invoke(true, emptyList())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val replaceMessage = if (duplicateEntries.size > 1) {
|
||||||
|
R.string.duplicate_replace_all
|
||||||
|
} else R.string.duplicate_replace
|
||||||
|
|
||||||
|
val message = if (duplicateEntries.size == 1) {
|
||||||
|
val list = when (listType) {
|
||||||
|
LibraryListType.BOOKMARKS -> getResultWatchState(duplicateEntries[0].id ?: 0).stringRes
|
||||||
|
LibraryListType.FAVORITES -> R.string.favorites_list_name
|
||||||
|
LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
|
||||||
|
}
|
||||||
|
|
||||||
|
context.getString(R.string.duplicate_message_single,
|
||||||
|
"${normalizeString(duplicateEntries[0].name)} (${context.getString(list)})"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val bulletPoints = duplicateEntries.joinToString("\n") {
|
||||||
|
val list = when (listType) {
|
||||||
|
LibraryListType.BOOKMARKS -> getResultWatchState(it.id ?: 0).stringRes
|
||||||
|
LibraryListType.FAVORITES -> R.string.favorites_list_name
|
||||||
|
LibraryListType.SUBSCRIPTIONS -> R.string.subscription_list_name
|
||||||
|
}
|
||||||
|
|
||||||
|
"• ${normalizeString(it.name)} (${context.getString(list)})"
|
||||||
|
}
|
||||||
|
|
||||||
|
context.getString(R.string.duplicate_message_multiple, bulletPoints)
|
||||||
|
}
|
||||||
|
|
||||||
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
|
val builder: AlertDialog.Builder = AlertDialog.Builder(context)
|
||||||
|
|
||||||
val dialogClickListener =
|
val dialogClickListener =
|
||||||
DialogInterface.OnClickListener { _, which ->
|
DialogInterface.OnClickListener { _, which ->
|
||||||
when (which) {
|
when (which) {
|
||||||
DialogInterface.BUTTON_POSITIVE -> {
|
DialogInterface.BUTTON_POSITIVE -> {
|
||||||
checkDuplicatesCallback.invoke(true, null)
|
checkDuplicatesCallback.invoke(true, emptyList())
|
||||||
}
|
}
|
||||||
DialogInterface.BUTTON_NEGATIVE -> {
|
DialogInterface.BUTTON_NEGATIVE -> {
|
||||||
checkDuplicatesCallback.invoke(false, null)
|
checkDuplicatesCallback.invoke(false, emptyList())
|
||||||
}
|
}
|
||||||
DialogInterface.BUTTON_NEUTRAL -> {
|
DialogInterface.BUTTON_NEUTRAL -> {
|
||||||
checkDuplicatesCallback.invoke(true, duplicateEntry.id)
|
checkDuplicatesCallback.invoke(true, duplicateEntries.map { it.id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setTitle(R.string.duplicate_title)
|
builder.setTitle(R.string.duplicate_title)
|
||||||
.setMessage(
|
.setMessage(message)
|
||||||
context.getString(message).format(
|
|
||||||
normalizeString(duplicateEntry.name),
|
|
||||||
context.getString(
|
|
||||||
getResultWatchState(duplicateEntry.id ?: 0).stringRes
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setPositiveButton(R.string.duplicate_add, dialogClickListener)
|
.setPositiveButton(R.string.duplicate_add, dialogClickListener)
|
||||||
.setNegativeButton(R.string.duplicate_cancel, dialogClickListener)
|
.setNegativeButton(R.string.duplicate_cancel, dialogClickListener)
|
||||||
.setNeutralButton(R.string.duplicate_replace, dialogClickListener)
|
.setNeutralButton(replaceMessage, dialogClickListener)
|
||||||
.show().setDefaultFocus()
|
.show().setDefaultFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,7 @@ object DataStoreHelper {
|
||||||
override var type: TvType?,
|
override var type: TvType?,
|
||||||
override var posterUrl: String?,
|
override var posterUrl: String?,
|
||||||
override val year: Int?,
|
override val year: Int?,
|
||||||
override val imdbId: String?,
|
override val imdbId: String? = null,
|
||||||
override var quality: SearchQuality? = null,
|
override var quality: SearchQuality? = null,
|
||||||
override var posterHeaders: Map<String, String>? = null
|
override var posterHeaders: Map<String, String>? = null
|
||||||
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
||||||
|
@ -405,7 +405,7 @@ object DataStoreHelper {
|
||||||
override var type: TvType?,
|
override var type: TvType?,
|
||||||
override var posterUrl: String?,
|
override var posterUrl: String?,
|
||||||
override val year: Int?,
|
override val year: Int?,
|
||||||
override val imdbId: String?,
|
override val imdbId: String? = null,
|
||||||
override var quality: SearchQuality? = null,
|
override var quality: SearchQuality? = null,
|
||||||
override var posterHeaders: Map<String, String>? = null
|
override var posterHeaders: Map<String, String>? = null
|
||||||
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
||||||
|
@ -433,7 +433,7 @@ object DataStoreHelper {
|
||||||
override var type: TvType?,
|
override var type: TvType?,
|
||||||
override var posterUrl: String?,
|
override var posterUrl: String?,
|
||||||
override val year: Int?,
|
override val year: Int?,
|
||||||
override val imdbId: String?,
|
override val imdbId: String? = null,
|
||||||
override var quality: SearchQuality? = null,
|
override var quality: SearchQuality? = null,
|
||||||
override var posterHeaders: Map<String, String>? = null
|
override var posterHeaders: Map<String, String>? = null
|
||||||
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, imdbId, quality, posterHeaders) {
|
||||||
|
|
|
@ -697,21 +697,19 @@
|
||||||
<string name="duplicate_title">Potential Duplicate Found</string>
|
<string name="duplicate_title">Potential Duplicate Found</string>
|
||||||
<string name="duplicate_add">Add</string>
|
<string name="duplicate_add">Add</string>
|
||||||
<string name="duplicate_replace">Replace</string>
|
<string name="duplicate_replace">Replace</string>
|
||||||
|
<string name="duplicate_replace_all">Replace All</string>
|
||||||
<string name="duplicate_cancel" translatable="false">@string/sort_cancel</string>
|
<string name="duplicate_cancel" translatable="false">@string/sort_cancel</string>
|
||||||
<string name="duplicate_message_bookmarks">
|
<string name="duplicate_message_single">
|
||||||
It appears that a potentially duplicate title \'%s\' with the watch status \'%s\' already exists in your library.
|
It appears that a potentially duplicate item already exists in your library: \'%1$s.\'
|
||||||
|
|
||||||
\n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
|
\n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
|
||||||
</string>
|
</string>
|
||||||
<string name="duplicate_message_subscriptions">
|
<string name="duplicate_message_multiple">
|
||||||
You are already subscribed to a title that appears to be a potential duplicate: \'%s.\'
|
Potential duplicate items have been found in your library:
|
||||||
|
|
||||||
\n\nWould you like to add this subscription anyway, replace the existing one, or cancel the action?
|
\n\n%1$s
|
||||||
</string>
|
|
||||||
<string name="duplicate_message_favorites">
|
|
||||||
It seems that a potentially duplicate title \'%s\' is already in your favorites.
|
|
||||||
|
|
||||||
\n\nWould you like to add this item anyway, replace the existing one, or cancel the action?
|
\n\nWould you like to add this item anyway, replace the existing ones, or cancel the action?
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue