Mangadex fixes
This commit is contained in:
parent
bc871cd2ee
commit
33a590d895
@ -59,6 +59,8 @@ class MdList(private val context: Context, id: Int) : TrackService(id) {
|
|||||||
if (remoteTrack.status != followStatus.int) {
|
if (remoteTrack.status != followStatus.int) {
|
||||||
if (mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus)) {
|
if (mdex.updateFollowStatus(MdUtil.getMangaId(track.tracking_url), followStatus)) {
|
||||||
remoteTrack.status = followStatus.int
|
remoteTrack.status = followStatus.int
|
||||||
|
} else {
|
||||||
|
track.status = remoteTrack.status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +84,25 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
private fun useLowQualityThumbnail() = false // sourcePreferences.getInt(SHOW_THUMBNAIL_PREF, 0) == LOW_QUALITY
|
private fun useLowQualityThumbnail() = false // sourcePreferences.getInt(SHOW_THUMBNAIL_PREF, 0) == LOW_QUALITY
|
||||||
|
|
||||||
|
private val apiMangaParser by lazy {
|
||||||
|
ApiMangaParser(baseHttpClient, mdLang.lang)
|
||||||
|
}
|
||||||
|
private val apiChapterParser by lazy {
|
||||||
|
ApiChapterParser()
|
||||||
|
}
|
||||||
|
private val followsHandler by lazy {
|
||||||
|
FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, mdList)
|
||||||
|
}
|
||||||
|
private val mangaHandler by lazy {
|
||||||
|
MangaHandler(baseHttpClient, headers, mdLang.lang, apiMangaParser, followsHandler)
|
||||||
|
}
|
||||||
|
private val similarHandler by lazy {
|
||||||
|
SimilarHandler(baseHttpClient, mdLang.lang)
|
||||||
|
}
|
||||||
|
private val mangaPlusHandler by lazy {
|
||||||
|
MangaPlusHandler(network.client)
|
||||||
|
}
|
||||||
|
|
||||||
/*override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
|
/*override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
|
||||||
urlImportFetchSearchManga(context, query) {
|
urlImportFetchSearchManga(context, query) {
|
||||||
importIdToMdId(query) {
|
importIdToMdId(query) {
|
||||||
@ -114,28 +133,28 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang, preferences.mangaDexForceLatestCovers().get()).fetchMangaDetailsObservable(manga, id)
|
return mangaHandler.fetchMangaDetailsObservable(manga, id, preferences.mangaDexForceLatestCovers().get())
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
|
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang, preferences.mangaDexForceLatestCovers().get()).getMangaDetails(manga, id)
|
return mangaHandler.getMangaDetails(manga, id, preferences.mangaDexForceLatestCovers().get())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang, preferences.mangaDexForceLatestCovers().get()).fetchChapterListObservable(manga)
|
return mangaHandler.fetchChapterListObservable(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> {
|
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang, preferences.mangaDexForceLatestCovers().get()).getChapterList(manga)
|
return mangaHandler.getChapterList(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
return if (chapter.scanlator == "MangaPlus") {
|
return if (chapter.scanlator == "MangaPlus") {
|
||||||
baseHttpClient.newCall(mangaPlusPageListRequest(chapter))
|
mangaPlusHandler.client.newCall(mangaPlusPageListRequest(chapter))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
val chapterId = ApiChapterParser().externalParse(response)
|
val chapterId = apiChapterParser.externalParse(response)
|
||||||
MangaPlusHandler(baseHttpClient).fetchPageList(chapterId)
|
mangaPlusHandler.fetchPageList(chapterId)
|
||||||
}
|
}
|
||||||
} else super.fetchPageList(chapter)
|
} else super.fetchPageList(chapter)
|
||||||
}
|
}
|
||||||
@ -146,7 +165,7 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
override fun fetchImage(page: Page): Observable<Response> {
|
override fun fetchImage(page: Page): Observable<Response> {
|
||||||
return if (page.imageUrl?.contains("mangaplus", true) == true) {
|
return if (page.imageUrl?.contains("mangaplus", true) == true) {
|
||||||
MangaPlusHandler(network.client).client.newCall(GET(page.imageUrl!!, headers))
|
mangaPlusHandler.client.newCall(GET(page.imageUrl!!, headers))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
} else super.fetchImage(page)
|
} else super.fetchImage(page)
|
||||||
}
|
}
|
||||||
@ -158,11 +177,11 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Response) {
|
override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Response) {
|
||||||
ApiMangaParser(baseHttpClient, mdLang.lang).parseIntoMetadata(metadata, input, emptyList())
|
apiMangaParser.parseIntoMetadata(metadata, input, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchFollows(page: Int): MangasPage {
|
override suspend fun fetchFollows(page: Int): MangasPage {
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).fetchFollows(page)
|
return followsHandler.fetchFollows(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val requiresLogin: Boolean = false
|
override val requiresLogin: Boolean = false
|
||||||
@ -199,6 +218,8 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
loginHelper.logout(MdUtil.getAuthHeaders(Headers.Builder().build(), preferences, mdList))
|
loginHelper.logout(MdUtil.getAuthHeaders(Headers.Builder().build(), preferences, mdList))
|
||||||
} catch (e: NoSessionException) {
|
} catch (e: NoSessionException) {
|
||||||
true
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.message?.equals("HTTP error 405") ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (result) {
|
return if (result) {
|
||||||
@ -208,30 +229,30 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchAllFollows(): List<Pair<SManga, MangaDexSearchMetadata>> {
|
override suspend fun fetchAllFollows(): List<Pair<SManga, MangaDexSearchMetadata>> {
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).fetchAllFollows()
|
return followsHandler.fetchAllFollows()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateReadingProgress(track: Track): Boolean {
|
suspend fun updateReadingProgress(track: Track): Boolean {
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).updateReadingProgress(track)
|
return followsHandler.updateReadingProgress(track)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateRating(track: Track): Boolean {
|
suspend fun updateRating(track: Track): Boolean {
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).updateRating(track)
|
return followsHandler.updateRating(track)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchTrackingInfo(url: String): Track {
|
override suspend fun fetchTrackingInfo(url: String): Track {
|
||||||
if (!isLogged()) {
|
if (!isLogged()) {
|
||||||
throw Exception("Not Logged in")
|
throw Exception("Not Logged in")
|
||||||
}
|
}
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).fetchTrackingInfo(url)
|
return followsHandler.fetchTrackingInfo(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getTrackingAndMangaInfo(track: Track): Pair<Track, MangaDexSearchMetadata?> {
|
suspend fun getTrackingAndMangaInfo(track: Track): Pair<Track, MangaDexSearchMetadata?> {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang).getTrackingInfo(track, useLowQualityThumbnail(), mdList)
|
return mangaHandler.getTrackingInfo(track, mdList)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean {
|
override suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean {
|
||||||
return FollowsHandler(baseHttpClient, headers, preferences, mdLang.lang, useLowQualityThumbnail(), mdList).updateFollowStatus(mangaID, followStatus)
|
return followsHandler.updateFollowStatus(mangaID, followStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFilterHeader(controller: BaseController<*>, onClick: () -> Unit): MangaDexFabHeaderAdapter {
|
override fun getFilterHeader(controller: BaseController<*>, onClick: () -> Unit): MangaDexFabHeaderAdapter {
|
||||||
@ -239,11 +260,11 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchRandomMangaUrl(): String {
|
override suspend fun fetchRandomMangaUrl(): String {
|
||||||
return MangaHandler(baseHttpClient, headers, mdLang.lang).fetchRandomMangaId()
|
return mangaHandler.fetchRandomMangaId()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getMangaSimilar(manga: MangaInfo): MangasPage {
|
suspend fun getMangaSimilar(manga: MangaInfo): MangasPage {
|
||||||
return SimilarHandler(baseHttpClient, mdLang.lang, preferences, useLowQualityThumbnail()).getSimilar(manga)
|
return similarHandler.getSimilar(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo remove when mangadex gets it cover api
|
// todo remove when mangadex gets it cover api
|
||||||
|
@ -4,7 +4,6 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
import androidx.recyclerview.widget.ConcatAdapter
|
||||||
@ -21,6 +20,7 @@ import eu.kanade.tachiyomi.widget.SimpleNavigationView
|
|||||||
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
||||||
import exh.savedsearches.EXHSavedSearch
|
import exh.savedsearches.EXHSavedSearch
|
||||||
import exh.source.getMainSource
|
import exh.source.getMainSource
|
||||||
|
import exh.util.under
|
||||||
|
|
||||||
class SourceFilterSheet(
|
class SourceFilterSheet(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
@ -155,22 +155,20 @@ class SourceFilterSheet(
|
|||||||
|
|
||||||
private fun getSavedSearchesChips(searches: List<EXHSavedSearch>): List<Chip> {
|
private fun getSavedSearchesChips(searches: List<EXHSavedSearch>): List<Chip> {
|
||||||
recycler.post {
|
recycler.post {
|
||||||
binding.saveSearchBtn.visibility = if (searches.size < MAX_SAVED_SEARCHES) View.VISIBLE else View.GONE
|
binding.saveSearchBtn.isVisible = searches.size under MAX_SAVED_SEARCHES
|
||||||
}
|
}
|
||||||
val chips: MutableList<Chip> = mutableListOf()
|
return searches.withIndex()
|
||||||
|
.sortedBy { it.value.name }
|
||||||
searches.withIndex().sortedBy { it.value.name }.forEach { (index, search) ->
|
.map { (index, search) ->
|
||||||
val chip = Chip(context).apply {
|
Chip(context).apply {
|
||||||
text = search.name
|
text = search.name
|
||||||
setOnClickListener { onSavedSearchClicked(index) }
|
setOnClickListener { onSavedSearchClicked(index) }
|
||||||
setOnLongClickListener {
|
setOnLongClickListener {
|
||||||
onSavedSearchDeleteClicked(index, search.name); true
|
onSavedSearchDeleteClicked(index, search.name); true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sortedBy { it.text.toString().toLowerCase() }
|
||||||
chips += chip
|
|
||||||
}
|
|
||||||
return chips.sortedBy { it.text.toString().toLowerCase() }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideFilterButton() {
|
fun hideFilterButton() {
|
||||||
|
@ -114,7 +114,11 @@ class ChaptersSettingsSheet(
|
|||||||
if (item is Item.DrawableSelection) {
|
if (item is Item.DrawableSelection) {
|
||||||
val scanlators = presenter.allChapterScanlators.toList()
|
val scanlators = presenter.allChapterScanlators.toList()
|
||||||
val filteredScanlators = presenter.manga.filtered_scanlators?.let { MdUtil.getScanlators(it) }
|
val filteredScanlators = presenter.manga.filtered_scanlators?.let { MdUtil.getScanlators(it) }
|
||||||
val preselected = if (filteredScanlators.isNullOrEmpty()) scanlators.mapIndexed { index, _ -> index }.toIntArray() else filteredScanlators.map { scanlators.indexOf(it) }.toIntArray()
|
val preselected = if (filteredScanlators.isNullOrEmpty()) {
|
||||||
|
scanlators.mapIndexed { index, _ -> index }
|
||||||
|
} else {
|
||||||
|
filteredScanlators.map { scanlators.indexOf(it) }
|
||||||
|
}.toIntArray()
|
||||||
|
|
||||||
MaterialDialog(context)
|
MaterialDialog(context)
|
||||||
.title(R.string.select_scanlators)
|
.title(R.string.select_scanlators)
|
||||||
|
@ -23,7 +23,6 @@ import okhttp3.Response
|
|||||||
import tachiyomi.source.model.ChapterInfo
|
import tachiyomi.source.model.ChapterInfo
|
||||||
import tachiyomi.source.model.MangaInfo
|
import tachiyomi.source.model.MangaInfo
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
|
class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
|
||||||
@ -138,7 +137,7 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
|
|||||||
cover = "https://coverapi.orell.dev/api/v1/mal/manga/$myAnimeListId/cover"
|
cover = "https://coverapi.orell.dev/api/v1/mal/manga/$myAnimeListId/cover"
|
||||||
}
|
}
|
||||||
if (cover == null) {
|
if (cover == null) {
|
||||||
cover = "https://coverapi.orell.dev/api/v1/mdaltimage/manga/$mdUuid/cover"
|
cover = MdUtil.formThumbUrl(mdUuid.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// val filteredChapters = filterChapterForChecking(networkApiManga)
|
// val filteredChapters = filterChapterForChecking(networkApiManga)
|
||||||
@ -155,8 +154,11 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
|
|||||||
|
|
||||||
// things that will go with the genre tags but aren't actually genre
|
// things that will go with the genre tags but aren't actually genre
|
||||||
val nonGenres = listOfNotNull(
|
val nonGenres = listOfNotNull(
|
||||||
networkManga.publicationDemographic?.let { RaisedTag("Demographic", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
networkManga.publicationDemographic
|
||||||
networkManga.contentRating?.let { RaisedTag("Content Rating", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
?.let { RaisedTag("Demographic", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
||||||
|
networkManga.contentRating
|
||||||
|
?.takeUnless { it == "safe" }
|
||||||
|
?.let { RaisedTag("Content Rating", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
||||||
)
|
)
|
||||||
|
|
||||||
val genres = nonGenres + networkManga.tags
|
val genres = nonGenres + networkManga.tags
|
||||||
@ -239,7 +241,7 @@ class ApiMangaParser(val client: OkHttpClient, private val lang: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun chapterListParse(chapterListResponse: List<ChapterResponse>, groupMap: Map<String, String>): List<ChapterInfo> {
|
fun chapterListParse(chapterListResponse: List<ChapterResponse>, groupMap: Map<String, String>): List<ChapterInfo> {
|
||||||
val now = Date().time
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
return chapterListResponse.asSequence()
|
return chapterListResponse.asSequence()
|
||||||
.map {
|
.map {
|
||||||
|
@ -16,6 +16,7 @@ import exh.md.handlers.serializers.MangaListResponse
|
|||||||
import exh.md.handlers.serializers.MangaResponse
|
import exh.md.handlers.serializers.MangaResponse
|
||||||
import exh.md.handlers.serializers.MangaStatusListResponse
|
import exh.md.handlers.serializers.MangaStatusListResponse
|
||||||
import exh.md.handlers.serializers.MangaStatusResponse
|
import exh.md.handlers.serializers.MangaStatusResponse
|
||||||
|
import exh.md.handlers.serializers.ResultResponse
|
||||||
import exh.md.handlers.serializers.UpdateReadingStatus
|
import exh.md.handlers.serializers.UpdateReadingStatus
|
||||||
import exh.md.utils.FollowStatus
|
import exh.md.utils.FollowStatus
|
||||||
import exh.md.utils.MdUtil
|
import exh.md.utils.MdUtil
|
||||||
@ -38,7 +39,6 @@ class FollowsHandler(
|
|||||||
val headers: Headers,
|
val headers: Headers,
|
||||||
val preferences: PreferencesHelper,
|
val preferences: PreferencesHelper,
|
||||||
private val lang: String,
|
private val lang: String,
|
||||||
private val useLowQualityCovers: Boolean,
|
|
||||||
private val mdList: MdList
|
private val mdList: MdList
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -78,8 +78,7 @@ class FollowsHandler(
|
|||||||
return response.map {
|
return response.map {
|
||||||
MdUtil.createMangaEntry(
|
MdUtil.createMangaEntry(
|
||||||
it,
|
it,
|
||||||
lang,
|
lang
|
||||||
useLowQualityCovers
|
|
||||||
).toSManga() to MangaDexSearchMetadata().apply {
|
).toSManga() to MangaDexSearchMetadata().apply {
|
||||||
followStatus = FollowStatus.fromDex(statuses[it.data.id]).int
|
followStatus = FollowStatus.fromDex(statuses[it.data.id]).int
|
||||||
}
|
}
|
||||||
@ -135,7 +134,9 @@ class FollowsHandler(
|
|||||||
jsonString.toRequestBody("application/json".toMediaType())
|
jsonString.toRequestBody("application/json".toMediaType())
|
||||||
)
|
)
|
||||||
).await()
|
).await()
|
||||||
postResult.isSuccessful
|
|
||||||
|
val body = postResult.parseAs<ResultResponse>(MdUtil.jsonParser)
|
||||||
|
body.result == "ok"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,18 +25,22 @@ import okhttp3.Request
|
|||||||
import rx.Observable
|
import rx.Observable
|
||||||
import tachiyomi.source.model.ChapterInfo
|
import tachiyomi.source.model.ChapterInfo
|
||||||
import tachiyomi.source.model.MangaInfo
|
import tachiyomi.source.model.MangaInfo
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
class MangaHandler(val client: OkHttpClient, val headers: Headers, private val lang: String, private val forceLatestCovers: Boolean = false) {
|
class MangaHandler(
|
||||||
|
val client: OkHttpClient,
|
||||||
|
val headers: Headers,
|
||||||
|
private val lang: String,
|
||||||
|
private val apiMangaParser: ApiMangaParser,
|
||||||
|
private val followsHandler: FollowsHandler
|
||||||
|
) {
|
||||||
|
|
||||||
suspend fun fetchMangaAndChapterDetails(manga: MangaInfo, sourceId: Long): Pair<MangaInfo, List<ChapterInfo>> {
|
suspend fun fetchMangaAndChapterDetails(manga: MangaInfo, sourceId: Long, forceLatestCovers: Boolean): Pair<MangaInfo, List<ChapterInfo>> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val response = client.newCall(mangaRequest(manga)).await()
|
val response = client.newCall(mangaRequest(manga)).await()
|
||||||
val covers = getCovers(manga, forceLatestCovers)
|
val covers = getCovers(manga, forceLatestCovers)
|
||||||
val parser = ApiMangaParser(client, lang)
|
|
||||||
|
|
||||||
parser.parseToManga(manga, response, covers, sourceId) to getChapterList(manga)
|
apiMangaParser.parseToManga(manga, response, covers, sourceId) to getChapterList(manga)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +49,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, private val l
|
|||||||
val covers = client.newCall(coverRequest(manga)).await().parseAs<ApiCovers>(MdUtil.jsonParser)
|
val covers = client.newCall(coverRequest(manga)).await().parseAs<ApiCovers>(MdUtil.jsonParser)
|
||||||
return covers.data.map { it.url }
|
return covers.data.map { it.url }
|
||||||
} else {*/
|
} else {*/
|
||||||
return emptyList<String>()
|
return emptyList()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,19 +57,19 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, private val l
|
|||||||
return withIOContext {
|
return withIOContext {
|
||||||
val request = GET(MdUtil.chapterUrl + urlChapterId)
|
val request = GET(MdUtil.chapterUrl + urlChapterId)
|
||||||
val response = client.newCall(request).await()
|
val response = client.newCall(request).await()
|
||||||
ApiMangaParser(client, lang).chapterParseForMangaId(response)
|
apiMangaParser.chapterParseForMangaId(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long): MangaInfo {
|
suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long, forceLatestCovers: Boolean): MangaInfo {
|
||||||
val response = withIOContext { client.newCall(mangaRequest(manga)).await() }
|
val response = withIOContext { client.newCall(mangaRequest(manga)).await() }
|
||||||
val covers = withIOContext { getCovers(manga, forceLatestCovers) }
|
val covers = withIOContext { getCovers(manga, forceLatestCovers) }
|
||||||
return ApiMangaParser(client, lang).parseToManga(manga, response, covers, sourceId)
|
return apiMangaParser.parseToManga(manga, response, covers, sourceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long): Observable<SManga> {
|
fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long, forceLatestCovers: Boolean): Observable<SManga> {
|
||||||
return runAsObservable({
|
return runAsObservable({
|
||||||
getMangaDetails(manga.toMangaInfo(), sourceId).toSManga()
|
getMangaDetails(manga.toMangaInfo(), sourceId, forceLatestCovers).toSManga()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +85,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, private val l
|
|||||||
|
|
||||||
val groupMap = getGroupMap(results)
|
val groupMap = getGroupMap(results)
|
||||||
|
|
||||||
ApiMangaParser(client, lang).chapterListParse(results, groupMap)
|
apiMangaParser.chapterListParse(results, groupMap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,29 +113,22 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, private val l
|
|||||||
suspend fun fetchRandomMangaId(): String {
|
suspend fun fetchRandomMangaId(): String {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val response = client.newCall(randomMangaRequest()).await()
|
val response = client.newCall(randomMangaRequest()).await()
|
||||||
ApiMangaParser(client, lang).randomMangaIdParse(response)
|
apiMangaParser.randomMangaIdParse(response)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getTrackingInfo(track: Track, useLowQualityCovers: Boolean, mdList: MdList): Pair<Track, MangaDexSearchMetadata?> {
|
suspend fun getTrackingInfo(track: Track, mdList: MdList): Pair<Track, MangaDexSearchMetadata?> {
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val metadata = async {
|
val metadata = async {
|
||||||
val mangaUrl = "/manga/" + MdUtil.getMangaId(track.tracking_url)
|
val mangaUrl = MdUtil.buildMangaUrl(MdUtil.getMangaId(track.tracking_url))
|
||||||
val manga = MangaInfo(mangaUrl, track.title)
|
val manga = MangaInfo(mangaUrl, track.title)
|
||||||
val response = client.newCall(mangaRequest(manga)).await()
|
val response = client.newCall(mangaRequest(manga)).await()
|
||||||
val metadata = MangaDexSearchMetadata()
|
val metadata = MangaDexSearchMetadata()
|
||||||
ApiMangaParser(client, lang).parseIntoMetadata(metadata, response, emptyList())
|
apiMangaParser.parseIntoMetadata(metadata, response, emptyList())
|
||||||
metadata
|
metadata
|
||||||
}
|
}
|
||||||
val remoteTrack = async {
|
val remoteTrack = async {
|
||||||
FollowsHandler(
|
followsHandler.fetchTrackingInfo(track.tracking_url)
|
||||||
client,
|
|
||||||
headers,
|
|
||||||
Injekt.get(),
|
|
||||||
lang,
|
|
||||||
useLowQualityCovers,
|
|
||||||
mdList
|
|
||||||
).fetchTrackingInfo(track.tracking_url)
|
|
||||||
}
|
}
|
||||||
remoteTrack.await() to null
|
remoteTrack.await() to null
|
||||||
}
|
}
|
||||||
|
@ -11,22 +11,28 @@ import okhttp3.OkHttpClient
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
class PageHandler(val client: OkHttpClient, val headers: Headers, private val dataSaver: Boolean) {
|
class PageHandler(
|
||||||
|
val client: OkHttpClient,
|
||||||
|
val headers: Headers,
|
||||||
|
private val dataSaver: Boolean,
|
||||||
|
private val apiChapterParser: ApiChapterParser,
|
||||||
|
private val mangaPlusHandler: MangaPlusHandler
|
||||||
|
) {
|
||||||
|
|
||||||
fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
if (chapter.scanlator.equals("MangaPlus")) {
|
if (chapter.scanlator.equals("MangaPlus")) {
|
||||||
return client.newCall(pageListRequest(chapter))
|
return client.newCall(pageListRequest(chapter))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
val chapterId = ApiChapterParser().externalParse(response)
|
val chapterId = apiChapterParser.externalParse(response)
|
||||||
MangaPlusHandler(client).fetchPageList(chapterId)
|
mangaPlusHandler.fetchPageList(chapterId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return client.newCall(pageListRequest(chapter))
|
return client.newCall(pageListRequest(chapter))
|
||||||
.asObservableSuccess()
|
.asObservableSuccess()
|
||||||
.map { response ->
|
.map { response ->
|
||||||
val host = MdUtil.atHomeUrlHostUrl("${MdUtil.atHomeUrl}/${MdUtil.getChapterId(chapter.url)}", client)
|
val host = MdUtil.atHomeUrlHostUrl("${MdUtil.atHomeUrl}/${MdUtil.getChapterId(chapter.url)}", client)
|
||||||
ApiChapterParser().pageListParse(response, host, dataSaver)
|
apiChapterParser.pageListParse(response, host, dataSaver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import rx.Observable
|
|||||||
/**
|
/**
|
||||||
* Returns the latest manga from the updates url since it actually respects the users settings
|
* Returns the latest manga from the updates url since it actually respects the users settings
|
||||||
*/
|
*/
|
||||||
class PopularHandler(val client: OkHttpClient, private val headers: Headers, private val lang: String, private val useLowQualityCovers: Boolean) {
|
class PopularHandler(val client: OkHttpClient, private val headers: Headers, private val lang: String) {
|
||||||
|
|
||||||
fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||||
return client.newCall(popularMangaRequest(page))
|
return client.newCall(popularMangaRequest(page))
|
||||||
@ -42,7 +42,7 @@ class PopularHandler(val client: OkHttpClient, private val headers: Headers, pri
|
|||||||
private fun popularMangaParse(response: Response): MangasPage {
|
private fun popularMangaParse(response: Response): MangasPage {
|
||||||
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
|
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
|
||||||
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
|
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
|
||||||
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang, useLowQualityCovers).toSManga() }
|
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang).toSManga() }
|
||||||
return MangasPage(mangaList, hasMoreResults)
|
return MangasPage(mangaList, hasMoreResults)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ import okhttp3.Request
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
|
||||||
class SearchHandler(val client: OkHttpClient, private val headers: Headers, val lang: String, val filterHandler: FilterHandler, private val useLowQualityCovers: Boolean) {
|
class SearchHandler(val client: OkHttpClient, private val headers: Headers, val lang: String, val filterHandler: FilterHandler, private val apiMangaParser: ApiMangaParser) {
|
||||||
|
|
||||||
fun fetchSearchManga(page: Int, query: String, filters: FilterList, sourceId: Long): Observable<MangasPage> {
|
fun fetchSearchManga(page: Int, query: String, filters: FilterList, sourceId: Long): Observable<MangasPage> {
|
||||||
return if (query.startsWith(PREFIX_ID_SEARCH)) {
|
return if (query.startsWith(PREFIX_ID_SEARCH)) {
|
||||||
@ -28,8 +28,8 @@ class SearchHandler(val client: OkHttpClient, private val headers: Headers, val
|
|||||||
.flatMap { response ->
|
.flatMap { response ->
|
||||||
runAsObservable({
|
runAsObservable({
|
||||||
val mangaResponse = response.parseAs<MangaResponse>(MdUtil.jsonParser)
|
val mangaResponse = response.parseAs<MangaResponse>(MdUtil.jsonParser)
|
||||||
val details = ApiMangaParser(client, lang)
|
val details = apiMangaParser
|
||||||
.parseToManga(MdUtil.createMangaEntry(mangaResponse, lang, useLowQualityCovers), response, emptyList(), sourceId).toSManga()
|
.parseToManga(MdUtil.createMangaEntry(mangaResponse, lang), response, emptyList(), sourceId).toSManga()
|
||||||
MangasPage(listOf(details), false)
|
MangasPage(listOf(details), false)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ class SearchHandler(val client: OkHttpClient, private val headers: Headers, val
|
|||||||
private fun searchMangaParse(response: Response): MangasPage {
|
private fun searchMangaParse(response: Response): MangasPage {
|
||||||
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
|
val mlResponse = response.parseAs<MangaListResponse>(MdUtil.jsonParser)
|
||||||
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
|
val hasMoreResults = mlResponse.limit + mlResponse.offset < mlResponse.total
|
||||||
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang, useLowQualityCovers).toSManga() }
|
val mangaList = mlResponse.results.map { MdUtil.createMangaEntry(it, lang).toSManga() }
|
||||||
return MangasPage(mangaList, hasMoreResults)
|
return MangasPage(mangaList, hasMoreResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package exh.md.handlers
|
package exh.md.handlers
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.await
|
import eu.kanade.tachiyomi.network.await
|
||||||
import eu.kanade.tachiyomi.network.parseAs
|
import eu.kanade.tachiyomi.network.parseAs
|
||||||
@ -15,7 +14,7 @@ import okhttp3.Request
|
|||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import tachiyomi.source.model.MangaInfo
|
import tachiyomi.source.model.MangaInfo
|
||||||
|
|
||||||
class SimilarHandler(val client: OkHttpClient, val lang: String, val preferences: PreferencesHelper, private val useLowQualityCovers: Boolean) {
|
class SimilarHandler(val client: OkHttpClient, val lang: String) {
|
||||||
|
|
||||||
suspend fun getSimilar(manga: MangaInfo): MangasPage {
|
suspend fun getSimilar(manga: MangaInfo): MangasPage {
|
||||||
val response = client.newCall(similarMangaRequest(manga)).await()
|
val response = client.newCall(similarMangaRequest(manga)).await()
|
||||||
@ -30,9 +29,9 @@ class SimilarHandler(val client: OkHttpClient, val lang: String, val preferences
|
|||||||
private fun similarMangaParse(response: Response): MangasPage {
|
private fun similarMangaParse(response: Response): MangasPage {
|
||||||
val mangaList = response.parseAs<SimilarMangaResponse>().matches.map {
|
val mangaList = response.parseAs<SimilarMangaResponse>().matches.map {
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
url = "/manga/" + it.id
|
url = MdUtil.buildMangaUrl(it.id)
|
||||||
title = MdUtil.cleanString(it.title[lang] ?: it.title["en"]!!)
|
title = MdUtil.cleanString(it.title[lang] ?: it.title["en"]!!)
|
||||||
thumbnail_url = "https://coverapi.orell.dev/api/v1/mdaltimage/manga/${it.id}/cover"
|
thumbnail_url = MdUtil.formThumbUrl(url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MangasPage(mangaList, false)
|
return MangasPage(mangaList, false)
|
||||||
|
@ -24,7 +24,7 @@ data class LoginBodyToken(val session: String, val refresh: String)
|
|||||||
* Response after logout
|
* Response after logout
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class LogoutResponse(val result: String)
|
data class ResultResponse(val result: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if session token is valid
|
* Check if session token is valid
|
||||||
|
@ -12,8 +12,8 @@ import exh.md.handlers.serializers.CheckTokenResponse
|
|||||||
import exh.md.handlers.serializers.LoginBodyToken
|
import exh.md.handlers.serializers.LoginBodyToken
|
||||||
import exh.md.handlers.serializers.LoginRequest
|
import exh.md.handlers.serializers.LoginRequest
|
||||||
import exh.md.handlers.serializers.LoginResponse
|
import exh.md.handlers.serializers.LoginResponse
|
||||||
import exh.md.handlers.serializers.LogoutResponse
|
|
||||||
import exh.md.handlers.serializers.RefreshTokenRequest
|
import exh.md.handlers.serializers.RefreshTokenRequest
|
||||||
|
import exh.md.handlers.serializers.ResultResponse
|
||||||
import exh.md.utils.MdUtil
|
import exh.md.utils.MdUtil
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
@ -78,7 +78,7 @@ class MangaDexLoginHelper(val client: OkHttpClient, val preferences: Preferences
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.code == 200 && loginResponse != null) {
|
if (response.code == 200 && loginResponse != null && loginResponse.result == "ok") {
|
||||||
LoginResult.Success(loginResponse.token)
|
LoginResult.Success(loginResponse.token)
|
||||||
} else {
|
} else {
|
||||||
LoginResult.Failure()
|
LoginResult.Failure()
|
||||||
@ -103,7 +103,7 @@ class MangaDexLoginHelper(val client: OkHttpClient, val preferences: Preferences
|
|||||||
|
|
||||||
suspend fun logout(authHeaders: Headers): Boolean {
|
suspend fun logout(authHeaders: Headers): Boolean {
|
||||||
val response = client.newCall(GET(MdUtil.logoutUrl, authHeaders, CacheControl.FORCE_NETWORK)).await()
|
val response = client.newCall(GET(MdUtil.logoutUrl, authHeaders, CacheControl.FORCE_NETWORK)).await()
|
||||||
val body = response.parseAs<LogoutResponse>(MdUtil.jsonParser)
|
val body = response.parseAs<ResultResponse>(MdUtil.jsonParser)
|
||||||
return body.result == "ok"
|
return body.result == "ok"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,13 +202,12 @@ class MdUtil {
|
|||||||
"(zh-Hant)",
|
"(zh-Hant)",
|
||||||
)
|
)
|
||||||
|
|
||||||
// guess the thumbnail url is .jpg this has a ~80% success rate
|
fun buildMangaUrl(mangaUuid: String): String {
|
||||||
fun formThumbUrl(mangaUrl: String, lowQuality: Boolean): String {
|
return "/manga/$mangaUuid"
|
||||||
var ext = ".jpg"
|
}
|
||||||
if (lowQuality) {
|
|
||||||
ext = ".thumb$ext"
|
fun formThumbUrl(mangaUrl: String): String {
|
||||||
}
|
return "https://coverapi.orell.dev/api/v1/mdaltimage/manga/${getMangaId(mangaUrl)}/cover"
|
||||||
return cdnUrl + "/images/manga/" + getMangaId(mangaUrl) + ext
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the ID from the manga url
|
// Get the ID from the manga url
|
||||||
@ -255,9 +254,9 @@ class MdUtil {
|
|||||||
return baseUrl + attr
|
return baseUrl + attr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getScanlators(scanlators: String?): List<String> {
|
fun getScanlators(scanlators: String?): Set<String> {
|
||||||
if (scanlators.isNullOrBlank()) return emptyList()
|
if (scanlators.isNullOrBlank()) return emptySet()
|
||||||
return scanlators.split(scanlatorSeparator).distinct()
|
return scanlators.split(scanlatorSeparator).toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getScanlatorString(scanlators: Set<String>): String {
|
fun getScanlatorString(scanlators: Set<String>): String {
|
||||||
@ -301,32 +300,27 @@ class MdUtil {
|
|||||||
fun parseDate(dateAsString: String): Long =
|
fun parseDate(dateAsString: String): Long =
|
||||||
dateFormatter.parse(dateAsString)?.time ?: 0
|
dateFormatter.parse(dateAsString)?.time ?: 0
|
||||||
|
|
||||||
fun createMangaEntry(json: MangaResponse, lang: String, lowQualityCovers: Boolean): MangaInfo {
|
fun createMangaEntry(json: MangaResponse, lang: String): MangaInfo {
|
||||||
val key = "/manga/" + json.data.id
|
val key = buildMangaUrl(json.data.id)
|
||||||
return MangaInfo(
|
return MangaInfo(
|
||||||
key = key,
|
key = key,
|
||||||
title = cleanString(json.data.attributes.title[lang] ?: json.data.attributes.title["en"]!!),
|
title = cleanString(json.data.attributes.title[lang] ?: json.data.attributes.title["en"]!!),
|
||||||
cover = formThumbUrl(key, lowQualityCovers)
|
cover = formThumbUrl(key)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sessionToken(preferences: PreferencesHelper, mdList: MdList) = preferences.trackToken(mdList).get().nullIfBlank()?.let {
|
fun getLoginBody(preferences: PreferencesHelper, mdList: MdList) = preferences.trackToken(mdList).get().nullIfBlank()?.let {
|
||||||
try {
|
try {
|
||||||
jsonParser.decodeFromString<LoginBodyToken>(it)
|
jsonParser.decodeFromString<LoginBodyToken>(it)
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
xLogD("Unable to load session token")
|
xLogD("Unable to load login body")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}?.session
|
}
|
||||||
|
|
||||||
fun refreshToken(preferences: PreferencesHelper, mdList: MdList) = preferences.trackToken(mdList).get().nullIfBlank()?.let {
|
fun sessionToken(preferences: PreferencesHelper, mdList: MdList) = getLoginBody(preferences, mdList)?.session
|
||||||
try {
|
|
||||||
jsonParser.decodeFromString<LoginBodyToken>(it)
|
fun refreshToken(preferences: PreferencesHelper, mdList: MdList) = getLoginBody(preferences, mdList)?.refresh
|
||||||
} catch (e: SerializationException) {
|
|
||||||
xLogD("Unable to load session token")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}?.refresh
|
|
||||||
|
|
||||||
fun updateLoginToken(token: LoginBodyToken, preferences: PreferencesHelper, mdList: MdList) {
|
fun updateLoginToken(token: LoginBodyToken, preferences: PreferencesHelper, mdList: MdList) {
|
||||||
preferences.trackToken(mdList).set(jsonParser.encodeToString(token))
|
preferences.trackToken(mdList).set(jsonParser.encodeToString(token))
|
||||||
|
@ -44,7 +44,7 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() {
|
|||||||
// var maxChapterNumber: Int? = null
|
// var maxChapterNumber: Int? = null
|
||||||
|
|
||||||
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
|
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
|
||||||
val key = mdUuid?.let { "/manga/$it" }
|
val key = mdUuid?.let { MdUtil.buildMangaUrl(it) }
|
||||||
|
|
||||||
val title = title
|
val title = title
|
||||||
|
|
||||||
|
@ -146,11 +146,11 @@ abstract class RaisedSearchMetadata {
|
|||||||
const val TAG_TYPE_VIRTUAL = -2
|
const val TAG_TYPE_VIRTUAL = -2
|
||||||
|
|
||||||
fun MutableList<RaisedTag>.toGenreString() =
|
fun MutableList<RaisedTag>.toGenreString() =
|
||||||
(this).filter { it.type != TAG_TYPE_VIRTUAL }
|
this.filter { it.type != TAG_TYPE_VIRTUAL }
|
||||||
.joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
|
.joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
|
||||||
|
|
||||||
fun MutableList<RaisedTag>.toGenreList() =
|
fun MutableList<RaisedTag>.toGenreList() =
|
||||||
(this).filter { it.type != TAG_TYPE_VIRTUAL }
|
this.filter { it.type != TAG_TYPE_VIRTUAL }
|
||||||
.map { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
|
.map { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
|
||||||
|
|
||||||
private val module = SerializersModule {
|
private val module = SerializersModule {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user