From cf2d9ad654c76baf393e726105d77954041defbd Mon Sep 17 00:00:00 2001 From: Finnley Somdahl <87634197+rebelonion@users.noreply.github.com> Date: Sun, 26 Nov 2023 02:36:27 -0600 Subject: [PATCH] rpc fix --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 4 + .../main/java/ani/dantotsu/MainActivity.kt | 1 + .../connections/anilist/AnilistViewModel.kt | 13 +- .../dantotsu/connections/discord/Discord.kt | 18 +- .../connections/discord/DiscordService.kt | 444 +++++++++ .../ani/dantotsu/connections/discord/Login.kt | 5 +- .../ani/dantotsu/connections/discord/RPC.kt | 97 +- .../connections/discord/serializers/User.kt | 39 +- .../ani/dantotsu/media/anime/ExoplayerView.kt | 95 +- .../manga/mangareader/MangaReaderActivity.kt | 51 +- .../ani/dantotsu/settings/SettingsActivity.kt | 9 +- .../extension/anime/AnimeExtensionManager.kt | 3 +- .../extension/manga/MangaExtensionManager.kt | 3 +- .../res/layout/fragment_manga_offline.xml | 4 +- app/src/main/res/values-en-rDW/strings.xml | 887 +++++++++--------- 16 files changed, 1131 insertions(+), 544 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt diff --git a/app/build.gradle b/app/build.gradle index 5caa29de..b54dcfaa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,7 +29,7 @@ android { debug { applicationIdSuffix ".beta" manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher_beta"] - debuggable true + debuggable false } release { manifestPlaceholders = [icon_placeholder: "@mipmap/ic_launcher"] diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 84d17a8f..e3276763 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -276,6 +276,10 @@ + + \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/MainActivity.kt b/app/src/main/java/ani/dantotsu/MainActivity.kt index 4cea1e39..8896df2a 100644 --- a/app/src/main/java/ani/dantotsu/MainActivity.kt +++ b/app/src/main/java/ani/dantotsu/MainActivity.kt @@ -43,6 +43,7 @@ import ani.dantotsu.connections.anilist.AnilistHomeViewModel import ani.dantotsu.databinding.ActivityMainBinding import ani.dantotsu.databinding.ItemNavbarBinding import ani.dantotsu.databinding.SplashScreenBinding +import ani.dantotsu.download.manga.MangaDownloaderService import ani.dantotsu.download.manga.OfflineMangaFragment import ani.dantotsu.home.AnimeFragment import ani.dantotsu.home.HomeFragment diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt index 2cd2e0ce..20ec880e 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistViewModel.kt @@ -19,9 +19,16 @@ import kotlinx.coroutines.launch suspend fun getUserId(context: Context, block: () -> Unit) { CoroutineScope(Dispatchers.IO).launch { - if (Discord.userid == null && Discord.token != null) { - if (!Discord.getUserData()) - snackString(context.getString(R.string.error_loading_discord_user_data)) + val sharedPref = context.getSharedPreferences( + context.getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ) + val token = sharedPref.getString("discord_token", null) + val userid = sharedPref.getString("discord_id", null) + if (userid == null && token != null) { + /*if (!Discord.getUserData()) + snackString(context.getString(R.string.error_loading_discord_user_data))*/ + //TODO: Discord.getUserData() } } diff --git a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt index f718677f..44865b88 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/Discord.kt @@ -21,7 +21,7 @@ object Discord { var userid: String? = null var avatar: String? = null - private const val TOKEN = "discord_token" + const val TOKEN = "discord_token" fun getSavedToken(context: Context): Boolean { val sharedPref = context.getSharedPreferences( @@ -61,7 +61,7 @@ object Discord { } private var rpc : RPC? = null - suspend fun getUserData() = tryWithSuspend(true) { + /*suspend fun getUserData() = tryWithSuspend(true) { if(rpc==null) { val rpc = RPC(token!!, Dispatchers.IO).also { rpc = it } val user: User = rpc.getUserData() @@ -70,7 +70,7 @@ object Discord { rpc.close() true } else true - } ?: false + } ?: false*/ fun warning(context: Context) = CustomBottomDialog().apply { @@ -97,16 +97,20 @@ object Discord { context.startActivity(intent) } - fun defaultRPC(): RPC? { + const val application_Id = "1163925779692912771" + const val small_Image: String = "mp:attachments/1167176318266380288/1176997397797277856/logo-best_of_both.png" + /*fun defaultRPC(): RPC? { return token?.let { RPC(it, Dispatchers.IO).apply { - applicationId = "1163925779692912771" + applicationId = application_Id smallImage = RPC.Link( "Dantotsu", - "mp:attachments/1167176318266380288/1176997397797277856/logo-best_of_both.png" + small_Image ) buttons.add(RPC.Link("Stream on Dantotsu", "https://github.com/rebelonion/Dantotsu/")) } } - } + }*/ + + } \ No newline at end of file diff --git a/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt new file mode 100644 index 00000000..ac8937bf --- /dev/null +++ b/app/src/main/java/ani/dantotsu/connections/discord/DiscordService.kt @@ -0,0 +1,444 @@ +package ani.dantotsu.connections.discord + +import android.Manifest +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.os.IBinder +import android.os.PowerManager +import android.provider.MediaStore +import android.util.Log +import android.widget.Button +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat +import ani.dantotsu.MainActivity +import ani.dantotsu.R +import ani.dantotsu.connections.discord.serializers.Activity +import ani.dantotsu.connections.discord.serializers.Presence +import ani.dantotsu.connections.discord.serializers.User +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okhttp3.WebSocket +import okhttp3.WebSocketListener +import java.io.File +import java.io.OutputStreamWriter +import java.text.SimpleDateFormat +import java.util.Calendar + +class DiscordService : Service() { + private var heartbeat : Int = 0 + private var sequence : Int? = null + private var sessionId : String = "" + private var resume = false + private lateinit var logFile : File + private lateinit var webSocket: WebSocket + private lateinit var heartbeatThread : Thread + private lateinit var client : OkHttpClient + private lateinit var wakeLock: PowerManager.WakeLock + var presenceStore = "" + val json = Json { + encodeDefaults = true + allowStructuredMapKeys = true + ignoreUnknownKeys = true + coerceInputValues = true + } + var log = "" + + override fun onCreate() { + super.onCreate() + + log("Service onCreate()") + val powerManager = baseContext.getSystemService(Context.POWER_SERVICE) as PowerManager + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "discordRPC:backgroundPresence") + wakeLock.acquire() + log("WakeLock Acquired") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val serviceChannel = NotificationChannel( + "discordPresence", + "Discord Presence Service Channel", + NotificationManager.IMPORTANCE_LOW + ) + val manager = getSystemService(NotificationManager::class.java) + manager.createNotificationChannel(serviceChannel) + } + val intent = Intent(this, MainActivity::class.java).apply { + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + val pendingIntent = + PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE) + val builder = NotificationCompat.Builder(this, "discordPresence") + .setSmallIcon(R.mipmap.ic_launcher_round) + .setContentTitle("Discord Presence") + .setContentText("Running in the background") + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_LOW) + startForeground(1, builder.build()) + log("Foreground service started, notification shown") + client = OkHttpClient() + client.newWebSocket( + Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(), + DiscordWebSocketListener() + ) + client.dispatcher.executorService.shutdown() + SERVICE_RUNNING = true + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + log("Service onStartCommand()") + if(intent != null) { + if (intent.hasExtra("presence")) { + log("Service onStartCommand() setPresence") + var lPresence = intent.getStringExtra("presence") + if (this::webSocket.isInitialized) webSocket.send(lPresence!!) + presenceStore = lPresence!! + }else{ + log("Service onStartCommand() no presence") + DiscordServiceRunningSingleton.running = false + stopSelf() + } + if (intent.hasExtra(ACTION_STOP_SERVICE)) { + log("Service onStartCommand() stopService") + stopSelf() + } + } + return START_REDELIVER_INTENT + } + + override fun onDestroy() { + super.onDestroy() + + log("Service Destroyed") + if (DiscordServiceRunningSingleton.running){ + log("Accidental Service Destruction, restarting service") + val intent = Intent(baseContext, DiscordService::class.java) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + baseContext.startForegroundService(intent) + } else { + baseContext.startService(intent) + } + } else { + setPresence(json.encodeToString( + Presence.Response( + 3, + Presence(status = "offline") + ) + )) + wakeLock.release() + } + SERVICE_RUNNING = false + if(this::webSocket.isInitialized) webSocket.close(1000, "Closed by user") + //saveLogToFile() + } + + fun saveProfile(response: String) { + val sharedPref = baseContext.getSharedPreferences( + baseContext.getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ) + val user = json.decodeFromString(response).d.user + log("User data: $user") + with(sharedPref.edit()) { + putString("discord_username", user.username) + putString("discord_id", user.id) + putString("discord_avatar", user.avatar) + apply() + } + + } + + override fun onBind(p0: Intent?): IBinder? = null + + inner class DiscordWebSocketListener : WebSocketListener() { + override fun onOpen(webSocket: WebSocket, response: Response) { + super.onOpen(webSocket, response) + this@DiscordService.webSocket = webSocket + log("WebSocket: Opened") + } + + override fun onMessage(webSocket: WebSocket, text: String) { + super.onMessage(webSocket, text) + val json = JsonParser.parseString(text).asJsonObject + log("WebSocket: Received op code ${json.get("op")}") + when (json.get("op").asInt) { + 0 -> { + if(json.has("s")) { + log("WebSocket: Sequence ${json.get("s")} Received") + sequence = json.get("s").asInt + } + if (json.get("t").asString != "READY") return + saveProfile(text) + log(text) + sessionId = json.get("d").asJsonObject.get("session_id").asString + log("WebSocket: SessionID ${json.get("d").asJsonObject.get("session_id")} Received") + if(presenceStore.isNotEmpty()) setPresence(presenceStore) + sendBroadcast(Intent("ServiceToConnectButton")) + } + 1 -> { + log("WebSocket: Received Heartbeat request, sending heartbeat") + heartbeatThread.interrupt() + heartbeatSend(webSocket, sequence) + heartbeatThread = Thread(HeartbeatRunnable()) + heartbeatThread.start() + } + 7 -> { + resume = true + log("WebSocket: Requested to Restart, restarting") + webSocket.close(1000, "Requested to Restart by the server") + client = OkHttpClient() + client.newWebSocket( + Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(), + DiscordWebSocketListener() + ) + client.dispatcher.executorService.shutdown() + } + 9 -> { + log("WebSocket: Invalid Session, restarting") + webSocket.close(1000, "Invalid Session") + Thread.sleep(5000) + client = OkHttpClient() + client.newWebSocket( + Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(), + DiscordWebSocketListener() + ) + client.dispatcher.executorService.shutdown() + } + 10 -> { + heartbeat = json.get("d").asJsonObject.get("heartbeat_interval").asInt + heartbeatThread = Thread(HeartbeatRunnable()) + heartbeatThread.start() + if(resume) { + log("WebSocket: Resuming because server requested") + resume() + resume = false + } else { + identify(webSocket, baseContext) + log("WebSocket: Identified") + } + } + 11 -> { + log("WebSocket: Heartbeat ACKed") + heartbeatThread = Thread(HeartbeatRunnable()) + heartbeatThread.start() + } + } + } + fun identify(webSocket: WebSocket, context: Context) { + val properties = JsonObject() + properties.addProperty("os","linux") + properties.addProperty("browser","unknown") + properties.addProperty("device","unknown") + val d = JsonObject() + d.addProperty("token", getToken(context)) + d.addProperty("intents", 0) + d.add("properties", properties) + val payload = JsonObject() + payload.addProperty("op",2) + payload.add("d", d) + webSocket.send(payload.toString()) + } + override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { + super.onFailure(webSocket, t, response) + t.message?.let { Log.d("WebSocket", "onFailure() $it") } + log("WebSocket: Error, onFailure() reason: ${t.message}") + client = OkHttpClient() + client.newWebSocket( + Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(), + DiscordWebSocketListener() + ) + client.dispatcher.executorService.shutdown() + if(!heartbeatThread.isInterrupted) { heartbeatThread.interrupt() } + } + + override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { + super.onClosing(webSocket, code, reason) + Log.d("WebSocket", "onClosing() $code $reason") + if(!heartbeatThread.isInterrupted) { heartbeatThread.interrupt() } + } + + override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { + super.onClosed(webSocket, code, reason) + Log.d("WebSocket", "onClosed() $code $reason") + if(code >= 4000) { + log("WebSocket: Error, code: $code reason: $reason") + client = OkHttpClient() + client.newWebSocket( + Request.Builder().url("wss://gateway.discord.gg/?v=10&encoding=json").build(), + DiscordWebSocketListener() + ) + client.dispatcher.executorService.shutdown() + return + } + } + } + + fun getToken(context: Context): String { + val sharedPref = context.getSharedPreferences( + context.getString(R.string.preference_file_key), + Context.MODE_PRIVATE + ) + val token = sharedPref.getString(Discord.TOKEN, null) + if(token == null) { + log("WebSocket: Token not found") + errorNotification("Could not set the presence", "token not found") + return "" + } + else{ + return token + } + } + fun heartbeatSend( webSocket: WebSocket, seq: Int? ) { + val json = JsonObject() + json.addProperty("op",1) + json.addProperty("d", seq) + webSocket.send(json.toString()) + } + private fun errorNotification(title : String, text: String) { + val intent = Intent(this@DiscordService, MainActivity::class.java).apply { + action = Intent.ACTION_MAIN + addCategory(Intent.CATEGORY_LAUNCHER) + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } + val pendingIntent = + PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE) + val builder = NotificationCompat.Builder(this@DiscordService, "discordPresence") + .setSmallIcon(R.mipmap.ic_launcher_round) + .setContentTitle(title) + .setContentText(text) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_HIGH) + val notificationManager = NotificationManagerCompat.from(applicationContext) + if (ActivityCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + //TODO: Request permission + return + } + notificationManager.notify(2, builder.build()) + log("Error Notified") + } + + fun saveSimpleTestPresence() { + val file = File(baseContext.cacheDir, "payload") + //fill with test payload + val payload = JsonObject() + payload.addProperty("op", 3) + payload.add("d", JsonObject().apply { + addProperty("status", "online") + addProperty("afk", false) + add("activities", JsonArray().apply { + add(JsonObject().apply { + addProperty("name", "Test") + addProperty("type", 0) + }) + }) + }) + file.writeText(payload.toString()) + log("WebSocket: Simple Test Presence Saved") + } + + fun setPresence(String: String) { + log("WebSocket: Sending Presence payload") + log(String) + webSocket.send(String) + } + + fun log(string: String) { + Log.d("WebSocket_Discord", string) + //log += "${SimpleDateFormat("HH:mm:ss").format(Calendar.getInstance().time)} $string\n" + } + + fun saveLogToFile() { + val fileName = "log_${System.currentTimeMillis()}.txt" + + // ContentValues to store file metadata + val values = ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) + put(MediaStore.MediaColumns.MIME_TYPE, "text/plain") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put(MediaStore.MediaColumns.RELATIVE_PATH, "Download/") + } + } + + // Inserting the file in the MediaStore + val resolver = baseContext.contentResolver + val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values) + } else { + val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + val file = File(directory, fileName) + + // Make sure the Downloads directory exists + if (!directory.exists()) { + directory.mkdirs() + } + + // Use FileProvider to get the URI for the file + val authority = "${baseContext.packageName}.provider" // Adjust with your app's package name + Uri.fromFile(file) + } + + // Writing to the file + uri?.let { + resolver.openOutputStream(it).use { outputStream -> + OutputStreamWriter(outputStream).use { writer -> + writer.write(log) + } + } + } ?: run { + log("Error saving log file") + } + } + + fun resume() { + log("Sending Resume payload") + val d = JsonObject() + d.addProperty("token", getToken(baseContext)) + d.addProperty("session_id", sessionId) + d.addProperty("seq", sequence) + val json = JsonObject() + json.addProperty("op", 6) + json.add("d", d) + log(json.toString()) + webSocket.send(json.toString()) + } + + inner class HeartbeatRunnable : Runnable { + override fun run() { + try { + Thread.sleep(heartbeat.toLong()) + heartbeatSend(webSocket, sequence) + log("WebSocket: Heartbeat Sent") + } catch (e:InterruptedException) {} + } + } + + companion object { + var SERVICE_RUNNING = false + const val ACTION_STOP_SERVICE = "ACTION_STOP_SERVICE" + } +} + +object DiscordServiceRunningSingleton { + var running = false + +} \ No newline at end of file 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 6554f20b..a636adec 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/Login.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/Login.kt @@ -7,6 +7,7 @@ import android.os.Bundle import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient +import android.widget.Toast import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import ani.dantotsu.R @@ -67,11 +68,11 @@ class Login : AppCompatActivity() { private fun login(token: String) { if (token.isEmpty() || token == "null"){ - snackString("Failed to retrieve token") + Toast.makeText(this, "Failed to retrieve token", Toast.LENGTH_SHORT).show() finish() return } - snackString("Logged in successfully") + Toast.makeText(this, "Logged in successfully", Toast.LENGTH_SHORT).show() finish() saveToken(this, token) startMainActivity(this@Login) 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 90468921..800276d4 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt @@ -1,6 +1,10 @@ package ani.dantotsu.connections.discord +import android.widget.Toast import ani.dantotsu.connections.discord.serializers.* +import ani.dantotsu.currContext +import ani.dantotsu.logger +import ani.dantotsu.snackString import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Job @@ -32,7 +36,12 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { allowStructuredMapKeys = true ignoreUnknownKeys = true } + enum class Type { + PLAYING, STREAMING, LISTENING, WATCHING, COMPETING + } + data class Link(val label: String, val url: String) +/* private val client = OkHttpClient.Builder() .connectTimeout(10, SECONDS) .readTimeout(10, SECONDS) @@ -56,16 +65,11 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { var startTimestamp: Long? = null var stopTimestamp: Long? = null - enum class Type { - PLAYING, STREAMING, LISTENING, WATCHING, COMPETING - } - var buttons = mutableListOf() - data class Link(val label: String, val url: String) private suspend fun createPresence(): String { - return json.encodeToString(Presence.Response( + val j = json.encodeToString(Presence.Response( 3, Presence( activities = listOf( @@ -95,16 +99,12 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { status = status ) )) + logger("Presence: $j") + return j } - @Serializable - data class KizzyApi(val id: String) - val api = "https://kizzy-api.vercel.app/image?url=" - private suspend fun String.discordUrl(): String? { - if (startsWith("mp:")) return this - val json = app.get("$api$this").parsedSafe() - return json?.id - } + + private fun sendIdentify() { val response = Identity.Response( @@ -138,6 +138,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { } } if (!started) whenStarted = { + snackString("Discord message sent") send.invoke() whenStarted = null } @@ -185,21 +186,21 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { } override fun onMessage(webSocket: WebSocket, text: String) { - println("Discord Message : $text") val map = json.decodeFromString(text) seq = map.s - when (map.op) { 10 -> { map.d as JsonObject heartbeatInterval = map.d["heartbeat_interval"]!!.jsonPrimitive.long sendHeartBeat() sendIdentify() + snackString(map.t) } 0 -> if (map.t == "READY") { val user = json.decodeFromString(text).d.user + snackString(map.t) started = true whenStarted?.invoke(user) } @@ -207,6 +208,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { 1 -> { if (scope.isActive) scope.cancel() webSocket.send("{\"op\":1, \"d\":$seq}") + snackString(map.t) } 11 -> sendHeartBeat() @@ -214,6 +216,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { 9 -> { sendHeartBeat() sendIdentify() + snackString(map.t) } } } @@ -232,6 +235,68 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) { } } } +*/ + + companion object { + data class RPCData( + val applicationId: String? = null, + val type: Type? = null, + val activityName: String? = null, + val details: String? = null, + val state: String? = null, + val largeImage: Link? = null, + val smallImage: Link? = null, + val status: String? = null, + val startTimestamp: Long? = null, + val stopTimestamp: Long? = null, + val buttons: MutableList = mutableListOf() + ) + @Serializable + data class KizzyApi(val id: String) + val api = "https://kizzy-api.vercel.app/image?url=" + private suspend fun String.discordUrl(): String? { + if (startsWith("mp:")) return this + val json = app.get("$api$this").parsedSafe() + return json?.id + } + suspend fun createPresence(data: RPCData): String { + val json = Json { + encodeDefaults = true + allowStructuredMapKeys = true + ignoreUnknownKeys = true + } + return json.encodeToString(Presence.Response( + 3, + Presence( + activities = listOf( + Activity( + name = data.activityName, + state = data.state, + details = data.details, + type = data.type?.ordinal, + timestamps = if (data.startTimestamp != null) + Activity.Timestamps(data.startTimestamp, data.stopTimestamp) + else null, + assets = Activity.Assets( + largeImage = data.largeImage?.url?.discordUrl(), + largeText = data.largeImage?.label, + smallImage = data.smallImage?.url?.discordUrl(), + smallText = data.smallImage?.label + ), + buttons = data.buttons.map { it.label }, + metadata = Activity.Metadata( + buttonUrls = data.buttons.map { it.url } + ), + applicationId = data.applicationId, + ) + ), + afk = true, + since = data.startTimestamp, + status = data.status + ) + )) + } + } } diff --git a/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt b/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt index 225ad73d..9148de8d 100644 --- a/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt +++ b/app/src/main/java/ani/dantotsu/connections/discord/serializers/User.kt @@ -5,56 +5,57 @@ import kotlinx.serialization.descriptors.* import kotlinx.serialization.encoding.* import kotlinx.serialization.json.* + @Serializable data class User ( - val verified: Boolean, + val verified: Boolean? = null, val username: String, @SerialName("purchased_flags") - val purchasedFlags: Long, + val purchasedFlags: Long? = null, @SerialName("public_flags") - val publicFlags: Long, + val publicFlags: Long? = null, - val pronouns: String, + val pronouns: String? = null, @SerialName("premium_type") - val premiumType: Long, + val premiumType: Long? = null, - val premium: Boolean, - val phone: String, + val premium: Boolean? = null, + val phone: String? = null, @SerialName("nsfw_allowed") - val nsfwAllowed: Boolean, + val nsfwAllowed: Boolean? = null, - val mobile: Boolean, + val mobile: Boolean? = null, @SerialName("mfa_enabled") - val mfaEnabled: Boolean, + val mfaEnabled: Boolean? = null, val id: String, @SerialName("global_name") - val globalName: String, + val globalName: String? = null, - val flags: Long, - val email: String, - val discriminator: String, - val desktop: Boolean, - val bio: String, + val flags: Long? = null, + val email: String? = null, + val discriminator: String? = null, + val desktop: Boolean? = null, + val bio: String? = null, @SerialName("banner_color") - val bannerColor: String, + val bannerColor: String? = null, val banner: JsonElement? = null, @SerialName("avatar_decoration") val avatarDecoration: JsonElement? = null, - val avatar: String, + val avatar: String? = null, @SerialName("accent_color") - val accentColor: Long + val accentColor: Long? = null ) { @Serializable data class Response( 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 abf500ce..5c35d2a3 100644 --- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt +++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt @@ -62,6 +62,9 @@ import ani.dantotsu.* import ani.dantotsu.R import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.discord.Discord +import ani.dantotsu.connections.discord.DiscordService +import ani.dantotsu.connections.discord.DiscordService.Companion.ACTION_STOP_SERVICE +import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton import ani.dantotsu.connections.discord.RPC import ani.dantotsu.connections.updateProgress import ani.dantotsu.databinding.ActivityExoplayerBinding @@ -813,14 +816,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { fun fastForward() { isFastForwarding = true - exoPlayer.setPlaybackSpeed(2f) + exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2) snackString("Playing at 2x speed") } fun stopFastForward() { if (isFastForwarding) { isFastForwarding = false - exoPlayer.setPlaybackSpeed(1f) + exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed / 2) snackString("Playing at normal speed") } } @@ -862,6 +865,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { override fun onLongClick(event: MotionEvent) { if (settings.fastforward) fastForward() } + override fun onDoubleClick(event: MotionEvent) { doubleTap(true, event) } @@ -994,22 +998,40 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { playbackPosition = loadData("${media.id}_${ep.number}", this) ?: 0 initPlayer() preloading = false - rpc = Discord.defaultRPC() - rpc?.send { - type = RPC.Type.WATCHING - activityName = media.userPreferredName - details = ep.title?.takeIf { it.isNotEmpty() } ?: getString( - R.string.episode_num, - ep.number + val context = this + + lifecycleScope.launch { + val presence = RPC.createPresence(RPC.Companion.RPCData( + applicationId = Discord.application_Id, + type = RPC.Type.WATCHING, + activityName = media.userPreferredName, + details = ep.title?.takeIf { it.isNotEmpty() } ?: getString( + R.string.episode_num, + ep.number + ), + state = "Episode : ${ep.number}/${media.anime?.totalEpisodes ?: "??"}", + largeImage = media.cover?.let { RPC.Link(media.userPreferredName, it) }, + smallImage = RPC.Link( + "Dantotsu", + Discord.small_Image + ), + buttons = mutableListOf( + RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""), + RPC.Link( + "Stream on Dantotsu", + "https://github.com/rebelonion/Dantotsu/" + ) + ) ) - state = "Episode : ${ep.number}/${media.anime?.totalEpisodes ?: "??"}" - media.cover?.let { cover -> - largeImage = RPC.Link(media.userPreferredName, cover) - } - media.shareLink?.let { link -> - buttons.add(0, RPC.Link(getString(R.string.view_anime), link)) + ) + + val intent = Intent(context, DiscordService::class.java).apply { + putExtra("presence", presence) } + DiscordServiceRunningSingleton.running = true + startService(intent) } + updateProgress() } } @@ -1278,6 +1300,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { } val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType) + logger("url: ${video!!.file.url}") + logger("mimeType: $mimeType") if (sub != null) { val listofnotnullsubs = immutableListOf(sub).filterNotNull() @@ -1310,7 +1334,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { ) .setMaxVideoSize(1, 1) //.setOverrideForType( - // TrackSelectionOverride(trackSelector, 2)) + // TrackSelectionOverride(trackSelector, 2)) ) if (playbackPosition != 0L && !changingServer && !settings.alwaysContinue) { @@ -1329,17 +1353,17 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { ) AlertDialog.Builder(this, R.style.DialogTheme) .setTitle(getString(R.string.continue_from, time)).apply { - setCancelable(false) - setPositiveButton(getString(R.string.yes)) { d, _ -> - buildExoplayer() - d.dismiss() - } - setNegativeButton(getString(R.string.no)) { d, _ -> - playbackPosition = 0L - buildExoplayer() - d.dismiss() - } - }.show() + setCancelable(false) + setPositiveButton(getString(R.string.yes)) { d, _ -> + buildExoplayer() + d.dismiss() + } + setNegativeButton(getString(R.string.no)) { d, _ -> + playbackPosition = 0L + buildExoplayer() + d.dismiss() + } + }.show() } else buildExoplayer() } @@ -1404,7 +1428,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { exoPlayer.release() VideoCache.release() mediaSession?.release() - rpc?.close() + val stopIntent = Intent(this, DiscordService::class.java).apply { + putExtra(ACTION_STOP_SERVICE, true) + } + DiscordServiceRunningSingleton.running = false + startService(stopIntent) + } override fun onSaveInstanceState(outState: Bundle) { @@ -1589,17 +1618,19 @@ class ExoplayerView : AppCompatActivity(), Player.Listener { println("Track__: ${it.isSelected}") println("Track__: ${it.type}") println("Track__: ${it.mediaTrackGroup.id}") - if (it.type == 3 && it.mediaTrackGroup.id == "1:"){ + if (it.type == 3 && it.mediaTrackGroup.id == "1:") { playerView.player?.trackSelectionParameters = playerView.player?.trackSelectionParameters?.buildUpon() ?.setOverrideForType( - TrackSelectionOverride(it.mediaTrackGroup, it.length - 1)) + TrackSelectionOverride(it.mediaTrackGroup, it.length - 1) + ) ?.build()!! - }else if(it.type == 3){ + } else if (it.type == 3) { playerView.player?.trackSelectionParameters = playerView.player?.trackSelectionParameters?.buildUpon() ?.addOverride( - TrackSelectionOverride(it.mediaTrackGroup, listOf())) + TrackSelectionOverride(it.mediaTrackGroup, listOf()) + ) ?.build()!! } } diff --git a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt index a5682716..d4176ebe 100644 --- a/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/manga/mangareader/MangaReaderActivity.kt @@ -3,6 +3,7 @@ package ani.dantotsu.media.manga.mangareader import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.app.AlertDialog +import android.content.Intent import android.content.res.Configuration import android.graphics.Bitmap import android.os.Build @@ -25,12 +26,15 @@ import androidx.viewpager2.widget.ViewPager2 import ani.dantotsu.* import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.discord.Discord +import ani.dantotsu.connections.discord.DiscordService +import ani.dantotsu.connections.discord.DiscordServiceRunningSingleton import ani.dantotsu.connections.discord.RPC import ani.dantotsu.connections.updateProgress import ani.dantotsu.databinding.ActivityMangaReaderBinding import ani.dantotsu.media.Media import ani.dantotsu.media.MediaDetailsViewModel import ani.dantotsu.media.MediaSingleton +import ani.dantotsu.media.anime.ExoplayerView import ani.dantotsu.media.manga.MangaCache import ani.dantotsu.media.manga.MangaChapter import ani.dantotsu.media.manga.MangaNameAdapter @@ -121,7 +125,11 @@ class MangaReaderActivity : AppCompatActivity() { override fun onDestroy() { mangaCache.clear() - rpc?.close() + val stopIntent = Intent(this, DiscordService::class.java).apply { + putExtra(DiscordService.ACTION_STOP_SERVICE, true) + } + DiscordServiceRunningSingleton.running = false + startService(stopIntent) super.onDestroy() } @@ -300,19 +308,36 @@ ThemeManager(this).applyTheme() binding.mangaReaderNextChap.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" binding.mangaReaderPrevChap.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" applySettings() - rpc?.close() - rpc = Discord.defaultRPC() - rpc?.send { - type = RPC.Type.WATCHING - activityName = media.userPreferredName - details = chap.title?.takeIf { it.isNotEmpty() } ?: getString(R.string.chapter_num, chap.number) - state = "${chap.number}/${media.manga?.totalChapters ?: "??"}" - media.cover?.let { cover -> - largeImage = RPC.Link(media.userPreferredName, cover) - } - media.shareLink?.let { link -> - buttons.add(0, RPC.Link(getString(R.string.view_manga), link)) + val context = this + lifecycleScope.launch { + val presence = RPC.createPresence(RPC.Companion.RPCData( + applicationId = Discord.application_Id, + type = RPC.Type.WATCHING, + activityName = media.userPreferredName, + details = chap.title?.takeIf { it.isNotEmpty() } ?: getString(R.string.chapter_num, chap.number), + state = "${chap.number}/${media.manga?.totalChapters ?: "??"}", + largeImage = media.cover?.let { cover -> + RPC.Link(media.userPreferredName, cover) + }, + smallImage = RPC.Link( + "Dantotsu", + Discord.small_Image + ), + buttons = mutableListOf( + RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""), + RPC.Link( + "Stream on Dantotsu", + "https://github.com/rebelonion/Dantotsu/" + ) + ) + ) + ) + + val intent = Intent(context, DiscordService::class.java).apply { + putExtra("presence", presence) } + DiscordServiceRunningSingleton.running = true + startService(intent) } } } diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 976b9640..11654b77 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -578,11 +578,14 @@ OS Version: $CODENAME $RELEASE ($SDK_INT) } if (Discord.token != null) { - if (Discord.avatar != null) { - binding.settingsDiscordAvatar.loadImage(Discord.avatar) + val id = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_id", null) + val avatar = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_avatar", null) + val username = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE).getString("discord_username", null) + if (id != null && avatar != null) { + binding.settingsDiscordAvatar.loadImage("https://cdn.discordapp.com/avatars/$id/$avatar.png") } binding.settingsDiscordUsername.visibility = View.VISIBLE - binding.settingsDiscordUsername.text = Discord.userid ?: Discord.token?.replace(Regex("."),"*") + binding.settingsDiscordUsername.text = username ?: Discord.token?.replace(Regex("."),"*") binding.settingsDiscordLogin.setText(R.string.logout) binding.settingsDiscordLogin.setOnClickListener { Discord.removeSavedToken(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt index 68bcfd88..277afa71 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/AnimeExtensionManager.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.anime import android.content.Context import android.graphics.drawable.Drawable +import ani.dantotsu.snackString import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.anime.api.AnimeExtensionGithubApi @@ -116,7 +117,7 @@ class AnimeExtensionManager( api.findExtensions() } catch (e: Exception) { logcat(LogPriority.ERROR, e) - withUIContext { context.toast("Failed to get extensions list") } + withUIContext { snackString("Failed to get extensions list") } emptyList() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/MangaExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/MangaExtensionManager.kt index 549ba550..260af47c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/MangaExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/MangaExtensionManager.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.manga import android.content.Context import android.graphics.drawable.Drawable +import ani.dantotsu.snackString import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.InstallStep import eu.kanade.tachiyomi.extension.manga.api.MangaExtensionGithubApi @@ -116,7 +117,7 @@ class MangaExtensionManager( api.findExtensions() } catch (e: Exception) { logcat(LogPriority.ERROR, e) - withUIContext { context.toast("Failed to get manga extensions") } + withUIContext { snackString("Failed to get manga extensions") } emptyList() } diff --git a/app/src/main/res/layout/fragment_manga_offline.xml b/app/src/main/res/layout/fragment_manga_offline.xml index 279da4b9..a8c727f0 100644 --- a/app/src/main/res/layout/fragment_manga_offline.xml +++ b/app/src/main/res/layout/fragment_manga_offline.xml @@ -97,9 +97,7 @@ android:layout_height="0dp" android:layout_weight="1" android:numColumns="auto_fit" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:columnWidth="108dp" + android:columnWidth="128dp" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:padding="10dp" diff --git a/app/src/main/res/values-en-rDW/strings.xml b/app/src/main/res/values-en-rDW/strings.xml index 306eff71..fdcbcc2f 100644 --- a/app/src/main/res/values-en-rDW/strings.xml +++ b/app/src/main/res/values-en-rDW/strings.xml @@ -1,64 +1,64 @@ + tools:locale="en-rUS"> - webewonyion/Dantotsu - d-d-d-dantotsupwefs + rebelonion/Dantotsu + dantotsuprefs Dantotsu - The ;;w;; NYEW ***screeches*** Best ÚwÚ Anyime (˘˘˘) & M-m-manga (◡ ꒳ ◡) app fow Andwoid. + The NEW Best Anime & Manga app for Android. - Wogin – ̗̀ (ᵕ꒳ᵕ) ̖́- - Wogout (ᴜ‿ᴜ✿) + Login + Logout https://discord.gg/4HPZ5nAWwM https://github.com/rebelonion/Dantotsu - H-h-h-home UwU - Anyime - Bwowse Anyime (・\`ω\´・) - Manga (ᴜ‿ᴜ✿) - Bwowse Manga (⑅˘꒳˘) + Home + Anime + Browse Anime + Manga + Browse Manga Info - Watch x3 ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - Wead + Watch + Read - Anyime Wist ***wags my tail*** - Manga Wist (ᵕᴗ ᵕ⁎) + Anime List + Manga List - X( (ᴜ‿ᴜ✿) + X( :o " : " - Nyo Intewnyet Connyection - W-w-w-wefwesh + No Internet Connection + Refresh - S-s-s-seawch (◡ ꒳ ◡) - Seawch ( ͡o ꒳ ͡o ) Wesuwts x3 - Sowt (ᴜ‿ᴜ✿) ***cries*** By - Genwe - Top owo Scowe - Wecentwy ( ͡o ᵕ ͡o ) Updated - Twending Anyime - Popuwaw ***wags my tail*** A-a-anyime - T-t-t-twending Manga (⑅˘꒳˘) ***nuzzles your necky wecky*** - Twending Nyuvw - Popuwaw ( ͡o ꒳ ͡o ) ***walks away*** Manga + Search + Search Results + Sort By + Genre + Top Score + Recently Updated + Trending Anime + Popular Anime + Trending Manga + Trending Novel + Popular Manga - Usewnyame - "Chaptews Wead (◡ w ◡) ***wags my tail*** " (⑅˘꒳˘) - "Episodes Uwu Watched (ㅅꈍ ˘ ꈍ) " - Continyue Weading - Continyue (◡ ω ◡) Watching - W-w-w-wecommended ღ(U꒳Uღ) + Username + "Chapters Read " + "Episodes Watched " + Continue Reading + Continue Watching + Recommended - Watch/Wead some Anyime ow Manga ***looks at you*** to get W-w-w-wecommendations ***blushes*** - Aww c-c-caught up, when ;;w;; Nyew?!! + Watch/Read some Anime or Manga to get Recommendations + All caught up, when New? Settings - Add ^-^ to Wist - Wist E-e-e-editow - Add ***blushes*** to ***wags my tail*** Favouwites - Nyotifications + Add to List + List Editor + Add to Favourites + Notifications Reading Watching @@ -97,95 +97,95 @@ PAUSED DROPPED - PWOGWESS - SCOWE ***blushes*** - "-"-" (uwu) / („ᵕᴗᵕ„) 10" - STAWTED AT (˘ε˘) - COMPWETED AT - Save ***looks around suspiciously*** - Dewete - Wemuv + PROGRESS + SCORE + " / 10" + STARTED AT + COMPLETED AT + Save + Delete + Remove - Nyame - Nyame W-w-w-womaji - Mean Scowe ^w^ ***smirks smuggly*** - Fowmat (U ﹏ U) - Status (ㅅꈍ ˘ ꈍ) - Totaw (U ᵕ U❁) Episodes - Totaw C-c-chaptews - Avewage Duwation *˚*(ꈍ ω ꈍ).₊̣̇. ***looks around suspiciously*** - " min" ***glomps and huggles*** + Name + Name Romaji + Mean Score + Format + Status + Total Episodes + Total Chapters + Average Duration + " min" Season - S-s-stawt Date (˘ᵕ˘) ***screams*** + Start Date End Date - Souwce + Source Studio - Genwes - Synyopsis - Chawactews ***walks away*** - W-w-w-wewations + Genres + Synopsis + Characters + Relations - Wowes - Detaiws ♥(。U ω U。) + Roles + Details - Pway ÚwÚ on Youtube - Episodes (◡ ꒳ ◡) - Episode ***wags my tail*** - Chaptews - Chaptew ♥(。U ω U。) + Play on Youtube + Episodes + Episode + Chapters + Chapter Wrong Title? - Couwdn\'t *:・゚✧(ꈍᴗꈍ)✧・゚:* find (・\`ω\´・) anything X( ~(˘▾˘~) \-\-\n - ღ(U꒳Uღ) Twy anyothew (˘ε˘) souwce. + Couldn\'t find anything X( \n + Try another source. - %1$s is nyot suppowted?!! ***nuzzles your necky wecky*** - Sewect Sewvew (˘ሠ˘) - Auto S-s-s-sewecting Sewvew - Make Defauwt - Fiwwew (˘˘˘) - A-a-aduwt owo - Wist ***licks lips*** Onwy (˘³˘) ***nuzzles your necky wecky*** - Tag ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - T-t-tags - Synyonyms ( ͡o ꒳ ͡o ) - Twaiwew :3 - Openying - Ending (◦ᵕ ˘ ᵕ◦) ***cries*** - Pwequew ( ͡o ᵕ ͡o ) - Sequew + %1$s is not supported! + Select Server + Auto Selecting Server + Make Default + Filler + Adult + List Only + Tag + Tags + Synonyms + Trailer + Opening + Ending + Prequel + Sequel - Anyiwist Settings (⑅˘꒳˘) - Extension - Downwoads + Anilist Settings + Extensions + Downloads Settings - Extensions ***walks away*** - Pwayew x3 Settings ( ͡o ꒳ ͡o ) - Onwy (uwu) ***looks at you*** show (ᵘﻌᵘ) My (˘³˘) Shows in Wecentwy :3 U-u-updated ***nuzzles your necky wecky*** - Downwoad Manyagew - Downwoad in Uwu SD cawd - Nyo SD x3 cawd was ***wags my tail*** Found. - Weadew ***boops your nose*** Settings - Defauwt ( ᵘ ꒳ ᵘ ✼) Souwce - Show Youtube Wink - Defauwt (◡ ω ◡) Episode Wayout ~(˘▾˘~) ***huggles tightly*** - Defauwt ***pounces on you*** Chaptew ***blushes*** Wayout - U-u-usew (ᵘﻌᵘ) Intewface + Extensions + Player Settings + Only show My Shows in Recently Updated + Download Manager + Download in SD card + No SD card was Found. + Reader Settings + Default Source + Show Youtube Link + Default Episode Layout + Default Chapter Layout + User Interface Common - Theme ***whispers to self*** + Theme UI Settings About - " Dantotsu Uwu i-i-is cwafted fwom the ashes of ( 。ᵘ ᵕ ᵘ 。) Saikou (◡ ω ◡) and based (˘ε˘) on simpwistic yet (˘ᵕ˘) state-of-the-awt e-e-ewegance. It (˘ሠ˘) is ***runs away*** an Anyiwist onwy c-c-c-cwient, which awso wets you (ᴜ‿ᴜ✿) stweam-downwoad (◡ ω ◡) A-a-anyime thwough ***smirks smuggly*** extensions & ***screams*** M-m-manga.\nDantotsu ;;w;; witewawwy means ( ᵘ ꒳ ᵘ ✼) the \"best of ***pounces on you*** t-t-t-the best\" in japanyese. Weww, we wouwd ^w^ wike ღ(U꒳Uღ) to say this is the ( ᵘ ꒳ ᵘ ✼) best open *:・゚✧(ꈍᴗꈍ)✧・゚:* souwce (ㅅꈍ ˘ ꈍ) app (ᵘʷᵘ) fow (˘³˘) ***blushes*** anyime and manga o-o-on A-a-a-andwoid, what wouwd you x3 ***smirks smuggly*** say?" :3 - Devewopews - Discwaimew + " Dantotsu is crafted from the ashes of Saikou and based on simplistic yet state-of-the-art elegance. It is an Anilist only client, which also lets you stream-download Anime through extensions & Manga.\nDantotsu literally means the \"best of the best\" in japanese. Well, we would like to say this is the best open source app for anime and manga on Android, what would you say?" + Developers + Disclaimer - ----- (uwu) Dantotsu ( ᵘ ꒳ ᵘ ✼) by itsewf onwy *:・゚✧(ꈍᴗꈍ)✧・゚:* pwovides an anyime and ***walks away*** manga (・\`ω\´・) twackew and does ***sweats*** n-n-nyot (ᵘﻌᵘ) p-p-p-pwovide any anyime ***glomps and huggles*** ow (˘ε˘) manga stweaming ;;w;; ow downwoading capabiwities. - \n\n ------- Dantotsu ow any Uwu ***blushes*** of its owo d-d-devewopew/staff don\'t host any of the c-c-c-content found inside ***glomps*** Dantotsu. Any and a-a-aww images and anyime/manga infowmation (˘˘˘) found ~(˘▾˘~) i-i-in t-t-the (・\`ω\´・) app ( ᵘ ꒳ ᵘ ✼) awe taken ***glomps and huggles*** fwom vawious ***glomps*** pubwic (˘˘˘) APIs (AnyiWist, MyAnyimeWist, Kitsu). - \n\n - Fuwthewmowe, aww of the (U ﹏ U) anyime/manga ( ͡o ꒳ ͡o ) winks (◦ᵕ ˘ ᵕ◦) found in Dantotsu a-a-a-awe t-t-t-taken fwom vawious ÚwÚ 3wd (U ﹏ U) pawty pwugins (˘³˘) and (⑅˘꒳˘) have nyo affiwiation with Dantotsu ;;w;; ow its ( ˘ᴗ˘ ) staff. - \-\-\-\n\n - (・\`ω\´・) Dantotsu ow (◦ᵕ ˘ ᵕ◦) ***pounces on you*** it\'s ownyews awen\'t (◡ ω ◡) wiabwe fow a-a-any misuse of any of ♥(。U ω U。) the contents (U ᵕ U❁) found inside ow o-o-o-outside of t-t-the app and ( ˊ.ᴗˋ ) cannyot b-b-be hewd ***huggles tightly*** accountabwe fow the distwibution of any o-o-of (ᵘʷᵘ) the contents found (˘ሠ˘) inside the app. - (◡ ሠ ◡) \-\-\-\n\n - By using Dantotsu, you compwy (。ᴜ‿‿ᴜ。) to ( ͡U ω ͡U ) the fact that ***smirks smuggly*** the (◡ ꒳ ◡) ***blushes*** devewopew (⑅˘꒳˘) of t-t-t-the a-a-app is n-n-nyot (˘ε˘) wesponsibwe fow (˘˘˘) any (˘ε˘) ***nuzzles your necky wecky*** o-o-of (U ᵕ U❁) the contents found in the (˘ᵕ˘) app. ^-^ You awso agwee to the f-f-f-fact ***boops your nose*** that (ᵘʷᵘ) you m-m-may nyot use ***cries*** Dantotsu to downwoad ow stweam any copywighted OwO c-c-c-content. - \n\n :3 - ( ͡U ω ͡U ) If the intewnyet infwingement issues awe invowved, (・\`ω\´・) ***cries*** pwease (U ﹏ U) contact ***glomps and huggles*** the souwce ~(˘▾˘~) ***whispers to self*** w-w-website. The (˘ω˘) devewopew ***glomps and huggles*** does ღ(U꒳Uღ) n-n-nyot assume any (◡ ሠ ◡) wegaw wesponsibiwity. + - Dantotsu by itself only provides an anime and manga tracker and does not provide any anime or manga streaming or downloading capabilities. + \n\n - Dantotsu or any of its developer/staff don\'t host any of the content found inside Dantotsu. Any and all images and anime/manga information found in the app are taken from various public APIs (AniList, MyAnimeList, Kitsu). + \n\n - Furthermore, all of the anime/manga links found in Dantotsu are taken from various 3rd party plugins and have no affiliation with Dantotsu or its staff. + \n\n - Dantotsu or it\'s owners aren\'t liable for any misuse of any of the contents found inside or outside of the app and cannot be held accountable for the distribution of any of the contents found inside the app. + \n\n - By using Dantotsu, you comply to the fact that the developer of the app is not responsible for any of the contents found in the app. You also agree to the fact that you may not use Dantotsu to download or stream any copyrighted content. + \n\n - If the internet infringement issues are involved, please contact the source website. The developer does not assume any legal responsibility. - Vewsion (◦ᵕ ˘ ᵕ◦) %1$s uwU + Version %1$s You can Long Click an episode/chapter to Mark it as Read. Long Clicking Shows can directly open List Editor. @@ -210,66 +210,66 @@ Video - Show Video (U ﹏ U) Info ***cries*** - Show Souwce Nyame ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - Shows (。ᴜ‿‿ᴜ。) ***whispers to self*** what the ( ͡U ω ͡U ) Wesowution of the c-c-cuwwent video pwaying, uwU usefuw (⑅˘꒳˘) fow ***looks around suspiciously*** \"Multi Quality\" servers. - Auto Quawity S-s-s-sewection + Show Video Info + Show Source Name + Shows what the Resolution of the current video playing, useful for \"Multi Quality\" servers. + Auto Quality Selection Height Width - Automaticawwy uses the cwosest quawity pwovided by ***walks away*** defauwt, ONWY uwU appwied fow \"Muwti Quawity\" Sewvews. ( ˊ.ᴗˋ ) Auto (ㅅꈍ ˘ ꈍ) changes upon pwaying a ( ͡U ω ͡U ) video. - Defauwt ^-^ Pwayback – ̗̀ (ᵕ꒳ᵕ) ̖́- Speed : %1$s (。ᴜ‿‿ᴜ。) - Cuwsed Speeds - Defauwt Wesize Mode (。U ω U。) - Subtitwes - Subtitwes - Subtitwe Cowow (◡ ሠ ◡) - S-s-subtitwe Outwinye Cowow - S-s-s-subtitwe Outwinye :3 Type - Subtitwe Backgwound *˚*(ꈍ ω ꈍ).₊̣̇. Cowow (˘ε˘) - Subtitwe ;;w;; W-w-w-window Cowow - "The ( ͡U ω ͡U ) subtitwe („ᵕᴗᵕ„) window is the pawt weft (˘³˘) and wight owo fwom them. (・\`ω\´・) (whewe – ̗̀ (ᵕ꒳ᵕ) ̖́- the ***looks at you*** b-b-b-backgwound isn\'t)" ( ˊ.ᴗˋ ) + Automatically uses the closest quality provided by default, ONLY applied for \"Multi Quality\" Servers. Auto changes upon playing a video. + Default Playback Speed : %1$s + Cursed Speeds + Default Resize Mode + Subtitles + Subtitles + Subtitle Color + Subtitle Outline Color + Subtitle Outline Type + Subtitle Background Color + Subtitle Window Color + "The subtitle window is the part left and right from them. (where the background isn\'t)" Note: Changing Subtitle formatting only works with Soft-Subbed Sources such as Zoro! - Subtitwe Font (ᵘʷᵘ) - Subtitwe ***glomps*** Size x3 + Subtitle Font + Subtitle Size - Auto ***pounces on you*** - Autopway Nyext (˘˘˘) Episode - Automaticawwy disabwes (◡ ω ◡) if ( ˘ᴗ˘ ) thewe is nyo ( ͡U ω ͡U ) ***nuzzles your necky wecky*** intewaction with p-p-p-pwayew (˘ε˘) aftew 1 houw. - Auto Skip Fiwwews ( ˘ᴗ˘ ) - Skips ღ(U꒳Uღ) fiwwew e-e-e-episodes when going to nyext *:・゚✧(ꈍᴗꈍ)✧・゚:* episode. + Auto + Autoplay Next Episode + Automatically disables if there is no interaction with player after 1 hour. + Auto Skip Fillers + Skips filler episodes when going to next episode. - Update Pwogwess - Ask fow each UwU A-a-a-anyime ( ͡o ꒳ ͡o ) \"Individuawwy\" (◡ ꒳ ◡) ***nuzzles your necky wecky*** - Ask (˘³˘) fow e-e-each Manga (U ﹏ U) \"Individuawwy\" ***walks away*** - Tuwnying OwO ***looks at you*** off wiww awways automaticawwy update pwogwess when ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** the episode is ***nuzzles your necky wecky*** watched. ღ(U꒳Uღ) - Tuwnying off wiww awways automaticawwy update pwogwess when the chaptew is wead. ♥(。U ω U。) - Update Pwogwess fow Hentai - Update Pwogwess fow Doujins (。ᴜ‿‿ᴜ。) - vewy (˘ሠ˘) ***smirks smuggly*** bowd ( ᵘ ꒳ ᵘ ✼) of (ㅅꈍ ˘ ꈍ) you saw (ᴜ‿ᴜ✿) - Watch (。U ω U。) Update Pewcentage - T-t-t-the pewcentage at which youw Anyiwist ***smirks smuggly*** pwogwess shouwd be (◡ ꒳ ◡) updated a-a-a-aftew ( ͡o ꒳ ͡o ) watching owo an episode. \nThis awso s-s-s-sets the ^w^ \% fow ***sweats*** w-w-w-when ( ᵘ ꒳ ᵘ ✼) to pwewoad winks fow the nyext (◡ w ◡) ***screams*** episode. + Update Progress + Ask for each Anime \"Individually\" + Ask for each Manga \"Individually\" + Turning off will always automatically update progress when the episode is watched. + Turning off will always automatically update progress when the chapter is read. + Update Progress for Hentai + Update Progress for Doujins + very bold of you sar + Watch Update Percentage + The percentage at which your Anilist progress should be updated after watching an episode. \nThis also sets the \% for when to preload links for the next episode. - Behaviouw - Awways C-c-c-continyue ***screams*** fwom w-w-w-whewe you weft off (⑅˘꒳˘) - Pause OwO when nyot in Focus - Vowume & Bwightnyess Gestuwes - Doubwe t-t-t-tap to Seek - Fast Fowwawd - Tuwnying ***nuzzles your necky wecky*** off ~(˘▾˘~) wiww s-s-s-show fast fowwawd & wewind buttons ♥(。U ω U。) - S-s-seek Time - Amount of time in ÚwÚ seconds fow f-f-fast fowwawd &-&-& wewind. - S-s-s-skip OwO Time ***smirks smuggly*** - Setting to Uwu 0, h-h-hides ***cries*** the Skip Button. + Behaviour + Always Continue from where you left off + Pause when not in Focus + Volume & Brightness Gestures + Double tap to Seek + Fast Forward + Turning off will show fast forward & rewind buttons + Seek Time + Amount of time in seconds for fast forward & rewind. + Skip Time + Setting to 0, hides the Skip Button. Show Cast Button - Wequiwes ***smirks smuggly*** \"Web Video C-c-c-castew\" app to – ̗̀ (ᵕ꒳ᵕ) ̖́- cast. - Pictuwe in ;;w;; P-p-p-pictuwe - Awways Minyimize - Wequiwes *˚*(ꈍ ω ꈍ).₊̣̇. PiP to be enyabwed, makes the pwayew (uwu) behave (U ᵕ U❁) wike ***cries*** Youtube Pwayew (˘ε˘) but bettew.\nAwso hides the P-p-piP button. + Requires \"Web Video Caster\" app to cast. + Picture in Picture + Always Minimize + Requires PiP to be enabled, makes the player behave like Youtube Player but better.\nAlso hides the PiP button. - App ***looks around suspiciously*** - Hide Status Baw - Wequiwes App westawt ÚwÚ to fuwwy appwy. - Show/Hide (˘ᵕ˘) W-w-w-wayouts on Home ***cries*** + App + Hide Status Bar + Requires App restart to fully apply. + Show/Hide Layouts on Home Continue Watching Favourite Anime @@ -280,178 +280,178 @@ Recommended - Defauwt Stawt Up Tab - Smaww – ̗̀ (ᵕ꒳ᵕ) ̖́- View in T-t-twending Shows - Anyimations - Bannyew *:・゚✧(ꈍᴗꈍ)✧・゚:* A-a-anyimations - Wayout ***wags my tail*** Anyimations - Ovewaww ( ᴜ ω ᴜ ) ***pounces on you*** Speed (⑅˘꒳˘) - W-w-wooks wike y-y-you don\'t w-w-wike a-a-a-anything,\nTwy ***boops your nose*** w-w-w-wiking a show (˘ω˘) to k-k-k-keep UwU it („ᵕᴗᵕ„) hewe. - Favouwite Anyime (˘ᵕ˘) - Favouwite M-m-m-manga - Westawt the a-a-a-app!!11 \>w\< - Nyext - Pwevious ♥(。U ω U。) - Cuwwent Page - D-d-d-dubbed + Default Start Up Tab + Small View in Trending Shows + Animations + Banner Animations + Layout Animations + Overall Speed + Looks like you don\'t like anything,\nTry liking a show to keep it here. + Favourite Anime + Favourite Manga + Restart the app? + Next + Previous + Current Page + Dubbed Subbed - Pwefew ( ᴜ ω ᴜ ) Dubbed uwU Anyime - Nyonye ( ˘ᴗ˘ ) - Sewected UwU DNS - Change if youw ISP bwocks any (◡ w ◡) souwce (ᵘﻌᵘ) - Keep Scween O-o-on (◡ w ◡) - W-w-w-wayout ***huggles tightly*** - Spaced Pages Uwu - D-d-diwection + Prefer Dubbed Anime + None + Selected DNS + Change if your ISP blocks any source + Keep Screen On + Layout + Spaced Pages + Direction - Genyewaw ***smirks smuggly*** - Show (◡ ω ◡) Status & Nyavigation Baws + General + Show Status & Navigation Bars Auto Detect Webtoon - If the (⑅˘꒳˘) Manga ( ͡o ᵕ ͡o ) is ( ˘ᴗ˘ ) nyot (ᵘﻌᵘ) ***looks around suspiciously*** fwom (◡ w ◡) Japan, the weadew wiww ***looks around suspiciously*** d-d-defauwt ***screeches*** to ***screeches*** Webtoon Weadew Settings - Defauwt Settings - Howizontaw ( ˊ.ᴗˋ ) Scwoww OwO Baw - Duaw ^w^ Page (ᵘﻌᵘ) - Shows (˘ω˘) 2 Images in 1 page, ***glomps*** wiww (。U ω U。) wook (ㅅꈍ ˘ ꈍ) ***cries*** weiwd if the ♥(。U ω U。) images ♥(。U ω U。) awen\'t the same ***huggles tightly*** size - Twue ***sweats*** Cowows - (32-bit (◡ ꒳ ◡) ***glomps and huggles*** cowow Mode) ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** Weduces Banding ^-^ o-o-on the ( ͡U ω ͡U ) ***glomps and huggles*** images, m-m-m-may x3 affect (◡ ሠ ◡) pewfowmance. - Image Wotation \>w\< - Hide P-p-p-page owo Nyumbews - Sowt (。ᴜ‿‿ᴜ。) by ( ˘ᴗ˘ ) Titwe x3 - S-s-sowt (U ᵕ U❁) by Wast owo ***blushes*** U-u-u-updated - Sowt ( ᴜ ω ᴜ ) by S-s-scowe - Ovew Scwoww to ^-^ Nyext/Pwevious ***walks away*** Chaptew - Change pages ***glomps*** with Vowume Buttons ***glomps*** - Pwivate - Wwap ***cries*** I-i-images ;;w;; - Mostwy (⑅˘꒳˘) ***blushes*** usefuw fow ***screeches*** wawgew devices, wemuvs ^w^ space owo between images, *˚*(ꈍ ω ꈍ).₊̣̇. if UwU they (˘ε˘) exist. (◦ᵕ ˘ ᵕ◦) - Wewoad :3 - S-s-shawe ( ˘ᴗ˘ ) ***smirks smuggly*** + If the Manga is not from Japan, the reader will default to Webtoon Reader Settings + Default Settings + Horizontal Scroll Bar + Dual Page + Shows 2 Images in 1 page, will look weird if the images aren\'t the same size + True Colors + (32-bit color Mode) Reduces Banding on the images, may affect performance. + Image Rotation + Hide Page Numbers + Sort by Title + Sort by Last Updated + Sort by Score + Over Scroll to Next/Previous Chapter + Change pages with Volume Buttons + Private + Wrap Images + Mostly useful for larger devices, removes space between images, if they exist. + Reload + Share Skip - Show ^w^ Skip UwU Time Stamp Button - Awways (・\`ω\´・) Woad Time Stamps ~(˘▾˘~) + Show Skip Time Stamp Button + Always Load Time Stamps Time Stamps - Othew - Auto Skip Uwu OP / ED :3 - Wequiwes Time – ̗̀ (ᵕ꒳ᵕ) ̖́- Stamps t-t-t-to ***screeches*** be Enyabwed - TOTAW WEPEATS - Custom x3 ***glomps and huggles*** Wists - Want to (ㅅꈍ ˘ ꈍ) suppowt (˘ᵕ˘) Dantotsu\'s Maintainyew?\nConsidew (uwu) Donyating ^-^ - Nyo d-d-d-donyation goaw :3 ***boops your nose*** atm - Fiwtew - Yeaw \>w\< - Appwy - Cancew - This S-s-season - Nyext Season (⑅˘꒳˘) - Pwevious Season (。U ω U。) - Incwude UwU Wist - Cawendaw - Pwannyed A-a-anyime - Pwannyed (ㅅꈍ ˘ ꈍ) Manga ^w^ - Open i-i-image by Wong Cwicking (ᵘﻌᵘ) - A-a-awways continyue \>w\< Shows ***nuzzles your necky wecky*** - Usefuw :3 if you x3 awe getting (◡ w ◡) Handshake ^w^ F-f-faiws - Use P-p-p-pwoxy fow Timestamps - Awways check fow App (˘ሠ˘) ***licks lips*** Updates ( ͡o ꒳ ͡o ) - Authow ***runs away*** - Vewsions - FAQ ^-^ + Other + Auto Skip OP / ED + Requires Time Stamps to be Enabled + TOTAL REPEATS + Custom Lists + Want to support Dantotsu\'s Maintainer?\nConsider Donating + No donation goal atm + Filter + Year + Apply + Cancel + This Season + Next Season + Previous Season + Include List + Calendar + Planned Anime + Planned Manga + Open image by Long Clicking + Always continue Shows + Useful if you are getting Handshake Fails + Use Proxy for Timestamps + Always check for App Updates + Author + Versions + FAQ Accounts - MyAnyimeWist (◡ ω ◡) - Wogin ***boops your nose*** with Anyiwist!!1! - Anyiwist ***looks around suspiciously*** - H-h-h-how ÚwÚ ***nuzzles your necky wecky*** does this ***huggles tightly*** wowk\?!! + MyAnimeList + Login with Anilist! + Anilist + How does this work\? - (˘ᵕ˘) Dantotsu is (U ﹏ U) an (uwu) Anyiwist Based App, (˘ω˘) s-s-s-so fow syncing (◡ ሠ ◡) w-w-w-with youw MAW Account, It ^w^ nyeeds an Anyiwist (U ﹏ U) account to be wogged (˘ω˘) in. - (◡ w ◡) \nOnce ***screams*** wogged UwU in with both (ᵕᴗ ᵕ⁎) Anyiwist and MAW accounts, the *˚*(ꈍ ω ꈍ).₊̣̇. app wiww automaticawwy update youw MAW ***runs away*** account ~(˘▾˘~) whenyevew: - \n- Add a (⑅˘꒳˘) nyew Media - \n- Edit a ( ᴜ ω ᴜ ) Media - \n- (。ᴜ‿‿ᴜ。) ***looks at you*** Dewete a Media + Dantotsu is an Anilist Based App, so for syncing with your MAL Account, It needs an Anilist account to be logged in. + \nOnce logged in with both Anilist and MAL accounts, the app will automatically update your MAL account whenever: + \n- Add a new Media + \n- Edit a Media + \n- Delete a Media - \n\nNyote: The (。ᴜ‿‿ᴜ。) ***screams*** app wiww nyot sync owd Media\'s uwU ***pounces on you*** fwom (◡ w ◡) Anyiwist to MAW, It\'s *:・゚✧(ꈍᴗꈍ)✧・゚:* wecommended UwU to sync ( ͡U ω ͡U ) them. - \n- Check __FAQs__ f-f-f-fow UwU _Easy Method_ + \n\nNote: The app will not sync old Media\'s from Anilist to MAL, It\'s recommended to sync them. + \n- Check __FAQs__ for _Easy Method_ - (ᵕᴗ ᵕ⁎) ***pounces on you*** \n\nAnd (ㅅꈍ ˘ ꈍ) fow (◡ ω ◡) _Intewmediates_ : - \n- [How to (。ᴜ‿‿ᴜ。) sync A-a-anyiwist data with ( ͡U ω ͡U ) MAL](https://anilist.co/forum/thread/2654) - \>w\< \n- [How to sync MAW ***smirks smuggly*** data with Anilist](https://anilist.co/forum/thread/3393) + \n\nAnd for _Intermediates_ : + \n- [How to sync Anilist data with MAL](https://anilist.co/forum/thread/2654) + \n- [How to sync MAL data with Anilist](https://anilist.co/forum/thread/3393) - ÚwÚ \-\-\-\n\n_It (ᵘﻌᵘ) is nyot wequiwed (˘ω˘) to (U ᵕ U❁) sync (˘ε˘) ***licks lips*** b-b-both MAW OwO and Anyiwist (˘ω˘) accounts._ + \n\n_It is not required to sync both MAL and Anilist accounts._ - Show nyotification fow (。U ω U。) Checking Subscwiptions - Nyotification fow Checking Subscwiptions - Subscwiptions Update ^-^ F-f-f-fwequency : %1$s (⑅˘꒳˘) - Subscwiptions ;;w;; Update F-f-fwequency - A-a-amount of (˘ω˘) time fow ***screeches*** Dantotsu \>w\< to owo pewiodicawwy UwU c-c-check fow ღ(U꒳Uღ) nyew *:・゚✧(ꈍᴗꈍ)✧・゚:* Episodes/Chaptews\n(Wess time wiww cause mowe b-b-b-battewy consumption) - D-d-don\'t Update - Woading N-n-n-nyext Chaptew - Gwid - Sowt by ( ͡U ω ͡U ) Wewease Date ***cries*** - Cwop Bowdews - NYOTE + Show notification for Checking Subscriptions + Notification for Checking Subscriptions + Subscriptions Update Frequency : %1$s + Subscriptions Update Frequency + Amount of time for Dantotsu to periodically check for new Episodes/Chapters\n(Less time will cause more battery consumption) + Don\'t Update + Loading Next Chapter + Grid + Sort by Release Date + Crop Borders + NOTE - D-D-D-DAMN?!?! YOU TWUWY owo AWE (˘³˘) J-J-JOBWESS\nYOU (˘³˘) WEACHED T-T-THE END ***wags my tail*** - Couwdn\'t ***pounces on you*** find ^w^ a-a-any Fiwe ( 。ᵘ ᵕ ᵘ 。) M-m-manyagew (⑅˘꒳˘) to open SD c-c-c-cawd - Ewwow woading data %1$s - Y-y-y-you Wong Cwick the button x3 to c-c-c-check fow ***wags my tail*** App Update ( ˘ᴗ˘ ) + DAMN! YOU TRULY ARE JOBLESS\nYOU REACHED THE END + Couldn\'t find any File Manager to open SD card + Error loading data %1$s + You Long Click the button to check for App Update Saved to:\n%s - Setting pwogwess t-t-t-to %1$d ***huggles tightly*** - Pwease (ᵘʷᵘ) W-w-w-wogin into (◡ ሠ ◡) a-a-a-anyiwist ÚwÚ account?!?! ( ᴜ ω ᴜ ) - Congwats Vwo ***whispers to self*** - Pwease Wewoad. - Copied "%1$s" – ̗̀ (ᵕ꒳ᵕ) ̖́- - Pwease ~(˘▾˘~) pewfowm BACK a-a-a-again to – ̗̀ (ᵕ꒳ᵕ) ̖́- Exit ( 。ᵘ ᵕ ᵘ 。) - Nyo I-i-intewnyet Connyection ***licks lips*** - Seems ^w^ wike that wasn\'t found OwO on Anyiwist. - Disabwed Auto (◦ᵕ ˘ ᵕ◦) Skipping OP & (◡ w ◡) E-E-ED (◡ ሠ ◡) - A-a-a-auto ( ˘ᴗ˘ ) Skipping ***looks at you*** O-O-O-OP & ED (⑅˘꒳˘) - Copied (◦ᵕ ˘ ᵕ◦) t-t-to Cwipboawd - T-t-t-this is the ***runs away*** 1st Episode!!1! - You ;;w;; can wong cwick OwO Wist Editow x3 button (˘ሠ˘) to ( ͡o ᵕ ͡o ) Weset Auto Update ^-^ - A-a-autopway ;;w;; cancewwed, (ᵕᴗ ᵕ⁎) nyo (uwu) Intewaction (˘ε˘) fow m-m-mowe than 1 Houw. - Couwdn\'t auto (ㅅꈍ ˘ ꈍ) sewect *:・゚✧(ꈍᴗꈍ)✧・゚:* the (。U ω U。) sewvew, ♥(。U ω U。) P-p-pwease (ᴜ‿ᴜ✿) twy a-a-again?!?! - W-w-wogging in MAW (ᴜ‿ᴜ✿) - Getting ***wags my tail*** Usew ***huggles tightly*** Data - Nyo nyext Episode Found!!1! - Twy Enyabwing ÚwÚ ***huggles tightly*** Bannyew Anyimations fwom (ㅅꈍ ˘ ꈍ) Settings - Pwease ***glomps*** Wogin with Anyiwist?!! - Auto owo U-u-update ♥(。U ω U。) Pwogwess has nyow (ᵘﻌᵘ) been Weset-ed - Can\'t Wait, (⑅˘꒳˘) h-h-huh?!?1 OwO finye X( - Downwoading… ( ˘ᴗ˘ ) - Nyext owo Chaptew Nyot Found - This is the 1st ***walks away*** Chaptew?!?1 - Aduwt S-s-s-stuff?( (uwu) ͡° ͜ʖ – ̗̀ (ᵕ꒳ᵕ) ̖́- ͡° ) ***looks at you*** - What ;;w;; did you even open?!?! - E-e-ewwow getting Data fwom Anyiwist. - Empty Wesponse, (uwu) Does youw i-i-intewnyet pewhaps ( ͡o ᵕ ͡o ) suck!!1! (・\`ω\´・) - Ewwow (˘ε˘) woading (・\`ω\´・) MAW Usew (◡ w ◡) Data - Faiwed ***screams*** to woad ***huggles tightly*** data fwom ***wags my tail*** MAW - Ewwow woading ***screams*** Anyiwist Usew ( ͡o ᵕ ͡o ) Data (ㅅꈍ ˘ ꈍ) - Couwdn\'t find ^w^ episode (・\`ω\´・) : %1$s - Wist (ᵘﻌᵘ) Updated - Deweted ( ͡o ᵕ ͡o ) fwom Wist - Nyo (◡ ꒳ ◡) Wist (U ᵕ U❁) ID found, wewoading… - Checking :3 fow Update - Don\'t show (◡ ꒳ ◡) again ( ˘ᴗ˘ ) fow v-v-v-vewsion (˘ᵕ˘) %-%-%-%1$s ***smirks smuggly*** - Nyo ( ͡o ꒳ ͡o ) ***wags my tail*** U-u-update Found - Downwoading Update ( 。ᵘ ᵕ ᵘ 。) %1$s - Pwease give (ㅅꈍ ˘ ꈍ) pewmission to access owo Fiwes & ;;w;; Fowdews :3 fwom Settings, :3 & Twy again. - Stawted D-d-d-downwoading\n%1$s - Pwease ***boops your nose*** i-i-i-instaww 1DM - Pwease ***whispers to self*** i-i-instaww ADM - Ewwow getting ***glomps and huggles*** Image Data - Woading Image Faiwed - Copied (◡ ω ◡) device info - S-s-seems wike Anyiwist (◡ ꒳ ◡) ***cries*** is down, maybe twy (⑅˘꒳˘) using a VPN (˘ሠ˘) ow you can w-w-wait ( ᴜ ω ᴜ ) fow it UwU to come ♥(。U ω U。) back. - Faiwed ***licks lips*** to woad (˘ε˘) saved (◡ w ◡) data of (˘˘˘) %1$d - Wasn\'t abwe t-t-to get access (˘ε˘) - Maw Wogin (ᵘﻌᵘ) : Uwi nyot UwU ***smirks smuggly*** Found (˘ω˘) - Maw (◦ᵕ ˘ ᵕ◦) Wogin : codeChawwenge nyot found - Maw Wogin (ᵕᴗ ᵕ⁎) : Code ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** n-n-n-nyot (⑅˘꒳˘) ***blushes*** pwesent in (◡ ꒳ ◡) Wediwected (・\`ω\´・) UWI - W-w-w-wefwesh Token (◡ w ◡) : Faiwed to woad S-s-saved Token - Wefweshing Token ***wags my tail*** Faiwed ***huggles tightly*** - Episode %1$d wiww (˘ε˘) ***smirks smuggly*** be ***looks around suspiciously*** weweased in ***sweats*** - %1$d days %2$d hws %3$d mins %4$d (⑅˘꒳˘) secs uwU + Setting progress to %1$d + Please Login into anilist account! + Congrats Vro + Please Reload. + Copied "%1$s" + Please perform BACK again to Exit + No Internet Connection + Seems like that wasn\'t found on Anilist. + Disabled Auto Skipping OP & ED + Auto Skipping OP & ED + Copied to Clipboard + This is the 1st Episode! + You can long click List Editor button to Reset Auto Update + Autoplay cancelled, no Interaction for more than 1 Hour. + Couldn\'t auto select the server, Please try again! + Logging in MAL + Getting User Data + No next Episode Found! + Try Enabling Banner Animations from Settings + Please Login with Anilist! + Auto Update Progress has now been Reset-ed + Can\'t Wait, huh? fine X( + Downloading… + Next Chapter Not Found + This is the 1st Chapter! + Adult Stuff?( ͡° ͜ʖ ͡° ) + What did you even open? + Error getting Data from Anilist. + Empty Response, Does your internet perhaps suck? + Error loading MAL User Data + Failed to load data from MAL + Error loading Anilist User Data + Couldn\'t find episode : %1$s + List Updated + Deleted from List + No List ID found, reloading… + Checking for Update + Don\'t show again for version %1$s + No Update Found + Downloading Update %1$s + Please give permission to access Files & Folders from Settings, & Try again. + Started Downloading\n%1$s + Please install 1DM + Please install ADM + Error getting Image Data + Loading Image Failed + Copied device info + Seems like Anilist is down, maybe try using a VPN or you can wait for it to come back. + Failed to load saved data of %1$d + Wasn\'t able to get access + Mal Login : Uri not Found + Mal Login : codeChallenge not found + Mal Login : Code not present in Redirected URI + Refresh Token : Failed to load Saved Token + Refreshing Token Failed + Episode %1$d will be released in + %1$d days %2$d hrs %3$d mins %4$d secs Score Popular @@ -461,63 +461,63 @@ What? MAIN - SUPPOWTING (˘ሠ˘) + SUPPORTING - FINYISHED ♥(。U ω U。) - WEWEASING \>w\< - NYOT (・\`ω\´・) YET ***screeches*** WEWEASED - CANCEWWED ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - HIATUS ***glomps and huggles*** + FINISHED + RELEASING + NOT YET RELEASED + CANCELLED + HIATUS - A-A-A-ADAPTATION - PAWENT ^w^ - CHAWACTEW ***huggles tightly*** - SUMMAWY - AWTEWNYATIVE - OTHEW uwU - SOUWCE - CONTAINS ***smirks smuggly*** + ADAPTATION + PARENT + CHARACTER + SUMMARY + ALTERNATIVE + OTHER + SOURCE + CONTAINS - Wead on Dantotsu + Read on Dantotsu Watch on Dantotsu - "Continyue : Episode " - "Continyue ***screams*** : " ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** + "Continue : Episode " + "Continue : " "Episode " - "-"-"Episode %1$s" (⑅˘꒳˘) ***whispers to self*** - "-"-"-"Chaptew "-"-"-" - "Chaptew (˘³˘) %1$s" (˘³˘) - - just OwO got ***looks at you*** weweased!!1! - Checking (⑅˘꒳˘) Subscwiptions + "Episode %1$s" + "Chapter " + "Chapter %1$s" + - just got released! + Checking Subscriptions - Speed ÚwÚ - Auto Update ( ˘ᴗ˘ ) pwogwess (ᵕᴗ ᵕ⁎) fow (U ᵕ U❁) ***licks lips*** %1$s!!11 - Continyue fwom (˘ᵕ˘) %1$s!!1! ***cries*** - Update ღ(U꒳Uღ) pwogwess ( ͡U ω ͡U ) on anyiwist?!?1 - "Don\'t ask again fow %1$s" ^-^ - Defauwt ***glomps*** Speed - Defauwt Wesize Mode *˚*(ꈍ ω ꈍ).₊̣̇. - Pwimawy ***runs away*** Sub Cowow - Outwinye Sub C-c-cowow ***walks away*** - Outwinye Type - Subtitwe (◦ᵕ ˘ ᵕ◦) Font + Speed + Auto Update progress for %1$s? + Continue from %1$s? + Update progress on anilist? + "Don\'t ask again for %1$s" + Default Speed + Default Resize Mode + Primary Sub Color + Outline Sub Color + Outline Type + Subtitle Font - Y-y-y-yes - Nyo (uwu) - Cwose - Nyo Chaptew ***cries*** - Tuwn on (ᴜ‿ᴜ✿) 18+ C-c-content fwom youw x3 Anyiwist UwU Settings (◡ w ◡) - Avaiwabwe (◡ w ◡) - Wet\'s Go (˘˘˘) + Yes + No + Close + No Chapter + Turn on 18+ Content from your Anilist Settings + Available + Let\'s Go Cope - "Watched (ㅅꈍ ˘ ꈍ) " UwU ***screams*** - "Wead (˘ε˘) " + "Watched " + "Read " " out of " - " *˚*(ꈍ ω ꈍ).₊̣̇. out Uwu of ***screeches*** " UwU - "Totaw of " - "-"-"Totaw of " - "-"-"-"Nyo ♥(。U ω U。) Descwiption (◡ ሠ ◡) Avaiwabwe" ÚwÚ + " out of " + "Total of " + "Total of " + "No Description Available" Top to Bottom @@ -532,104 +532,105 @@ Continuous - Sewected ***licks lips*** - F-f-found + Selected + Found - "__Age:__ ***screams*** " - \n"__Biwthday:__ " - \n"__Gendew:__ ***screams*** " ***pounces on you***" + "__Age:__ " + \n"__Birthday:__ " + \n"__Gender:__ "" - Mawe - Femawe (ᵕᴗ ᵕ⁎) + Male + Female - What is Dantotsu?\n Why s-s-s-shouwd y-y-you u-u-use Dantotsu?!?1 („ᵕᴗᵕ„) - Dantotsu is cwafted („ᵕᴗᵕ„) f-f-f-fwom – ̗̀ (ᵕ꒳ᵕ) ̖́- t-t-t-the ashes *:・゚✧(ꈍᴗꈍ)✧・゚:* of Saikou and based on simpwistic ***looks at you*** yet state-of-the-awt ***runs away*** ewegance. It is ~(˘▾˘~) a-a-an Anyiwist onwy cwient, which awso wets you :3 stweam ( ˘ᴗ˘ ) & downwoad Anyime ( ͡o ꒳ ͡o ) / Manga ( ˘ᴗ˘ ) thwough e-e-e-extensions. \n>Dantotsu (断トツ; Dan-totsu) witewawwy m-m-m-means the ( ͡o ꒳ ͡o ) best of ***whispers to self*** the best in Japanyese. ( ᴜ ω ᴜ ) Weww, ***boops your nose*** we wouwd wike t-t-t-to (˘˘˘) ***looks around suspiciously*** s-s-say t-t-this x3 is t-t-t-the (˘˘˘) ***whispers to self*** best open souwce ~(˘▾˘~) app UwU ***looks at you*** fow ***smirks smuggly*** anyime and manga Uwu on Andwoid, b-b-b-but (ᵕᴗ ᵕ⁎) hey, Twy it (⑅˘꒳˘) out youwsewf & ***nuzzles your necky wecky*** judge?!?1 + What is Dantotsu?\n Why should you use Dantotsu? + Dantotsu is crafted from the ashes of Saikou and based on simplistic yet state-of-the-art elegance. It is an Anilist only client, which also lets you stream & download Anime / Manga through extensions. \n>Dantotsu (断トツ; Dan-totsu) literally means the best of the best in Japanese. Well, we would like to say this is the best open source app for anime and manga on Android, but hey, Try it out yourself & judge! - What (U ᵕ U❁) awe ( ᴜ ω ᴜ ) some featuwes o-o-of (ᴜ‿ᴜ✿) Dantotsu!!11 - Some (ᵘʷᵘ) mentionyabwe featuwes of Dantotsu awe\n\n- (uwu) Easy and functionyaw way to ( 。ᵘ ᵕ ᵘ 。) both, watch ( ᴜ ω ᴜ ) anyime and wead ***pounces on you*** manga and \>w\< wight nyuvws, Ad Fwee.\n- A (˘ሠ˘) compwetewy (◡ ꒳ ◡) open (。U ω U。) souwce app with a nyice UI :3 & Anyimations\n- 3wd pawty p-p-p-pwugin suppowt ***screams*** \-\-\-\n- (⑅˘꒳˘) S-s-s-synchwonyize anyime and m-m-m-manga (・\`ω\´・) weaw-time with AnyiWist. Easiwy c-c-c-categowize anyime (U ᵕ U❁) and Uwu manga based on youw cuwwent ( 。ᵘ ᵕ ᵘ 。) status. (Powewed by AnyiWist)\n- ***smirks smuggly*** Find aww shows using thowoughwy a-a-and fwequentwy updated wist of (・\`ω\´・) aww t-t-t-twending, (˘˘˘) p-p-popuwaw and ongoing (ᴜ‿ᴜ✿) anyime (◡ ω ◡) based on scowes.\n- ღ(U꒳Uღ) View (◦ᵕ ˘ ᵕ◦) extensive ***blushes*** detaiws (⑅˘꒳˘) about anyime *˚*(ꈍ ω ꈍ).₊̣̇. shows, movies and manga – ̗̀ (ᵕ꒳ᵕ) ̖́- t-t-t-titwes. It awso featuwes (˘ε˘) abiwity to countdown to (◡ ሠ ◡) t-t-the nyext episode of aiwing (⑅˘꒳˘) a-a-anyime. (。ᴜ‿‿ᴜ。) (Powewed ( ᴜ ω ᴜ ) by AnyiWist & MyAnyimeWist) ***licks lips*** + What are some features of Dantotsu? + Some mentionable features of Dantotsu are\n\n- Easy and functional way to both, watch anime and read manga and light novels, Ad Free.\n- A completely open source app with a nice UI & Animations\n- 3rd party plugin support \n- Synchronize anime and manga real-time with AniList. Easily categorize anime and manga based on your current status. (Powered by AniList)\n- Find all shows using thoroughly and frequently updated list of all trending, popular and ongoing anime based on scores.\n- View extensive details about anime shows, movies and manga titles. It also features ability to countdown to the next episode of airing anime. (Powered by AniList & MyAnimeList) - What (˘ᵕ˘) awe Awtifacts?!! - Whenyevew (U ᵕ U❁) a devewopew ( ˊ.ᴗˋ ) commits ow puww wequests a featuwe ow (・\`ω\´・) fix, GitHub ღ(U꒳Uღ) automaticawwy makes an ***glomps*** APK fiwe f-f-f-fow ;;w;; you to \>w\< use. (◡ ሠ ◡) T-t-this A-A-APK (uwu) ***blushes*** is cawwed ***huggles tightly*** an („ᵕᴗᵕ„) ***looks at you*** Awtifact. Awtifacts thwough puww wequests („ᵕᴗᵕ„) may ow may nyot (ᴜ‿ᴜ✿) b-b-be owo added to the main w-w-w-wewease of the app. Awtifacts have ***blushes*** a highew chance of having ***glomps*** bugs and gwitches. To knyow if nyew (˘ሠ˘) awtifacts awe (˘³˘) a-a-avaiwabwe, staw ***pounces on you*** t-t-the Dantotsu ( ˘ᴗ˘ ) wepositowy and tuwn on nyotifications\n\nTo downwoad an ***screams*** Awtifact:\n1) Sign (˘³˘) In/Up in GitHub\n2) Go to D-d-d-dantotsu\n3) Go (˘ሠ˘) to actions\n4) *:・゚✧(ꈍᴗꈍ)✧・゚:* Pwess on ;;w;; the wowkfwow wun ^w^ you want to downwoad ***licks lips*** the awtifact of.\n5) (ᵘﻌᵘ) Pwess on awtifact\n6) Extwact t-t-the fiwe uwU using (◡ ω ◡) ***walks away*** a ***glomps*** zip extwactow\n7) Instaww a-a-and enjoy. ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** + What are Artifacts? + Whenever a developer commits or pull requests a feature or fix, GitHub automatically makes an APK file for you to use. This APK is called an Artifact. Artifacts through pull requests may or may not be added to the main release of the app. Artifacts have a higher chance of having bugs and glitches. To know if new artifacts are available, star the Dantotsu repository and turn on notifications\n\nTo download an Artifact:\n1) Sign In/Up in GitHub\n2) Go to Dantotsu\n3) Go to actions\n4) Press on the workflow run you want to download the artifact of.\n5) Press on artifact\n6) Extract the file using a zip extractor\n7) Install and enjoy. - Is Dantotsu avaiwabwe fow (◡ ሠ ◡) PC?!?1 - Cuwwentwy nyo x3 (fow both Windows ( ͡o ꒳ ͡o ) and Winyux). Thewe ***pounces on you*** isn\'t any ÚwÚ estimation – ̗̀ (ᵕ꒳ᵕ) ̖́- when it wiww be avaiwabwe. But you can downwoad a-a-any ***smirks smuggly*** Andwoid e-e-e-emuwatow and \>w\< wun (ᵕᴗ ᵕ⁎) Dantotsu on uwU it. F-f-fow Windows ÚwÚ 11 usews, ^w^ they can use the (◡ ω ◡) Windows ^w^ Subsystem fow Andwoid (WSA) to ***runs away*** wun ( ᵘ ꒳ ᵘ ✼) ***screeches*** Dantotsu \>w\< in ***glomps*** Windows. + Is Dantotsu available for PC? + Currently no (for both Windows and Linux). There isn\'t any estimation when it will be available. But you can download any Android emulator and run Dantotsu on it. For Windows 11 users, they can use the Windows Subsystem for Android (WSA) to run Dantotsu in Windows. - Is (ㅅꈍ ˘ ꈍ) Dantotsu avaiwabwe fow (ㅅꈍ ˘ ꈍ) iOS!? - Nyo, (⑅˘꒳˘) and ***huggles tightly*** cuwwentwy nyo pwans to suppowt iOS + Is Dantotsu available for iOS? + No, and currently no plans to support iOS - Why awe ***pounces on you*** my stats nyot updating!!11 - T-t-this (˘³˘) ***screams*** i-i-i-is \>w\< because ***looks around suspiciously*** it *:・゚✧(ꈍᴗꈍ)✧・゚:* updates \>w\< evewy 48 houws ***pounces on you*** automaticawwy (by Anyiwist). ***glomps*** If you :3 weawwy nyeed to update youw (˘ሠ˘) stats, you can f-f-f-fowce owo update youw stats a-a-a-aftew going t-t-to this [link](https://anilist.co/settings/lists). ( 。ᵘ ᵕ ᵘ 。) + Why are my stats not updating? + This is because it updates every 48 hours automatically (by Anilist). If you really need to update your stats, you can force update your stats after going to this [link](https://anilist.co/settings/lists). - How to ( ˘ᴗ˘ ) downwoad uwU Episodes!!11 ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - 1. Downwoad ***looks at you*** 1-1-1DM ow (˘ε˘) ADM f-f-f-fwom Googwe (uwu) Pway Stowe. - \n2. (⑅˘꒳˘) ***looks at you*** Entew the (˘˘˘) app, give stowage access and ( ˊ.ᴗˋ ) set ( ͡o ᵕ ͡o ) y-y-y-youw (ᵕᴗ ᵕ⁎) p-p-p-pwefewences (downwoading speed, ( ᴜ ω ᴜ ) d-d-d-downwoading p-p-path etc(optionyaw)) - ( ᵘ ꒳ ᵘ ✼) \n3. ( 。ᵘ ᵕ ᵘ 。) Nyow go ( ᴜ ω ᴜ ) to D-d-d-dantotsu > Settings > ( ᵘ ꒳ ᵘ ✼) Common OwO > Downwoad M-m-m-manyagews and x3 c-c-choose the downwoad ( ͡o ꒳ ͡o ) manyagew you just s-s-s-set up. - (◡ w ◡) \n4. ♥(。U ω U。) Go to youw desiwed episode (˘ε˘) ***licks lips*** a-a-and pwess (˘ω˘) o-o-on – ̗̀ (ᵕ꒳ᵕ) ̖́- ***looks around suspiciously*** the downwoad :3 i-i-icon of any sewvew. Thewe m-m-m-may be a-a-a-a popup to (˘ε˘) set ***walks away*** y-y-youw (˘ሠ˘) pwefewences ( ͡o ꒳ ͡o ) again, just pwess uwU ***glomps and huggles*** on ♥(。U ω U。) "Downwoad" ***sweats*** and ***looks at you*** it wiww be ***walks away*** saved i-i-i-in the diwected path. + How to download Episodes? + 1. Download 1DM or ADM from Google Play Store. + \n2. Enter the app, give storage access and set your preferences (downloading speed, downloading path etc(optional)) + \n3. Now go to Dantotsu > Settings > Common > Download Managers and choose the download manager you just set up. + \n4. Go to your desired episode and press on the download icon of any server. There may be a popup to set your preferences again, just press on "Download" and it will be saved in the directed path. - \n\nNyote: ***huggles tightly*** Diwect downwoads ***pounces on you*** awe awso possibwe ***smirks smuggly*** without a-a-a manyagew but i-i-it\'s nyot ***wags my tail*** wecommended. + \n\nNote: Direct downloads are also possible without a manager but it\'s not recommended. - How to ***pounces on you*** downwoad ღ(U꒳Uღ) Manga C-c-c-chaptews!? (U ᵕ U❁) - It is yet n-n-n-nyot p-p-p-possibwe (◡ ሠ ◡) to (˘ε˘) downwoad chaptews :3 in (・\`ω\´・) D-d-dantotsu but this owo featuwe owo wiww be impwemented owo soon. (◡ w ◡) + How to download Manga Chapters? + It is yet not possible to download chapters in Dantotsu but this feature will be implemented soon. - H-h-how (⑅˘꒳˘) to UwU enyabwe NSFW (・\`ω\´・) content?!?! (U ᵕ U❁) - You can enyabwe nsfw ***nuzzles your necky wecky*** content Uwu by e-e-e-enyabwing 18+ \>w\< contents fwom ( ͡o ꒳ ͡o ) this [link](https://anilist.co/settings/media). + How to enable NSFW content? + You can enable nsfw content by enabling 18+ contents from this [link](https://anilist.co/settings/media). - How t-t-t-to (◡ ω ◡) impowt *˚*(ꈍ ω ꈍ).₊̣̇. my owo MAW/Kitsu wist ( ˘ᴗ˘ ) to Anyiwist!!1! - Hewe uwU is how UwU you do (˘ε˘) it,\n\nExpowt:\n\n1. Go to this [link](https://malscraper.azurewebsites.net).\n2. ( 。ᵘ ᵕ ᵘ 。) Give youw (◡ ሠ ◡) Kitsu/MAW ( ͡o ᵕ ͡o ) usewnyame and (U ᵕ U❁) downwoad both anyime (U ᵕ U❁) and m-m-manga ***runs away*** wist. (They wiww ( ͡U ω ͡U ) be (˘ሠ˘) in OwO XMW fowmat)\nNyote: (・\`ω\´・) You have OwO to wwite the (⑅˘꒳˘) u-u-usewnyame of (uwu) the twackew UwU you sewected\n\nImpowt:\n\n1. Aftew expowting youw ***screams*** anyime and manga wist fwom Kitsu/MAW, nyow go (ᴜ‿ᴜ✿) ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** [here](https://anilist.co/settings/import) ( 。ᵘ ᵕ ᵘ 。) \n2. (uwu) Sewect/dwop the ***smirks smuggly*** anyime XMW fiwe on the :3 box abuv.\n|→Sewect/dwop the m-m-manga X-X-X-XMW fiwe on (˘˘˘) the box bewow. + How to import my MAL/Kitsu list to Anilist? + Here is how you do it,\n\nExport:\n\n1. Go to this [link](https://malscraper.azurewebsites.net).\n2. Give your Kitsu/MAL username and download both anime and manga list. (They will be in XML format)\nNote: You have to write the username of the tracker you selected\n\nImport:\n\n1. After exporting your anime and manga list from Kitsu/MAL, now go [here](https://anilist.co/settings/import) \n2. Select/drop the anime XML file on the box above.\n|→Select/drop the manga XML file on the box below. - How to impowt (。ᴜ‿‿ᴜ。) my Anyiwist/Kitsu wist ;;w;; to MAW!!11 - Hewe is h-h-how you d-d-d-do it,\n\nExpowt:\n\n1. Go to ( ˊ.ᴗˋ ) this Uwu [link](https://malscraper.azurewebsites.net/). \n2. (⑅˘꒳˘) Give ***sweats*** youw Anyiwist usewnyame/Kitsu ***runs away*** ID (◡ w ◡) in the x3 ***whispers to self*** \'Usewnyame/Kitsu ***boops your nose*** Usew ID\' (˘ሠ˘) box. \n3. Sewect wist type and enyabwe \'update_on_impowt\'. \n4. Downwoad („ᵕᴗᵕ„) the ( ͡o ꒳ ͡o ) fiwe; it (˘ω˘) wiww be ***smirks smuggly*** in ***runs away*** .xmw fowmat. Be suwe to d-d-downwoad both ***glomps and huggles*** Anyime (˘ᵕ˘) and UwU Manga wists.\n\nImpowt:\n1. To Uwu impowt i-i-it in youw MAW account, go – ̗̀ (ᵕ꒳ᵕ) ̖́- to this [link](https://myanimelist.net/import.php) and c-c-choose (˘˘˘) \'MyAnyimeWist Impowt\' as impowt type. uwU \n2. Pwess (ᴜ‿ᴜ✿) on \'Choose Fiwe\'and sewect the downwoaded anyime/manga wist XMW ***nuzzles your necky wecky*** fiwe. \n3. (˘ሠ˘) Pwess on \'Impowt Data\'. (U ﹏ U) \nCongwatuwations, you just uwU impowted the sewected wist to OwO ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** youw ^w^ MAW account. OwO + How to import my Anilist/Kitsu list to MAL? + Here is how you do it,\n\nExport:\n\n1. Go to this [link](https://malscraper.azurewebsites.net/). \n2. Give your Anilist username/Kitsu ID in the \'Username/Kitsu User ID\' box. \n3. Select list type and enable \'update_on_import\'. \n4. Download the file; it will be in .xml format. Be sure to download both Anime and Manga lists.\n\nImport:\n1. To import it in your MAL account, go to this [link](https://myanimelist.net/import.php) and choose \'MyAnimeList Import\' as import type. \n2. Press on \'Choose File\'and select the downloaded anime/manga list XML file. \n3. Press on \'Import Data\'. \nCongratulations, you just imported the selected list to your MAL account. - Why can\'t I – ̗̀ (ᵕ꒳ᵕ) ̖́- find a ***looks around suspiciously*** specific anyime/manga titwe!!1! (˘ω˘) - Wet\'s say ( ͡o ꒳ ͡o ) you a-a-awe wooking (◡ ሠ ◡) fow Castwevanyia in Uwu Dantotsu. But ÚwÚ Anyiwist ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** doesn\'t have it, ;;w;; so (。ᴜ‿‿ᴜ。) Dantotsu doesn\'t ( ͡o ꒳ ͡o ) eithew.\nA sowution to the abuv pwobwem is a-a-a-as f-f-f-fowwows:\n1) ***huggles tightly*** G-g-g-go to any (uwu) anyime (⑅˘꒳˘) that\'s (˘ᵕ˘) nyot ^w^ in youw wist.\n2) owo Go to the watch section.\n3) S-s-sewect any souwce and (。U ω U。) pwess on owo the \'Wwong Titwe?\'.\n4) Nyow seawch fow (˘³˘) Castwevanyia (⑅˘꒳˘) (The anyime you wewe wooking fow) ;;w;; and (˘ᵕ˘) sewect it.\n5) ENJOY!\n\nIf you ~(˘▾˘~) can\'t (˘ε˘) find (ᵕᴗ ᵕ⁎) t-t-the anyime ^w^ even ( ᴜ ω ᴜ ) ***blushes*** thwough ( ͡o ᵕ ͡o ) these steps, (˘ሠ˘) then that\'s UwU bad wuck fow you, bud. Even (˘˘˘) that ;;w;; souwce d-d-doesn\'t have it. Twy a diffewent s-s-s-souwce. + Why can\'t I find a specific anime/manga title? + Let\'s say you are looking for Castlevania in Dantotsu. But Anilist doesn\'t have it, so Dantotsu doesn\'t either.\nA solution to the above problem is as follows:\n1) Go to any anime that\'s not in your list.\n2) Go to the watch section.\n3) Select any source and press on the \'Wrong Title?\'.\n4) Now search for Castlevania (The anime you were looking for) and select it.\n5) ENJOY!\n\nIf you can\'t find the anime even through these steps, then that\'s bad luck for you, bud. Even that source doesn\'t have it. Try a different source. - How ÚwÚ to f-f-fix s-s-s-souwces (˘ᵕ˘) sewecting ^-^ a-a-a-a (⑅˘꒳˘) compwetewy wwong t-t-titwe!!1! ***glomps and huggles*** - Dantotsu (ᵘʷᵘ) itsewf *:・゚✧(ꈍᴗꈍ)✧・゚:* doesn\'t host anything ***looks around suspiciously*** but (˘³˘) wewies on othew souwces. ( ˘ᴗ˘ ) When showing the e-e-episodes, it chooses the 1st ( ͡U ω ͡U ) wesuwt given by t-t-t-the souwce aftew (⑅˘꒳˘) seawching fow the titwe. ***whispers to self*** Dantotsu („ᵕᴗᵕ„) has nyo ( ͡o ᵕ ͡o ) way of (uwu) detecting if that\'s UwU wegit ow nyot. (ㅅꈍ ˘ ꈍ) So, fow t-t-this, we h-h-have the \'Wwong (ᵕᴗ ᵕ⁎) Titwe?\' just bewow the (◡ w ◡) souwce ( 。ᵘ ᵕ ᵘ 。) nyame(abuv wayouts). You can ( ͡o ꒳ ͡o ) choose (◡ ω ◡) the cowwect wesuwt/titwe by pwessing o-o-on it and ***whispers to self*** enjoy its episodes. + How to fix sources selecting a completely wrong title? + Dantotsu itself doesn\'t host anything but relies on other sources. When showing the episodes, it chooses the 1st result given by the source after searching for the title. Dantotsu has no way of detecting if that\'s legit or not. So, for this, we have the \'Wrong Title?\' just below the source name(above layouts). You can choose the correct result/title by pressing on it and enjoy its episodes. - How to ***huggles tightly*** w-w-wead (ㅅꈍ ˘ ꈍ) cowouwed *˚*(ꈍ ω ꈍ).₊̣̇. mangas!!1! (。ᴜ‿‿ᴜ。) - Awe y-y-you in seawch of (・\`ω\´・) cowouwed manga?!! Sowwy to bweak it to you but an extwemewy ;;w;; s-s-s-smaww amount of x3 mangas have (・\`ω\´・) cowouwed vewsion. Those UwU which has ;;w;; ***walks away*** a (˘ሠ˘) cowouwed vewsion is awso :3 avaiwabwe in ( ᵘ ꒳ ᵘ ✼) Dantotsu. Wet\'s say you want ( 。ᵘ ᵕ ᵘ 。) t-t-t-to wead the cowouwed vewsion of chainsaw ;;w;; man. Then fowwow the bewow ***cries*** steps ↓\n\n1) Go ***blushes*** to Chainsaw (ᴜ‿ᴜ✿) ***cries*** Man\n2) Pwess on (U ﹏ U) \'Wead\'\n3) Sewect :3 any wowking souwce\n4) Pwess on \-\-\'Wwong Titwe\'\n5) Sewect the (◡ ሠ ◡) cowowed vewsion ***licks lips*** chainsaw man\n6) (ᵘﻌᵘ) Enjoy\n\nNyote: Many souwces don\'t h-h-h-have the c-c-cowouwed ( 。ᵘ ᵕ ᵘ 。) vewsion *˚*(ꈍ ω ꈍ).₊̣̇. avaiwabwe (U ᵕ U❁) even if it\'s avaiwabwe somewhewe (U ᵕ U❁) on ***blushes*** the intewnyet. (˘ε˘) So (◡ w ◡) twy a ***boops your nose*** d-d-d-diffewent s-s-s-souwce. If ღ(U꒳Uღ) ***screeches*** nyonye of the souwces have i-i-it, ღ(U꒳Uღ) then a cowouwed vewsion – ̗̀ (ᵕ꒳ᵕ) ̖́- of youw (ᵕᴗ ᵕ⁎) desiwed :3 manga simpwy doesn\'t ♥(。U ω U。) ***looks around suspiciously*** e-e-e-exist. (U ﹏ U) If (。ᴜ‿‿ᴜ。) ***huggles tightly*** you (◡ w ◡) can find ***screeches*** it x3 on ***looks at you*** any manga (ᴜ‿ᴜ✿) s-s-s-site on the intewnyet, you can suggest (ᵘʷᵘ) that (˘˘˘) site thwough the Discowd sewvew. + How to read coloured mangas? + Are you in search of coloured manga? Sorry to break it to you but an extremely small amount of mangas have coloured version. Those which has a coloured version is also available in Dantotsu. Let\'s say you want to read the coloured version of chainsaw man. Then follow the below steps ↓\n\n1) Go to Chainsaw Man\n2) Press on \'Read\'\n3) Select any working source\n4) Press on \'Wrong Title\'\n5) Select the colored version chainsaw man\n6) Enjoy\n\nNote: Many sources don\'t have the coloured version available even if it\'s available somewhere on the internet. So try a different source. If none of the sources have it, then a coloured version of your desired manga simply doesn\'t exist. If you can find it on any manga site on the internet, you can suggest that site through the Discord server. - Handshake faiws!!11 Why awe nyo timestamps nyot woading!!11 ( ͡U ω ͡U ) - You can fix this issue ( 。ᵘ ᵕ ᵘ 。) by enyabwing \'Pwoxy\' (◡ ꒳ ◡) fwom (ᵘʷᵘ) \n\`Settings > ( 。ᵘ ᵕ ᵘ 。) Anyime >->-> ( ͡U ω ͡U ) ***boops your nose*** Pwayew S-s-s-settings (ᴜ‿ᴜ✿) ***walks away*** > Timestamps (uwu) > ( ˘ᴗ˘ ) Pwoxy\`.\nIf ***cries*** the timestamps awe stiww ***looks around suspiciously*** nyot ***looks at you*** woading but the (⑅˘꒳˘) handshake faiwed popup (⑅˘꒳˘) is (◡ ሠ ◡) fixed, then the (◦ᵕ ˘ ᵕ◦) episode you ***runs away*** awe \>w\< watching just doesn\'t uwU have ( 。ᵘ ᵕ ᵘ 。) timestamps y-y-y-yet (ㅅꈍ ˘ ꈍ) fow (・\`ω\´・) it. + Handshake fails? Why are no timestamps not loading? + You can fix this issue by enabling \'Proxy\' from \n\`Settings > Anime > Player Settings > Timestamps > Proxy\`.\nIf the timestamps are still not loading but the handshake failed popup is fixed, then the episode you are watching just doesn\'t have timestamps yet for it. - Having twoubwe ~(˘▾˘~) ***licks lips*** with a souwce?!?1 - Some basic fixes wouwd be (◡ ω ◡) :\n\n• Westawt the OwO app. \n• Use a-a-a-a ***screeches*** diffewent DNS fwom (・\`ω\´・) youw settings, pwefewabwy, CwoudFwawe. :3 \n• ***smirks smuggly*** V-V-V-VPN (ᴜ‿ᴜ✿) m-m-might wowk – ̗̀ (ᵕ꒳ᵕ) ̖́- as weww. \n\nIf you wefuse (◦ᵕ ˘ ᵕ◦) ***blushes*** to twy the abuv steps ღ(U꒳Uღ) then ***huggles tightly*** just (。U ω U。) use *˚*(ꈍ ω ꈍ).₊̣̇. a diffewent souwce.\n\nNyote: UwU Awwanyime fixes itsewf ~(˘▾˘~) most of ***looks at you*** the – ̗̀ (ᵕ꒳ᵕ) ̖́- time. ( ˘ᴗ˘ ) + Having trouble with a source? + Some basic fixes would be :\n\n• Restart the app. \n• Use a different DNS from your settings, preferably, CloudFlare. \n• VPN might work as well. \n\nIf you refuse to try the above steps then just use a different source.\n\nNote: Allanime fixes itself most of the time. - S-s-s-some usefuw tips and twicks - The \>w\< fowwowing ( ˘ᴗ˘ ) pwesents some ***screams*** tips ( ͡U ω ͡U ) and (˘ω˘) twicks you m-m-may ow (ᵘﻌᵘ) ***blushes*** may nyot knyow about - \n \n \n ***breaks into your house and aliases neofetch to rm -rf --no-preserve-root /*** - By howd pwessing the Dantotsu (◡ w ◡) wogo in ( ͡o ꒳ ͡o ) settings, you ÚwÚ c-c-can c-c-c-check (⑅˘꒳˘) if (◡ ሠ ◡) thewe a-a-awe a-a-any (。U ω U。) n-n-n-nyew (˘³˘) updates manyuawwy. \n \n - ***screams*** Howd pwessing an ewwow ***nuzzles your necky wecky*** m-m-message/tag/synyonym ow OwO titwe wiww c-c-c-copy ***glomps*** it. (uwu) \n (ㅅꈍ ˘ ꈍ) \n - ***huggles tightly*** You \>w\< can ***runs away*** open an episode ^w^ with (˘ᵕ˘) ***pounces on you*** othew apps ( ˘ᴗ˘ ) by h-h-h-howd (uwu) pwessing any sewvew ***boops your nose*** fow ^-^ that :3 episode. This hewps uwU in ^w^ stweaming the episode using othew owo video („ᵕᴗᵕ„) ***screeches*** pwayews ^-^ ow ***sweats*** downwoad the episode using (U ﹏ U) downwoad manyagews. \n \n - (U ᵕ U❁) You can set u-u-up (⑅˘꒳˘) custom wists using (ㅅꈍ ˘ ꈍ) this [link](https://anilist.co/settings/lists). (you (◡ ω ◡) nyeed (ᵕᴗ ᵕ⁎) to ***glomps and huggles*** be signyed in) ;;w;; ***pounces on you*** \n x3 \n (。ᴜ‿‿ᴜ。) - If youw episode/chaptew (˘ε˘) is nyot being (ᵕᴗ ᵕ⁎) pwogwessed (ᵘʷᵘ) automaticawwy aftew (◡ ω ◡) you finyish w-w-watching/weading i-i-it, then howd ~(˘▾˘~) pwess ***blushes*** the s-s-status baw(pwannying/wepeating/watching button) of that anyime/manga. The nyext (ᴜ‿ᴜ✿) time you stawt a chaptew/finyish (ᵘﻌᵘ) an episode, (ᵕᴗ ᵕ⁎) you wiww ÚwÚ stumbwe upon a (˘ω˘) popup. Pwess yes ( ᴜ ω ᴜ ) thewe. + Some useful tips and tricks + The following presents some tips and tricks you may or may not know about - \n \n \n - By hold pressing the Dantotsu logo in settings, you can check if there are any new updates manually. \n \n - Hold pressing an error message/tag/synonym or title will copy it. \n \n - You can open an episode with other apps by hold pressing any server for that episode. This helps in streaming the episode using other video players or download the episode using download managers. \n \n - You can set up custom lists using this [link](https://anilist.co/settings/lists). (you need to be signed in) \n \n - If your episode/chapter is not being progressed automatically after you finish watching/reading it, then hold press the status bar(planning/repeating/watching button) of that anime/manga. The next time you start a chapter/finish an episode, you will stumble upon a popup. Press yes there. - Subscwibed!? :3 Weceiving („ᵕᴗᵕ„) ***licks lips*** nyotifications, w-w-when (ᵕᴗ ᵕ⁎) n-n-nyew episodes awe weweased on %1$s. + Subscribed! Receiving notifications, when new episodes are released on %1$s. - Unsubscwibed, \>w\< you w-w-w-wiww nyot weceive ***licks lips*** any ***glomps and huggles*** n-n-nyotifications. - E-e-episodes (・\`ω\´・) - Episode Uwu - Chaptew - C-c-c-chaptews + Unsubscribed, you will not receive any notifications. + Episodes + Episode + Chapter + Chapters - "Fowmat : %1$s" - "Sowt : %1$s" - "-"-"Nyot %1$s" - Seawch (◡ ꒳ ◡) by Image (˘ε˘) - Upwoad Image - Simiwawity: %-%-%1$s %% - Fwom %1$s (˘ᵕ˘) to %-%-%2$s (・\`ω\´・) - Invawid ***boops your nose*** UWW (⑅˘꒳˘) - Nyo ( ͡o ᵕ ͡o ) A-a-a-anyiwist ID found ( ˘ᴗ˘ ) - Ewwow woading (。ᴜ‿‿ᴜ。) i-i-i-image - Successfuwwy (ᵘʷᵘ) Wogged ( ˘ᴗ˘ ) Out (uwu) - Twy *:・゚✧(ꈍᴗꈍ)✧・゚:* ***blushes*** w-w-wogging-in again - Ewwow woading Discowd (˘³˘) Usew D-d-data - B-b-b-by wogging – ̗̀ (ᵕ꒳ᵕ) ̖́- in, youw ***cries*** discowd ♥(。U ω U。) wiww nyow s-s-show ( ᴜ ω ᴜ ) what ***blushes*** y-y-you awe watching & weading on Dantotsu\n\nIf you awe on ÚwÚ invisibwe ***screeches*** mode, wogging in wiww ÚwÚ make you ***cries*** onwinye, (◡ ω ◡) when y-y-you open Dantotsu.\n\nThis d-d-d-does bweak ÚwÚ the Discowd TOS. \nAwthough (˘ε˘) Discowd h-h-has nyevew bannyed anyonye fow using *:・゚✧(ꈍᴗꈍ)✧・゚:* Custom Wich (・\`ω\´・) Pwesence(what ( ᵘ ꒳ ᵘ ✼) Dantotsu uses), ~(˘▾˘~) You ^w^ have stiww ♥(。U ω U。) been w-w-w-wawnyed.\n\nDantotsu (◦ᵕ ˘ ᵕ◦) is nyot wesponsibwe ( ͡o ꒳ ͡o ) fow anything that (uwu) happens to youw a-a-account. ***looks at you*** - Wawnying - View ***glomps*** Anyime ***looks at you*** - V-v-view M-m-manga ***smirks smuggly*** - Fowce Wegacy Instawwew + "Format : %1$s" + "Sort : %1$s" + "Not %1$s" + Search by Image + Upload Image + Similarity: %1$s %% + From %1$s to %2$s + Invalid URL + No Anilist ID found + Error loading image + Successfully Logged Out + Try logging-in again + Error loading Discord User Data + + Warning + View Anime + View Manga + Force Legacy Installer Extensions - NSFW Extensions (⑅˘꒳˘) - S-s-s-skip woading ღ(U꒳Uღ) e-e-extension icons - Use Matewiaw You + NSFW Extensions + Skip loading extension icons + Use Material You Extension-specific DNS Theme: + User Agent - \ No newline at end of file +