diff --git a/README.md b/README.md
index a1b1d673..6b713a2a 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@ Dantotsu is an [Anilist](https://anilist.co/) only client.
> **Dantotsu (断トツ; Dan-totsu)** literally means "the best of the best" in Japanese. Try it out for yourself and be the judge!
+
+
## Terms of Use
By downloading, installing, or using this application, you agree to:
- Use the application in compliance with all applicable laws
diff --git a/app/build.gradle b/app/build.gradle
index 535d8922..0604d87e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,9 +17,8 @@ android {
applicationId "ani.dantotsu"
minSdk 21
targetSdk 35
- versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "3.2.2"
- versionCode versionName.split("\\.").collect { it.toInteger() * 100 }.join("") as Integer
+ versionCode 300200200
signingConfig signingConfigs.debug
}
@@ -48,10 +47,6 @@ android {
manifestPlaceholders.icon_placeholder_round = "@mipmap/ic_launcher_alpha_round"
debuggable System.getenv("CI") == null
isDefault true
- debuggable true
- jniDebuggable true
- minifyEnabled false
- shrinkResources false
}
debug {
applicationIdSuffix ".beta"
@@ -85,26 +80,25 @@ android {
dependencies {
// FireBase
- googleImplementation platform('com.google.firebase:firebase-bom:33.13.0')
- googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.4.0'
- googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.4.3'
+ googleImplementation platform('com.google.firebase:firebase-bom:33.0.0')
+ googleImplementation 'com.google.firebase:firebase-analytics-ktx:22.0.0'
+ googleImplementation 'com.google.firebase:firebase-crashlytics-ktx:19.0.0'
// Core
- implementation 'androidx.appcompat:appcompat:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.browser:browser:1.8.0'
- implementation 'androidx.core:core-ktx:1.16.0'
- implementation 'androidx.fragment:fragment-ktx:1.8.6'
- implementation 'androidx.activity:activity-ktx:1.10.1'
+ implementation 'androidx.core:core-ktx:1.13.1'
+ implementation 'androidx.fragment:fragment-ktx:1.6.2'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.multidex:multidex:2.0.1'
- implementation "androidx.work:work-runtime-ktx:2.10.1"
+ implementation "androidx.work:work-runtime-ktx:2.9.0"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.github.Blatzar:NiceHttp:0.4.4'
- implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3'
implementation 'androidx.preference:preference-ktx:1.2.1'
- implementation 'androidx.webkit:webkit:1.13.0'
+ implementation 'androidx.webkit:webkit:1.11.0'
implementation "com.anggrayudi:storage:1.5.5"
implementation "androidx.biometric:biometric:1.1.0"
@@ -118,7 +112,7 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.3.0'
// Exoplayer
- ext.exo_version = '1.6.1'
+ ext.exo_version = '1.5.0'
implementation "androidx.media3:media3-exoplayer:$exo_version"
implementation "androidx.media3:media3-ui:$exo_version"
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
@@ -129,7 +123,7 @@ dependencies {
implementation "androidx.media3:media3-cast:$exo_version"
implementation "androidx.mediarouter:mediarouter:1.7.0"
// Media3 extension
- implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.4"
+ implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.3"
// UI
implementation 'com.google.android.material:material:1.12.0'
@@ -138,7 +132,7 @@ dependencies {
implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
implementation 'com.alexvasilkov:gesture-views:2.8.3'
implementation 'com.github.VipulOG:ebook-reader:0.1.6'
- implementation 'androidx.paging:paging-runtime-ktx:3.3.6'
+ implementation 'androidx.paging:paging-runtime-ktx:3.2.1'
implementation 'com.github.eltos:simpledialogfragments:v3.7'
implementation 'com.github.AAChartModel:AAChartCore-Kotlin:7.2.3'
@@ -167,13 +161,13 @@ dependencies {
implementation 'ca.gosyer:voyager-navigator:1.0.0-rc07'
implementation 'com.squareup.logcat:logcat:0.1'
implementation 'uy.kohesive.injekt:injekt-core:1.16.+'
- implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14'
- implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.14'
+ implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12'
+ implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.12'
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps'
- implementation 'com.squareup.okio:okio:3.9.1'
- implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.14'
- implementation 'org.jsoup:jsoup:1.18.1'
- implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.7.3'
+ implementation 'com.squareup.okio:okio:3.8.0'
+ implementation 'com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.12'
+ implementation 'org.jsoup:jsoup:1.16.1'
+ implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.6.3'
implementation 'com.jakewharton.rxrelay:rxrelay:1.2.0'
implementation 'com.github.tachiyomiorg:unifile:17bec43'
implementation 'com.github.gpanther:java-nat-sort:natural-comparator-1.1'
diff --git a/app/src/main/java/ani/dantotsu/App.kt b/app/src/main/java/ani/dantotsu/App.kt
index 0b5751e0..057c6d22 100644
--- a/app/src/main/java/ani/dantotsu/App.kt
+++ b/app/src/main/java/ani/dantotsu/App.kt
@@ -113,28 +113,21 @@ class App : MultiDexApplication() {
}
}
- val scope = CoroutineScope(Dispatchers.IO)
- scope.launch {
+ CoroutineScope(Dispatchers.IO).launch {
animeExtensionManager = Injekt.get()
- launch {
- animeExtensionManager.findAvailableExtensions()
- }
+ animeExtensionManager.findAvailableExtensions()
Logger.log("Anime Extensions: ${animeExtensionManager.installedExtensionsFlow.first()}")
AnimeSources.init(animeExtensionManager.installedExtensionsFlow)
}
- scope.launch {
+ CoroutineScope(Dispatchers.IO).launch {
mangaExtensionManager = Injekt.get()
- launch {
- mangaExtensionManager.findAvailableExtensions()
- }
+ mangaExtensionManager.findAvailableExtensions()
Logger.log("Manga Extensions: ${mangaExtensionManager.installedExtensionsFlow.first()}")
MangaSources.init(mangaExtensionManager.installedExtensionsFlow)
}
- scope.launch {
+ CoroutineScope(Dispatchers.IO).launch {
novelExtensionManager = Injekt.get()
- launch {
- novelExtensionManager.findAvailableExtensions()
- }
+ novelExtensionManager.findAvailableExtensions()
Logger.log("Novel Extensions: ${novelExtensionManager.installedExtensionsFlow.first()}")
NovelSources.init(novelExtensionManager.installedExtensionsFlow)
}
diff --git a/app/src/main/java/ani/dantotsu/Network.kt b/app/src/main/java/ani/dantotsu/Network.kt
index f9d8b7f2..ce536e8d 100644
--- a/app/src/main/java/ani/dantotsu/Network.kt
+++ b/app/src/main/java/ani/dantotsu/Network.kt
@@ -11,11 +11,7 @@ import com.lagradost.nicehttp.addGenericDns
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkHelper.Companion.defaultUserAgentProvider
import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi
@@ -89,42 +85,12 @@ object Mapper : ResponseParser {
}
}
-/**
- * Performs parallel processing of collection items without blocking threads.
- * Each operation runs in its own coroutine on the specified dispatcher.
- *
- * @param dispatcher The CoroutineDispatcher to use for parallel operations (defaults to IO)
- * @param f The suspend function to apply to each item
- * @return List of results in the same order as the original collection
- */
-suspend fun Collection.asyncMap(
- dispatcher: CoroutineDispatcher = Dispatchers.IO,
- f: suspend (A) -> B
-): List = coroutineScope {
- map { item ->
- async(dispatcher) {
- f(item)
- }
- }.awaitAll()
+fun Collection.asyncMap(f: suspend (A) -> B): List = runBlocking {
+ map { async { f(it) } }.map { it.await() }
}
-/**
- * Performs parallel processing of collection items without blocking threads,
- * filtering out null results.
- *
- * @param dispatcher The CoroutineDispatcher to use for parallel operations (defaults to IO)
- * @param f The suspend function to apply to each item
- * @return List of non-null results in the same order as the original collection
- */
-suspend fun Collection.asyncMapNotNull(
- dispatcher: CoroutineDispatcher = Dispatchers.IO,
- f: suspend (A) -> B?
-): List = coroutineScope {
- map { item ->
- async(dispatcher) {
- f(item)
- }
- }.mapNotNull { it.await() }
+fun Collection.asyncMapNotNull(f: suspend (A) -> B?): List = runBlocking {
+ map { async { f(it) } }.mapNotNull { it.await() }
}
fun logError(e: Throwable, post: Boolean = true, snackbar: Boolean = true) {
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
index f33c03e9..513da55b 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt
@@ -47,9 +47,9 @@ class Login : AppCompatActivity() {
view.evaluateJavascript(
"""
(function() {
- const m = []; webpackChunkdiscord_app.push([[""], {}, e => {for (let c in e.c)m.push(e.c[c])}]);
- return m.find(n => n?.exports?.default?.getToken !== void 0)?.exports?.default?.getToken();
-})()
+ const wreq = (webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken();
+ return wreq;
+ })()
""".trimIndent()
) { result ->
login(result.trim('"'))
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
index 20bafb39..eec99b96 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
@@ -50,8 +50,9 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
val assetApi = RPCExternalAsset(data.applicationId, token!!, client, json)
suspend fun String.discordUrl() = assetApi.getDiscordUri(this)
- return json.encodeToString(Presence.Response(
- 3,
+ return json.encodeToString(
+ Presence.Response(
+ 3,
Presence(
activities = listOf(
Activity(
diff --git a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
index 1e1d2e19..bcb21c8c 100644
--- a/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
+++ b/app/src/main/java/ani/dantotsu/media/MediaDetailsActivity.kt
@@ -4,7 +4,6 @@ import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Intent
import android.content.res.Configuration
-import android.graphics.Color
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.view.GestureDetector
@@ -13,8 +12,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.ImageView
-import androidx.activity.SystemBarStyle
-import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
@@ -22,10 +19,8 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.core.text.bold
import androidx.core.text.color
-import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible
import androidx.core.view.marginBottom
-import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import androidx.fragment.app.Fragment
@@ -84,7 +79,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
-
super.onCreate(savedInstanceState)
var media: Media = intent.getSerialized("media") ?: mediaSingleton ?: emptyMedia()
val id = intent.getIntExtra("mediaId", -1)
@@ -115,7 +109,6 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
// Ui init
initActivity(this)
-
binding.mediaViewPager.updateLayoutParams {
bottomMargin = navBarHeight
}
@@ -139,12 +132,10 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
val navBarBottomMargin = if (resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE
) 0 else navBarHeight
- binding.mediaBottomBarContainer.setPadding(
- navBar.paddingLeft,
- navBar.paddingTop,
- navBar.paddingRight + navBarRightMargin,
- navBar.paddingBottom + navBarBottomMargin
- )
+ navBar.updateLayoutParams {
+ rightMargin = navBarRightMargin
+ bottomMargin = navBarBottomMargin
+ }
binding.mediaBanner.updateLayoutParams { height += statusBarHeight }
binding.mediaBannerNoKen.updateLayoutParams { height += statusBarHeight }
binding.mediaClose.updateLayoutParams { topMargin += statusBarHeight }
diff --git a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
index 7fa32de9..5c66f6a2 100644
--- a/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
+++ b/app/src/main/java/ani/dantotsu/media/SubtitleDownloader.kt
@@ -1,8 +1,6 @@
package ani.dantotsu.media
import android.content.Context
-import androidx.core.net.toFile
-import androidx.core.net.toUri
import ani.dantotsu.download.DownloadedType
import ani.dantotsu.download.DownloadsManager
import ani.dantotsu.parsers.SubtitleType
@@ -23,32 +21,28 @@ class SubtitleDownloader {
suspend fun loadSubtitleType(url: String): SubtitleType =
withContext(Dispatchers.IO) {
return@withContext try {
- if (!url.startsWith("file")) {
- // Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it
- val networkHelper = Injekt.get()
- val request = Request.Builder()
- .url(url)
- .build()
+ // Initialize the NetworkHelper instance. Replace this line based on how you usually initialize it
+ val networkHelper = Injekt.get()
+ val request = Request.Builder()
+ .url(url)
+ .build()
- val response = networkHelper.client.newCall(request).execute()
+ val response = networkHelper.client.newCall(request).execute()
- // Check if response is successful
- if (response.isSuccessful) {
- val responseBody = response.body.string()
+ // Check if response is successful
+ if (response.isSuccessful) {
+ val responseBody = response.body.string()
- val subtitleType = getType(responseBody)
-
- subtitleType
- } else {
- SubtitleType.UNKNOWN
+ val subtitleType = when {
+ responseBody.contains("[Script Info]") -> SubtitleType.ASS
+ responseBody.contains("WEBVTT") -> SubtitleType.VTT
+ else -> SubtitleType.SRT
}
- } else {
- val uri = url.toUri()
- val file = uri.toFile()
- val fileBody = file.readText()
- val subtitleType = getType(fileBody)
+
subtitleType
+ } else {
+ SubtitleType.UNKNOWN
}
} catch (e: Exception) {
Logger.log(e)
@@ -56,15 +50,6 @@ class SubtitleDownloader {
}
}
- private fun getType(content: String): SubtitleType {
- return when {
- content.contains("[Script Info]") -> SubtitleType.ASS
- content.contains("WEBVTT") -> SubtitleType.VTT
- content.contains("SRT") -> SubtitleType.SRT
- else -> SubtitleType.UNKNOWN
- }
- }
-
//actually downloads lol
@Deprecated("handled externally")
suspend fun downloadSubtitle(
diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
index 75332806..4de053dd 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
@@ -2,6 +2,7 @@ package ani.dantotsu.media.anime
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
+import android.app.AlertDialog
import android.app.Dialog
import android.app.PictureInPictureParams
import android.app.PictureInPictureUiState
@@ -18,6 +19,7 @@ import android.media.AudioManager.AUDIOFOCUS_GAIN
import android.media.AudioManager.AUDIOFOCUS_LOSS
import android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
import android.media.AudioManager.STREAM_MUSIC
+import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.CountDownTimer
@@ -73,7 +75,9 @@ import androidx.media3.common.TrackSelectionOverride
import androidx.media3.common.Tracks
import androidx.media3.common.text.CueGroup
import androidx.media3.common.util.UnstableApi
+import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSource
+import androidx.media3.datasource.HttpDataSource
import androidx.media3.datasource.cache.CacheDataSource
import androidx.media3.datasource.okhttp.OkHttpDataSource
import androidx.media3.exoplayer.DefaultLoadControl
@@ -171,10 +175,10 @@ import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
+import kotlin.collections.set
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt
-import androidx.core.net.toUri
@UnstableApi
@SuppressLint("ClickableViewAccessibility")
@@ -1308,7 +1312,7 @@ class ExoplayerView :
setTitle(R.string.speed)
singleChoiceItems(speedsName, curSpeed) { i ->
PrefManager.setCustomVal("${media.id}_speed", i)
- speed = speeds.getOrNull(i) ?: 1f
+ speed = speeds[i]
curSpeed = i
playbackParameters = PlaybackParameters(speed)
exoPlayer.playbackParameters = playbackParameters
@@ -1358,7 +1362,7 @@ class ExoplayerView :
val showProgressDialog =
if (PrefManager.getVal(PrefName.AskIndividualPlayer)) {
PrefManager.getCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
true,
)
} else {
@@ -1380,7 +1384,7 @@ class ExoplayerView :
setCancelable(false)
setPosButton(R.string.yes) {
PrefManager.setCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
false,
)
PrefManager.setCustomVal(
@@ -1391,7 +1395,7 @@ class ExoplayerView :
}
setNegButton(R.string.no) {
PrefManager.setCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
false,
)
PrefManager.setCustomVal(
@@ -1605,27 +1609,29 @@ class ExoplayerView :
emptyList().toMutableList()
ext.subtitles.forEach { subtitle ->
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle.file.url
+ // var localFile: String? = null
if (subtitle.type == SubtitleType.UNKNOWN) {
runBlocking {
val type = SubtitleDownloader.loadSubtitleType(subtitleUrl)
- val fileUri = (subtitleUrl).toUri()
+ val fileUri = Uri.parse(subtitleUrl)
sub +=
MediaItem.SubtitleConfiguration
.Builder(fileUri)
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.setMimeType(
when (type) {
- SubtitleType.VTT -> MimeTypes.TEXT_VTT
+ SubtitleType.VTT -> MimeTypes.TEXT_SSA
SubtitleType.ASS -> MimeTypes.TEXT_SSA
- SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP
- else -> MimeTypes.TEXT_UNKNOWN
+ SubtitleType.SRT -> MimeTypes.TEXT_SSA
+ else -> MimeTypes.TEXT_SSA
},
).setId("69")
.setLanguage(subtitle.language)
.build()
}
+ println("sub: $sub")
} else {
- val subUri = subtitleUrl.toUri()
+ val subUri = Uri.parse(subtitleUrl)
sub +=
MediaItem.SubtitleConfiguration
.Builder(subUri)
@@ -1655,18 +1661,27 @@ class ExoplayerView :
followRedirects(true)
followSslRedirects(true)
}.build()
- val httpDataSourceFactory =
- OkHttpDataSource.Factory(httpClient).apply {
- setDefaultRequestProperties(defaultHeaders)
- video?.file?.headers?.let {
- setDefaultRequestProperties(it)
+ val dataSourceFactory =
+ DataSource.Factory {
+ val dataSource: HttpDataSource =
+ OkHttpDataSource.Factory(httpClient).createDataSource()
+ defaultHeaders.forEach {
+ dataSource.setRequestProperty(it.key, it.value)
}
+ video?.file?.headers?.forEach {
+ dataSource.setRequestProperty(it.key, it.value)
+ }
+ dataSource
}
- val defaultDataSourceFactory = DefaultDataSource.Factory(this, httpDataSourceFactory)
+ val dafuckDataSourceFactory = DefaultDataSource.Factory(this)
cacheFactory =
CacheDataSource.Factory().apply {
setCache(VideoCache.getInstance(this@ExoplayerView))
- setUpstreamDataSourceFactory(defaultDataSourceFactory)
+ if (ext.server.offline) {
+ setUpstreamDataSourceFactory(dafuckDataSourceFactory)
+ } else {
+ setUpstreamDataSourceFactory(dataSourceFactory)
+ }
setCacheWriteDataSinkFactory(null)
}
@@ -1928,7 +1943,7 @@ class ExoplayerView :
if (PrefManager.getVal(PrefName.TextviewSubtitles)) {
exoSubtitleView.visibility = View.GONE
customSubtitleView.visibility = View.VISIBLE
- val newCues = cueGroup.cues.map { it.text.toString() ?: "" }
+ val newCues = cueGroup.cues.map { it.text.toString() }
if (newCues.isEmpty()) {
customSubtitleView.text = ""
@@ -2488,7 +2503,7 @@ class ExoplayerView :
val videoURL = video?.file?.url ?: return
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle!!.file.url
val shareVideo = Intent(Intent.ACTION_VIEW)
- shareVideo.setDataAndType(videoURL.toUri(), "video/*")
+ shareVideo.setDataAndType(Uri.parse(videoURL), "video/*")
shareVideo.setPackage("com.instantbits.cast.webvideo")
if (subtitle != null) shareVideo.putExtra("subtitle", subtitleUrl)
shareVideo.putExtra(
@@ -2510,7 +2525,7 @@ class ExoplayerView :
} catch (ex: ActivityNotFoundException) {
val intent = Intent(Intent.ACTION_VIEW)
val uriString = "market://details?id=com.instantbits.cast.webvideo"
- intent.data = uriString.toUri()
+ intent.data = Uri.parse(uriString)
startActivity(intent)
}
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt
index f47f416f..359c64a5 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt
@@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
-import java.io.FileNotFoundException
import java.io.FileOutputStream
data class ImageData(
@@ -77,7 +76,7 @@ fun saveImage(
uri?.let {
contentResolver.openOutputStream(it)?.use { os ->
bitmap.compress(format, quality, os)
- } ?: throw FileNotFoundException("Failed to open output stream for URI: $uri")
+ }
}
} else {
val directory =
@@ -87,20 +86,12 @@ fun saveImage(
}
val file = File(directory, filename)
-
- // Check if the file already exists
- if (file.exists()) {
- println("File already exists: ${file.absolutePath}")
- return
- }
-
FileOutputStream(file).use { outputStream ->
bitmap.compress(format, quality, outputStream)
}
}
- } catch (e: FileNotFoundException) {
- println("File not found: ${e.message}")
} catch (e: Exception) {
+ // Handle exception here
println("Exception while saving image: ${e.message}")
}
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
index 2ad28a75..63ef7408 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -66,7 +66,6 @@ import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
import eu.kanade.tachiyomi.source.ConfigurableSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
@@ -233,35 +232,25 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
}
fun multiDownload(n: Int) {
- lifecycleScope.launch {
- // Get the last viewed chapter
- val selected = media.userProgress ?: 0
- val chapters = media.manga?.chapters?.values?.toList()
- // Ensure chapters are available in the extensions
- if (chapters.isNullOrEmpty() || n < 1) return@launch
- // Find the index of the last viewed chapter
- val progressChapterIndex = (chapters.indexOfFirst {
- MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected
- } + 1).coerceAtLeast(0)
- // Calculate the end value for the range of chapters to download
- val endIndex = (progressChapterIndex + n).coerceAtMost(chapters.size)
- // Get the list of chapters to download
- val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
- // Trigger the download for each chapter sequentially
- for (chapter in chaptersToDownload) {
- try {
- downloadChapterSequentially(chapter)
- } catch (e: Exception) {
- Toast.makeText(requireContext(), "Failed to download chapter: ${chapter.title}", Toast.LENGTH_SHORT).show()
- }
- }
- Toast.makeText(requireContext(), "All downloads completed!", Toast.LENGTH_SHORT).show()
- }
- }
- private suspend fun downloadChapterSequentially(chapter: MangaChapter) {
- withContext(Dispatchers.IO) {
+ // Get last viewed chapter
+ val selected = media.userProgress
+ val chapters = media.manga?.chapters?.values?.toList()
+ // Filter by selected language
+ val progressChapterIndex = (chapters?.indexOfFirst {
+ MediaNameAdapter.findChapterNumber(it.number)?.toInt() == selected
+ } ?: 0) + 1
+
+ if (progressChapterIndex < 0 || n < 1 || chapters == null) return
+
+ // Calculate the end index
+ val endIndex = minOf(progressChapterIndex + n, chapters.size)
+
+ // Make sure there are enough chapters
+ val chaptersToDownload = chapters.subList(progressChapterIndex, endIndex)
+
+
+ for (chapter in chaptersToDownload) {
onMangaChapterDownloadClick(chapter)
- delay(2000) // A 2-second download
}
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
index f534ce6b..f343d70b 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AnimeSources.kt
@@ -26,6 +26,7 @@ object AnimeSources : WatchSources() {
)
isInitialized = true
+ // Update as StateFlow emits new values
fromExtensions.collect { extensions ->
list = sortPinnedAnimeSources(
createParsersFromExtensions(extensions),
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index 0f2a438a..adfd857e 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -226,18 +226,8 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
?: return emptyList())
return try {
- // TODO(1.6): Remove else block when dropping support for ext lib <1.6
- if ((source as AnimeHttpSource).javaClass.declaredMethods.any { it.name == "getHosterList" }){
- val hosters = source.getHosterList(sEpisode)
- val allVideos = hosters.flatMap { hoster ->
- val videos = source.getVideoList(hoster)
- videos.map { it.copy(videoTitle = "${hoster.hosterName} - ${it.videoTitle}") }
- }
- allVideos.map { videoToVideoServer(it) }
- } else {
- val videos = source.getVideoList(sEpisode)
- videos.map { videoToVideoServer(it) }
- }
+ val videos = source.getVideoList(sEpisode)
+ videos.map { videoToVideoServer(it) }
} catch (e: Exception) {
Logger.log("Exception occurred: ${e.message}")
emptyList()
@@ -586,7 +576,7 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
number,
format!!,
FileUrl(videoUrl, headersMap),
- null
+ if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble()
)
}
@@ -646,6 +636,7 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
}
private fun trackToSubtitle(track: Track): Subtitle {
+ //use Dispatchers.IO to make a HTTP request to determine the subtitle type
var type: SubtitleType?
runBlocking {
type = findSubtitleType(track.url)
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
index 38230a86..f0bf91d0 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
@@ -193,7 +193,8 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.OverridePassword, true)
}
val password = view.passwordInput.text.toString()
- val confirmPassword = view.confirmPasswordInput.text.toString()
+ val confirmPassword =
+ view.confirmPasswordInput.text.toString()
if (password == confirmPassword && password.isNotEmpty()) {
PrefManager.setVal(PrefName.AppPassword, password)
if (view.biometricCheckbox.isChecked) {
@@ -201,11 +202,13 @@ class SettingsCommonActivity : AppCompatActivity() {
BiometricManager
.from(applicationContext)
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ==
- BiometricManager.BIOMETRIC_SUCCESS
+ BiometricManager.BIOMETRIC_SUCCESS
if (canBiometricPrompt) {
val biometricPrompt =
- BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
+ BiometricPromptUtils.createBiometricPrompt(
+ this@SettingsCommonActivity
+ ) { _ ->
val token = UUID.randomUUID().toString()
PrefManager.setVal(
PrefName.BiometricToken,
@@ -235,12 +238,14 @@ class SettingsCommonActivity : AppCompatActivity() {
setOnShowListener {
view.passwordInput.requestFocus()
val canAuthenticate =
- BiometricManager.from(applicationContext).canAuthenticate(
- BiometricManager.Authenticators.BIOMETRIC_WEAK,
- ) == BiometricManager.BIOMETRIC_SUCCESS
+ BiometricManager.from(applicationContext)
+ .canAuthenticate(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ ) == BiometricManager.BIOMETRIC_SUCCESS
view.biometricCheckbox.isVisible = canAuthenticate
view.biometricCheckbox.isChecked =
- PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
+ PrefManager.getVal(PrefName.BiometricToken, "")
+ .isNotEmpty()
view.forgotPasswordCheckbox.isChecked =
PrefManager.getVal(PrefName.OverridePassword)
}
@@ -314,7 +319,8 @@ class SettingsCommonActivity : AppCompatActivity() {
setTitle(R.string.change_download_location)
setMessage(R.string.download_location_msg)
setPosButton(R.string.ok) {
- val oldUri = PrefManager.getVal(PrefName.DownloadsDir)
+ val oldUri =
+ PrefManager.getVal(PrefName.DownloadsDir)
launcher.registerForCallback { success ->
if (success) {
toast(getString(R.string.please_wait))
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
index c5ca440a..f1225ed4 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
@@ -82,9 +82,18 @@ class SettingsNotificationActivity : AppCompatActivity() {
setTitle(R.string.subscriptions_checking_time)
singleChoiceItems(timeNames, curTime) { i ->
curTime = i
- it.settingsTitle.text = getString(R.string.subscriptions_checking_time_s, timeNames[i])
- PrefManager.setVal(PrefName.SubscriptionNotificationInterval, curTime)
- TaskScheduler.create(context, PrefManager.getVal(PrefName.UseAlarmManager)).scheduleAllTasks(context)
+ it.settingsTitle.text = getString(
+ R.string.subscriptions_checking_time_s,
+ timeNames[i]
+ )
+ PrefManager.setVal(
+ PrefName.SubscriptionNotificationInterval,
+ curTime
+ )
+ TaskScheduler.create(
+ context,
+ PrefManager.getVal(PrefName.UseAlarmManager)
+ ).scheduleAllTasks(context)
}
show()
}
@@ -120,21 +129,22 @@ class SettingsNotificationActivity : AppCompatActivity() {
.toMutableSet()
val selected = types.map { filteredTypes.contains(it) }.toBooleanArray()
context.customAlertDialog().apply {
- setTitle(R.string.anilist_notification_filters)
- multiChoiceItems(
+ setTitle(R.string.anilist_notification_filters)
+ multiChoiceItems(
types.map { name ->
name.replace("_", " ").lowercase().replaceFirstChar {
- if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
- } }.toTypedArray(),
+ if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
+ }
+ }.toTypedArray(),
selected
) { updatedSelected ->
- types.forEachIndexed { index, type ->
- if (updatedSelected[index]) {
- filteredTypes.add(type)
- } else {
- filteredTypes.remove(type)
+ types.forEachIndexed { index, type ->
+ if (updatedSelected[index]) {
+ filteredTypes.add(type)
+ } else {
+ filteredTypes.remove(type)
+ }
}
- }
PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes)
}
show()
@@ -152,8 +162,8 @@ class SettingsNotificationActivity : AppCompatActivity() {
icon = R.drawable.ic_round_notifications_none_24,
onClick = {
context.customAlertDialog().apply {
- setTitle(R.string.subscriptions_checking_time)
- singleChoiceItems(
+ setTitle(R.string.subscriptions_checking_time)
+ singleChoiceItems(
aItems.toTypedArray(),
PrefManager.getVal(PrefName.AnilistNotificationInterval)
) { i ->
@@ -181,11 +191,11 @@ class SettingsNotificationActivity : AppCompatActivity() {
icon = R.drawable.ic_round_notifications_none_24,
onClick = {
context.customAlertDialog().apply {
- setTitle(R.string.subscriptions_checking_time)
- singleChoiceItems(
+ setTitle(R.string.subscriptions_checking_time)
+ singleChoiceItems(
cItems.toTypedArray(),
PrefManager.getVal(PrefName.CommentNotificationInterval)
- ) { i ->
+ ) { i ->
PrefManager.setVal(PrefName.CommentNotificationInterval, i)
it.settingsTitle.text =
getString(
@@ -225,9 +235,9 @@ class SettingsNotificationActivity : AppCompatActivity() {
switch = { isChecked, view ->
if (isChecked) {
context.customAlertDialog().apply {
- setTitle(R.string.use_alarm_manager)
- setMessage(R.string.use_alarm_manager_confirm)
- setPosButton(R.string.use) {
+ setTitle(R.string.use_alarm_manager)
+ setMessage(R.string.use_alarm_manager_confirm)
+ setPosButton(R.string.use) {
PrefManager.setVal(PrefName.UseAlarmManager, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
index 8133640a..648c26bd 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
@@ -96,7 +96,8 @@ class SettingsThemeActivity : AppCompatActivity(), SimpleDialog.OnDialogResultLi
themeSwitcher.apply {
setText(themeText)
setAdapter(
- ArrayAdapter(context,
+ ArrayAdapter(
+ context,
R.layout.item_dropdown,
ThemeManager.Companion.Theme.entries.map {
it.theme.substring(
diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
index 9fa7552e..188da9d3 100644
--- a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
@@ -52,14 +52,15 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
}
groupedSubscriptions.forEach { (parserName, mediaList) ->
- adapter.add(SubscriptionSource(
- parserName,
- mediaList.toMutableList(),
- adapter,
- getParserIcon(parserName)
- ) { group ->
- adapter.remove(group)
- })
+ adapter.add(
+ SubscriptionSource(
+ parserName,
+ mediaList.toMutableList(),
+ adapter,
+ getParserIcon(parserName)
+ ) { group ->
+ adapter.remove(group)
+ })
}
}
diff --git a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
index 036dd1b4..74683ab1 100644
--- a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
+++ b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
@@ -3,8 +3,6 @@ package ani.dantotsu.util
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
-import android.os.Build
-import android.view.WindowManager
import android.view.View
import ani.dantotsu.R
@@ -207,14 +205,8 @@ class AlertDialogBuilder(private val context: Context) {
onShow?.invoke()
}
dialog.window?.apply {
- setDimAmount(0.5f)
+ setDimAmount(0.8f)
attributes.windowAnimations = android.R.style.Animation_Dialog
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- val params = attributes
- params.flags = params.flags or WindowManager.LayoutParams.FLAG_BLUR_BEHIND
- params.setBlurBehindRadius(20)
- attributes = params
- }
}
dialog.show()
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt b/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt
index 4b54360a..171b07b2 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.animesource
-import eu.kanade.tachiyomi.animesource.model.Hoster
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
@@ -49,25 +48,6 @@ interface AnimeSource {
return fetchEpisodeList(anime).awaitSingle()
}
- /**
- * Get the list of hoster for an episode. The first hoster in the list should
- * be the preferred hoster.
- *
- * @since extensions-lib 16
- * @param episode the episode.
- * @return the hosters for the episode.
- */
- suspend fun getHosterList(episode: SEpisode): List = throw IllegalStateException("Not used")
-
- /**
- * Get the list of videos for a hoster.
- *
- * @since extensions-lib 16
- * @param hoster the hoster.
- * @return the videos for the hoster.
- */
- suspend fun getVideoList(hoster: Hoster): List