fix: download name comparison

This commit is contained in:
rebelonion 2024-04-05 17:44:59 -05:00
parent f6c7b09d9b
commit 5fcbfeb3db
6 changed files with 91 additions and 43 deletions

View file

@ -3,7 +3,7 @@ package ani.dantotsu.download
import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import ani.dantotsu.download.DownloadsManager.Companion.findValidName
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaType
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
@ -12,7 +12,6 @@ import ani.dantotsu.util.Logger
import com.anggrayudi.storage.callback.FolderCallback
import com.anggrayudi.storage.file.deleteRecursively
import com.anggrayudi.storage.file.findFolder
import com.anggrayudi.storage.file.moveFileTo
import com.anggrayudi.storage.file.moveFolderTo
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
@ -20,6 +19,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.xdrop.fuzzywuzzy.FuzzySearch
import java.io.Serializable
class DownloadsManager(private val context: Context) {
@ -127,7 +127,12 @@ class DownloadsManager(private val context: Context) {
}
}
fun moveDownloadsDir(context: Context, oldUri: Uri, newUri: Uri, finished: (Boolean, String) -> Unit) {
fun moveDownloadsDir(
context: Context,
oldUri: Uri,
newUri: Uri,
finished: (Boolean, String) -> Unit
) {
try {
if (oldUri == newUri) {
finished(false, "Source and destination are the same")
@ -141,18 +146,41 @@ class DownloadsManager(private val context: Context) {
DocumentFile.fromTreeUri(context, newUri) ?: throw Exception("New base is null")
val folder =
oldBase.findFolder(BASE_LOCATION) ?: throw Exception("Base folder not found")
folder.moveFolderTo(context, newBase, false, BASE_LOCATION, object:
folder.moveFolderTo(context, newBase, false, BASE_LOCATION, object :
FolderCallback() {
override fun onFailed(errorCode: ErrorCode) {
when (errorCode) {
ErrorCode.CANCELED -> finished(false, "Move canceled")
ErrorCode.CANNOT_CREATE_FILE_IN_TARGET -> finished(false, "Cannot create file in target")
ErrorCode.INVALID_TARGET_FOLDER -> finished(true, "Invalid target folder") // seems to still work
ErrorCode.NO_SPACE_LEFT_ON_TARGET_PATH -> finished(false, "No space left on target path")
ErrorCode.CANNOT_CREATE_FILE_IN_TARGET -> finished(
false,
"Cannot create file in target"
)
ErrorCode.INVALID_TARGET_FOLDER -> finished(
true,
"Invalid target folder"
) // seems to still work
ErrorCode.NO_SPACE_LEFT_ON_TARGET_PATH -> finished(
false,
"No space left on target path"
)
ErrorCode.UNKNOWN_IO_ERROR -> finished(false, "Unknown IO error")
ErrorCode.SOURCE_FOLDER_NOT_FOUND -> finished(false, "Source folder not found")
ErrorCode.STORAGE_PERMISSION_DENIED -> finished(false, "Storage permission denied")
ErrorCode.TARGET_FOLDER_CANNOT_HAVE_SAME_PATH_WITH_SOURCE_FOLDER -> finished(false, "Target folder cannot have same path with source folder")
ErrorCode.SOURCE_FOLDER_NOT_FOUND -> finished(
false,
"Source folder not found"
)
ErrorCode.STORAGE_PERMISSION_DENIED -> finished(
false,
"Storage permission denied"
)
ErrorCode.TARGET_FOLDER_CANNOT_HAVE_SAME_PATH_WITH_SOURCE_FOLDER -> finished(
false,
"Target folder cannot have same path with source folder"
)
else -> finished(false, "Failed to move downloads: $errorCode")
}
Logger.log("Failed to move downloads: $errorCode")
@ -164,7 +192,7 @@ class DownloadsManager(private val context: Context) {
super.onCompleted(result)
}
})
}
}
} catch (e: Exception) {
snackString("Error: ${e.message}")
@ -189,7 +217,7 @@ class DownloadsManager(private val context: Context) {
val baseDirectory = getBaseDirectory(context, downloadedType.type)
val directory =
baseDirectory?.findFolder(downloadedType.title)?.findFolder(downloadedType.chapter)
downloadsList.remove(downloadedType)
// Check if the directory exists and delete it recursively
if (directory?.exists() == true) {
val deleted = directory.deleteRecursively(context, false)
@ -226,11 +254,7 @@ class DownloadsManager(private val context: Context) {
private const val MANGA_SUB_LOCATION = "Manga"
private const val ANIME_SUB_LOCATION = "Anime"
private const val NOVEL_SUB_LOCATION = "Novel"
private const val RESERVED_CHARS = "|\\?*<\":>+[]/'"
fun String?.findValidName(): String {
return this?.filterNot { RESERVED_CHARS.contains(it) } ?: ""
}
/**
* Get and create a base directory for the given type
@ -238,7 +262,6 @@ class DownloadsManager(private val context: Context) {
* @param type the type of media
* @return the base directory
*/
private fun getBaseDirectory(context: Context, type: MediaType): DocumentFile? {
val baseDirectory = Uri.parse(PrefManager.getVal<String>(PrefName.DownloadsDir))
if (baseDirectory == Uri.EMPTY) return null
@ -283,7 +306,12 @@ class DownloadsManager(private val context: Context) {
}
}
fun getDirSize(context: Context, type: MediaType, title: String, chapter: String? = null): Long {
fun getDirSize(
context: Context,
type: MediaType,
title: String,
chapter: String? = null
): Long {
val directory = getSubDirectory(context, type, false, title, chapter) ?: return 0
var size = 0L
directory.listFiles().forEach {
@ -303,9 +331,27 @@ class DownloadsManager(private val context: Context) {
}
}
private const val RATIO_THRESHOLD = 95
fun Media.compareName(name: String): Boolean {
val mainName = mainName().findValidName().lowercase()
val ratio = FuzzySearch.ratio(mainName, name.lowercase())
return ratio > RATIO_THRESHOLD
}
fun String.compareName(name: String): Boolean {
val mainName = findValidName().lowercase()
val compareName = name.findValidName().lowercase()
val ratio = FuzzySearch.ratio(mainName, compareName)
return ratio > RATIO_THRESHOLD
}
}
}
private const val RESERVED_CHARS = "|\\?*<\":>+[]/'"
private fun String?.findValidName(): String {
return this?.filterNot { RESERVED_CHARS.contains(it) } ?: ""
}
data class DownloadedType(
val pTitle: String, val pChapter: String, val type: MediaType
) : Serializable {

View file

@ -33,7 +33,7 @@ import ani.dantotsu.currActivity
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.findValidName
import ani.dantotsu.download.DownloadsManager.Companion.compareName
import ani.dantotsu.initActivity
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
@ -175,7 +175,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
// Get the OfflineAnimeModel that was clicked
val item = adapter.getItem(position) as OfflineAnimeModel
val media =
downloadManager.animeDownloadedTypes.firstOrNull { it.title == item.title.findValidName() }
downloadManager.animeDownloadedTypes.firstOrNull { it.title.compareName(item.title) }
media?.let {
lifecycleScope.launch {
val mediaModel = getMedia(it)

View file

@ -168,7 +168,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
// Get the OfflineMangaModel that was clicked
val item = adapter.getItem(position) as OfflineMangaModel
val media =
downloadManager.mangaDownloadedTypes.firstOrNull { it.title == item.title }
downloadManager.mangaDownloadedTypes.firstOrNull { it.title.contains(item.title) }
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title == item.title }
media?.let {
lifecycleScope.launch {

View file

@ -14,7 +14,6 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.math.MathUtils
@ -25,7 +24,6 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.offline.DownloadService
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -35,14 +33,14 @@ import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.findValidName
import ani.dantotsu.download.DownloadsManager.Companion.compareName
import ani.dantotsu.download.anime.AnimeDownloaderService
import ani.dantotsu.dp
import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.media.MediaDetailsViewModel
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.MediaType
import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription
@ -425,17 +423,27 @@ class AnimeWatchFragment : Fragment() {
}
fun onAnimeEpisodeDownloadClick(i: String) {
activity?.let{
activity?.let {
if (!hasDirAccess(it)) {
(it as MediaDetailsActivity).accessAlertDialog(it.launcher) { success ->
if (success) {
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager, isDownload = true)
model.onEpisodeClick(
media,
i,
requireActivity().supportFragmentManager,
isDownload = true
)
} else {
snackString("Permission is required to download")
}
}
} else {
model.onEpisodeClick(media, i, requireActivity().supportFragmentManager, isDownload = true)
model.onEpisodeClick(
media,
i,
requireActivity().supportFragmentManager,
isDownload = true
)
}
}
}
@ -472,10 +480,6 @@ class AnimeWatchFragment : Fragment() {
)
) {
val taskName = AnimeDownloaderService.AnimeDownloadTask.getTaskName(media.mainName(), i)
val id = PrefManager.getAnimeDownloadPreferences().getString(
taskName,
""
) ?: ""
PrefManager.getAnimeDownloadPreferences().edit().remove(taskName).apply()
episodeAdapter.deleteDownload(i)
}
@ -542,7 +546,7 @@ class AnimeWatchFragment : Fragment() {
episodeAdapter.updateType(style ?: PrefManager.getVal(PrefName.AnimeDefaultView))
episodeAdapter.notifyItemRangeInserted(0, arr.size)
for (download in downloadManager.animeDownloadedTypes) {
if (download.title == media.mainName().findValidName()) {
if (media.compareName(download.title)) {
episodeAdapter.stopDownload(download.chapter)
}
}

View file

@ -16,7 +16,6 @@ import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@ -35,7 +34,7 @@ import ani.dantotsu.R
import ani.dantotsu.databinding.FragmentAnimeWatchBinding
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.findValidName
import ani.dantotsu.download.DownloadsManager.Companion.compareName
import ani.dantotsu.download.manga.MangaDownloaderService
import ani.dantotsu.download.manga.MangaServiceDataSingleton
import ani.dantotsu.dp
@ -194,7 +193,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
)
for (download in downloadManager.mangaDownloadedTypes) {
if (download.title == media.mainName().findValidName()) {
if (media.compareName(download.title)) {
chapterAdapter.stopDownload(download.chapter)
}
}

View file

@ -1,8 +1,6 @@
package ani.dantotsu.parsers
import android.app.Application
import android.net.Uri
import android.os.Environment
import ani.dantotsu.currContext
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
@ -10,13 +8,13 @@ import ani.dantotsu.download.anime.AnimeDownloaderService.AnimeDownloadTask.Comp
import ani.dantotsu.media.MediaType
import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl
import me.xdrop.fuzzywuzzy.FuzzySearch
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.File
import java.util.Locale
class OfflineAnimeParser : AnimeParser() {
@ -80,6 +78,7 @@ class OfflineAnimeParser : AnimeParser() {
val titles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
val returnTitles: MutableList<String> = mutableListOf()
for (title in titles) {
Logger.log("Comparing $title to $query")
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
returnTitles.add(title)
}
@ -112,7 +111,7 @@ class OfflineAnimeParser : AnimeParser() {
}
class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
class OfflineVideoExtractor(private val videoServer: VideoServer) : VideoExtractor() {
override val server: VideoServer
get() = videoServer
@ -132,7 +131,7 @@ class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
private fun getSubtitle(title: String, episode: String): List<Subtitle>? {
currContext()?.let {
DownloadsManager.getSubDirectory(
getSubDirectory(
it,
MediaType.ANIME,
false,
@ -144,7 +143,7 @@ class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
Subtitle(
"Downloaded Subtitle",
file.uri.toString(),
determineSubtitletype(file.name ?: "")
determineSubtitleType(file.name ?: "")
)
)
}
@ -153,7 +152,7 @@ class OfflineVideoExtractor(val videoServer: VideoServer) : VideoExtractor() {
return null
}
fun determineSubtitletype(url: String): SubtitleType {
private fun determineSubtitleType(url: String): SubtitleType {
return when {
url.lowercase(Locale.ROOT).endsWith("ass") -> SubtitleType.ASS
url.lowercase(Locale.ROOT).endsWith("vtt") -> SubtitleType.VTT