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..bcda959a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,8 +18,8 @@ android {
minSdk 21
targetSdk 35
versionCode((System.currentTimeMillis() / 60000).toInteger())
- versionName "3.2.2"
- versionCode versionName.split("\\.").collect { it.toInteger() * 100 }.join("") as Integer
+ versionName "3.2.1"
+ versionCode 300200100
signingConfig signingConfigs.debug
}
@@ -48,10 +48,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 +81,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 +113,7 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.3.0'
// Exoplayer
- ext.exo_version = '1.6.1'
+ ext.exo_version = '1.5.1'
implementation "androidx.media3:media3-exoplayer:$exo_version"
implementation "androidx.media3:media3-ui:$exo_version"
implementation "androidx.media3:media3-exoplayer-hls:$exo_version"
@@ -138,7 +133,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 +162,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/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..465949a4 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")
@@ -423,8 +427,7 @@ class ExoplayerView :
false -> 0f
}
- val textElevation =
- PrefManager.getVal(PrefName.SubBottomMargin) / 50 * resources.displayMetrics.heightPixels
+ val textElevation = PrefManager.getVal(PrefName.SubBottomMargin) / 50 * resources.displayMetrics.heightPixels
textView.translationY = -textElevation
}
@@ -603,9 +606,9 @@ class ExoplayerView :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pipEnabled =
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
- PrefManager.getVal(
- PrefName.Pip,
- )
+ PrefManager.getVal(
+ PrefName.Pip,
+ )
if (pipEnabled) {
exoPip.visibility = View.VISIBLE
exoPip.setOnClickListener {
@@ -1041,8 +1044,7 @@ class ExoplayerView :
}
}
- override fun onSingleClick(event: MotionEvent) =
- if (isSeeking) doubleTap(false, event) else handleController()
+ override fun onSingleClick(event: MotionEvent) = if (isSeeking) doubleTap(false, event) else handleController()
},
)
val rewindArea = playerView.findViewById(R.id.exo_rewind_area)
@@ -1077,8 +1079,7 @@ class ExoplayerView :
}
}
- override fun onSingleClick(event: MotionEvent) =
- if (isSeeking) doubleTap(true, event) else handleController()
+ override fun onSingleClick(event: MotionEvent) = if (isSeeking) doubleTap(true, event) else handleController()
},
)
val forwardArea = playerView.findViewById(R.id.exo_forward_area)
@@ -1308,7 +1309,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 +1359,7 @@ class ExoplayerView :
val showProgressDialog =
if (PrefManager.getVal(PrefName.AskIndividualPlayer)) {
PrefManager.getCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
true,
)
} else {
@@ -1380,7 +1381,7 @@ class ExoplayerView :
setCancelable(false)
setPosButton(R.string.yes) {
PrefManager.setCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
false,
)
PrefManager.setCustomVal(
@@ -1391,7 +1392,7 @@ class ExoplayerView :
}
setNegButton(R.string.no) {
PrefManager.setCustomVal(
- "${media.id}_progressDialog",
+ "${media.id}_ProgressDialog",
false,
)
PrefManager.setCustomVal(
@@ -1448,8 +1449,7 @@ class ExoplayerView :
else -> mutableListOf()
}
val startTimestamp = Calendar.getInstance()
- val durationInSeconds =
- if (exoPlayer.duration != C.TIME_UNSET) (exoPlayer.duration / 1000).toInt() else 1440
+ val durationInSeconds = if (exoPlayer.duration != C.TIME_UNSET) (exoPlayer.duration / 1000).toInt() else 1440
val endTimestamp =
Calendar.getInstance().apply {
@@ -1502,12 +1502,12 @@ class ExoplayerView :
@Suppress("UNCHECKED_CAST")
val list =
(
- PrefManager.getNullableCustomVal(
- "continueAnimeList",
- listOf(),
- List::class.java,
- ) as List
- ).toMutableList()
+ PrefManager.getNullableCustomVal(
+ "continueAnimeList",
+ listOf(),
+ List::class.java,
+ ) as List
+ ).toMutableList()
if (list.contains(media.id)) list.remove(media.id)
list.add(media.id)
PrefManager.setCustomVal("continueAnimeList", list)
@@ -1567,11 +1567,7 @@ class ExoplayerView :
subtitle = intent.getSerialized("subtitle")
?: when (
val subLang: String? =
- PrefManager.getNullableCustomVal(
- "subLang_${media.id}",
- null,
- String::class.java
- )
+ PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)
) {
null -> {
when (episode.selectedSubtitle) {
@@ -1579,12 +1575,8 @@ class ExoplayerView :
-1 ->
ext.subtitles.find {
it.language.contains(lang, ignoreCase = true) ||
- it.language.contains(
- getLanguageCode(lang),
- ignoreCase = true
- )
+ it.language.contains(getLanguageCode(lang), ignoreCase = true)
}
-
else -> ext.subtitles.getOrNull(episode.selectedSubtitle!!)
}
}
@@ -1605,27 +1597,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 +1649,26 @@ 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)
}
@@ -1715,18 +1717,16 @@ class ExoplayerView :
val docFile =
directory.listFiles().firstOrNull {
it.name?.endsWith(".mp4") == true ||
- it.name?.endsWith(".mkv") == true ||
- it.name?.endsWith(
- ".${
- Injekt
- .get()
- .extension
- ?.extension
- ?.getFileExtension()
- ?.first ?: "ts"
- }",
- ) ==
- true
+ it.name?.endsWith(".mkv") == true ||
+ it.name?.endsWith(
+ ".${Injekt
+ .get()
+ .extension
+ ?.extension
+ ?.getFileExtension()
+ ?.first ?: "ts"}",
+ ) ==
+ true
}
if (docFile != null) {
val uri = docFile.uri
@@ -1840,30 +1840,30 @@ class ExoplayerView :
"%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(playbackPosition),
TimeUnit.MILLISECONDS.toMinutes(playbackPosition) -
- TimeUnit.HOURS.toMinutes(
- TimeUnit.MILLISECONDS.toHours(
- playbackPosition,
- ),
+ TimeUnit.HOURS.toMinutes(
+ TimeUnit.MILLISECONDS.toHours(
+ playbackPosition,
),
+ ),
TimeUnit.MILLISECONDS.toSeconds(playbackPosition) -
- TimeUnit.MINUTES.toSeconds(
- TimeUnit.MILLISECONDS.toMinutes(
- playbackPosition,
- ),
+ TimeUnit.MINUTES.toSeconds(
+ TimeUnit.MILLISECONDS.toMinutes(
+ playbackPosition,
),
+ ),
)
- customAlertDialog().apply {
- setTitle(getString(R.string.continue_from, time))
- setCancelable(false)
- setPosButton(getString(R.string.yes)) {
- buildExoplayer()
+ customAlertDialog().apply {
+ setTitle(getString(R.string.continue_from, time))
+ setCancelable(false)
+ setPosButton(getString(R.string.yes)) {
+ buildExoplayer()
+ }
+ setNegButton(getString(R.string.no)) {
+ playbackPosition = 0L
+ buildExoplayer()
+ }
+ show()
}
- setNegButton(getString(R.string.no)) {
- playbackPosition = 0L
- buildExoplayer()
- }
- show()
- }
} else {
buildExoplayer()
}
@@ -1940,9 +1940,7 @@ class ExoplayerView :
val currentPosition = exoPlayer.currentPosition
- if ((lastSubtitle?.length
- ?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)
- ) {
+ if ((lastSubtitle?.length ?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)) {
activeSubtitles.clear()
}
@@ -2189,7 +2187,7 @@ class ExoplayerView :
currentTimeStamp =
model.timeStamps.value?.find { timestamp ->
timestamp.interval.startTime < playerCurrentTime &&
- playerCurrentTime < (timestamp.interval.endTime - 1)
+ playerCurrentTime < (timestamp.interval.endTime - 1)
}
val new = currentTimeStamp
@@ -2215,8 +2213,7 @@ class ExoplayerView :
override fun onTick(millisUntilFinished: Long) {
if (new == null) {
skipTimeButton.visibility = View.GONE
- exoSkip.isVisible =
- PrefManager.getVal(PrefName.SkipTime) > 0
+ exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
disappeared = false
functionstarted = false
cancelTimer()
@@ -2225,8 +2222,7 @@ class ExoplayerView :
override fun onFinish() {
skipTimeButton.visibility = View.GONE
- exoSkip.isVisible =
- PrefManager.getVal(PrefName.SkipTime) > 0
+ exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
disappeared = true
functionstarted = false
cancelTimer()
@@ -2314,7 +2310,7 @@ class ExoplayerView :
tracks.groups.forEach {
println(
"Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" +
- "Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}",
+ "Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}",
)
when (it.type) {
TRACK_TYPE_AUDIO -> {
@@ -2369,7 +2365,7 @@ class ExoplayerView :
when (error.errorCode) {
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS,
PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
- -> {
+ -> {
toast("Source Exception : ${error.message}")
isPlayerPlaying = true
sourceClick()
@@ -2407,9 +2403,9 @@ class ExoplayerView :
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
val episodeEnd =
exoPlayer.currentPosition / episodeLength >
- PrefManager.getVal(
- PrefName.WatchPercentage,
- )
+ PrefManager.getVal(
+ PrefName.WatchPercentage,
+ )
val episode0 = currentEpisodeIndex == 0 && PrefManager.getVal(PrefName.ChapterZeroPlayer)
if (!incognito && (episodeEnd || episode0) && Anilist.userid != null
) {
@@ -2488,7 +2484,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 +2506,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/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