fix: download name comparison
This commit is contained in:
parent
f6c7b09d9b
commit
5fcbfeb3db
6 changed files with 91 additions and 43 deletions
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue