reformat
This commit is contained in:
parent
1df528c0dc
commit
afa960c808
171 changed files with 3458 additions and 1915 deletions
|
@ -16,7 +16,7 @@ fun updateProgress(media: Media, number: String) {
|
|||
if (Anilist.userid != null) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val a = number.toFloatOrNull()?.roundToInt()
|
||||
if ((a?:0) > (media.userProgress?:0)) {
|
||||
if ((a ?: 0) > (media.userProgress ?: 0)) {
|
||||
Anilist.mutation.editList(
|
||||
media.id,
|
||||
a,
|
||||
|
|
|
@ -10,7 +10,7 @@ import ani.dantotsu.currContext
|
|||
import ani.dantotsu.openLinkInBrowser
|
||||
import ani.dantotsu.tryWithSuspend
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
|
||||
object Anilist {
|
||||
val query: AnilistQueries = AnilistQueries()
|
||||
|
@ -29,7 +29,12 @@ object Anilist {
|
|||
var tags: Map<Boolean, List<String>>? = null
|
||||
|
||||
val sortBy = listOf(
|
||||
"SCORE_DESC","POPULARITY_DESC","TRENDING_DESC","TITLE_ENGLISH","TITLE_ENGLISH_DESC","SCORE"
|
||||
"SCORE_DESC",
|
||||
"POPULARITY_DESC",
|
||||
"TRENDING_DESC",
|
||||
"TITLE_ENGLISH",
|
||||
"TITLE_ENGLISH_DESC",
|
||||
"SCORE"
|
||||
)
|
||||
|
||||
val seasons = listOf(
|
||||
|
@ -51,11 +56,11 @@ object Anilist {
|
|||
private val cal: Calendar = Calendar.getInstance()
|
||||
private val currentYear = cal.get(Calendar.YEAR)
|
||||
private val currentSeason: Int = when (cal.get(Calendar.MONTH)) {
|
||||
0, 1, 2 -> 0
|
||||
3, 4, 5 -> 1
|
||||
6, 7, 8 -> 2
|
||||
0, 1, 2 -> 0
|
||||
3, 4, 5 -> 1
|
||||
6, 7, 8 -> 2
|
||||
9, 10, 11 -> 3
|
||||
else -> 0
|
||||
else -> 0
|
||||
}
|
||||
|
||||
private fun getSeason(next: Boolean): Pair<String, Int> {
|
||||
|
@ -132,7 +137,12 @@ object Anilist {
|
|||
if (token != null || force) {
|
||||
if (token != null && useToken) headers["Authorization"] = "Bearer $token"
|
||||
|
||||
val json = client.post("https://graphql.anilist.co/", headers, data = data, cacheTime = cache ?: 10)
|
||||
val json = client.post(
|
||||
"https://graphql.anilist.co/",
|
||||
headers,
|
||||
data = data,
|
||||
cacheTime = cache ?: 10
|
||||
)
|
||||
if (!json.text.startsWith("{")) throw Exception(currContext()?.getString(R.string.anilist_down))
|
||||
if (show) println("Response : ${json.text}")
|
||||
json.parsed()
|
||||
|
|
|
@ -20,7 +20,7 @@ class AnilistMutations {
|
|||
repeat: Int? = null,
|
||||
notes: String? = null,
|
||||
status: String? = null,
|
||||
private:Boolean? = null,
|
||||
private: Boolean? = null,
|
||||
startedAt: FuzzyDate? = null,
|
||||
completedAt: FuzzyDate? = null,
|
||||
customList: List<String>? = null
|
||||
|
@ -41,7 +41,7 @@ class AnilistMutations {
|
|||
${if (repeat != null) ""","repeat":$repeat""" else ""}
|
||||
${if (notes != null) ""","notes":"${notes.replace("\n", "\\n")}"""" else ""}
|
||||
${if (status != null) ""","status":"$status"""" else ""}
|
||||
${if (customList !=null) ""","customLists":[${customList.joinToString { "\"$it\"" }}]""" else ""}
|
||||
${if (customList != null) ""","customLists":[${customList.joinToString { "\"$it\"" }}]""" else ""}
|
||||
}""".replace("\n", "").replace(""" """, "")
|
||||
println(variables)
|
||||
executeQuery<JsonObject>(query, variables, show = true)
|
||||
|
|
|
@ -2,13 +2,13 @@ package ani.dantotsu.connections.anilist
|
|||
|
||||
import android.app.Activity
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.checkGenreTime
|
||||
import ani.dantotsu.checkId
|
||||
import ani.dantotsu.connections.anilist.Anilist.authorRoles
|
||||
import ani.dantotsu.connections.anilist.Anilist.executeQuery
|
||||
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||
import ani.dantotsu.connections.anilist.api.Page
|
||||
import ani.dantotsu.connections.anilist.api.Query
|
||||
import ani.dantotsu.checkGenreTime
|
||||
import ani.dantotsu.checkId
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.logError
|
||||
|
@ -113,9 +113,13 @@ class AnilistQueries {
|
|||
name = i.node?.name?.userPreferred,
|
||||
image = i.node?.image?.medium,
|
||||
banner = media.banner ?: media.cover,
|
||||
role = when (i.role.toString()){
|
||||
"MAIN" -> currContext()?.getString(R.string.main_role) ?: "MAIN"
|
||||
"SUPPORTING" -> currContext()?.getString(R.string.supporting_role) ?: "SUPPORTING"
|
||||
role = when (i.role.toString()) {
|
||||
"MAIN" -> currContext()?.getString(R.string.main_role)
|
||||
?: "MAIN"
|
||||
|
||||
"SUPPORTING" -> currContext()?.getString(R.string.supporting_role)
|
||||
?: "SUPPORTING"
|
||||
|
||||
else -> i.role.toString()
|
||||
}
|
||||
)
|
||||
|
@ -129,11 +133,16 @@ class AnilistQueries {
|
|||
val m = Media(mediaEdge)
|
||||
media.relations?.add(m)
|
||||
if (m.relation == "SEQUEL") {
|
||||
media.sequel = if ((media.sequel?.popularity ?: 0) < (m.popularity ?: 0)) m else media.sequel
|
||||
media.sequel =
|
||||
if ((media.sequel?.popularity ?: 0) < (m.popularity
|
||||
?: 0)
|
||||
) m else media.sequel
|
||||
|
||||
} else if (m.relation == "PREQUEL") {
|
||||
media.prequel =
|
||||
if ((media.prequel?.popularity ?: 0) < (m.popularity ?: 0)) m else media.prequel
|
||||
if ((media.prequel?.popularity ?: 0) < (m.popularity
|
||||
?: 0)
|
||||
) m else media.prequel
|
||||
}
|
||||
}
|
||||
media.relations?.sortByDescending { it.popularity }
|
||||
|
@ -199,17 +208,19 @@ class AnilistQueries {
|
|||
)
|
||||
}
|
||||
|
||||
media.anime.nextAiringEpisodeTime = fetchedMedia.nextAiringEpisode?.airingAt?.toLong()
|
||||
media.anime.nextAiringEpisodeTime =
|
||||
fetchedMedia.nextAiringEpisode?.airingAt?.toLong()
|
||||
|
||||
fetchedMedia.externalLinks?.forEach { i ->
|
||||
when (i.site.lowercase()) {
|
||||
"youtube" -> media.anime.youtube = i.url
|
||||
"crunchyroll" -> media.crunchySlug = i.url?.split("/")?.getOrNull(3)
|
||||
"vrv" -> media.vrvId = i.url?.split("/")?.getOrNull(4)
|
||||
"youtube" -> media.anime.youtube = i.url
|
||||
"crunchyroll" -> media.crunchySlug =
|
||||
i.url?.split("/")?.getOrNull(3)
|
||||
|
||||
"vrv" -> media.vrvId = i.url?.split("/")?.getOrNull(4)
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (media.manga != null) {
|
||||
} else if (media.manga != null) {
|
||||
fetchedMedia.staff?.edges?.find { authorRoles.contains(it.role?.trim()) }?.node?.let {
|
||||
media.manga.author = Author(
|
||||
it.id.toString(),
|
||||
|
@ -241,10 +252,10 @@ class AnilistQueries {
|
|||
return media
|
||||
}
|
||||
|
||||
suspend fun continueMedia(type: String,planned:Boolean=false): ArrayList<Media> {
|
||||
suspend fun continueMedia(type: String, planned: Boolean = false): ArrayList<Media> {
|
||||
val returnArray = arrayListOf<Media>()
|
||||
val map = mutableMapOf<Int, Media>()
|
||||
val statuses = if(!planned) arrayOf("CURRENT", "REPEATING") else arrayOf("PLANNING")
|
||||
val statuses = if (!planned) arrayOf("CURRENT", "REPEATING") else arrayOf("PLANNING")
|
||||
suspend fun repeat(status: String) {
|
||||
val response =
|
||||
executeQuery<Query.MediaListCollection>(""" { MediaListCollection(userId: ${Anilist.userid}, type: $type, status: $status , sort: UPDATED_TIME ) { lists { entries { progress private score(format:POINT_100) status media { id idMal type isAdult status chapters episodes nextAiringEpisode {episode} meanScore isFavourite format bannerImage coverImage{large} title { english romaji userPreferred } } } } } } """)
|
||||
|
@ -275,21 +286,21 @@ class AnilistQueries {
|
|||
var hasNextPage = true
|
||||
var page = 0
|
||||
|
||||
suspend fun getNextPage(page:Int): List<Media> {
|
||||
suspend fun getNextPage(page: Int): List<Media> {
|
||||
val response =
|
||||
executeQuery<Query.User>("""{User(id:${Anilist.userid}){id favourites{${if (anime) "anime" else "manga"}(page:$page){pageInfo{hasNextPage}edges{favouriteOrder node{id idMal isAdult mediaListEntry{ progress private score(format:POINT_100) status } chapters isFavourite format episodes nextAiringEpisode{episode}meanScore isFavourite format startDate{year month day} title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}}}}""")
|
||||
val favourites = response?.data?.user?.favourites
|
||||
val apiMediaList = if (anime) favourites?.anime else favourites?.manga
|
||||
hasNextPage = apiMediaList?.pageInfo?.hasNextPage ?: false
|
||||
return apiMediaList?.edges?.mapNotNull {
|
||||
it.node?.let { i->
|
||||
it.node?.let { i ->
|
||||
Media(i).apply { isFav = true }
|
||||
}
|
||||
} ?: return listOf()
|
||||
}
|
||||
|
||||
val responseArray = arrayListOf<Media>()
|
||||
while(hasNextPage){
|
||||
while (hasNextPage) {
|
||||
page++
|
||||
responseArray.addAll(getNextPage(page))
|
||||
}
|
||||
|
@ -361,7 +372,11 @@ class AnilistQueries {
|
|||
return default
|
||||
}
|
||||
|
||||
suspend fun getMediaLists(anime: Boolean, userId: Int, sortOrder: String? = null): MutableMap<String, ArrayList<Media>> {
|
||||
suspend fun getMediaLists(
|
||||
anime: Boolean,
|
||||
userId: Int,
|
||||
sortOrder: String? = null
|
||||
): MutableMap<String, ArrayList<Media>> {
|
||||
val response =
|
||||
executeQuery<Query.MediaListCollection>("""{ MediaListCollection(userId: $userId, type: ${if (anime) "ANIME" else "MANGA"}) { lists { name isCustomList entries { status progress private score(format:POINT_100) updatedAt media { id idMal isAdult type status chapters episodes nextAiringEpisode {episode} bannerImage meanScore isFavourite format coverImage{large} startDate{year month day} title {english romaji userPreferred } } } } user { id mediaListOptions { rowOrder animeList { sectionOrder } mangaList { sectionOrder } } } } }""")
|
||||
val sorted = mutableMapOf<String, ArrayList<Media>>()
|
||||
|
@ -388,7 +403,7 @@ class AnilistQueries {
|
|||
if (unsorted.containsKey(it)) sorted[it] = unsorted[it]!!
|
||||
}
|
||||
unsorted.forEach {
|
||||
if(!sorted.containsKey(it.key)) sorted[it.key] = it.value
|
||||
if (!sorted.containsKey(it.key)) sorted[it.key] = it.value
|
||||
}
|
||||
|
||||
sorted["Favourites"] = favMedia(anime)
|
||||
|
@ -399,11 +414,18 @@ class AnilistQueries {
|
|||
val sort = sortOrder ?: options?.rowOrder
|
||||
for (i in sorted.keys) {
|
||||
when (sort) {
|
||||
"score" -> sorted[i]?.sortWith { b, a -> compareValuesBy(a, b, { it.userScore }, { it.meanScore }) }
|
||||
"title" -> sorted[i]?.sortWith(compareBy { it.userPreferredName })
|
||||
"score" -> sorted[i]?.sortWith { b, a ->
|
||||
compareValuesBy(
|
||||
a,
|
||||
b,
|
||||
{ it.userScore },
|
||||
{ it.meanScore })
|
||||
}
|
||||
|
||||
"title" -> sorted[i]?.sortWith(compareBy { it.userPreferredName })
|
||||
"updatedAt" -> sorted[i]?.sortWith(compareByDescending { it.userUpdatedAt })
|
||||
"release" -> sorted[i]?.sortWith(compareByDescending { it.startDate })
|
||||
"id" -> sorted[i]?.sortWith(compareBy { it.id })
|
||||
"release" -> sorted[i]?.sortWith(compareByDescending { it.startDate })
|
||||
"id" -> sorted[i]?.sortWith(compareBy { it.id })
|
||||
}
|
||||
}
|
||||
return sorted
|
||||
|
@ -559,18 +581,36 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
|||
${if (seasonYear != null) ""","seasonYear":"$seasonYear"""" else ""}
|
||||
${if (season != null) ""","season":"$season"""" else ""}
|
||||
${if (search != null) ""","search":"$search"""" else ""}
|
||||
${if (sort!=null) ""","sort":"$sort"""" else ""}
|
||||
${if (sort != null) ""","sort":"$sort"""" else ""}
|
||||
${if (format != null) ""","format":"${format.replace(" ", "_")}"""" else ""}
|
||||
${if (genres?.isNotEmpty() == true) ""","genres":[${genres.joinToString { "\"$it\"" }}]""" else ""}
|
||||
${
|
||||
if (excludedGenres?.isNotEmpty() == true)
|
||||
""","excludedGenres":[${excludedGenres.joinToString { "\"${it.replace("Not ", "")}\"" }}]"""
|
||||
""","excludedGenres":[${
|
||||
excludedGenres.joinToString {
|
||||
"\"${
|
||||
it.replace(
|
||||
"Not ",
|
||||
""
|
||||
)
|
||||
}\""
|
||||
}
|
||||
}]"""
|
||||
else ""
|
||||
}
|
||||
${if (tags?.isNotEmpty() == true) ""","tags":[${tags.joinToString { "\"$it\"" }}]""" else ""}
|
||||
${
|
||||
if (excludedTags?.isNotEmpty() == true)
|
||||
""","excludedTags":[${excludedTags.joinToString { "\"${it.replace("Not ", "")}\"" }}]"""
|
||||
""","excludedTags":[${
|
||||
excludedTags.joinToString {
|
||||
"\"${
|
||||
it.replace(
|
||||
"Not ",
|
||||
""
|
||||
)
|
||||
}\""
|
||||
}
|
||||
}]"""
|
||||
else ""
|
||||
}
|
||||
}""".replace("\n", " ").replace(""" """, "")
|
||||
|
@ -622,7 +662,7 @@ query (${"$"}page: Int = 1, ${"$"}id: Int, ${"$"}type: MediaType, ${"$"}isAdult:
|
|||
greater: Long = 0,
|
||||
lesser: Long = System.currentTimeMillis() / 1000 - 10000
|
||||
): MutableList<Media>? {
|
||||
suspend fun execute(page:Int = 1):Page?{
|
||||
suspend fun execute(page: Int = 1): Page? {
|
||||
val query = """{
|
||||
Page(page:$page,perPage:50) {
|
||||
pageInfo {
|
||||
|
@ -668,7 +708,7 @@ Page(page:$page,perPage:50) {
|
|||
}""".replace("\n", " ").replace(""" """, "")
|
||||
return executeQuery<Query.Page>(query, force = true)?.data?.page
|
||||
}
|
||||
if(smaller) {
|
||||
if (smaller) {
|
||||
val response = execute()?.airingSchedules ?: return null
|
||||
val idArr = mutableListOf<Int>()
|
||||
val listOnly = loadData("recently_list_only") ?: false
|
||||
|
@ -682,11 +722,11 @@ Page(page:$page,perPage:50) {
|
|||
else null
|
||||
}
|
||||
}.toMutableList()
|
||||
}else{
|
||||
} else {
|
||||
var i = 1
|
||||
val list = mutableListOf<Media>()
|
||||
var res : Page? = null
|
||||
suspend fun next(){
|
||||
var res: Page? = null
|
||||
suspend fun next() {
|
||||
res = execute(i)
|
||||
list.addAll(res?.airingSchedules?.mapNotNull { j ->
|
||||
j.media?.let {
|
||||
|
@ -694,10 +734,10 @@ Page(page:$page,perPage:50) {
|
|||
Media(it).apply { relation = "${j.episode},${j.airingAt}" }
|
||||
} else null
|
||||
}
|
||||
}?: listOf())
|
||||
} ?: listOf())
|
||||
}
|
||||
next()
|
||||
while (res?.pageInfo?.hasNextPage == true){
|
||||
while (res?.pageInfo?.hasNextPage == true) {
|
||||
next()
|
||||
i++
|
||||
}
|
||||
|
@ -822,19 +862,20 @@ Page(page:$page,perPage:50) {
|
|||
var page = 0
|
||||
while (hasNextPage) {
|
||||
page++
|
||||
hasNextPage = executeQuery<Query.Studio>(query(page), force = true)?.data?.studio?.media?.let {
|
||||
it.edges?.forEach { i ->
|
||||
i.node?.apply {
|
||||
val status = status.toString()
|
||||
val year = startDate?.year?.toString() ?: "TBA"
|
||||
val title = if (status != "CANCELLED") year else status
|
||||
if (!yearMedia.containsKey(title))
|
||||
yearMedia[title] = arrayListOf()
|
||||
yearMedia[title]?.add(Media(this))
|
||||
hasNextPage =
|
||||
executeQuery<Query.Studio>(query(page), force = true)?.data?.studio?.media?.let {
|
||||
it.edges?.forEach { i ->
|
||||
i.node?.apply {
|
||||
val status = status.toString()
|
||||
val year = startDate?.year?.toString() ?: "TBA"
|
||||
val title = if (status != "CANCELLED") year else status
|
||||
if (!yearMedia.containsKey(title))
|
||||
yearMedia[title] = arrayListOf()
|
||||
yearMedia[title]?.add(Media(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
it.pageInfo?.hasNextPage == true
|
||||
} ?: false
|
||||
it.pageInfo?.hasNextPage == true
|
||||
} ?: false
|
||||
}
|
||||
if (yearMedia.contains("CANCELLED")) {
|
||||
val a = yearMedia["CANCELLED"]!!
|
||||
|
@ -896,7 +937,10 @@ Page(page:$page,perPage:50) {
|
|||
|
||||
while (hasNextPage) {
|
||||
page++
|
||||
hasNextPage = executeQuery<Query.Author>(query(page), force = true)?.data?.author?.staffMedia?.let {
|
||||
hasNextPage = executeQuery<Query.Author>(
|
||||
query(page),
|
||||
force = true
|
||||
)?.data?.author?.staffMedia?.let {
|
||||
it.edges?.forEach { i ->
|
||||
i.node?.apply {
|
||||
val status = status.toString()
|
||||
|
|
|
@ -7,8 +7,8 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.discord.Discord
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.connections.mal.MAL
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.media.Media
|
||||
import ani.dantotsu.others.AppUpdater
|
||||
import ani.dantotsu.snackString
|
||||
|
@ -45,39 +45,57 @@ suspend fun getUserId(context: Context, block: () -> Unit) {
|
|||
}
|
||||
} else true
|
||||
|
||||
if(anilist) block.invoke()
|
||||
if (anilist) block.invoke()
|
||||
}
|
||||
|
||||
class AnilistHomeViewModel : ViewModel() {
|
||||
private val listImages: MutableLiveData<ArrayList<String?>> = MutableLiveData<ArrayList<String?>>(arrayListOf())
|
||||
private val listImages: MutableLiveData<ArrayList<String?>> =
|
||||
MutableLiveData<ArrayList<String?>>(arrayListOf())
|
||||
|
||||
fun getListImages(): LiveData<ArrayList<String?>> = listImages
|
||||
suspend fun setListImages() = listImages.postValue(Anilist.query.getBannerImages())
|
||||
|
||||
private val animeContinue: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
private val animeContinue: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
fun getAnimeContinue(): LiveData<ArrayList<Media>> = animeContinue
|
||||
suspend fun setAnimeContinue() = animeContinue.postValue(Anilist.query.continueMedia("ANIME"))
|
||||
|
||||
private val animeFav: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
private val animeFav: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
fun getAnimeFav(): LiveData<ArrayList<Media>> = animeFav
|
||||
suspend fun setAnimeFav() = animeFav.postValue(Anilist.query.favMedia(true))
|
||||
|
||||
private val animePlanned: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
fun getAnimePlanned(): LiveData<ArrayList<Media>> = animePlanned
|
||||
suspend fun setAnimePlanned() = animePlanned.postValue(Anilist.query.continueMedia("ANIME", true))
|
||||
private val animePlanned: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
fun getAnimePlanned(): LiveData<ArrayList<Media>> = animePlanned
|
||||
suspend fun setAnimePlanned() =
|
||||
animePlanned.postValue(Anilist.query.continueMedia("ANIME", true))
|
||||
|
||||
private val mangaContinue: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
private val mangaContinue: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
fun getMangaContinue(): LiveData<ArrayList<Media>> = mangaContinue
|
||||
suspend fun setMangaContinue() = mangaContinue.postValue(Anilist.query.continueMedia("MANGA"))
|
||||
|
||||
private val mangaFav: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
private val mangaFav: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
fun getMangaFav(): LiveData<ArrayList<Media>> = mangaFav
|
||||
suspend fun setMangaFav() = mangaFav.postValue(Anilist.query.favMedia(false))
|
||||
|
||||
private val mangaPlanned: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
fun getMangaPlanned(): LiveData<ArrayList<Media>> = mangaPlanned
|
||||
suspend fun setMangaPlanned() = mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true))
|
||||
private val mangaPlanned: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
fun getMangaPlanned(): LiveData<ArrayList<Media>> = mangaPlanned
|
||||
suspend fun setMangaPlanned() =
|
||||
mangaPlanned.postValue(Anilist.query.continueMedia("MANGA", true))
|
||||
|
||||
private val recommendation: MutableLiveData<ArrayList<Media>> =
|
||||
MutableLiveData<ArrayList<Media>>(null)
|
||||
|
||||
private val recommendation: MutableLiveData<ArrayList<Media>> = MutableLiveData<ArrayList<Media>>(null)
|
||||
fun getRecommendation(): LiveData<ArrayList<Media>> = recommendation
|
||||
suspend fun setRecommendation() = recommendation.postValue(Anilist.query.recommendations())
|
||||
|
||||
|
@ -100,7 +118,9 @@ class AnilistAnimeViewModel : ViewModel() {
|
|||
var notSet = true
|
||||
lateinit var searchResults: SearchResults
|
||||
private val type = "ANIME"
|
||||
private val trending: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
|
||||
private val trending: MutableLiveData<MutableList<Media>> =
|
||||
MutableLiveData<MutableList<Media>>(null)
|
||||
|
||||
fun getTrending(): LiveData<MutableList<Media>> = trending
|
||||
suspend fun loadTrending(i: Int) {
|
||||
val (season, year) = Anilist.currentSeasons[i]
|
||||
|
@ -116,7 +136,9 @@ class AnilistAnimeViewModel : ViewModel() {
|
|||
)
|
||||
}
|
||||
|
||||
private val updated: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
|
||||
private val updated: MutableLiveData<MutableList<Media>> =
|
||||
MutableLiveData<MutableList<Media>>(null)
|
||||
|
||||
fun getUpdated(): LiveData<MutableList<Media>> = updated
|
||||
suspend fun loadUpdated() = updated.postValue(Anilist.query.recentlyUpdated())
|
||||
|
||||
|
@ -164,15 +186,33 @@ class AnilistMangaViewModel : ViewModel() {
|
|||
var notSet = true
|
||||
lateinit var searchResults: SearchResults
|
||||
private val type = "MANGA"
|
||||
private val trending: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
|
||||
private val trending: MutableLiveData<MutableList<Media>> =
|
||||
MutableLiveData<MutableList<Media>>(null)
|
||||
|
||||
fun getTrending(): LiveData<MutableList<Media>> = trending
|
||||
suspend fun loadTrending() =
|
||||
trending.postValue(Anilist.query.search(type, perPage = 10, sort = Anilist.sortBy[2], hd = true)?.results)
|
||||
trending.postValue(
|
||||
Anilist.query.search(
|
||||
type,
|
||||
perPage = 10,
|
||||
sort = Anilist.sortBy[2],
|
||||
hd = true
|
||||
)?.results
|
||||
)
|
||||
|
||||
private val updated: MutableLiveData<MutableList<Media>> =
|
||||
MutableLiveData<MutableList<Media>>(null)
|
||||
|
||||
private val updated: MutableLiveData<MutableList<Media>> = MutableLiveData<MutableList<Media>>(null)
|
||||
fun getTrendingNovel(): LiveData<MutableList<Media>> = updated
|
||||
suspend fun loadTrendingNovel() =
|
||||
updated.postValue(Anilist.query.search(type, perPage = 10, sort = Anilist.sortBy[2], format = "NOVEL")?.results)
|
||||
updated.postValue(
|
||||
Anilist.query.search(
|
||||
type,
|
||||
perPage = 10,
|
||||
sort = Anilist.sortBy[2],
|
||||
format = "NOVEL"
|
||||
)?.results
|
||||
)
|
||||
|
||||
private val mangaPopular = MutableLiveData<SearchResults?>(null)
|
||||
fun getPopular(): LiveData<SearchResults?> = mangaPopular
|
||||
|
|
|
@ -6,19 +6,20 @@ import android.os.Bundle
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import ani.dantotsu.logError
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.startMainActivity
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
|
||||
class Login : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
LangSet.setLocale(this)
|
||||
ThemeManager(this).applyTheme()
|
||||
ThemeManager(this).applyTheme()
|
||||
val data: Uri? = intent?.data
|
||||
logger(data.toString())
|
||||
try {
|
||||
Anilist.token = Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
|
||||
Anilist.token =
|
||||
Regex("""(?<=access_token=).+(?=&token_type)""").find(data.toString())!!.value
|
||||
val filename = "anilistToken"
|
||||
this.openFileOutput(filename, Context.MODE_PRIVATE).use {
|
||||
it.write(Anilist.token!!.toByteArray())
|
||||
|
|
|
@ -27,7 +27,15 @@ data class SearchResults(
|
|||
val list = mutableListOf<SearchChip>()
|
||||
sort?.let {
|
||||
val c = currContext()!!
|
||||
list.add(SearchChip("SORT", c.getString(R.string.filter_sort, c.resources.getStringArray(R.array.sort_by)[Anilist.sortBy.indexOf(it)])))
|
||||
list.add(
|
||||
SearchChip(
|
||||
"SORT",
|
||||
c.getString(
|
||||
R.string.filter_sort,
|
||||
c.resources.getStringArray(R.array.sort_by)[Anilist.sortBy.indexOf(it)]
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
format?.let {
|
||||
list.add(SearchChip("FORMAT", currContext()!!.getString(R.string.filter_format, it)))
|
||||
|
@ -42,27 +50,37 @@ data class SearchResults(
|
|||
list.add(SearchChip("GENRE", it))
|
||||
}
|
||||
excludedGenres?.forEach {
|
||||
list.add(SearchChip("EXCLUDED_GENRE", currContext()!!.getString(R.string.filter_exclude, it)))
|
||||
list.add(
|
||||
SearchChip(
|
||||
"EXCLUDED_GENRE",
|
||||
currContext()!!.getString(R.string.filter_exclude, it)
|
||||
)
|
||||
)
|
||||
}
|
||||
tags?.forEach {
|
||||
list.add(SearchChip("TAG", it))
|
||||
}
|
||||
excludedTags?.forEach {
|
||||
list.add(SearchChip("EXCLUDED_TAG", currContext()!!.getString(R.string.filter_exclude, it)))
|
||||
list.add(
|
||||
SearchChip(
|
||||
"EXCLUDED_TAG",
|
||||
currContext()!!.getString(R.string.filter_exclude, it)
|
||||
)
|
||||
)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun removeChip(chip: SearchChip) {
|
||||
when (chip.type) {
|
||||
"SORT" -> sort = null
|
||||
"FORMAT" -> format = null
|
||||
"SEASON" -> season = null
|
||||
"SEASON_YEAR" -> seasonYear = null
|
||||
"GENRE" -> genres?.remove(chip.text)
|
||||
"SORT" -> sort = null
|
||||
"FORMAT" -> format = null
|
||||
"SEASON" -> season = null
|
||||
"SEASON_YEAR" -> seasonYear = null
|
||||
"GENRE" -> genres?.remove(chip.text)
|
||||
"EXCLUDED_GENRE" -> excludedGenres?.remove(chip.text)
|
||||
"TAG" -> tags?.remove(chip.text)
|
||||
"EXCLUDED_TAG" -> excludedTags?.remove(chip.text)
|
||||
"TAG" -> tags?.remove(chip.text)
|
||||
"EXCLUDED_TAG" -> excludedTags?.remove(chip.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,15 +5,15 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import ani.dantotsu.loadMedia
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.startMainActivity
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
|
||||
class UrlMedia : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
LangSet.setLocale(this)
|
||||
ThemeManager(this).applyTheme()
|
||||
ThemeManager(this).applyTheme()
|
||||
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
|
||||
var isMAL = false
|
||||
var continueMedia = true
|
||||
|
@ -23,6 +23,9 @@ ThemeManager(this).applyTheme()
|
|||
isMAL = data?.host != "anilist.co"
|
||||
id = data?.pathSegments?.getOrNull(1)?.toIntOrNull()
|
||||
} else loadMedia = id
|
||||
startMainActivity(this, bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia))
|
||||
startMainActivity(
|
||||
this,
|
||||
bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -3,23 +3,24 @@ package ani.dantotsu.connections.anilist.api
|
|||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
class Query{
|
||||
class Query {
|
||||
@Serializable
|
||||
data class Viewer(
|
||||
@SerialName("data")
|
||||
val data : Data?
|
||||
){
|
||||
val data: Data?
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Viewer")
|
||||
val user: ani.dantotsu.connections.anilist.api.User?
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Media(
|
||||
@SerialName("data")
|
||||
val data : Data?
|
||||
){
|
||||
val data: Data?
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Media")
|
||||
|
@ -30,12 +31,12 @@ class Query{
|
|||
@Serializable
|
||||
data class Page(
|
||||
@SerialName("data")
|
||||
val data : Data?
|
||||
){
|
||||
val data: Data?
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Page")
|
||||
val page : ani.dantotsu.connections.anilist.api.Page?
|
||||
val page: ani.dantotsu.connections.anilist.api.Page?
|
||||
)
|
||||
}
|
||||
// data class AiringSchedule(
|
||||
|
@ -49,8 +50,8 @@ class Query{
|
|||
@Serializable
|
||||
data class Character(
|
||||
@SerialName("data")
|
||||
val data : Data?
|
||||
){
|
||||
val data: Data?
|
||||
) {
|
||||
|
||||
@Serializable
|
||||
data class Data(
|
||||
|
@ -63,7 +64,7 @@ class Query{
|
|||
data class Studio(
|
||||
@SerialName("data")
|
||||
val data: Data?
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Studio")
|
||||
|
@ -76,7 +77,7 @@ class Query{
|
|||
data class Author(
|
||||
@SerialName("data")
|
||||
val data: Data?
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("Staff")
|
||||
|
@ -95,8 +96,8 @@ class Query{
|
|||
@Serializable
|
||||
data class MediaListCollection(
|
||||
@SerialName("data")
|
||||
val data : Data?
|
||||
){
|
||||
val data: Data?
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("MediaListCollection")
|
||||
|
@ -108,7 +109,7 @@ class Query{
|
|||
data class GenreCollection(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("GenreCollection")
|
||||
|
@ -120,7 +121,7 @@ class Query{
|
|||
data class MediaTagCollection(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("MediaTagCollection")
|
||||
|
@ -132,7 +133,7 @@ class Query{
|
|||
data class User(
|
||||
@SerialName("data")
|
||||
val data: Data
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Data(
|
||||
@SerialName("User")
|
||||
|
|
|
@ -3,7 +3,7 @@ package ani.dantotsu.connections.anilist.api
|
|||
import kotlinx.serialization.SerialName
|
||||
import java.io.Serializable
|
||||
import java.text.DateFormatSymbols
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
data class FuzzyDate(
|
||||
|
@ -16,9 +16,11 @@ data class FuzzyDate(
|
|||
fun isEmpty(): Boolean {
|
||||
return year == null && month == null && day == null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return if ( isEmpty() ) "??" else toStringOrEmpty()
|
||||
return if (isEmpty()) "??" else toStringOrEmpty()
|
||||
}
|
||||
|
||||
fun toStringOrEmpty(): String {
|
||||
return listOfNotNull(
|
||||
day?.toString(),
|
||||
|
@ -29,16 +31,21 @@ data class FuzzyDate(
|
|||
|
||||
fun getToday(): FuzzyDate {
|
||||
val cal = Calendar.getInstance()
|
||||
return FuzzyDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH))
|
||||
return FuzzyDate(
|
||||
cal.get(Calendar.YEAR),
|
||||
cal.get(Calendar.MONTH) + 1,
|
||||
cal.get(Calendar.DAY_OF_MONTH)
|
||||
)
|
||||
}
|
||||
|
||||
fun toVariableString(): String {
|
||||
return listOfNotNull(
|
||||
year?.let {"year:$it"},
|
||||
month?.let {"month:$it"},
|
||||
day?.let {"day:$it"}
|
||||
year?.let { "year:$it" },
|
||||
month?.let { "month:$it" },
|
||||
day?.let { "day:$it" }
|
||||
).joinToString(",", "{", "}")
|
||||
}
|
||||
|
||||
fun toMALString(): String {
|
||||
val padding = '0'
|
||||
val values = listOf(
|
||||
|
@ -46,7 +53,7 @@ data class FuzzyDate(
|
|||
month?.toString()?.padStart(2, padding),
|
||||
day?.toString()?.padStart(2, padding)
|
||||
)
|
||||
return values.takeWhile {it is String}.joinToString("-")
|
||||
return values.takeWhile { it is String }.joinToString("-")
|
||||
}
|
||||
|
||||
// fun toInt(): Int {
|
||||
|
@ -54,8 +61,8 @@ data class FuzzyDate(
|
|||
// }
|
||||
|
||||
override fun compareTo(other: FuzzyDate): Int = when {
|
||||
year != other.year -> (year ?: 0) - (other.year ?: 0)
|
||||
year != other.year -> (year ?: 0) - (other.year ?: 0)
|
||||
month != other.month -> (month ?: 0) - (other.month ?: 0)
|
||||
else -> (day ?: 0) - (other.day ?: 0)
|
||||
else -> (day ?: 0) - (other.day ?: 0)
|
||||
}
|
||||
}
|
|
@ -116,7 +116,7 @@ data class Media(
|
|||
@SerialName("characters") var characters: CharacterConnection?,
|
||||
|
||||
// The staff who produced the media
|
||||
@SerialName("staffPreview") var staff: StaffConnection?,
|
||||
@SerialName("staffPreview") var staff: StaffConnection?,
|
||||
|
||||
// The companies who produced the media
|
||||
@SerialName("studios") var studios: StudioConnection?,
|
||||
|
@ -292,7 +292,7 @@ data class MediaList(
|
|||
@SerialName("hiddenFromStatusLists") var hiddenFromStatusLists: Boolean?,
|
||||
|
||||
// Map of booleans for which custom lists the entry are in
|
||||
@SerialName("customLists") var customLists: Map<String,Boolean>?,
|
||||
@SerialName("customLists") var customLists: Map<String, Boolean>?,
|
||||
|
||||
// Map of advanced scores with name keys
|
||||
// @SerialName("advancedScores") var advancedScores: Json?,
|
||||
|
@ -355,7 +355,7 @@ data class MediaTrailer(
|
|||
|
||||
@Serializable
|
||||
data class MediaTagCollection(
|
||||
@SerialName("tags") var tags : List<MediaTag>?
|
||||
@SerialName("tags") var tags: List<MediaTag>?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
|
|
@ -2,6 +2,7 @@ package ani.dantotsu.connections.anilist.api
|
|||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Recommendation(
|
||||
// The id of the recommendation
|
||||
|
@ -22,6 +23,7 @@ data class Recommendation(
|
|||
// The user that first created the recommendation
|
||||
@SerialName("user") var user: User?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class RecommendationConnection(
|
||||
//@SerialName("edges") var edges: List<RecommendationEdge>?,
|
||||
|
|
|
@ -9,7 +9,7 @@ data class Staff(
|
|||
@SerialName("id") var id: Int,
|
||||
|
||||
// The names of the staff member
|
||||
@SerialName("name") var name: StaffName?,
|
||||
@SerialName("name") var name: StaffName?,
|
||||
|
||||
// The primary language of the staff member. Current values: Japanese, English, Korean, Italian, Spanish, Portuguese, French, German, Hebrew, Hungarian, Chinese, Arabic, Filipino, Catalan, Finnish, Turkish, Dutch, Swedish, Thai, Tagalog, Malaysian, Indonesian, Vietnamese, Nepali, Hindi, Urdu
|
||||
@SerialName("languageV2") var languageV2: String?,
|
||||
|
@ -80,8 +80,8 @@ data class Staff(
|
|||
)
|
||||
|
||||
@Serializable
|
||||
data class StaffName (
|
||||
var userPreferred:String?
|
||||
data class StaffName(
|
||||
var userPreferred: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
@ -96,6 +96,6 @@ data class StaffConnection(
|
|||
|
||||
@Serializable
|
||||
data class StaffEdge(
|
||||
var role:String?,
|
||||
var role: String?,
|
||||
var node: Staff?
|
||||
)
|
|
@ -80,10 +80,10 @@ data class UserOptions(
|
|||
@SerialName("displayAdultContent") var displayAdultContent: Boolean?,
|
||||
|
||||
// Whether the user receives notifications when a show they are watching aires
|
||||
@SerialName("airingNotifications") var airingNotifications: Boolean?,
|
||||
@SerialName("airingNotifications") var airingNotifications: Boolean?,
|
||||
//
|
||||
// Profile highlight color (blue, purple, pink, orange, red, green, gray)
|
||||
@SerialName("profileColor") var profileColor: String?,
|
||||
// Profile highlight color (blue, purple, pink, orange, red, green, gray)
|
||||
@SerialName("profileColor") var profileColor: String?,
|
||||
//
|
||||
// // Notification options
|
||||
// // @SerialName("notificationOptions") var notificationOptions: List<NotificationOption>?,
|
||||
|
|
|
@ -5,14 +5,11 @@ import android.content.Intent
|
|||
import android.widget.TextView
|
||||
import androidx.core.content.edit
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.discord.serializers.User
|
||||
import ani.dantotsu.others.CustomBottomDialog
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.tryWith
|
||||
import ani.dantotsu.tryWithSuspend
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import java.io.File
|
||||
|
||||
object Discord {
|
||||
|
@ -60,7 +57,7 @@ object Discord {
|
|||
}
|
||||
}
|
||||
|
||||
private var rpc : RPC? = null
|
||||
private var rpc: RPC? = null
|
||||
|
||||
|
||||
fun warning(context: Context) = CustomBottomDialog().apply {
|
||||
|
@ -88,7 +85,8 @@ object Discord {
|
|||
}
|
||||
|
||||
const val application_Id = "1163925779692912771"
|
||||
const val small_Image: String = "mp:attachments/1167176318266380288/1176997397797277856/logo-best_of_both.png"
|
||||
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 {
|
||||
|
|
|
@ -16,14 +16,11 @@ 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 ani.dantotsu.isOnline
|
||||
|
@ -39,18 +36,16 @@ 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 heartbeat: Int = 0
|
||||
private var sequence: Int? = null
|
||||
private var sessionId: String = ""
|
||||
private var resume = false
|
||||
private lateinit var logFile : File
|
||||
private lateinit var logFile: File
|
||||
private lateinit var webSocket: WebSocket
|
||||
private lateinit var heartbeatThread : Thread
|
||||
private lateinit var client : OkHttpClient
|
||||
private lateinit var heartbeatThread: Thread
|
||||
private lateinit var client: OkHttpClient
|
||||
private lateinit var wakeLock: PowerManager.WakeLock
|
||||
var presenceStore = ""
|
||||
val json = Json {
|
||||
|
@ -66,7 +61,10 @@ class DiscordService : Service() {
|
|||
|
||||
log("Service onCreate()")
|
||||
val powerManager = baseContext.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "discordRPC:backgroundPresence")
|
||||
wakeLock = powerManager.newWakeLock(
|
||||
PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"discordRPC:backgroundPresence"
|
||||
)
|
||||
wakeLock.acquire()
|
||||
log("WakeLock Acquired")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
@ -104,13 +102,13 @@ class DiscordService : Service() {
|
|||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
log("Service onStartCommand()")
|
||||
if(intent != null) {
|
||||
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{
|
||||
} else {
|
||||
log("Service onStartCommand() no presence")
|
||||
DiscordServiceRunningSingleton.running = false
|
||||
client.dispatcher.executorService.shutdown()
|
||||
|
@ -126,7 +124,7 @@ class DiscordService : Service() {
|
|||
|
||||
override fun onDestroy() {
|
||||
log("Service Destroyed")
|
||||
if (DiscordServiceRunningSingleton.running){
|
||||
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) {
|
||||
|
@ -135,17 +133,19 @@ class DiscordService : Service() {
|
|||
baseContext.startService(intent)
|
||||
}
|
||||
} else {
|
||||
if(this::webSocket.isInitialized)
|
||||
setPresence(json.encodeToString(
|
||||
Presence.Response(
|
||||
3,
|
||||
Presence(status = "offline")
|
||||
if (this::webSocket.isInitialized)
|
||||
setPresence(
|
||||
json.encodeToString(
|
||||
Presence.Response(
|
||||
3,
|
||||
Presence(status = "offline")
|
||||
)
|
||||
)
|
||||
)
|
||||
))
|
||||
wakeLock.release()
|
||||
}
|
||||
SERVICE_RUNNING = false
|
||||
if(this::webSocket.isInitialized) webSocket.close(1000, "Closed by user")
|
||||
if (this::webSocket.isInitialized) webSocket.close(1000, "Closed by user")
|
||||
super.onDestroy()
|
||||
//saveLogToFile()
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ class DiscordService : Service() {
|
|||
log("WebSocket: Received op code ${json.get("op")}")
|
||||
when (json.get("op").asInt) {
|
||||
0 -> {
|
||||
if(json.has("s")) {
|
||||
if (json.has("s")) {
|
||||
log("WebSocket: Sequence ${json.get("s")} Received")
|
||||
sequence = json.get("s").asInt
|
||||
}
|
||||
|
@ -193,9 +193,10 @@ class DiscordService : Service() {
|
|||
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)
|
||||
if (presenceStore.isNotEmpty()) setPresence(presenceStore)
|
||||
sendBroadcast(Intent("ServiceToConnectButton"))
|
||||
}
|
||||
|
||||
1 -> {
|
||||
log("WebSocket: Received Heartbeat request, sending heartbeat")
|
||||
heartbeatThread.interrupt()
|
||||
|
@ -203,33 +204,38 @@ class DiscordService : Service() {
|
|||
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(),
|
||||
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(),
|
||||
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) {
|
||||
if (resume) {
|
||||
log("WebSocket: Resuming because server requested")
|
||||
resume()
|
||||
resume = false
|
||||
|
@ -238,6 +244,7 @@ class DiscordService : Service() {
|
|||
log("WebSocket: Identified")
|
||||
}
|
||||
}
|
||||
|
||||
11 -> {
|
||||
log("WebSocket: Heartbeat ACKed")
|
||||
heartbeatThread = Thread(HeartbeatRunnable())
|
||||
|
@ -245,29 +252,31 @@ class DiscordService : Service() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun identify(webSocket: WebSocket, context: Context) {
|
||||
val properties = JsonObject()
|
||||
properties.addProperty("os","linux")
|
||||
properties.addProperty("browser","unknown")
|
||||
properties.addProperty("device","unknown")
|
||||
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.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)
|
||||
if(!isOnline(baseContext)) {
|
||||
if (!isOnline(baseContext)) {
|
||||
log("WebSocket: Error, onFailure() reason: No Internet")
|
||||
errorNotification("Could not set the presence", "No Internet")
|
||||
return
|
||||
} else{
|
||||
} else {
|
||||
retryAttempts++
|
||||
if(retryAttempts >= maxRetryAttempts) {
|
||||
if (retryAttempts >= maxRetryAttempts) {
|
||||
log("WebSocket: Error, onFailure() reason: Max Retry Attempts")
|
||||
errorNotification("Could not set the presence", "Max Retry Attempts")
|
||||
return
|
||||
|
@ -281,19 +290,23 @@ class DiscordService : Service() {
|
|||
DiscordWebSocketListener()
|
||||
)
|
||||
client.dispatcher.executorService.shutdown()
|
||||
if(::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
|
||||
if (::heartbeatThread.isInitialized && !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.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
|
||||
if (::heartbeatThread.isInitialized && !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) {
|
||||
if (code >= 4000) {
|
||||
log("WebSocket: Error, code: $code reason: $reason")
|
||||
client = OkHttpClient()
|
||||
client.newWebSocket(
|
||||
|
@ -311,23 +324,24 @@ class DiscordService : Service() {
|
|||
context.getString(R.string.preference_file_key),
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
val token = sharedPref.getString(Discord.TOKEN, null)
|
||||
if(token == null) {
|
||||
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{
|
||||
} else {
|
||||
return token
|
||||
}
|
||||
}
|
||||
fun heartbeatSend( webSocket: WebSocket, seq: Int? ) {
|
||||
|
||||
fun heartbeatSend(webSocket: WebSocket, seq: Int?) {
|
||||
val json = JsonObject()
|
||||
json.addProperty("op",1)
|
||||
json.addProperty("op", 1)
|
||||
json.addProperty("d", seq)
|
||||
webSocket.send(json.toString())
|
||||
}
|
||||
private fun errorNotification(title : String, text: String) {
|
||||
|
||||
private fun errorNotification(title: String, text: String) {
|
||||
val intent = Intent(this@DiscordService, MainActivity::class.java).apply {
|
||||
action = Intent.ACTION_MAIN
|
||||
addCategory(Intent.CATEGORY_LAUNCHER)
|
||||
|
@ -401,7 +415,8 @@ class DiscordService : Service() {
|
|||
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 directory =
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||
val file = File(directory, fileName)
|
||||
|
||||
// Make sure the Downloads directory exists
|
||||
|
@ -410,7 +425,8 @@ class DiscordService : Service() {
|
|||
}
|
||||
|
||||
// Use FileProvider to get the URI for the file
|
||||
val authority = "${baseContext.packageName}.provider" // Adjust with your app's package name
|
||||
val authority =
|
||||
"${baseContext.packageName}.provider" // Adjust with your app's package name
|
||||
Uri.fromFile(file)
|
||||
}
|
||||
|
||||
|
@ -445,7 +461,8 @@ class DiscordService : Service() {
|
|||
Thread.sleep(heartbeat.toLong())
|
||||
heartbeatSend(webSocket, sequence)
|
||||
log("WebSocket: Heartbeat Sent")
|
||||
} catch (e:InterruptedException) {}
|
||||
} catch (e: InterruptedException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,12 @@ 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
|
||||
import ani.dantotsu.connections.discord.Discord.saveToken
|
||||
import ani.dantotsu.logger
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.startMainActivity
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.snackString
|
||||
|
||||
class Login : AppCompatActivity() {
|
||||
|
||||
|
@ -40,17 +37,22 @@ class Login : AppCompatActivity() {
|
|||
}
|
||||
WebView.setWebContentsDebuggingEnabled(true)
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
// Check if the URL is the one expected after a successful login
|
||||
if (request?.url.toString() != "https://discord.com/login") {
|
||||
// Delay the script execution to ensure the page is fully loaded
|
||||
view?.postDelayed({
|
||||
view.evaluateJavascript("""
|
||||
view.evaluateJavascript(
|
||||
"""
|
||||
(function() {
|
||||
const wreq = (webpackChunkdiscord_app.push([[''],{},e=>{m=[];for(let c in e.c)m.push(e.c[c])}]),m).find(m=>m?.exports?.default?.getToken!==void 0).exports.default.getToken();
|
||||
return wreq;
|
||||
})()
|
||||
""".trimIndent()) { result ->
|
||||
""".trimIndent()
|
||||
) { result ->
|
||||
login(result.trim('"'))
|
||||
}
|
||||
}, 2000)
|
||||
|
@ -67,7 +69,7 @@ class Login : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun login(token: String) {
|
||||
if (token.isEmpty() || token == "null"){
|
||||
if (token.isEmpty() || token == "null") {
|
||||
Toast.makeText(this, "Failed to retrieve token", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
return
|
||||
|
|
|
@ -1,30 +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
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import ani.dantotsu.connections.discord.serializers.Activity
|
||||
import ani.dantotsu.connections.discord.serializers.Presence
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.long
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.WebSocket
|
||||
import okhttp3.WebSocketListener
|
||||
import java.util.concurrent.TimeUnit.*
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import ani.dantotsu.client as app
|
||||
|
||||
|
@ -36,6 +16,7 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
|
|||
allowStructuredMapKeys = true
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
PLAYING, STREAMING, LISTENING, WATCHING, COMPETING
|
||||
}
|
||||
|
@ -56,14 +37,17 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
|
|||
val stopTimestamp: Long? = null,
|
||||
val buttons: MutableList<Link> = 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<KizzyApi>()
|
||||
return json?.id
|
||||
}
|
||||
|
||||
suspend fun createPresence(data: RPCData): String {
|
||||
val json = Json {
|
||||
encodeDefaults = true
|
||||
|
|
|
@ -2,8 +2,9 @@ package ani.dantotsu.connections.discord.serializers
|
|||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Activity (
|
||||
data class Activity(
|
||||
@SerialName("application_id")
|
||||
val applicationId: String? = null,
|
||||
val name: String? = null,
|
||||
|
|
|
@ -12,13 +12,13 @@ data class Identity(
|
|||
) {
|
||||
|
||||
@Serializable
|
||||
data class Response (
|
||||
data class Response(
|
||||
val op: Long,
|
||||
val d: Identity
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Properties (
|
||||
data class Properties(
|
||||
@SerialName("\$os")
|
||||
val os: String,
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@ package ani.dantotsu.connections.discord.serializers
|
|||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Presence (
|
||||
data class Presence(
|
||||
val activities: List<Activity> = listOf(),
|
||||
val afk: Boolean = true,
|
||||
val since: Long? = null,
|
||||
val status: String? = null
|
||||
){
|
||||
) {
|
||||
@Serializable
|
||||
data class Response (
|
||||
data class Response(
|
||||
val op: Long,
|
||||
val d: Presence
|
||||
)
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package ani.dantotsu.connections.discord.serializers
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
|
||||
@Serializable
|
||||
data class User (
|
||||
data class User(
|
||||
val verified: Boolean? = null,
|
||||
val username: String,
|
||||
|
||||
|
@ -71,7 +70,7 @@ data class User (
|
|||
)
|
||||
}
|
||||
|
||||
fun userAvatar():String{
|
||||
fun userAvatar(): String {
|
||||
return "https://cdn.discordapp.com/avatars/$id/$avatar.png"
|
||||
}
|
||||
}
|
|
@ -4,11 +4,17 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import ani.dantotsu.*
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.client
|
||||
import ani.dantotsu.connections.mal.MAL.clientId
|
||||
import ani.dantotsu.connections.mal.MAL.saveResponse
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.logError
|
||||
import ani.dantotsu.others.LangSet
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.startMainActivity
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.tryWithSuspend
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -16,7 +22,7 @@ class Login : AppCompatActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
LangSet.setLocale(this)
|
||||
ThemeManager(this).applyTheme()
|
||||
ThemeManager(this).applyTheme()
|
||||
try {
|
||||
val data: Uri = intent?.data
|
||||
?: throw Exception(getString(R.string.mal_login_uri_not_found))
|
||||
|
@ -46,9 +52,8 @@ ThemeManager(this).applyTheme()
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e:Exception){
|
||||
logError(e,snackbar = false)
|
||||
} catch (e: Exception) {
|
||||
logError(e, snackbar = false)
|
||||
startMainActivity(this)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,13 @@ import android.net.Uri
|
|||
import android.util.Base64
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import ani.dantotsu.*
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.client
|
||||
import ani.dantotsu.currContext
|
||||
import ani.dantotsu.loadData
|
||||
import ani.dantotsu.openLinkInBrowser
|
||||
import ani.dantotsu.saveData
|
||||
import ani.dantotsu.tryWithSuspend
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.File
|
||||
|
@ -94,6 +100,6 @@ object MAL {
|
|||
@SerialName("expires_in") var expiresIn: Long,
|
||||
@SerialName("access_token") val accessToken: String,
|
||||
@SerialName("refresh_token") val refreshToken: String,
|
||||
): java.io.Serializable
|
||||
) : java.io.Serializable
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package ani.dantotsu.connections.mal
|
||||
|
||||
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||
import ani.dantotsu.client
|
||||
import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||
import ani.dantotsu.tryWithSuspend
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
|
@ -43,18 +43,18 @@ class MALQueries {
|
|||
start: FuzzyDate? = null,
|
||||
end: FuzzyDate? = null
|
||||
) {
|
||||
if(idMAL==null) return
|
||||
if (idMAL == null) return
|
||||
val data = mutableMapOf("status" to convertStatus(isAnime, status))
|
||||
if (progress != null)
|
||||
data[if (isAnime) "num_watched_episodes" else "num_chapters_read"] = progress.toString()
|
||||
data[if (isAnime) "is_rewatching" else "is_rereading"] = (status == "REPEATING").toString()
|
||||
if (score != null)
|
||||
data["score"] = score.div(10).toString()
|
||||
if(rewatch!=null)
|
||||
data[if(isAnime) "num_times_rewatched" else "num_times_reread"] = rewatch.toString()
|
||||
if(start!=null)
|
||||
if (rewatch != null)
|
||||
data[if (isAnime) "num_times_rewatched" else "num_times_reread"] = rewatch.toString()
|
||||
if (start != null)
|
||||
data["start_date"] = start.toMALString()
|
||||
if(end!=null)
|
||||
if (end != null)
|
||||
data["finish_date"] = end.toMALString()
|
||||
tryWithSuspend {
|
||||
client.put(
|
||||
|
@ -65,8 +65,8 @@ class MALQueries {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun deleteList(isAnime: Boolean, idMAL: Int?){
|
||||
if(idMAL==null) return
|
||||
suspend fun deleteList(isAnime: Boolean, idMAL: Int?) {
|
||||
if (idMAL == null) return
|
||||
tryWithSuspend {
|
||||
client.delete(
|
||||
"$apiUrl/${if (isAnime) "anime" else "manga"}/$idMAL/my_list_status",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue