From 7053a7b4b2482f536a81d0a12fa3ff6c2c852928 Mon Sep 17 00:00:00 2001
From: rebel onion <87634197+rebelonion@users.noreply.github.com>
Date: Thu, 16 Jan 2025 20:27:24 -0600
Subject: [PATCH 01/16] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 76bece4b..6b713a2a 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ 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:
From 6fd3515d2c8ef16090db5fb6c0084094f0e3a3aa Mon Sep 17 00:00:00 2001
From: Sadwhy <99601717+Sadwhy@users.noreply.github.com>
Date: Fri, 17 Jan 2025 13:33:01 +0600
Subject: [PATCH 02/16] stuff (#567)
* Add blur to dialog for devices that support it
* More adjustable seek time
* Bump exo player
---
app/build.gradle | 4 ++--
.../main/java/ani/dantotsu/util/AlertDialogBuilder.kt | 10 +++++++++-
app/src/main/res/layout/activity_player_settings.xml | 8 ++++----
3 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index f7aab7ca..bcda959a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -113,7 +113,7 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.3.0'
// Exoplayer
- ext.exo_version = '1.5.0'
+ 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"
@@ -124,7 +124,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.3"
+ implementation "com.github.anilbeesetti.nextlib:nextlib-media3ext:0.8.4"
// UI
implementation 'com.google.android.material:material:1.12.0'
diff --git a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
index 74683ab1..036dd1b4 100644
--- a/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
+++ b/app/src/main/java/ani/dantotsu/util/AlertDialogBuilder.kt
@@ -3,6 +3,8 @@ 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
@@ -205,8 +207,14 @@ class AlertDialogBuilder(private val context: Context) {
onShow?.invoke()
}
dialog.window?.apply {
- setDimAmount(0.8f)
+ setDimAmount(0.5f)
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/res/layout/activity_player_settings.xml b/app/src/main/res/layout/activity_player_settings.xml
index 04244f03..e376c2f6 100644
--- a/app/src/main/res/layout/activity_player_settings.xml
+++ b/app/src/main/res/layout/activity_player_settings.xml
@@ -1102,10 +1102,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
- android:stepSize="5.0"
- android:value="15.0"
- android:valueFrom="5.0"
- android:valueTo="45.0"
+ android:stepSize="1.0"
+ android:value="10.0"
+ android:valueFrom="1.0"
+ android:valueTo="50.0"
app:labelBehavior="floating"
app:labelStyle="@style/fontTooltip"
app:thumbColor="?attr/colorSecondary"
From 6c49839f87f500a7a70f29529c5c2c1e12c4a11f Mon Sep 17 00:00:00 2001
From: Daniele Santoru <30676094+danyev3@users.noreply.github.com>
Date: Wed, 2 Apr 2025 07:09:33 +0200
Subject: [PATCH 03/16] Fixed missing manga pages when downloading (#586)
---
.../dantotsu/download/manga/MangaDownloaderService.kt | 10 ++++++++--
.../java/ani/dantotsu/media/manga/MangaReadFragment.kt | 2 +-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index 58e7de38..2ea82108 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -232,12 +232,18 @@ class MangaDownloaderService : Service() {
image.page,
image.source
)
+ if (bitmap == null) {
+ snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
+ }
retryCount++
}
- if (bitmap != null) {
- saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
+ if (bitmap == null) {
+ outputDir.deleteRecursively(this@MangaDownloaderService, false)
+ throw Exception("${task.chapter} - Unable to download all pages after $retryCount attempts. Try again.")
}
+
+ saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
farthest++
builder.setProgress(task.imageData.size, farthest, false)
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 6b7eb6bc..63ef7408 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -474,7 +474,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
scanlator = chapter.scanlator ?: "Unknown",
imageData = images,
sourceMedia = media,
- retries = 2,
+ retries = 25,
simultaneousDownloads = 2
)
From a684aac0b1ad824e4fc6178ff2b042249135d4ce Mon Sep 17 00:00:00 2001
From: Rishvaish <68911202+rishabpuranika@users.noreply.github.com>
Date: Wed, 2 Apr 2025 10:40:39 +0530
Subject: [PATCH 04/16] To install multiple mangas (#582)
users can enter the value required to install as there is an EditText field instead of the Text View
---
.../dantotsu/media/manga/MangaReadAdapter.kt | 24 ++++++++++++-------
app/src/main/res/layout/dialog_layout.xml | 23 ++++++++++++------
2 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
index a76d44a4..ddf98974 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
@@ -7,9 +7,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.CheckBox
+import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.NumberPicker
+import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString
import androidx.core.content.ContextCompat.startActivity
@@ -265,19 +267,22 @@ class MangaReadAdapter(
}
// Multi download
- downloadNo.text = "0"
+ //downloadNo.text = "0"
mediaDownloadTop.setOnClickListener {
- // Alert dialog asking for the number of chapters to download
fragment.requireContext().customAlertDialog().apply {
setTitle("Multi Chapter Downloader")
setMessage("Enter the number of chapters to download")
- val input = NumberPicker(currContext())
- input.minValue = 1
- input.maxValue = 20
- input.value = 1
+ val input = View.inflate(currContext(), R.layout.dialog_layout, null)
+ val editText = input.findViewById(R.id.downloadNo)
setCustomView(input)
setPosButton(R.string.ok) {
- downloadNo.text = "${input.value}"
+ val value = editText.text.toString().toIntOrNull()
+ if (value != null && value > 0) {
+ downloadNo.setText(value.toString(), TextView.BufferType.EDITABLE)
+ fragment.multiDownload(value)
+ } else {
+ toast("Please enter a valid number")
+ }
}
setNegButton(R.string.cancel)
show()
@@ -382,8 +387,9 @@ class MangaReadAdapter(
setCustomView(root)
setPosButton("OK") {
if (run) fragment.onIconPressed(style, reversed)
- if (downloadNo.text != "0") {
- fragment.multiDownload(downloadNo.text.toString().toInt())
+ val value = downloadNo.text.toString().toIntOrNull()
+ if (value != null && value > 0) {
+ fragment.multiDownload(value)
}
if (refresh) fragment.loadChapters(source, true)
}
diff --git a/app/src/main/res/layout/dialog_layout.xml b/app/src/main/res/layout/dialog_layout.xml
index 3db06395..3c9df3b8 100644
--- a/app/src/main/res/layout/dialog_layout.xml
+++ b/app/src/main/res/layout/dialog_layout.xml
@@ -8,7 +8,7 @@
android:padding="16dp">
@@ -160,8 +160,8 @@
android:orientation="horizontal">
-
+ tools:text="Number" />
+
+
+
+
+
+
+
+
Date: Wed, 2 Apr 2025 10:52:58 +0530
Subject: [PATCH 05/16] move stuff to dev (#587)
* Update README.md
* Fixed missing manga pages when downloading (#586)
* To install multiple mangas (#582)
users can enter the value required to install as there is an EditText field instead of the Text View
---------
Co-authored-by: rebel onion <87634197+rebelonion@users.noreply.github.com>
Co-authored-by: Daniele Santoru <30676094+danyev3@users.noreply.github.com>
Co-authored-by: Rishvaish <68911202+rishabpuranika@users.noreply.github.com>
---
README.md | 2 +-
.../download/manga/MangaDownloaderService.kt | 10 ++++++--
.../dantotsu/media/manga/MangaReadAdapter.kt | 24 ++++++++++++-------
.../dantotsu/media/manga/MangaReadFragment.kt | 2 +-
app/src/main/res/layout/dialog_layout.xml | 23 ++++++++++++------
5 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 76bece4b..6b713a2a 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ 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:
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index 58e7de38..2ea82108 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -232,12 +232,18 @@ class MangaDownloaderService : Service() {
image.page,
image.source
)
+ if (bitmap == null) {
+ snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
+ }
retryCount++
}
- if (bitmap != null) {
- saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
+ if (bitmap == null) {
+ outputDir.deleteRecursively(this@MangaDownloaderService, false)
+ throw Exception("${task.chapter} - Unable to download all pages after $retryCount attempts. Try again.")
}
+
+ saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
farthest++
builder.setProgress(task.imageData.size, farthest, false)
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
index a76d44a4..ddf98974 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
@@ -7,9 +7,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.CheckBox
+import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.NumberPicker
+import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString
import androidx.core.content.ContextCompat.startActivity
@@ -265,19 +267,22 @@ class MangaReadAdapter(
}
// Multi download
- downloadNo.text = "0"
+ //downloadNo.text = "0"
mediaDownloadTop.setOnClickListener {
- // Alert dialog asking for the number of chapters to download
fragment.requireContext().customAlertDialog().apply {
setTitle("Multi Chapter Downloader")
setMessage("Enter the number of chapters to download")
- val input = NumberPicker(currContext())
- input.minValue = 1
- input.maxValue = 20
- input.value = 1
+ val input = View.inflate(currContext(), R.layout.dialog_layout, null)
+ val editText = input.findViewById(R.id.downloadNo)
setCustomView(input)
setPosButton(R.string.ok) {
- downloadNo.text = "${input.value}"
+ val value = editText.text.toString().toIntOrNull()
+ if (value != null && value > 0) {
+ downloadNo.setText(value.toString(), TextView.BufferType.EDITABLE)
+ fragment.multiDownload(value)
+ } else {
+ toast("Please enter a valid number")
+ }
}
setNegButton(R.string.cancel)
show()
@@ -382,8 +387,9 @@ class MangaReadAdapter(
setCustomView(root)
setPosButton("OK") {
if (run) fragment.onIconPressed(style, reversed)
- if (downloadNo.text != "0") {
- fragment.multiDownload(downloadNo.text.toString().toInt())
+ val value = downloadNo.text.toString().toIntOrNull()
+ if (value != null && value > 0) {
+ fragment.multiDownload(value)
}
if (refresh) fragment.loadChapters(source, true)
}
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 6b7eb6bc..63ef7408 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -474,7 +474,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
scanlator = chapter.scanlator ?: "Unknown",
imageData = images,
sourceMedia = media,
- retries = 2,
+ retries = 25,
simultaneousDownloads = 2
)
diff --git a/app/src/main/res/layout/dialog_layout.xml b/app/src/main/res/layout/dialog_layout.xml
index 3db06395..3c9df3b8 100644
--- a/app/src/main/res/layout/dialog_layout.xml
+++ b/app/src/main/res/layout/dialog_layout.xml
@@ -8,7 +8,7 @@
android:padding="16dp">
@@ -160,8 +160,8 @@
android:orientation="horizontal">
-
+ tools:text="Number" />
+
+
+
+
+
+
+
+
Date: Wed, 23 Apr 2025 14:58:42 +0530
Subject: [PATCH 06/16] hope for the best
* Update README.md
* To install multiple mangas
users can enter the value required to install as there is an EditText field instead of the Text View
* Issues
1)Installation of many mangas at same time now made to one to increase the installation efficiency
2)Installation order from the latest progresses chapter to the limit index
3)Tried to resolve the app crash bug
* Issues
1)Installation of many mangas at same time now made to one to increase the installation efficiency
2)Installation order from the latest progresses chapter to the limit index
3)Tried to resolve the app crash bug
---------
Co-authored-by: rebel onion <87634197+rebelonion@users.noreply.github.com>
---
.../ani/dantotsu/media/manga/MangaCache.kt | 13 ++++-
.../dantotsu/media/manga/MangaReadFragment.kt | 47 ++++++++++++-------
2 files changed, 40 insertions(+), 20 deletions(-)
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 359c64a5..f47f416f 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaCache.kt
@@ -16,6 +16,7 @@ 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(
@@ -76,7 +77,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 =
@@ -86,12 +87,20 @@ 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 63ef7408..2ad28a75 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -66,6 +66,7 @@ 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
@@ -232,25 +233,35 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
}
fun multiDownload(n: Int) {
- // 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) {
+ 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) {
onMangaChapterDownloadClick(chapter)
+ delay(2000) // A 2-second download
}
}
From 69c44b7d2006919fe36e7cac36a8db1dfefb4a37 Mon Sep 17 00:00:00 2001
From: rebel onion <87634197+rebelonion@users.noreply.github.com>
Date: Wed, 14 May 2025 21:40:06 -0500
Subject: [PATCH 07/16] chore: formatting changes
---
app/build.gradle | 5 +-
.../ani/dantotsu/connections/discord/RPC.kt | 5 +-
.../ani/dantotsu/media/anime/ExoplayerView.kt | 129 ++++++++++--------
.../settings/SettingsCommonActivity.kt | 22 +--
.../settings/SettingsNotificationActivity.kt | 52 ++++---
.../settings/SettingsThemeActivity.kt | 3 +-
.../settings/SubscriptionsBottomDialog.kt | 17 +--
build.gradle | 2 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
9 files changed, 137 insertions(+), 100 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index f7aab7ca..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.1"
- versionCode 300200100
+ versionName "3.2.2"
+ versionCode 300200200
signingConfig signingConfigs.debug
}
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/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
index 465949a4..4de053dd 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
@@ -427,7 +427,8 @@ 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
}
@@ -606,9 +607,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 {
@@ -1044,7 +1045,8 @@ 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)
@@ -1079,7 +1081,8 @@ 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)
@@ -1449,7 +1452,8 @@ 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 +1506,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,7 +1571,11 @@ 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) {
@@ -1575,8 +1583,12 @@ 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!!)
}
}
@@ -1651,7 +1663,8 @@ class ExoplayerView :
}.build()
val dataSourceFactory =
DataSource.Factory {
- val dataSource: HttpDataSource = OkHttpDataSource.Factory(httpClient).createDataSource()
+ val dataSource: HttpDataSource =
+ OkHttpDataSource.Factory(httpClient).createDataSource()
defaultHeaders.forEach {
dataSource.setRequestProperty(it.key, it.value)
}
@@ -1717,16 +1730,18 @@ 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 +1855,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()
- }
- setNegButton(getString(R.string.no)) {
- playbackPosition = 0L
- buildExoplayer()
- }
- show()
+ 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()
+ }
} else {
buildExoplayer()
}
@@ -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 = ""
@@ -1940,7 +1955,9 @@ 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()
}
@@ -2187,7 +2204,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
@@ -2213,7 +2230,8 @@ 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()
@@ -2222,7 +2240,8 @@ 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()
@@ -2310,7 +2329,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 -> {
@@ -2365,7 +2384,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()
@@ -2403,9 +2422,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
) {
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/build.gradle b/build.gradle
index 25cddf4d..510137b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,7 +12,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.7.3'
+ classpath 'com.android.tools.build:gradle:8.9.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.google.devtools.ksp:symbol-processing-api:$ksp_version"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bde28e38..f63f6097 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Aug 30 19:57:04 IST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
From 7d0894cd923866a0091162fcb8f0ec1680632ef1 Mon Sep 17 00:00:00 2001
From: rebel onion <87634197+rebelonion@users.noreply.github.com>
Date: Wed, 14 May 2025 22:35:50 -0500
Subject: [PATCH 08/16] chore: bump extension interface
---
README.md | 2 -
app/build.gradle | 2 +-
.../ani/dantotsu/parsers/AniyomiAdapter.kt | 16 +-
.../tachiyomi/animesource/AnimeSource.kt | 20 ++
.../tachiyomi/animesource/model/Hoster.kt | 81 ++++++
.../tachiyomi/animesource/model/Video.kt | 273 +++++++++++++-----
6 files changed, 311 insertions(+), 83 deletions(-)
create mode 100644 app/src/main/java/eu/kanade/tachiyomi/animesource/model/Hoster.kt
diff --git a/README.md b/README.md
index 6b713a2a..a1b1d673 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,6 @@ 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 bcda959a..b983443c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -19,7 +19,7 @@ android {
targetSdk 35
versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "3.2.1"
- versionCode 300200100
+ versionCode versionName.split("\\.").collect { it.toInteger() * 100 }.join("") as Integer
signingConfig signingConfigs.debug
}
diff --git a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
index adfd857e..fa1fa9a2 100644
--- a/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/parsers/AniyomiAdapter.kt
@@ -226,8 +226,18 @@ class DynamicAnimeParser(extension: AnimeExtension.Installed) : AnimeParser() {
?: return emptyList())
return try {
- val videos = source.getVideoList(sEpisode)
- videos.map { videoToVideoServer(it) }
+ // 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) }
+ }
} catch (e: Exception) {
Logger.log("Exception occurred: ${e.message}")
emptyList()
@@ -576,7 +586,7 @@ class VideoServerPassthrough(private val videoServer: VideoServer) : VideoExtrac
number,
format!!,
FileUrl(videoUrl, headersMap),
- if (aniVideo.totalContentLength == 0L) null else aniVideo.bytesDownloaded.toDouble()
+ null
)
}
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 171b07b2..4b54360a 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/animesource/AnimeSource.kt
@@ -1,5 +1,6 @@
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
@@ -48,6 +49,25 @@ 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
+
+
diff --git a/app/src/main/res/layout/activity_media.xml b/app/src/main/res/layout/activity_media.xml
index 89d856c9..a6690a1f 100644
--- a/app/src/main/res/layout/activity_media.xml
+++ b/app/src/main/res/layout/activity_media.xml
@@ -353,11 +353,18 @@
+
+
+
From 1fbbe3e77c473c9e30cee6167834f13c24fdb577 Mon Sep 17 00:00:00 2001
From: rebel onion <87634197+rebelonion@users.noreply.github.com>
Date: Fri, 23 May 2025 08:20:41 -0500
Subject: [PATCH 16/16] fix: replace runBlocking with non-blocking async
operations
---
app/src/main/java/ani/dantotsu/Network.kt | 42 ++++++++++++++++++++---
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/ani/dantotsu/Network.kt b/app/src/main/java/ani/dantotsu/Network.kt
index ce536e8d..f9d8b7f2 100644
--- a/app/src/main/java/ani/dantotsu/Network.kt
+++ b/app/src/main/java/ani/dantotsu/Network.kt
@@ -11,7 +11,11 @@ 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
@@ -85,12 +89,42 @@ object Mapper : ResponseParser {
}
}
-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.
+ * 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.asyncMapNotNull(f: suspend (A) -> B?): List = runBlocking {
- map { async { f(it) } }.mapNotNull { 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 logError(e: Throwable, post: Boolean = true, snackbar: Boolean = true) {