Fix Mangadex Login, Fix Mangadex tracking, Set Mangadex track status to Reading on tracked
This commit is contained in:
parent
ccdae6bb9a
commit
5dace4fd74
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.model.toMangaInfo
|
||||
import eu.kanade.tachiyomi.util.lang.awaitSingle
|
||||
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import exh.md.utils.FollowStatus
|
||||
import exh.md.utils.MdUtil
|
||||
import tachiyomi.source.model.MangaInfo
|
||||
@ -46,6 +47,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
override suspend fun add(track: Track): Track = update(track)
|
||||
|
||||
override suspend fun update(track: Track): Track {
|
||||
return withIOContext {
|
||||
val mdex = mdex ?: throw MangaDexNotFoundException()
|
||||
|
||||
val remoteTrack = mdex.fetchTrackingInfo(track.tracking_url)
|
||||
@ -56,7 +58,6 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
if (remoteTrack.status != followStatus.int) {
|
||||
mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus)
|
||||
remoteTrack.status = followStatus.int
|
||||
// db.insertFlatMetadataAsync(mangaMetadata.flatten()).await()
|
||||
}
|
||||
|
||||
if (track.score.toInt() > 0) {
|
||||
@ -75,7 +76,6 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
track.status = FollowStatus.READING.int
|
||||
mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), newFollowStatus)
|
||||
remoteTrack.status = newFollowStatus.int
|
||||
// db.insertFlatMetadataAsync(mangaMetadata.flatten()).await()
|
||||
}
|
||||
|
||||
mdex.updateReadingProgress(track)
|
||||
@ -83,21 +83,24 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
// When followStatus has been changed to unfollowed 0 out read chapters since dex does
|
||||
track.last_chapter_read = 0
|
||||
}
|
||||
return track
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCompletionStatus(): Int = FollowStatus.COMPLETED.int
|
||||
|
||||
override suspend fun bind(track: Track): Track = update(refresh(track))
|
||||
override suspend fun bind(track: Track): Track = update(refresh(track).also { it.status = FollowStatus.READING.int })
|
||||
|
||||
override suspend fun refresh(track: Track): Track {
|
||||
return withIOContext {
|
||||
val mdex = mdex ?: throw MangaDexNotFoundException()
|
||||
val (remoteTrack, mangaMetadata) = mdex.getTrackingAndMangaInfo(track)
|
||||
track.copyPersonalFrom(remoteTrack)
|
||||
if (track.total_chapters == 0 && mangaMetadata.status == SManga.COMPLETED) {
|
||||
track.total_chapters = mangaMetadata.maxChapterNumber ?: 0
|
||||
}
|
||||
return track
|
||||
track
|
||||
}
|
||||
}
|
||||
|
||||
fun createInitialTracker(dbManga: Manga, mdManga: Manga = dbManga): Track {
|
||||
@ -110,8 +113,9 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<TrackSearch> {
|
||||
return withIOContext {
|
||||
val mdex = mdex ?: throw MangaDexNotFoundException()
|
||||
return mdex.fetchSearchManga(0, query, mdex.getFilterList())
|
||||
mdex.fetchSearchManga(0, query, mdex.getFilterList())
|
||||
.flatMap { page ->
|
||||
runAsObservable({
|
||||
page.mangas.map {
|
||||
@ -121,6 +125,7 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
||||
}
|
||||
.awaitSingle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun toTrackSearch(mangaInfo: MangaInfo): TrackSearch = TrackSearch.create(TrackManager.MDLIST).apply {
|
||||
tracking_url = MdUtil.baseUrl + mangaInfo.key
|
||||
|
@ -4,7 +4,6 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import androidx.core.text.HtmlCompat
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
@ -48,12 +47,17 @@ import exh.source.DelegatedHttpSource
|
||||
import exh.ui.metadata.adapters.MangaDexDescriptionAdapter
|
||||
import exh.util.urlImportFetchSearchManga
|
||||
import exh.widget.preference.MangadexLoginDialog
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.int
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okio.EOFException
|
||||
import rx.Observable
|
||||
import tachiyomi.source.model.ChapterInfo
|
||||
import tachiyomi.source.model.MangaInfo
|
||||
@ -73,8 +77,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
RandomMangaSource {
|
||||
override val lang: String = delegate.lang
|
||||
|
||||
override val headers: Headers
|
||||
get() = super.headers.newBuilder().apply {
|
||||
override val headers: Headers = super.headers.newBuilder().apply {
|
||||
add("X-Requested-With", "XMLHttpRequest")
|
||||
add("Referer", MdUtil.baseUrl)
|
||||
}.build()
|
||||
@ -198,13 +201,10 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
add("login_password", password)
|
||||
add("no_js", "1")
|
||||
add("remember_me", "1")
|
||||
add("two_factor", twoFactorCode)
|
||||
}
|
||||
|
||||
twoFactorCode.let {
|
||||
formBody.add("two_factor", it)
|
||||
}
|
||||
|
||||
val response = client.newCall(
|
||||
client.newCall(
|
||||
POST(
|
||||
"${MdUtil.baseUrl}/ajax/actions.ajax.php?function=login",
|
||||
headers,
|
||||
@ -212,12 +212,13 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
)
|
||||
).await()
|
||||
|
||||
withIOContext { response.body?.string() }.let { result ->
|
||||
if (result != null && result.isEmpty()) {
|
||||
true
|
||||
val response = client.newCall(GET(MdUtil.apiUrl + MdUtil.isLoggedInApi, headers)).await()
|
||||
|
||||
withIOContext { response.body?.string() }.let { jsonData ->
|
||||
if (jsonData != null) {
|
||||
MdUtil.jsonParser.decodeFromString<JsonObject>(jsonData)["code"]?.let { it as? JsonPrimitive }?.int == 200
|
||||
} else {
|
||||
val error = result?.let { HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_COMPACT).toString() }
|
||||
throw Exception(error)
|
||||
throw Exception("Json data was null")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,12 +237,18 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
val result = client.newCall(
|
||||
POST("${MdUtil.baseUrl}/ajax/actions.ajax.php?function=logout", headers).newBuilder().addHeader(REMEMBER_ME, token).build()
|
||||
).await()
|
||||
try {
|
||||
val resultStr = withIOContext { result.body?.string() }
|
||||
if (resultStr?.contains("success", true) == true) {
|
||||
network.cookieManager.remove(httpUrl)
|
||||
trackManager.mdList.logout()
|
||||
return@withIOContext true
|
||||
}
|
||||
} catch (e: EOFException) {
|
||||
network.cookieManager.remove(httpUrl)
|
||||
trackManager.mdList.logout()
|
||||
return@withIOContext true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
@ -281,7 +288,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
}
|
||||
|
||||
override suspend fun fetchRandomMangaUrl(): String {
|
||||
return MangaHandler(client, headers, mdLang).fetchRandomMangaId()
|
||||
return withIOContext { MangaHandler(client, headers, mdLang).fetchRandomMangaId() }
|
||||
}
|
||||
|
||||
fun fetchMangaSimilar(manga: Manga): Observable<MangasPage> {
|
||||
|
@ -28,6 +28,7 @@ import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okio.EOFException
|
||||
|
||||
class FollowsHandler(val client: OkHttpClient, val headers: Headers, val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) {
|
||||
|
||||
@ -75,7 +76,6 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
|
||||
/**
|
||||
* fetch follow status used when fetching status for 1 manga
|
||||
*/
|
||||
|
||||
private fun followStatusParse(response: Response): Track {
|
||||
val followsPageResult = try {
|
||||
response.parseAs<FollowsIndividualSerializer>(MdUtil.jsonParser)
|
||||
@ -84,15 +84,11 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
|
||||
throw e
|
||||
}
|
||||
|
||||
if (followsPageResult.data == null) {
|
||||
throw Exception("Invalid response ${followsPageResult.code}")
|
||||
}
|
||||
|
||||
val track = Track.create(TrackManager.MDLIST)
|
||||
if (followsPageResult.code == 404) {
|
||||
track.status = FollowStatus.UNFOLLOWED.int
|
||||
} else {
|
||||
val follow = followsPageResult.data
|
||||
val follow = followsPageResult.data ?: throw Exception("Invalid response ${followsPageResult.code}")
|
||||
track.status = follow.followType
|
||||
if (follow.chapter.isNotBlank()) {
|
||||
track.last_chapter_read = follow.chapter.toFloat().floor()
|
||||
@ -153,7 +149,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
|
||||
.await()
|
||||
}
|
||||
|
||||
withIOContext { response.body?.string().isNullOrEmpty() }
|
||||
response.succeeded()
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,11 +168,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
|
||||
)
|
||||
).await()
|
||||
|
||||
withIOContext {
|
||||
response.body?.string()
|
||||
.also { xLogD(it) }
|
||||
.let { it != null && it.isEmpty() }
|
||||
}
|
||||
response.succeeded()
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,10 +180,21 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
|
||||
"${MdUtil.baseUrl}/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}",
|
||||
headers
|
||||
)
|
||||
)
|
||||
.await()
|
||||
).await()
|
||||
|
||||
withIOContext { response.body?.string().isNullOrEmpty() }
|
||||
response.succeeded()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Response.succeeded() = withIOContext {
|
||||
try {
|
||||
body?.string().let { body ->
|
||||
(body != null && body.isEmpty()).also {
|
||||
if (!it) xLogD(body)
|
||||
}
|
||||
}
|
||||
} catch (e: EOFException) {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import exh.md.handlers.serializers.ApiCovers
|
||||
import exh.md.handlers.serializers.ApiMangaSerializer
|
||||
import exh.md.utils.MdUtil
|
||||
import exh.metadata.metadata.MangaDexSearchMetadata
|
||||
import kotlinx.coroutines.async
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
@ -30,7 +31,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
|
||||
// TODO make use of this
|
||||
suspend fun fetchMangaAndChapterDetails(manga: MangaInfo, sourceId: Long): Pair<MangaInfo, List<ChapterInfo>> {
|
||||
return withIOContext {
|
||||
val apiNetworkManga = client.newCall(apiRequest(manga)).await().parseAs<ApiMangaSerializer>()
|
||||
val apiNetworkManga = client.newCall(apiRequest(manga)).await().parseAs<ApiMangaSerializer>(MdUtil.jsonParser)
|
||||
val covers = getCovers(manga, forceLatestCovers)
|
||||
val parser = ApiMangaParser(lang)
|
||||
|
||||
@ -120,17 +121,22 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val lang: Str
|
||||
}
|
||||
|
||||
suspend fun getTrackingInfo(track: Track, useLowQualityCovers: Boolean): Pair<Track, MangaDexSearchMetadata> {
|
||||
return withIOContext {
|
||||
val metadata = async {
|
||||
val mangaUrl = MdUtil.mapMdIdToMangaUrl(MdUtil.getMangaId(track.tracking_url).toInt())
|
||||
val manga = MangaInfo(mangaUrl, track.title)
|
||||
val response = client.newCall(apiRequest(manga)).await()
|
||||
val metadata = MangaDexSearchMetadata()
|
||||
ApiMangaParser(lang).parseIntoMetadata(metadata, response, emptyList())
|
||||
val remoteTrack = FollowsHandler(client, headers, Injekt.get(), useLowQualityCovers).fetchTrackingInfo(track.tracking_url)
|
||||
return remoteTrack to metadata
|
||||
metadata
|
||||
}
|
||||
val remoteTrack = async { FollowsHandler(client, headers, Injekt.get(), useLowQualityCovers).fetchTrackingInfo(track.tracking_url) }
|
||||
remoteTrack.await() to metadata.await()
|
||||
}
|
||||
}
|
||||
|
||||
private fun randomMangaRequest(): Request {
|
||||
return GET(MdUtil.baseUrl + MdUtil.randMangaPage, cache = CacheControl.Builder().noCache().build())
|
||||
return GET(MdUtil.baseUrl + MdUtil.randMangaPage, cache = CacheControl.FORCE_NETWORK)
|
||||
}
|
||||
|
||||
private fun apiRequest(manga: MangaInfo): Request {
|
||||
|
@ -11,7 +11,7 @@ data class FollowsPageSerializer(
|
||||
@Serializable
|
||||
data class FollowsIndividualSerializer(
|
||||
val code: Int,
|
||||
val data: FollowPage?
|
||||
val data: FollowPage? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
@ -31,6 +31,7 @@ class MdUtil {
|
||||
const val apiChapterSuffix = "?mark_read=0"
|
||||
const val groupSearchUrl = "$baseUrl/groups/0/1/"
|
||||
const val followsAllApi = "/v2/user/me/followed-manga"
|
||||
const val isLoggedInApi = "/v2/user/me"
|
||||
const val followsMangaApi = "/v2/user/me/manga/"
|
||||
const val apiCovers = "/covers"
|
||||
const val reportUrl = "https://api.mangadex.network/report"
|
||||
@ -51,10 +52,16 @@ class MdUtil {
|
||||
val englishDescriptionTags = listOf(
|
||||
"[b][u]English:",
|
||||
"[b][u]English",
|
||||
"English:",
|
||||
"English :",
|
||||
"[English]:",
|
||||
"English Translaton:",
|
||||
"[B][ENG][/B]"
|
||||
)
|
||||
|
||||
val bbCodeToRemove = listOf(
|
||||
"list", "*", "hr", "u", "b", "i", "s", "center", "spoiler="
|
||||
)
|
||||
val descriptionLanguages = listOf(
|
||||
"=FRANCAIS=",
|
||||
"[b] Spanish: [/ b]",
|
||||
@ -78,19 +85,23 @@ class MdUtil {
|
||||
"\r\n\r\nItalian\r\n",
|
||||
"Arabic /",
|
||||
"Descriptions in Other Languages",
|
||||
"Español /",
|
||||
"Español:",
|
||||
"Espanol",
|
||||
"[Españ",
|
||||
"Españ",
|
||||
"Farsi/",
|
||||
"Français",
|
||||
"French - ",
|
||||
"Francois",
|
||||
"French:",
|
||||
"French/",
|
||||
"French /",
|
||||
"German/",
|
||||
"German /",
|
||||
"Hindi /",
|
||||
"Bahasa Indonesia",
|
||||
"Indonesia:",
|
||||
"Indonesian:",
|
||||
"Indonesian :",
|
||||
"Indo:",
|
||||
"[u]Indonesian",
|
||||
"Italian / ",
|
||||
@ -98,9 +109,16 @@ class MdUtil {
|
||||
"Italian/",
|
||||
"Italiano",
|
||||
"Italian:",
|
||||
"Italian summary:",
|
||||
"Japanese /",
|
||||
"Original Japanese",
|
||||
"Official Japanese Translation",
|
||||
"Official Chinese Translation",
|
||||
"Official French Translation",
|
||||
"Official Indonesian Translation",
|
||||
"Links:",
|
||||
"Pasta-Pizza-Mandolino/Italiano",
|
||||
"Persian/فارسی",
|
||||
"Persian /فارسی",
|
||||
"Polish /",
|
||||
"Polish Summary /",
|
||||
@ -108,6 +126,8 @@ class MdUtil {
|
||||
"Polski",
|
||||
"Português",
|
||||
"Portuguese (BR)",
|
||||
"PT/BR:",
|
||||
"Pt/Br:",
|
||||
"Pt-Br:",
|
||||
"Portuguese /",
|
||||
"[right]",
|
||||
@ -115,6 +135,8 @@ class MdUtil {
|
||||
"Résume Français",
|
||||
"RÉSUMÉ FRANCAIS :",
|
||||
"RUS:",
|
||||
"Ru/Pyc",
|
||||
"\\r\\nRUS\\r\\n",
|
||||
"Russia/",
|
||||
"Russian /",
|
||||
"Spanish:",
|
||||
@ -162,23 +184,22 @@ class MdUtil {
|
||||
fun removeTimeParamUrl(url: String): String = url.substringBeforeLast("?")
|
||||
|
||||
fun cleanString(string: String): String {
|
||||
var cleanedString = string
|
||||
|
||||
bbCodeToRemove.forEach {
|
||||
cleanedString = cleanedString.replace("[$it]", "", true)
|
||||
.replace("[/$it]", "", true)
|
||||
}
|
||||
|
||||
val bbRegex =
|
||||
"""\[(\w+)[^]]*](.*?)\[/\1]""".toRegex()
|
||||
var intermediate = string
|
||||
.replace("[list]", "", true)
|
||||
.replace("[/list]", "", true)
|
||||
.replace("[*]", "")
|
||||
.replace("[hr]", "", true)
|
||||
.replace("[u]", "", true)
|
||||
.replace("[/u]", "", true)
|
||||
.replace("[b]", "", true)
|
||||
.replace("[/b]", "", true)
|
||||
|
||||
// Recursively remove nested bbcode
|
||||
while (bbRegex.containsMatchIn(intermediate)) {
|
||||
intermediate = intermediate.replace(bbRegex, "$2")
|
||||
while (bbRegex.containsMatchIn(cleanedString)) {
|
||||
cleanedString = cleanedString.replace(bbRegex, "$2")
|
||||
}
|
||||
return Parser.unescapeEntities(intermediate, false)
|
||||
|
||||
return Parser.unescapeEntities(cleanedString, false)
|
||||
}
|
||||
|
||||
fun cleanDescription(string: String): String {
|
||||
|
Loading…
x
Reference in New Issue
Block a user