fix: allow deprecated media to be played
This commit is contained in:
parent
3c46c21a25
commit
3622d91886
12 changed files with 647 additions and 103 deletions
381
app/src/main/java/ani/dantotsu/download/DownloadCompat.kt
Normal file
381
app/src/main/java/ani/dantotsu/download/DownloadCompat.kt
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
package ani.dantotsu.download
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
|
import android.widget.Toast
|
||||||
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
|
import ani.dantotsu.currActivity
|
||||||
|
import ani.dantotsu.currContext
|
||||||
|
import ani.dantotsu.download.anime.OfflineAnimeModel
|
||||||
|
import ani.dantotsu.download.manga.OfflineMangaModel
|
||||||
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.media.MediaNameAdapter
|
||||||
|
import ani.dantotsu.media.MediaType
|
||||||
|
import ani.dantotsu.parsers.Episode
|
||||||
|
import ani.dantotsu.parsers.MangaChapter
|
||||||
|
import ani.dantotsu.parsers.MangaImage
|
||||||
|
import ani.dantotsu.parsers.Subtitle
|
||||||
|
import ani.dantotsu.parsers.SubtitleType
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.InstanceCreator
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.SAnimeImpl
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.SEpisodeImpl
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapterImpl
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.io.File
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
class DownloadCompat {
|
||||||
|
companion object {
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun loadMediaCompat(downloadedType: DownloadedType): Media? {
|
||||||
|
val type = when (downloadedType.type) {
|
||||||
|
MediaType.MANGA -> "Manga"
|
||||||
|
MediaType.ANIME -> "Anime"
|
||||||
|
else -> "Novel"
|
||||||
|
}
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/$type/${downloadedType.titleName}"
|
||||||
|
)
|
||||||
|
//load media.json and convert to media class with gson
|
||||||
|
return try {
|
||||||
|
val gson = GsonBuilder()
|
||||||
|
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
|
||||||
|
SChapterImpl() // Provide an instance of SChapterImpl
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(SAnime::class.java, InstanceCreator<SAnime> {
|
||||||
|
SAnimeImpl() // Provide an instance of SAnimeImpl
|
||||||
|
})
|
||||||
|
.registerTypeAdapter(SEpisode::class.java, InstanceCreator<SEpisode> {
|
||||||
|
SEpisodeImpl() // Provide an instance of SEpisodeImpl
|
||||||
|
})
|
||||||
|
.create()
|
||||||
|
val media = File(directory, "media.json")
|
||||||
|
val mediaJson = media.readText()
|
||||||
|
gson.fromJson(mediaJson, Media::class.java)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.log("Error loading media.json: ${e.message}")
|
||||||
|
Logger.log(e)
|
||||||
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun loadOfflineAnimeModelCompat(downloadedType: DownloadedType): OfflineAnimeModel {
|
||||||
|
val type = when (downloadedType.type) {
|
||||||
|
MediaType.MANGA -> "Manga"
|
||||||
|
MediaType.ANIME -> "Anime"
|
||||||
|
else -> "Novel"
|
||||||
|
}
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/$type/${downloadedType.titleName}"
|
||||||
|
)
|
||||||
|
//load media.json and convert to media class with gson
|
||||||
|
try {
|
||||||
|
val mediaModel = loadMediaCompat(downloadedType)!!
|
||||||
|
val cover = File(directory, "cover.jpg")
|
||||||
|
val coverUri: Uri? = if (cover.exists()) {
|
||||||
|
Uri.fromFile(cover)
|
||||||
|
} else null
|
||||||
|
val banner = File(directory, "banner.jpg")
|
||||||
|
val bannerUri: Uri? = if (banner.exists()) {
|
||||||
|
Uri.fromFile(banner)
|
||||||
|
} else null
|
||||||
|
val title = mediaModel.mainName()
|
||||||
|
val score = ((if (mediaModel.userScore == 0) (mediaModel.meanScore
|
||||||
|
?: 0) else mediaModel.userScore) / 10.0).toString()
|
||||||
|
val isOngoing =
|
||||||
|
mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
|
||||||
|
val isUserScored = mediaModel.userScore != 0
|
||||||
|
val watchedEpisodes = (mediaModel.userProgress ?: "~").toString()
|
||||||
|
val totalEpisode =
|
||||||
|
if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString() + " | " + (mediaModel.anime.totalEpisodes
|
||||||
|
?: "~").toString()) else (mediaModel.anime?.totalEpisodes ?: "~").toString()
|
||||||
|
val chapters = " Chapters"
|
||||||
|
val totalEpisodesList =
|
||||||
|
if (mediaModel.anime?.nextAiringEpisode != null) (mediaModel.anime.nextAiringEpisode.toString()) else (mediaModel.anime?.totalEpisodes
|
||||||
|
?: "~").toString()
|
||||||
|
return OfflineAnimeModel(
|
||||||
|
title,
|
||||||
|
score,
|
||||||
|
totalEpisode,
|
||||||
|
totalEpisodesList,
|
||||||
|
watchedEpisodes,
|
||||||
|
type,
|
||||||
|
chapters,
|
||||||
|
isOngoing,
|
||||||
|
isUserScored,
|
||||||
|
coverUri,
|
||||||
|
bannerUri
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.log("Error loading media.json: ${e.message}")
|
||||||
|
Logger.log(e)
|
||||||
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
|
return OfflineAnimeModel(
|
||||||
|
"unknown",
|
||||||
|
"0",
|
||||||
|
"??",
|
||||||
|
"??",
|
||||||
|
"??",
|
||||||
|
"movie",
|
||||||
|
"hmm",
|
||||||
|
isOngoing = false,
|
||||||
|
isUserScored = false,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun loadOfflineMangaModelCompat(downloadedType: DownloadedType): OfflineMangaModel {
|
||||||
|
val type = when (downloadedType.type) {
|
||||||
|
MediaType.MANGA -> "Manga"
|
||||||
|
MediaType.ANIME -> "Anime"
|
||||||
|
else -> "Novel"
|
||||||
|
}
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/$type/${downloadedType.titleName}"
|
||||||
|
)
|
||||||
|
//load media.json and convert to media class with gson
|
||||||
|
try {
|
||||||
|
val mediaModel = loadMediaCompat(downloadedType)!!
|
||||||
|
val cover = File(directory, "cover.jpg")
|
||||||
|
val coverUri: Uri? = if (cover.exists()) {
|
||||||
|
Uri.fromFile(cover)
|
||||||
|
} else null
|
||||||
|
val banner = File(directory, "banner.jpg")
|
||||||
|
val bannerUri: Uri? = if (banner.exists()) {
|
||||||
|
Uri.fromFile(banner)
|
||||||
|
} else null
|
||||||
|
val title = mediaModel.mainName()
|
||||||
|
val score = ((if (mediaModel.userScore == 0) (mediaModel.meanScore
|
||||||
|
?: 0) else mediaModel.userScore) / 10.0).toString()
|
||||||
|
val isOngoing =
|
||||||
|
mediaModel.status == currActivity()!!.getString(R.string.status_releasing)
|
||||||
|
val isUserScored = mediaModel.userScore != 0
|
||||||
|
val readchapter = (mediaModel.userProgress ?: "~").toString()
|
||||||
|
val totalchapter = "${mediaModel.manga?.totalChapters ?: "??"}"
|
||||||
|
val chapters = " Chapters"
|
||||||
|
return OfflineMangaModel(
|
||||||
|
title,
|
||||||
|
score,
|
||||||
|
totalchapter,
|
||||||
|
readchapter,
|
||||||
|
type,
|
||||||
|
chapters,
|
||||||
|
isOngoing,
|
||||||
|
isUserScored,
|
||||||
|
coverUri,
|
||||||
|
bannerUri
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.log("Error loading media.json: ${e.message}")
|
||||||
|
Logger.log(e)
|
||||||
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
|
return OfflineMangaModel(
|
||||||
|
"unknown",
|
||||||
|
"0",
|
||||||
|
"??",
|
||||||
|
"??",
|
||||||
|
"movie",
|
||||||
|
"hmm",
|
||||||
|
isOngoing = false,
|
||||||
|
isUserScored = false,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
suspend fun loadEpisodesCompat(
|
||||||
|
animeLink: String,
|
||||||
|
extra: Map<String, String>?,
|
||||||
|
sAnime: SAnime
|
||||||
|
): List<Episode> {
|
||||||
|
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"${animeLocation}/$animeLink"
|
||||||
|
)
|
||||||
|
//get all of the folder names and add them to the list
|
||||||
|
val episodes = mutableListOf<Episode>()
|
||||||
|
if (directory.exists()) {
|
||||||
|
directory.listFiles()?.forEach {
|
||||||
|
//put the title and episdode number in the extra data
|
||||||
|
val extraData = mutableMapOf<String, String>()
|
||||||
|
extraData["title"] = animeLink
|
||||||
|
extraData["episode"] = it.name
|
||||||
|
if (it.isDirectory) {
|
||||||
|
val episode = Episode(
|
||||||
|
it.name,
|
||||||
|
"$animeLink - ${it.name}",
|
||||||
|
it.name,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
extra = extraData,
|
||||||
|
sEpisode = SEpisodeImpl()
|
||||||
|
)
|
||||||
|
episodes.add(episode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
episodes.sortBy { MediaNameAdapter.findEpisodeNumber(it.number) }
|
||||||
|
return episodes
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
suspend fun loadChaptersCompat(
|
||||||
|
mangaLink: String,
|
||||||
|
extra: Map<String, String>?,
|
||||||
|
sManga: SManga
|
||||||
|
): List<MangaChapter> {
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/Manga/$mangaLink"
|
||||||
|
)
|
||||||
|
//get all of the folder names and add them to the list
|
||||||
|
val chapters = mutableListOf<MangaChapter>()
|
||||||
|
if (directory.exists()) {
|
||||||
|
directory.listFiles()?.forEach {
|
||||||
|
if (it.isDirectory) {
|
||||||
|
val chapter = MangaChapter(
|
||||||
|
it.name,
|
||||||
|
"$mangaLink/${it.name}",
|
||||||
|
it.name,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
SChapter.create()
|
||||||
|
)
|
||||||
|
chapters.add(chapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chapters.sortBy { MediaNameAdapter.findChapterNumber(it.number) }
|
||||||
|
return chapters
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
suspend fun loadImagesCompat(chapterLink: String, sChapter: SChapter): List<MangaImage> {
|
||||||
|
val directory = File(
|
||||||
|
currContext()?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/Manga/$chapterLink"
|
||||||
|
)
|
||||||
|
val images = mutableListOf<MangaImage>()
|
||||||
|
val imageNumberRegex = Regex("""(\d+)\.jpg$""")
|
||||||
|
if (directory.exists()) {
|
||||||
|
directory.listFiles()?.forEach {
|
||||||
|
if (it.isFile) {
|
||||||
|
val image = MangaImage(it.absolutePath, false, null)
|
||||||
|
images.add(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
images.sortBy { image ->
|
||||||
|
val matchResult = imageNumberRegex.find(image.url.url)
|
||||||
|
matchResult?.groups?.get(1)?.value?.toIntOrNull() ?: Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
for (image in images) {
|
||||||
|
Logger.log("imageNumber: ${image.url.url}")
|
||||||
|
}
|
||||||
|
return images
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun loadSubtitleCompat(title: String, episode: String): List<Subtitle>? {
|
||||||
|
currContext()?.let {
|
||||||
|
File(
|
||||||
|
it.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"$animeLocation/$title/$episode"
|
||||||
|
).listFiles()?.forEach {
|
||||||
|
if (it.name.contains("subtitle")) {
|
||||||
|
return listOf(
|
||||||
|
Subtitle(
|
||||||
|
"Downloaded Subtitle",
|
||||||
|
Uri.fromFile(it).toString(),
|
||||||
|
determineSubtitletype(it.absolutePath)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineSubtitletype(url: String): SubtitleType {
|
||||||
|
return when {
|
||||||
|
url.lowercase(Locale.ROOT).endsWith("ass") -> SubtitleType.ASS
|
||||||
|
url.lowercase(Locale.ROOT).endsWith("vtt") -> SubtitleType.VTT
|
||||||
|
else -> SubtitleType.SRT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun removeMediaCompat(context: Context, title: String, type: MediaType) {
|
||||||
|
val subDirectory = if (type == MediaType.MANGA) {
|
||||||
|
"Manga"
|
||||||
|
} else if (type == MediaType.ANIME) {
|
||||||
|
"Anime"
|
||||||
|
} else {
|
||||||
|
"Novel"
|
||||||
|
}
|
||||||
|
val directory = File(
|
||||||
|
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/$subDirectory/$title"
|
||||||
|
)
|
||||||
|
if (directory.exists()) {
|
||||||
|
directory.deleteRecursively()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("external storage is deprecated, use SAF instead")
|
||||||
|
fun removeDownloadCompat(context: Context, downloadedType: DownloadedType) {
|
||||||
|
val directory = if (downloadedType.type == MediaType.MANGA) {
|
||||||
|
File(
|
||||||
|
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/Manga/${downloadedType.titleName}/${downloadedType.chapterName}"
|
||||||
|
)
|
||||||
|
} else if (downloadedType.type == MediaType.ANIME) {
|
||||||
|
File(
|
||||||
|
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/Anime/${downloadedType.titleName}/${downloadedType.chapterName}"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
File(
|
||||||
|
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
|
||||||
|
"Dantotsu/Novel/${downloadedType.titleName}/${downloadedType.chapterName}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the directory exists and delete it recursively
|
||||||
|
if (directory.exists()) {
|
||||||
|
val deleted = directory.deleteRecursively()
|
||||||
|
if (deleted) {
|
||||||
|
Toast.makeText(context, "Successfully deleted", Toast.LENGTH_SHORT).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, "Failed to delete directory", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val animeLocation = "Dantotsu/Anime"
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package ani.dantotsu.download
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.removeDownloadCompat
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.removeMediaCompat
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
import ani.dantotsu.media.MediaType
|
import ani.dantotsu.media.MediaType
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
@ -58,6 +60,7 @@ class DownloadsManager(private val context: Context) {
|
||||||
toast: Boolean = true,
|
toast: Boolean = true,
|
||||||
onFinished: () -> Unit
|
onFinished: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
removeDownloadCompat(context, downloadedType)
|
||||||
downloadsList.remove(downloadedType)
|
downloadsList.remove(downloadedType)
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
removeDirectory(downloadedType, toast)
|
removeDirectory(downloadedType, toast)
|
||||||
|
@ -69,6 +72,7 @@ class DownloadsManager(private val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeMedia(title: String, type: MediaType) {
|
fun removeMedia(title: String, type: MediaType) {
|
||||||
|
removeMediaCompat(context, title, type)
|
||||||
val baseDirectory = getBaseDirectory(context, type)
|
val baseDirectory = getBaseDirectory(context, type)
|
||||||
val directory = baseDirectory?.findFolder(title)
|
val directory = baseDirectory?.findFolder(title)
|
||||||
if (directory?.exists() == true) {
|
if (directory?.exists() == true) {
|
||||||
|
@ -84,15 +88,15 @@ class DownloadsManager(private val context: Context) {
|
||||||
}
|
}
|
||||||
when (type) {
|
when (type) {
|
||||||
MediaType.MANGA -> {
|
MediaType.MANGA -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == MediaType.MANGA }
|
downloadsList.removeAll { it.titleName == title && it.type == MediaType.MANGA }
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.ANIME -> {
|
MediaType.ANIME -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == MediaType.ANIME }
|
downloadsList.removeAll { it.titleName == title && it.type == MediaType.ANIME }
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.NOVEL -> {
|
MediaType.NOVEL -> {
|
||||||
downloadsList.removeAll { it.title == title && it.type == MediaType.NOVEL }
|
downloadsList.removeAll { it.titleName == title && it.type == MediaType.NOVEL }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
saveDownloads()
|
saveDownloads()
|
||||||
|
@ -115,7 +119,7 @@ class DownloadsManager(private val context: Context) {
|
||||||
if (directory?.exists() == true && directory.isDirectory) {
|
if (directory?.exists() == true && directory.isDirectory) {
|
||||||
val files = directory.listFiles()
|
val files = directory.listFiles()
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
if (!downloadsSubLists.any { it.title == file.name }) {
|
if (!downloadsSubLists.any { it.titleName == file.name }) {
|
||||||
file.deleteRecursively(context, false)
|
file.deleteRecursively(context, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,8 +128,8 @@ class DownloadsManager(private val context: Context) {
|
||||||
val iterator = downloadsList.iterator()
|
val iterator = downloadsList.iterator()
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
val download = iterator.next()
|
val download = iterator.next()
|
||||||
val downloadDir = directory?.findFolder(download.title)
|
val downloadDir = directory?.findFolder(download.titleName)
|
||||||
if ((downloadDir?.exists() == false && download.type == type) || download.title.isBlank()) {
|
if ((downloadDir?.exists() == false && download.type == type) || download.titleName.isBlank()) {
|
||||||
iterator.remove()
|
iterator.remove()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,16 +215,17 @@ class DownloadsManager(private val context: Context) {
|
||||||
|
|
||||||
fun queryDownload(title: String, chapter: String, type: MediaType? = null): Boolean {
|
fun queryDownload(title: String, chapter: String, type: MediaType? = null): Boolean {
|
||||||
return if (type == null) {
|
return if (type == null) {
|
||||||
downloadsList.any { it.title == title && it.chapter == chapter }
|
downloadsList.any { it.titleName == title && it.chapterName == chapter }
|
||||||
} else {
|
} else {
|
||||||
downloadsList.any { it.title == title && it.chapter == chapter && it.type == type }
|
downloadsList.any { it.titleName == title && it.chapterName == chapter && it.type == type }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun removeDirectory(downloadedType: DownloadedType, toast: Boolean) {
|
private fun removeDirectory(downloadedType: DownloadedType, toast: Boolean) {
|
||||||
val baseDirectory = getBaseDirectory(context, downloadedType.type)
|
val baseDirectory = getBaseDirectory(context, downloadedType.type)
|
||||||
val directory =
|
val directory =
|
||||||
baseDirectory?.findFolder(downloadedType.title)?.findFolder(downloadedType.chapter)
|
baseDirectory?.findFolder(downloadedType.titleName)
|
||||||
|
?.findFolder(downloadedType.chapterName)
|
||||||
downloadsList.remove(downloadedType)
|
downloadsList.remove(downloadedType)
|
||||||
// Check if the directory exists and delete it recursively
|
// Check if the directory exists and delete it recursively
|
||||||
if (directory?.exists() == true) {
|
if (directory?.exists() == true) {
|
||||||
|
@ -369,10 +374,16 @@ private fun String?.findValidName(): String {
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DownloadedType(
|
data class DownloadedType(
|
||||||
val pTitle: String, val pChapter: String, val type: MediaType
|
private val pTitle: String?,
|
||||||
|
private val pChapter: String?,
|
||||||
|
val type: MediaType,
|
||||||
|
@Deprecated("use pTitle instead")
|
||||||
|
private val title: String? = null,
|
||||||
|
@Deprecated("use pChapter instead")
|
||||||
|
private val chapter: String? = null
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
val title: String
|
val titleName: String
|
||||||
get() = pTitle.findValidName()
|
get() = title?:pTitle.findValidName()
|
||||||
val chapter: String
|
val chapterName: String
|
||||||
get() = pChapter.findValidName()
|
get() = chapter?:pChapter.findValidName()
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import ani.dantotsu.bottomBar
|
||||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.currActivity
|
import ani.dantotsu.currActivity
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadMediaCompat
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadOfflineAnimeModelCompat
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.compareName
|
import ani.dantotsu.download.DownloadsManager.Companion.compareName
|
||||||
|
@ -175,7 +177,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
// Get the OfflineAnimeModel that was clicked
|
// Get the OfflineAnimeModel that was clicked
|
||||||
val item = adapter.getItem(position) as OfflineAnimeModel
|
val item = adapter.getItem(position) as OfflineAnimeModel
|
||||||
val media =
|
val media =
|
||||||
downloadManager.animeDownloadedTypes.firstOrNull { it.title.compareName(item.title) }
|
downloadManager.animeDownloadedTypes.firstOrNull { it.titleName.compareName(item.title) }
|
||||||
media?.let {
|
media?.let {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val mediaModel = getMedia(it)
|
val mediaModel = getMedia(it)
|
||||||
|
@ -287,10 +289,10 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
}
|
}
|
||||||
downloadsJob = Job()
|
downloadsJob = Job()
|
||||||
CoroutineScope(Dispatchers.IO + downloadsJob).launch {
|
CoroutineScope(Dispatchers.IO + downloadsJob).launch {
|
||||||
val animeTitles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
|
val animeTitles = downloadManager.animeDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val newAnimeDownloads = mutableListOf<OfflineAnimeModel>()
|
val newAnimeDownloads = mutableListOf<OfflineAnimeModel>()
|
||||||
for (title in animeTitles) {
|
for (title in animeTitles) {
|
||||||
val tDownloads = downloadManager.animeDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.animeDownloadedTypes.filter { it.titleName == title }
|
||||||
val download = tDownloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineAnimeModel = loadOfflineAnimeModel(download)
|
val offlineAnimeModel = loadOfflineAnimeModel(download)
|
||||||
newAnimeDownloads += offlineAnimeModel
|
newAnimeDownloads += offlineAnimeModel
|
||||||
|
@ -313,7 +315,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
return try {
|
return try {
|
||||||
val directory = DownloadsManager.getSubDirectory(
|
val directory = DownloadsManager.getSubDirectory(
|
||||||
context ?: currContext()!!, downloadedType.type,
|
context ?: currContext()!!, downloadedType.type,
|
||||||
false, downloadedType.title
|
false, downloadedType.titleName
|
||||||
)
|
)
|
||||||
val gson = GsonBuilder()
|
val gson = GsonBuilder()
|
||||||
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
|
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
|
||||||
|
@ -327,7 +329,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
val media = directory?.findFile("media.json")
|
val media = directory?.findFile("media.json")
|
||||||
?: return null
|
?: return loadMediaCompat(downloadedType)
|
||||||
val mediaJson =
|
val mediaJson =
|
||||||
media.openInputStream(context ?: currContext()!!)?.bufferedReader().use {
|
media.openInputStream(context ?: currContext()!!)?.bufferedReader().use {
|
||||||
it?.readText()
|
it?.readText()
|
||||||
|
@ -352,7 +354,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
try {
|
try {
|
||||||
val directory = DownloadsManager.getSubDirectory(
|
val directory = DownloadsManager.getSubDirectory(
|
||||||
context ?: currContext()!!, downloadedType.type,
|
context ?: currContext()!!, downloadedType.type,
|
||||||
false, downloadedType.title
|
false, downloadedType.titleName
|
||||||
)
|
)
|
||||||
val mediaModel = getMedia(downloadedType)!!
|
val mediaModel = getMedia(downloadedType)!!
|
||||||
val cover = directory?.findFile("cover.jpg")
|
val cover = directory?.findFile("cover.jpg")
|
||||||
|
@ -391,22 +393,26 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
|
||||||
bannerUri
|
bannerUri
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Logger.log("Error loading media.json: ${e.message}")
|
return try {
|
||||||
Logger.log(e)
|
loadOfflineAnimeModelCompat(downloadedType)
|
||||||
Injekt.get<CrashlyticsInterface>().logException(e)
|
} catch (e: Exception) {
|
||||||
return OfflineAnimeModel(
|
Logger.log("Error loading media.json: ${e.message}")
|
||||||
"unknown",
|
Logger.log(e)
|
||||||
"0",
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
"??",
|
OfflineAnimeModel(
|
||||||
"??",
|
"unknown",
|
||||||
"??",
|
"0",
|
||||||
"movie",
|
"??",
|
||||||
"hmm",
|
"??",
|
||||||
isOngoing = false,
|
"??",
|
||||||
isUserScored = false,
|
"movie",
|
||||||
null,
|
"hmm",
|
||||||
null
|
isOngoing = false,
|
||||||
)
|
isUserScored = false,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import ani.dantotsu.bottomBar
|
||||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.currActivity
|
import ani.dantotsu.currActivity
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
|
import ani.dantotsu.download.DownloadCompat
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadOfflineMangaModelCompat
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.compareName
|
import ani.dantotsu.download.DownloadsManager.Companion.compareName
|
||||||
|
@ -169,8 +171,8 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
// Get the OfflineMangaModel that was clicked
|
// Get the OfflineMangaModel that was clicked
|
||||||
val item = adapter.getItem(position) as OfflineMangaModel
|
val item = adapter.getItem(position) as OfflineMangaModel
|
||||||
val media =
|
val media =
|
||||||
downloadManager.mangaDownloadedTypes.firstOrNull { it.title.compareName(item.title) }
|
downloadManager.mangaDownloadedTypes.firstOrNull { it.titleName.compareName(item.title) }
|
||||||
?: downloadManager.novelDownloadedTypes.firstOrNull { it.title.compareName(item.title) }
|
?: downloadManager.novelDownloadedTypes.firstOrNull { it.titleName.compareName(item.title) }
|
||||||
media?.let {
|
media?.let {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
ContextCompat.startActivity(
|
ContextCompat.startActivity(
|
||||||
|
@ -190,7 +192,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
// Get the OfflineMangaModel that was clicked
|
// Get the OfflineMangaModel that was clicked
|
||||||
val item = adapter.getItem(position) as OfflineMangaModel
|
val item = adapter.getItem(position) as OfflineMangaModel
|
||||||
val type: MediaType =
|
val type: MediaType =
|
||||||
if (downloadManager.mangaDownloadedTypes.any { it.title == item.title }) {
|
if (downloadManager.mangaDownloadedTypes.any { it.titleName == item.title }) {
|
||||||
MediaType.MANGA
|
MediaType.MANGA
|
||||||
} else {
|
} else {
|
||||||
MediaType.NOVEL
|
MediaType.NOVEL
|
||||||
|
@ -278,19 +280,19 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
downloads = listOf()
|
downloads = listOf()
|
||||||
downloadsJob = Job()
|
downloadsJob = Job()
|
||||||
CoroutineScope(Dispatchers.IO + downloadsJob).launch {
|
CoroutineScope(Dispatchers.IO + downloadsJob).launch {
|
||||||
val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
|
val mangaTitles = downloadManager.mangaDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val newMangaDownloads = mutableListOf<OfflineMangaModel>()
|
val newMangaDownloads = mutableListOf<OfflineMangaModel>()
|
||||||
for (title in mangaTitles) {
|
for (title in mangaTitles) {
|
||||||
val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.mangaDownloadedTypes.filter { it.titleName == title }
|
||||||
val download = tDownloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineMangaModel = loadOfflineMangaModel(download)
|
val offlineMangaModel = loadOfflineMangaModel(download)
|
||||||
newMangaDownloads += offlineMangaModel
|
newMangaDownloads += offlineMangaModel
|
||||||
}
|
}
|
||||||
downloads = newMangaDownloads
|
downloads = newMangaDownloads
|
||||||
val novelTitles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
|
val novelTitles = downloadManager.novelDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val newNovelDownloads = mutableListOf<OfflineMangaModel>()
|
val newNovelDownloads = mutableListOf<OfflineMangaModel>()
|
||||||
for (title in novelTitles) {
|
for (title in novelTitles) {
|
||||||
val tDownloads = downloadManager.novelDownloadedTypes.filter { it.title == title }
|
val tDownloads = downloadManager.novelDownloadedTypes.filter { it.titleName == title }
|
||||||
val download = tDownloads.first()
|
val download = tDownloads.first()
|
||||||
val offlineMangaModel = loadOfflineMangaModel(download)
|
val offlineMangaModel = loadOfflineMangaModel(download)
|
||||||
newNovelDownloads += offlineMangaModel
|
newNovelDownloads += offlineMangaModel
|
||||||
|
@ -315,7 +317,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
return try {
|
return try {
|
||||||
val directory = getSubDirectory(
|
val directory = getSubDirectory(
|
||||||
context ?: currContext()!!, downloadedType.type,
|
context ?: currContext()!!, downloadedType.type,
|
||||||
false, downloadedType.title
|
false, downloadedType.titleName
|
||||||
)
|
)
|
||||||
val gson = GsonBuilder()
|
val gson = GsonBuilder()
|
||||||
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
|
.registerTypeAdapter(SChapter::class.java, InstanceCreator<SChapter> {
|
||||||
|
@ -323,7 +325,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
val media = directory?.findFile("media.json")
|
val media = directory?.findFile("media.json")
|
||||||
?: return null
|
?: return DownloadCompat.loadMediaCompat(downloadedType)
|
||||||
val mediaJson =
|
val mediaJson =
|
||||||
media.openInputStream(context ?: currContext()!!)?.bufferedReader().use {
|
media.openInputStream(context ?: currContext()!!)?.bufferedReader().use {
|
||||||
it?.readText()
|
it?.readText()
|
||||||
|
@ -343,7 +345,7 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
try {
|
try {
|
||||||
val directory = getSubDirectory(
|
val directory = getSubDirectory(
|
||||||
context ?: currContext()!!, downloadedType.type,
|
context ?: currContext()!!, downloadedType.type,
|
||||||
false, downloadedType.title
|
false, downloadedType.titleName
|
||||||
)
|
)
|
||||||
val mediaModel = getMedia(downloadedType)!!
|
val mediaModel = getMedia(downloadedType)!!
|
||||||
val cover = directory?.findFile("cover.jpg")
|
val cover = directory?.findFile("cover.jpg")
|
||||||
|
@ -376,21 +378,25 @@ class OfflineMangaFragment : Fragment(), OfflineMangaSearchListener {
|
||||||
bannerUri
|
bannerUri
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Logger.log("Error loading media.json: ${e.message}")
|
return try {
|
||||||
Logger.log(e)
|
loadOfflineMangaModelCompat(downloadedType)
|
||||||
Injekt.get<CrashlyticsInterface>().logException(e)
|
} catch (e: Exception) {
|
||||||
return OfflineMangaModel(
|
Logger.log("Error loading media.json: ${e.message}")
|
||||||
"unknown",
|
Logger.log(e)
|
||||||
"0",
|
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||||
"??",
|
return OfflineMangaModel(
|
||||||
"??",
|
"unknown",
|
||||||
"movie",
|
"0",
|
||||||
"hmm",
|
"??",
|
||||||
isOngoing = false,
|
"??",
|
||||||
isUserScored = false,
|
"movie",
|
||||||
null,
|
"hmm",
|
||||||
null
|
isOngoing = false,
|
||||||
)
|
isUserScored = false,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,17 @@ import androidx.annotation.OptIn
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import androidx.media3.database.StandaloneDatabaseProvider
|
||||||
|
import androidx.media3.datasource.DataSource
|
||||||
|
import androidx.media3.datasource.HttpDataSource
|
||||||
|
import androidx.media3.datasource.cache.NoOpCacheEvictor
|
||||||
|
import androidx.media3.datasource.cache.SimpleCache
|
||||||
|
import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||||
|
import androidx.media3.exoplayer.offline.Download
|
||||||
|
import androidx.media3.exoplayer.offline.DownloadManager
|
||||||
|
import androidx.media3.exoplayer.scheduler.Requirements
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.defaultHeaders
|
||||||
import ani.dantotsu.download.DownloadedType
|
import ani.dantotsu.download.DownloadedType
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.anime.AnimeDownloaderService
|
import ani.dantotsu.download.anime.AnimeDownloaderService
|
||||||
|
@ -22,8 +32,12 @@ import ani.dantotsu.media.MediaType
|
||||||
import ani.dantotsu.parsers.Subtitle
|
import ani.dantotsu.parsers.Subtitle
|
||||||
import ani.dantotsu.parsers.Video
|
import ani.dantotsu.parsers.Video
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.io.File
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
object Helper {
|
object Helper {
|
||||||
|
@ -104,4 +118,92 @@ object Helper {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
@UnstableApi
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
fun downloadManager(context: Context): DownloadManager {
|
||||||
|
return download ?: let {
|
||||||
|
val database = Injekt.get<StandaloneDatabaseProvider>()
|
||||||
|
val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY)
|
||||||
|
val dataSourceFactory = DataSource.Factory {
|
||||||
|
//val dataSource: HttpDataSource = OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||||
|
val networkHelper = Injekt.get<NetworkHelper>()
|
||||||
|
val okHttpClient = networkHelper.client
|
||||||
|
val dataSource: HttpDataSource =
|
||||||
|
OkHttpDataSource.Factory(okHttpClient).createDataSource()
|
||||||
|
defaultHeaders.forEach {
|
||||||
|
dataSource.setRequestProperty(it.key, it.value)
|
||||||
|
}
|
||||||
|
dataSource
|
||||||
|
}
|
||||||
|
val threadPoolSize = Runtime.getRuntime().availableProcessors()
|
||||||
|
val executorService = Executors.newFixedThreadPool(threadPoolSize)
|
||||||
|
val downloadManager = DownloadManager(
|
||||||
|
context,
|
||||||
|
database,
|
||||||
|
getSimpleCache(context),
|
||||||
|
dataSourceFactory,
|
||||||
|
executorService
|
||||||
|
).apply {
|
||||||
|
requirements =
|
||||||
|
Requirements(Requirements.NETWORK or Requirements.DEVICE_STORAGE_NOT_LOW)
|
||||||
|
maxParallelDownloads = 3
|
||||||
|
}
|
||||||
|
downloadManager.addListener( //for testing
|
||||||
|
object : DownloadManager.Listener {
|
||||||
|
override fun onDownloadChanged(
|
||||||
|
downloadManager: DownloadManager,
|
||||||
|
download: Download,
|
||||||
|
finalException: Exception?
|
||||||
|
) {
|
||||||
|
if (download.state == Download.STATE_COMPLETED) {
|
||||||
|
Logger.log("Download Completed")
|
||||||
|
} else if (download.state == Download.STATE_FAILED) {
|
||||||
|
Logger.log("Download Failed")
|
||||||
|
} else if (download.state == Download.STATE_STOPPED) {
|
||||||
|
Logger.log("Download Stopped")
|
||||||
|
} else if (download.state == Download.STATE_QUEUED) {
|
||||||
|
Logger.log("Download Queued")
|
||||||
|
} else if (download.state == Download.STATE_DOWNLOADING) {
|
||||||
|
Logger.log("Download Downloading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
downloadManager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
@OptIn(UnstableApi::class)
|
||||||
|
fun getSimpleCache(context: Context): SimpleCache {
|
||||||
|
return if (simpleCache == null) {
|
||||||
|
val downloadDirectory = File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY)
|
||||||
|
val database = Injekt.get<StandaloneDatabaseProvider>()
|
||||||
|
simpleCache = SimpleCache(downloadDirectory, NoOpCacheEvictor(), database)
|
||||||
|
simpleCache!!
|
||||||
|
} else {
|
||||||
|
simpleCache!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Synchronized
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
private fun getDownloadDirectory(context: Context): File {
|
||||||
|
if (downloadDirectory == null) {
|
||||||
|
downloadDirectory = context.getExternalFilesDir(null)
|
||||||
|
if (downloadDirectory == null) {
|
||||||
|
downloadDirectory = context.filesDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return downloadDirectory!!
|
||||||
|
}
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
private var download: DownloadManager? = null
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
private const val DOWNLOAD_CONTENT_DIRECTORY = "Anime_Downloads"
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
private var simpleCache: SimpleCache? = null
|
||||||
|
@Deprecated("exoplayer download manager is no longer used")
|
||||||
|
private var downloadDirectory: File? = null
|
||||||
}
|
}
|
|
@ -55,8 +55,8 @@ class SubtitleDownloader {
|
||||||
context,
|
context,
|
||||||
downloadedType.type,
|
downloadedType.type,
|
||||||
false,
|
false,
|
||||||
downloadedType.title,
|
downloadedType.titleName,
|
||||||
downloadedType.chapter
|
downloadedType.chapterName
|
||||||
) ?: throw Exception("Could not create directory")
|
) ?: throw Exception("Could not create directory")
|
||||||
val type = loadSubtitleType(url)
|
val type = loadSubtitleType(url)
|
||||||
directory.findFile("subtitle.${type}")?.delete()
|
directory.findFile("subtitle.${type}")?.delete()
|
||||||
|
|
|
@ -553,8 +553,8 @@ class AnimeWatchFragment : Fragment() {
|
||||||
episodeAdapter.updateType(style ?: PrefManager.getVal(PrefName.AnimeDefaultView))
|
episodeAdapter.updateType(style ?: PrefManager.getVal(PrefName.AnimeDefaultView))
|
||||||
episodeAdapter.notifyItemRangeInserted(0, arr.size)
|
episodeAdapter.notifyItemRangeInserted(0, arr.size)
|
||||||
for (download in downloadManager.animeDownloadedTypes) {
|
for (download in downloadManager.animeDownloadedTypes) {
|
||||||
if (media.compareName(download.title)) {
|
if (media.compareName(download.titleName)) {
|
||||||
episodeAdapter.stopDownload(download.chapter)
|
episodeAdapter.stopDownload(download.chapterName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@ import ani.dantotsu.connections.updateProgress
|
||||||
import ani.dantotsu.databinding.ActivityExoplayerBinding
|
import ani.dantotsu.databinding.ActivityExoplayerBinding
|
||||||
import ani.dantotsu.defaultHeaders
|
import ani.dantotsu.defaultHeaders
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
||||||
|
import ani.dantotsu.download.video.Helper
|
||||||
import ani.dantotsu.dp
|
import ani.dantotsu.dp
|
||||||
import ani.dantotsu.getCurrentBrightnessValue
|
import ani.dantotsu.getCurrentBrightnessValue
|
||||||
import ani.dantotsu.hideSystemBars
|
import ani.dantotsu.hideSystemBars
|
||||||
|
@ -1481,26 +1482,38 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
|
||||||
val downloadedMediaItem = if (ext.server.offline) {
|
val downloadedMediaItem = if (ext.server.offline) {
|
||||||
val titleName = ext.server.name.split("/").first()
|
val titleName = ext.server.name.split("/").first()
|
||||||
val episodeName = ext.server.name.split("/").last()
|
val episodeName = ext.server.name.split("/").last()
|
||||||
|
downloadId = PrefManager.getAnimeDownloadPreferences()
|
||||||
|
.getString("$titleName - $episodeName", null) ?:
|
||||||
|
PrefManager.getAnimeDownloadPreferences()
|
||||||
|
.getString(ext.server.name, null)
|
||||||
|
val exoItem = if (downloadId != null) {
|
||||||
|
Helper.downloadManager(this)
|
||||||
|
.downloadIndex.getDownload(downloadId!!)?.request?.toMediaItem()
|
||||||
|
} else null
|
||||||
|
if (exoItem != null) {
|
||||||
|
exoItem
|
||||||
|
} else {
|
||||||
|
|
||||||
val directory = getSubDirectory(this, MediaType.ANIME, false, titleName, episodeName)
|
val directory =
|
||||||
if (directory != null) {
|
getSubDirectory(this, MediaType.ANIME, false, titleName, episodeName)
|
||||||
val files = directory.listFiles()
|
if (directory != null) {
|
||||||
println(files)
|
val files = directory.listFiles()
|
||||||
val docFile = directory.listFiles().firstOrNull {
|
println(files)
|
||||||
it.name?.endsWith(".mp4") == true || it.name?.endsWith(".mkv") == true
|
val docFile = directory.listFiles().firstOrNull {
|
||||||
}
|
it.name?.endsWith(".mp4") == true || it.name?.endsWith(".mkv") == true
|
||||||
if (docFile != null) {
|
}
|
||||||
val uri = docFile.uri
|
if (docFile != null) {
|
||||||
MediaItem.Builder().setUri(uri).setMimeType(mimeType).build()
|
val uri = docFile.uri
|
||||||
|
MediaItem.Builder().setUri(uri).setMimeType(mimeType).build()
|
||||||
|
} else {
|
||||||
|
snackString("File not found")
|
||||||
|
null
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
snackString("File not found")
|
snackString("Directory not found")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
snackString("Directory not found")
|
|
||||||
null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else null
|
} else null
|
||||||
|
|
||||||
mediaItem = if (downloadedMediaItem == null) {
|
mediaItem = if (downloadedMediaItem == null) {
|
||||||
|
|
|
@ -194,8 +194,8 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
)
|
)
|
||||||
|
|
||||||
for (download in downloadManager.mangaDownloadedTypes) {
|
for (download in downloadManager.mangaDownloadedTypes) {
|
||||||
if (media.compareName(download.title)) {
|
if (media.compareName(download.titleName)) {
|
||||||
chapterAdapter.stopDownload(download.chapter)
|
chapterAdapter.stopDownload(download.chapterName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ package ani.dantotsu.parsers
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadEpisodesCompat
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadSubtitleCompat
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
||||||
import ani.dantotsu.download.anime.AnimeDownloaderService.AnimeDownloadTask.Companion.getTaskName
|
import ani.dantotsu.download.anime.AnimeDownloaderService.AnimeDownloadTask.Companion.getTaskName
|
||||||
|
@ -53,8 +55,12 @@ class OfflineAnimeParser : AnimeParser() {
|
||||||
episodes.add(episode)
|
episodes.add(episode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
episodes.sortBy { MediaNameAdapter.findEpisodeNumber(it.number) }
|
return if (episodes.isNotEmpty()) {
|
||||||
return episodes
|
episodes.sortBy { MediaNameAdapter.findEpisodeNumber(it.number) }
|
||||||
|
episodes
|
||||||
|
} else {
|
||||||
|
loadEpisodesCompat(animeLink, extra, sAnime)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
@ -75,14 +81,16 @@ class OfflineAnimeParser : AnimeParser() {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun search(query: String): List<ShowResponse> {
|
override suspend fun search(query: String): List<ShowResponse> {
|
||||||
val titles = downloadManager.animeDownloadedTypes.map { it.title }.distinct()
|
val titles = downloadManager.animeDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val returnTitles: MutableList<String> = mutableListOf()
|
val returnTitlesPair: MutableList<Pair<String, Int>> = mutableListOf()
|
||||||
for (title in titles) {
|
for (title in titles) {
|
||||||
Logger.log("Comparing $title to $query")
|
Logger.log("Comparing $title to $query")
|
||||||
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
|
val score = FuzzySearch.ratio(title.lowercase(), query.lowercase())
|
||||||
returnTitles.add(title)
|
if (score > 80) {
|
||||||
|
returnTitlesPair.add(Pair(title, score))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val returnTitles = returnTitlesPair.sortedByDescending { it.second }.map { it.first }
|
||||||
val returnList: MutableList<ShowResponse> = mutableListOf()
|
val returnList: MutableList<ShowResponse> = mutableListOf()
|
||||||
for (title in returnTitles) {
|
for (title in returnTitles) {
|
||||||
returnList.add(ShowResponse(title, title, title))
|
returnList.add(ShowResponse(title, title, title))
|
||||||
|
@ -148,6 +156,7 @@ class OfflineVideoExtractor(private val videoServer: VideoServer) : VideoExtract
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loadSubtitleCompat(title, episode)?.let { return it }
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ani.dantotsu.parsers
|
package ani.dantotsu.parsers
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadChaptersCompat
|
||||||
|
import ani.dantotsu.download.DownloadCompat.Companion.loadImagesCompat
|
||||||
import ani.dantotsu.download.DownloadsManager
|
import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
||||||
import ani.dantotsu.media.MediaNameAdapter
|
import ani.dantotsu.media.MediaNameAdapter
|
||||||
|
@ -41,8 +43,12 @@ class OfflineMangaParser : MangaParser() {
|
||||||
chapters.add(chapter)
|
chapters.add(chapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chapters.sortBy { MediaNameAdapter.findChapterNumber(it.number) }
|
return if (chapters.isNotEmpty()) {
|
||||||
return chapters
|
chapters.sortBy { MediaNameAdapter.findChapterNumber(it.number) }
|
||||||
|
chapters
|
||||||
|
} else {
|
||||||
|
loadChaptersCompat(mangaLink, extra, sManga)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
@ -60,26 +66,32 @@ class OfflineMangaParser : MangaParser() {
|
||||||
images.add(image)
|
images.add(image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images.sortBy { image ->
|
|
||||||
val matchResult = imageNumberRegex.find(image.url.url)
|
|
||||||
matchResult?.groups?.get(1)?.value?.toIntOrNull() ?: Int.MAX_VALUE
|
|
||||||
}
|
|
||||||
for (image in images) {
|
for (image in images) {
|
||||||
Logger.log("imageNumber: ${image.url.url}")
|
Logger.log("imageNumber: ${image.url.url}")
|
||||||
}
|
}
|
||||||
return images
|
return if (images.isNotEmpty()) {
|
||||||
|
images.sortBy { image ->
|
||||||
|
val matchResult = imageNumberRegex.find(image.url.url)
|
||||||
|
matchResult?.groups?.get(1)?.value?.toIntOrNull() ?: Int.MAX_VALUE
|
||||||
|
}
|
||||||
|
images
|
||||||
|
} else {
|
||||||
|
loadImagesCompat(chapterLink, sChapter)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<ShowResponse> {
|
override suspend fun search(query: String): List<ShowResponse> {
|
||||||
val titles = downloadManager.mangaDownloadedTypes.map { it.title }.distinct()
|
val titles = downloadManager.mangaDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val returnTitles: MutableList<String> = mutableListOf()
|
val returnTitlesPair: MutableList<Pair<String, Int>> = mutableListOf()
|
||||||
for (title in titles) {
|
for (title in titles) {
|
||||||
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
|
val score = FuzzySearch.ratio(title.lowercase(), query.lowercase())
|
||||||
returnTitles.add(title)
|
if (score > 80) {
|
||||||
|
returnTitlesPair.add(Pair(title, score))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val returnTitles = returnTitlesPair.sortedByDescending { it.second }.map { it.first }
|
||||||
val returnList: MutableList<ShowResponse> = mutableListOf()
|
val returnList: MutableList<ShowResponse> = mutableListOf()
|
||||||
for (title in returnTitles) {
|
for (title in returnTitles) {
|
||||||
returnList.add(ShowResponse(title, title, title))
|
returnList.add(ShowResponse(title, title, title))
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ani.dantotsu.download.DownloadsManager
|
||||||
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
import ani.dantotsu.download.DownloadsManager.Companion.getSubDirectory
|
||||||
import ani.dantotsu.media.MediaNameAdapter
|
import ani.dantotsu.media.MediaNameAdapter
|
||||||
import ani.dantotsu.media.MediaType
|
import ani.dantotsu.media.MediaType
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
@ -48,13 +49,16 @@ class OfflineNovelParser : NovelParser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<ShowResponse> {
|
override suspend fun search(query: String): List<ShowResponse> {
|
||||||
val titles = downloadManager.novelDownloadedTypes.map { it.title }.distinct()
|
val titles = downloadManager.novelDownloadedTypes.map { it.titleName }.distinct()
|
||||||
val returnTitles: MutableList<String> = mutableListOf()
|
val returnTitlesPair: MutableList<Pair<String, Int>> = mutableListOf()
|
||||||
for (title in titles) {
|
for (title in titles) {
|
||||||
if (FuzzySearch.ratio(title.lowercase(), query.lowercase()) > 80) {
|
Logger.log("Comparing $title to $query")
|
||||||
returnTitles.add(title)
|
val score = FuzzySearch.ratio(title.lowercase(), query.lowercase())
|
||||||
|
if (score > 80) {
|
||||||
|
returnTitlesPair.add(Pair(title, score))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
val returnTitles = returnTitlesPair.sortedByDescending { it.second }.map { it.first }
|
||||||
val returnList: MutableList<ShowResponse> = mutableListOf()
|
val returnList: MutableList<ShowResponse> = mutableListOf()
|
||||||
for (title in returnTitles) {
|
for (title in returnTitles) {
|
||||||
//need to search the subdirectories for the ShowResponses
|
//need to search the subdirectories for the ShowResponses
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue