Added support for OriginLanguage filtering in latest and browse, fixed porn chapters too (#8993)
* Added originalLanguage filters * add content preference in chapter list * Remove zero width space * change default originLanguagepref to false * made originLang work in browse too * actually fixed nsfw chapter list maybe * added new MangaDex icons * filter all chapters with external links * change browse sort to followCount * add origin language filtering to browse and properly set default filter values * add all contentRating queries to the actual chapterlist request no matter what
| @ -6,7 +6,7 @@ ext { | |||||||
|     extName = 'MangaDex' |     extName = 'MangaDex' | ||||||
|     pkgNameSuffix = 'all.mangadex' |     pkgNameSuffix = 'all.mangadex' | ||||||
|     extClass = '.MangaDexFactory' |     extClass = '.MangaDexFactory' | ||||||
|     extVersionCode = 130 |     extVersionCode = 131 | ||||||
|     libVersion = '1.2' |     libVersion = '1.2' | ||||||
|     containsNsfw = true |     containsNsfw = true | ||||||
| } | } | ||||||
|  | |||||||
| Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.0 KiB | 
| Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.7 KiB | 
| Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 8.6 KiB | 
| Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB | 
| Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 24 KiB | 
| @ -80,4 +80,22 @@ object MDConstants { | |||||||
|     fun getContentRatingPornographicPrefKey(dexLang: String): String { |     fun getContentRatingPornographicPrefKey(dexLang: String): String { | ||||||
|         return "${contentRatingPornographicPref}_$dexLang" |         return "${contentRatingPornographicPref}_$dexLang" | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     private const val originalLanguageJapanesePref = "originalLanguageJapanese" | ||||||
|  | 
 | ||||||
|  |     fun getOriginalLanguageJapanesePref(dexLang: String): String { | ||||||
|  |         return "${originalLanguageJapanesePref}_$dexLang" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private const val originalLanguageChinesePref = "originalLanguageChinese" | ||||||
|  | 
 | ||||||
|  |     fun getOriginalLanguageChinesePref(dexLang: String): String { | ||||||
|  |         return "${originalLanguageChinesePref}_$dexLang" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private const val originalLanguageKoreanPref = "originalLanguageKorean" | ||||||
|  | 
 | ||||||
|  |     fun getOriginalLanguageKoreanPref(dexLang: String): String { | ||||||
|  |         return "${originalLanguageKoreanPref}_$dexLang" | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -61,7 +61,7 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
| 
 | 
 | ||||||
|     override fun popularMangaRequest(page: Int): Request { |     override fun popularMangaRequest(page: Int): Request { | ||||||
|         val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder().apply { |         val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder().apply { | ||||||
|             addQueryParameter("order[updatedAt]", "desc") |             addQueryParameter("order[followedCount]", "desc") | ||||||
|             addQueryParameter("limit", MDConstants.mangaLimit.toString()) |             addQueryParameter("limit", MDConstants.mangaLimit.toString()) | ||||||
|             addQueryParameter("offset", helper.getMangaListOffset(page)) |             addQueryParameter("offset", helper.getMangaListOffset(page)) | ||||||
|             addQueryParameter("includes[]", MDConstants.coverArt) |             addQueryParameter("includes[]", MDConstants.coverArt) | ||||||
| @ -89,6 +89,29 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
|             ) { |             ) { | ||||||
|                 addQueryParameter("contentRating[]", "pornographic") |                 addQueryParameter("contentRating[]", "pornographic") | ||||||
|             } |             } | ||||||
|  |             if (preferences.getBoolean( | ||||||
|  |                     MDConstants.getOriginalLanguageJapanesePref(dexLang), | ||||||
|  |                     false | ||||||
|  |                 ) | ||||||
|  |             ) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "ja") | ||||||
|  |             } | ||||||
|  |             // dex has zh and zh-hk for chinese manhua | ||||||
|  |             if (preferences.getBoolean( | ||||||
|  |                     MDConstants.getOriginalLanguageChinesePref(dexLang), | ||||||
|  |                     false | ||||||
|  |                 ) | ||||||
|  |             ) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "zh") | ||||||
|  |                 addQueryParameter("originalLanguage[]", "zh-hk") | ||||||
|  |             } | ||||||
|  |             if (preferences.getBoolean( | ||||||
|  |                     MDConstants.getOriginalLanguageKoreanPref(dexLang), | ||||||
|  |                     false | ||||||
|  |                 ) | ||||||
|  |             ) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "ko") | ||||||
|  |             } | ||||||
|         }.build().toUrl().toString() |         }.build().toUrl().toString() | ||||||
|         return GET( |         return GET( | ||||||
|             url = url, |             url = url, | ||||||
| @ -144,6 +167,17 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
|             if (preferences.getBoolean(MDConstants.getContentRatingPornographicPrefKey(dexLang), false)) { |             if (preferences.getBoolean(MDConstants.getContentRatingPornographicPrefKey(dexLang), false)) { | ||||||
|                 addQueryParameter("contentRating[]", "pornographic") |                 addQueryParameter("contentRating[]", "pornographic") | ||||||
|             } |             } | ||||||
|  |             if (preferences.getBoolean(MDConstants.getOriginalLanguageJapanesePref(dexLang), false)) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "ja") | ||||||
|  |             } | ||||||
|  |             // dex has zh and zh-hk for chinese manhua | ||||||
|  |             if (preferences.getBoolean(MDConstants.getOriginalLanguageChinesePref(dexLang), false)) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "zh") | ||||||
|  |                 addQueryParameter("originalLanguage[]", "zh-hk") | ||||||
|  |             } | ||||||
|  |             if (preferences.getBoolean(MDConstants.getOriginalLanguageKoreanPref(dexLang), false)) { | ||||||
|  |                 addQueryParameter("originalLanguage[]", "ko") | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             mangaIds.forEach { id -> |             mangaIds.forEach { id -> | ||||||
|                 addQueryParameter("ids[]", id) |                 addQueryParameter("ids[]", id) | ||||||
| @ -308,15 +342,16 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
|     /** |     /** | ||||||
|      * Required because api is paged |      * Required because api is paged | ||||||
|      */ |      */ | ||||||
|     private fun actualChapterListRequest(mangaId: String, offset: Int) = |     private fun actualChapterListRequest(mangaId: String, offset: Int): Request { | ||||||
|         GET( |         val url = helper.getChapterEndpoint(mangaId, offset, dexLang).toHttpUrlOrNull()!!.newBuilder().apply { | ||||||
|             url = helper.getChapterEndpoint(mangaId, offset, dexLang), |             addQueryParameter("contentRating[]", "safe") | ||||||
|             headers = headers, |             addQueryParameter("contentRating[]", "suggestive") | ||||||
|             cache = CacheControl.FORCE_NETWORK |             addQueryParameter("contentRating[]", "erotica") | ||||||
|         ) |             addQueryParameter("contentRating[]", "pornographic") | ||||||
| 
 |         }.build().toString() | ||||||
|  |         return GET(url, headers = headers, cache = CacheControl.FORCE_NETWORK) | ||||||
|  |     } | ||||||
|     override fun chapterListParse(response: Response): List<SChapter> { |     override fun chapterListParse(response: Response): List<SChapter> { | ||||||
| 
 |  | ||||||
|         if (response.isSuccessful.not()) { |         if (response.isSuccessful.not()) { | ||||||
|             throw Exception("HTTP ${response.code}") |             throw Exception("HTTP ${response.code}") | ||||||
|         } |         } | ||||||
| @ -350,15 +385,15 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
| 
 | 
 | ||||||
|             val now = Date().time |             val now = Date().time | ||||||
| 
 | 
 | ||||||
|             return chapterListResults.map { helper.createChapter(it) } |             return chapterListResults.mapNotNull { helper.createChapter(it) } | ||||||
|                 .filter { it.date_upload <= now && "MangaPlus" != it.scanlator |                 .filter { | ||||||
|                     && "Comikey" != it.scanlator} |                     it.date_upload <= now | ||||||
|  |                 } | ||||||
|         } catch (e: Exception) { |         } catch (e: Exception) { | ||||||
|             Log.e("MangaDex", "error parsing chapter list", e) |             Log.e("MangaDex", "error parsing chapter list", e) | ||||||
|             throw(e) |             throw(e) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     override fun pageListRequest(chapter: SChapter): Request { |     override fun pageListRequest(chapter: SChapter): Request { | ||||||
|         if (!helper.containsUuid(chapter.url)) { |         if (!helper.containsUuid(chapter.url)) { | ||||||
|             throw Exception("Migrate this manga from MangaDex to MangaDex to update it") |             throw Exception("Migrate this manga from MangaDex to MangaDex to update it") | ||||||
| @ -515,6 +550,48 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         val originalLanguageJapanesePref = SwitchPreferenceCompat(screen.context).apply { | ||||||
|  |             key = MDConstants.getOriginalLanguageJapanesePref(dexLang) | ||||||
|  |             title = "Japanese" | ||||||
|  |             summary = "If enabled, only shows content that was originally published in Japanese in both latest and browse" | ||||||
|  |             setDefaultValue(false) | ||||||
|  | 
 | ||||||
|  |             setOnPreferenceChangeListener { _, newValue -> | ||||||
|  |                 val checkValue = newValue as Boolean | ||||||
|  |                 preferences.edit() | ||||||
|  |                     .putBoolean(MDConstants.getOriginalLanguageJapanesePref(dexLang), checkValue) | ||||||
|  |                     .commit() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         val originalLanguageChinesePref = SwitchPreferenceCompat(screen.context).apply { | ||||||
|  |             key = MDConstants.getOriginalLanguageChinesePref(dexLang) | ||||||
|  |             title = "Chinese" | ||||||
|  |             summary = "If enabled, only shows content that was originally published in Chinese in both latest and browse" | ||||||
|  |             setDefaultValue(false) | ||||||
|  | 
 | ||||||
|  |             setOnPreferenceChangeListener { _, newValue -> | ||||||
|  |                 val checkValue = newValue as Boolean | ||||||
|  |                 preferences.edit() | ||||||
|  |                     .putBoolean(MDConstants.getOriginalLanguageChinesePref(dexLang), checkValue) | ||||||
|  |                     .commit() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         val originalLanguageKoreanPref = SwitchPreferenceCompat(screen.context).apply { | ||||||
|  |             key = MDConstants.getOriginalLanguageKoreanPref(dexLang) | ||||||
|  |             title = "Korean" | ||||||
|  |             summary = "If enabled, only shows content that was originally published in Korean in both latest and browse" | ||||||
|  |             setDefaultValue(false) | ||||||
|  | 
 | ||||||
|  |             setOnPreferenceChangeListener { _, newValue -> | ||||||
|  |                 val checkValue = newValue as Boolean | ||||||
|  |                 preferences.edit() | ||||||
|  |                     .putBoolean(MDConstants.getOriginalLanguageKoreanPref(dexLang), checkValue) | ||||||
|  |                     .commit() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         screen.addPreference(coverQualityPref) |         screen.addPreference(coverQualityPref) | ||||||
|         screen.addPreference(dataSaverPref) |         screen.addPreference(dataSaverPref) | ||||||
|         screen.addPreference(standardHttpsPortPref) |         screen.addPreference(standardHttpsPortPref) | ||||||
| @ -522,6 +599,9 @@ abstract class MangaDex(override val lang: String, val dexLang: String) : | |||||||
|         screen.addPreference(contentRatingSuggestivePref) |         screen.addPreference(contentRatingSuggestivePref) | ||||||
|         screen.addPreference(contentRatingEroticaPref) |         screen.addPreference(contentRatingEroticaPref) | ||||||
|         screen.addPreference(contentRatingPornographicPref) |         screen.addPreference(contentRatingPornographicPref) | ||||||
|  |         screen.addPreference(originalLanguageJapanesePref) | ||||||
|  |         screen.addPreference(originalLanguageChinesePref) | ||||||
|  |         screen.addPreference(originalLanguageKoreanPref) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun getFilterList(): FilterList = |     override fun getFilterList(): FilterList = | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ class MangaDexFilters { | |||||||
|     internal fun getMDFilterList(preferences: SharedPreferences, dexLang: String): FilterList { |     internal fun getMDFilterList(preferences: SharedPreferences, dexLang: String): FilterList { | ||||||
| 
 | 
 | ||||||
|         return FilterList( |         return FilterList( | ||||||
|             OriginalLanguageList(getOriginalLanguage()), |             OriginalLanguageList(getOriginalLanguage(preferences, dexLang)), | ||||||
|             ContentRatingList(getContentRating(preferences, dexLang)), |             ContentRatingList(getContentRating(preferences, dexLang)), | ||||||
|             DemographicList(getDemographics()), |             DemographicList(getDemographics()), | ||||||
|             StatusList(getStatus()), |             StatusList(getStatus()), | ||||||
| @ -24,8 +24,10 @@ class MangaDexFilters { | |||||||
| 
 | 
 | ||||||
|     private fun getContentRating(preferences: SharedPreferences, dexLang: String) = listOf( |     private fun getContentRating(preferences: SharedPreferences, dexLang: String) = listOf( | ||||||
|         ContentRating("Safe").apply { |         ContentRating("Safe").apply { | ||||||
|             state = |             state = preferences.getBoolean( | ||||||
|                 preferences.getBoolean(MDConstants.getContentRatingSafePrefKey(dexLang), true) |                 MDConstants.getContentRatingSafePrefKey(dexLang), | ||||||
|  |                 true | ||||||
|  |             ) | ||||||
|         }, |         }, | ||||||
|         ContentRating("Suggestive").apply { |         ContentRating("Suggestive").apply { | ||||||
|             state = preferences.getBoolean( |             state = preferences.getBoolean( | ||||||
| @ -78,10 +80,25 @@ class MangaDexFilters { | |||||||
|     private class OriginalLanguageList(originalLanguage: List<OriginalLanguage>) : |     private class OriginalLanguageList(originalLanguage: List<OriginalLanguage>) : | ||||||
|         Filter.Group<OriginalLanguage>("Original language", originalLanguage) |         Filter.Group<OriginalLanguage>("Original language", originalLanguage) | ||||||
| 
 | 
 | ||||||
|     private fun getOriginalLanguage() = listOf( |     private fun getOriginalLanguage(preferences: SharedPreferences, dexLang: String) = listOf( | ||||||
|         OriginalLanguage("Japanese (Manga)", "ja"), |         OriginalLanguage("Japanese (Manga)", "ja").apply { | ||||||
|         OriginalLanguage("Chinese (Manhua)", "zh"), |             state = preferences.getBoolean( | ||||||
|         OriginalLanguage("Korean (Manhwa)", "ko"), |                 MDConstants.getOriginalLanguageJapanesePref(dexLang), | ||||||
|  |                 false | ||||||
|  |             ) | ||||||
|  |         }, | ||||||
|  |         OriginalLanguage("Chinese (Manhua)", "zh").apply { | ||||||
|  |             state = preferences.getBoolean( | ||||||
|  |                 MDConstants.getOriginalLanguageChinesePref(dexLang), | ||||||
|  |                 false | ||||||
|  |             ) | ||||||
|  |         }, | ||||||
|  |         OriginalLanguage("Korean (Manhwa)", "ko").apply { | ||||||
|  |             state = preferences.getBoolean( | ||||||
|  |                 MDConstants.getOriginalLanguageKoreanPref(dexLang), | ||||||
|  |                 false | ||||||
|  |             ) | ||||||
|  |         }, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     internal class Tag(val id: String, name: String) : Filter.TriState(name) |     internal class Tag(val id: String, name: String) : Filter.TriState(name) | ||||||
|  | |||||||
| @ -172,7 +172,7 @@ class MangaDexHelper() { | |||||||
|             title = cleanString(mangaDto.data.attributes.title.asMdMap()["en"] ?: "") |             title = cleanString(mangaDto.data.attributes.title.asMdMap()["en"] ?: "") | ||||||
| 
 | 
 | ||||||
|             coverFileName?.let { |             coverFileName?.let { | ||||||
|                 thumbnail_url = when(coverSuffix != null && coverSuffix != "") { |                 thumbnail_url = when (coverSuffix != null && coverSuffix != "") { | ||||||
|                     true -> "${MDConstants.cdnUrl}/covers/${mangaDto.data.id}/$coverFileName$coverSuffix" |                     true -> "${MDConstants.cdnUrl}/covers/${mangaDto.data.id}/$coverFileName$coverSuffix" | ||||||
|                     else -> "${MDConstants.cdnUrl}/covers/${mangaDto.data.id}/$coverFileName" |                     else -> "${MDConstants.cdnUrl}/covers/${mangaDto.data.id}/$coverFileName" | ||||||
|                 } |                 } | ||||||
| @ -248,7 +248,7 @@ class MangaDexHelper() { | |||||||
|     /** |     /** | ||||||
|      * create the SChapter from json |      * create the SChapter from json | ||||||
|      */ |      */ | ||||||
|     fun createChapter(chapterDto: ChapterDto): SChapter { |     fun createChapter(chapterDto: ChapterDto): SChapter? { | ||||||
|         try { |         try { | ||||||
|             val data = chapterDto.data |             val data = chapterDto.data | ||||||
|             val attr = data.attributes |             val attr = data.attributes | ||||||
| @ -287,6 +287,10 @@ class MangaDexHelper() { | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             if (attr.externalUrl != null && attr.data.isEmpty()) { | ||||||
|  |                 return null | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // if volume, chapter and title is empty its a oneshot |             // if volume, chapter and title is empty its a oneshot | ||||||
|             if (chapterName.isEmpty()) { |             if (chapterName.isEmpty()) { | ||||||
|                 chapterName.add("Oneshot") |                 chapterName.add("Oneshot") | ||||||
|  | |||||||
| @ -33,4 +33,5 @@ data class ChapterAttributesDto( | |||||||
|     val data: List<String>, |     val data: List<String>, | ||||||
|     val dataSaver: List<String>, |     val dataSaver: List<String>, | ||||||
|     val hash: String, |     val hash: String, | ||||||
|  |     val externalUrl: String?, | ||||||
| ) | ) | ||||||
|  | |||||||
 loocool2
						loocool2