Refactor and cleanup a bunch of code

This commit is contained in:
Jobobby04 2020-10-27 19:52:32 -04:00
parent 114fb723dc
commit 9cba544ffd
62 changed files with 521 additions and 1384 deletions

View File

@ -39,7 +39,7 @@ import exh.eh.EHentaiThrottleManager
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.savedsearches.JsonSavedSearch import exh.savedsearches.JsonSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString

View File

@ -40,7 +40,7 @@ import exh.MERGED_SOURCE_ID
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.asObservable import exh.util.asObservable
import exh.util.await import exh.util.await
import exh.util.awaitSingle import exh.util.awaitSingle
@ -425,7 +425,7 @@ class LibraryUpdateService(
* @return a pair of the inserted and removed chapters. * @return a pair of the inserted and removed chapters.
*/ */
fun updateManga(manga: Manga): Observable<Pair<List<Chapter>, List<Chapter>>> { fun updateManga(manga: Manga): Observable<Pair<List<Chapter>, List<Chapter>>> {
val source = sourceManager.getOrStub(manga.source) val source = sourceManager.getOrStub(manga.source).getMainSource()
// Update manga details metadata in the background // Update manga details metadata in the background
if (preferences.autoUpdateMetadata()) { if (preferences.autoUpdateMetadata()) {
@ -447,8 +447,14 @@ class LibraryUpdateService(
.subscribe() .subscribe()
} }
return (
/* SY --> */ if (source is MergedSource) runBlocking { source.fetchChaptersAndSync(manga, false).asObservable() }
else /* SY <-- */ source.fetchChapterList(manga)
.map { syncChaptersWithSource(db, it, manga, source) }
// SY --> // SY -->
if (source.getMainSource() is MangaDex && trackManager.mdList.isLogged) { )
.doOnNext {
if (source is MangaDex && trackManager.mdList.isLogged) {
try { try {
val tracks = db.getTracks(manga).executeAsBlocking() val tracks = db.getTracks(manga).executeAsBlocking()
if (tracks.isEmpty() || tracks.all { it.sync_id != TrackManager.MDLIST }) { if (tracks.isEmpty() || tracks.all { it.sync_id != TrackManager.MDLIST }) {
@ -460,23 +466,6 @@ class LibraryUpdateService(
XLog.e(e) XLog.e(e)
} }
} }
// SY <--
return (
/* SY --> */ if (source is MergedSource) runBlocking { source.fetchChaptersAndSync(manga, false).asObservable() }
else /* SY <-- */ source.fetchChapterList(manga)
.map { syncChaptersWithSource(db, it, manga, source) }
// SY -->
)
.doOnNext {
if (source.getMainSource() is MangaDex) {
val tracks = db.getTracks(manga).executeAsBlocking()
if (tracks.isEmpty() || tracks.all { it.sync_id != TrackManager.MDLIST }) {
var track = trackManager.mdList.createInitialTracker(manga)
track = runBlocking { trackManager.mdList.refresh(track).awaitSingle() }
db.insertTrack(track).executeAsBlocking()
}
}
} }
// SY <-- // SY <--
} }
@ -548,7 +537,7 @@ class LibraryUpdateService(
// filter all follows from Mangadex and only add reading or rereading manga to library // filter all follows from Mangadex and only add reading or rereading manga to library
private fun syncFollows(): Observable<LibraryManga> { private fun syncFollows(): Observable<LibraryManga> {
val count = AtomicInteger(0) val count = AtomicInteger(0)
val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager)!! val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager) ?: return Observable.empty()
return mangaDex.fetchAllFollows(true) return mangaDex.fetchAllFollows(true)
.asObservable() .asObservable()
.map { listManga -> .map { listManga ->
@ -582,7 +571,7 @@ class LibraryUpdateService(
.doOnCompleted { .doOnCompleted {
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }
.map { LibraryManga() } .flatMap { Observable.empty() }
} }
/** /**
@ -616,7 +605,7 @@ class LibraryUpdateService(
.doOnCompleted { .doOnCompleted {
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
} }
.map { LibraryManga() } .flatMap { Observable.empty() }
} }
// SY <-- // SY <--

View File

@ -26,7 +26,7 @@ import exh.eh.EHTags
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.eh.EHentaiUpdateWorkerConstants import exh.eh.EHentaiUpdateWorkerConstants
import exh.eh.GalleryEntry import exh.eh.GalleryEntry
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.EH_GENRE_NAMESPACE import exh.metadata.metadata.EHentaiSearchMetadata.Companion.EH_GENRE_NAMESPACE
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT
@ -35,7 +35,6 @@ import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_WEAK
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.toGenreString import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.toGenreString
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.metadata.parseHumanReadableByteCount
import exh.ui.login.LoginController import exh.ui.login.LoginController
import exh.ui.metadata.adapters.EHentaiDescriptionAdapter import exh.ui.metadata.adapters.EHentaiDescriptionAdapter
import exh.util.UriFilter import exh.util.UriFilter
@ -239,7 +238,7 @@ class EHentai(
private fun getDateTag(element: Element?): Long? { private fun getDateTag(element: Element?): Long? {
val text = element?.text()?.nullIfBlank() val text = element?.text()?.nullIfBlank()
return if (text != null) { return if (text != null) {
val date = EX_DATE_FORMAT.parse(text) val date = MetadataUtil.EX_DATE_FORMAT.parse(text)
date?.time date?.time
} else null } else null
} }
@ -272,8 +271,8 @@ class EHentai(
/** /**
* Parse a list of galleries * Parse a list of galleries
*/ */
fun genericMangaParse(response: Response) = extendedGenericMangaParse(response.asJsoup()).let { private fun genericMangaParse(response: Response) = extendedGenericMangaParse(response.asJsoup()).let { mangaFromSource ->
MetadataMangasPage(it.first.map { it.manga }, it.second, it.first.map { it.metadata }) MetadataMangasPage(mangaFromSource.first.map { it.manga }, mangaFromSource.second, mangaFromSource.first.map { it.metadata })
} }
override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {} override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {}
@ -331,7 +330,7 @@ class EHentai(
url = EHentaiSearchMetadata.normalizeUrl(d.location()) url = EHentaiSearchMetadata.normalizeUrl(d.location())
name = "v1: " + d.selectFirst("#gn").text() name = "v1: " + d.selectFirst("#gn").text()
chapter_number = 1f chapter_number = 1f
date_upload = EX_DATE_FORMAT.parse( date_upload = MetadataUtil.EX_DATE_FORMAT.parse(
d.select("#gdd .gdt1").find { el -> d.select("#gdd .gdt1").find { el ->
el.text().toLowerCase() == "posted:" el.text().toLowerCase() == "posted:"
}!!.nextElementSibling().text() }!!.nextElementSibling().text()
@ -348,7 +347,7 @@ class EHentai(
this.url = EHentaiSearchMetadata.normalizeUrl(link) this.url = EHentaiSearchMetadata.normalizeUrl(link)
this.name = "v${index + 2}: $name" this.name = "v${index + 2}: $name"
this.chapter_number = index + 2f this.chapter_number = index + 2f
this.date_upload = EX_DATE_FORMAT.parse(posted)!!.time this.date_upload = MetadataUtil.EX_DATE_FORMAT.parse(posted)!!.time
} }
}.reversed() + self }.reversed() + self
} }
@ -455,7 +454,7 @@ class EHentai(
private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true): Request { private fun exGet(url: String, page: Int? = null, additionalHeaders: Headers? = null, cache: Boolean = true): Request {
return GET( return GET(
page?.let { page?.let {
addParam(url, "page", Integer.toString(page - 1)) addParam(url, "page", (page - 1).toString())
} ?: url, } ?: url,
additionalHeaders?.let { additionalHeadersNotNull -> additionalHeaders?.let { additionalHeadersNotNull ->
val headers = headers.newBuilder() val headers = headers.newBuilder()
@ -524,39 +523,35 @@ class EHentai(
override fun parseIntoMetadata(metadata: EHentaiSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: EHentaiSearchMetadata, input: Document) {
with(metadata) { with(metadata) {
with(input) { with(input) {
val url = input.location() val url = location()
gId = EHentaiSearchMetadata.galleryId(url) gId = EHentaiSearchMetadata.galleryId(url)
gToken = EHentaiSearchMetadata.galleryToken(url) gToken = EHentaiSearchMetadata.galleryToken(url)
exh = this@EHentai.exh exh = this@EHentai.exh
title = select("#gn").text().nullIfBlank()?.trim() title = select("#gn").text().trimOrNull()
altTitle = select("#gj").text().nullIfBlank()?.trim() altTitle = select("#gj").text().trimOrNull()
thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let { thumbnailUrl = select("#gd1 div").attr("style").nullIfBlank()?.let {
it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')')) it.substring(it.indexOf('(') + 1 until it.lastIndexOf(')'))
} }
genre = select(".cs") genre = select(".cs")
.attr("onclick") .attr("onclick")
.nullIfBlank() .trimOrNull()
?.trim()
?.substringAfterLast('/') ?.substringAfterLast('/')
?.removeSuffix("'") ?.removeSuffix("'")
uploader = select("#gdn").text().nullIfBlank()?.trim() uploader = select("#gdn").text().trimOrNull()
// Parse the table // Parse the table
select("#gdd tr").forEach { select("#gdd tr").forEach {
val left = it.select(".gdt1").text().nullIfBlank()?.trim() val left = it.select(".gdt1").text().trimOrNull()
val rightElement = it.selectFirst(".gdt2") val rightElement = it.selectFirst(".gdt2")
val right = rightElement.text().nullIfBlank()?.trim() val right = rightElement.text().trimOrNull()
if (left != null && right != null) { if (left != null && right != null) {
ignore { ignore {
when ( when (left.removeSuffix(":").toLowerCase()) {
left.removeSuffix(":") "posted" -> datePosted = MetadataUtil.EX_DATE_FORMAT.parse(right)!!.time
.toLowerCase()
) {
"posted" -> datePosted = EX_DATE_FORMAT.parse(right)!!.time
// Example gallery with parent: https://e-hentai.org/g/1390451/7f181c2426/ // Example gallery with parent: https://e-hentai.org/g/1390451/7f181c2426/
// Example JP gallery: https://exhentai.org/g/1375385/03519d541b/ // Example JP gallery: https://exhentai.org/g/1375385/03519d541b/
// Parent is older variation of the gallery // Parent is older variation of the gallery
@ -565,12 +560,12 @@ class EHentai(
} else null } else null
"visible" -> visible = right.nullIfBlank() "visible" -> visible = right.nullIfBlank()
"language" -> { "language" -> {
language = right.removeSuffix(TR_SUFFIX).trim().nullIfBlank() language = right.removeSuffix(TR_SUFFIX).trimOrNull()
translated = right.endsWith(TR_SUFFIX, true) translated = right.endsWith(TR_SUFFIX, true)
} }
"file size" -> size = parseHumanReadableByteCount(right)?.toLong() "file size" -> size = MetadataUtil.parseHumanReadableByteCount(right)?.toLong()
"length" -> length = right.removeSuffix("pages").trim().nullIfBlank()?.toInt() "length" -> length = right.removeSuffix("pages").trimOrNull()?.toInt()
"favorited" -> favorites = right.removeSuffix("times").trim().nullIfBlank()?.toInt() "favorited" -> favorites = right.removeSuffix("times").trimOrNull()?.toInt()
} }
} }
} }
@ -589,13 +584,11 @@ class EHentai(
averageRating = select("#rating_label") averageRating = select("#rating_label")
.text() .text()
.removePrefix("Average:") .removePrefix("Average:")
.trim() .trimOrNull()
.nullIfBlank()
?.toDouble() ?.toDouble()
ratingCount = select("#rating_count") ratingCount = select("#rating_count")
.text() .text()
.trim() .trimOrNull()
.nullIfBlank()
?.toInt() ?.toInt()
} }

View File

@ -104,7 +104,7 @@ class NHentai(delegate: HttpSource, val context: Context) :
tags.clear() tags.clear()
}.forEach { }.forEach {
if (it.first != null && it.second != null) { if (it.first != null && it.second != null) {
tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == "category") RaisedSearchMetadata.TAG_TYPE_VIRTUAL else NHentaiSearchMetadata.TAG_TYPE_DEFAULT)) tags.add(RaisedTag(it.first!!, it.second!!, if (it.first == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE) RaisedSearchMetadata.TAG_TYPE_VIRTUAL else NHentaiSearchMetadata.TAG_TYPE_DEFAULT))
} }
} }
} }

View File

@ -56,7 +56,7 @@ import eu.kanade.tachiyomi.widget.EmptyView
import exh.isEhBasedSource import exh.isEhBasedSource
import exh.md.similar.ui.EnableMangaDexSimilarDialogController import exh.md.similar.ui.EnableMangaDexSimilarDialogController
import exh.savedsearches.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import kotlinx.android.synthetic.main.main_activity.root_coordinator import kotlinx.android.synthetic.main.main_activity.root_coordinator
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop

View File

@ -12,11 +12,10 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import exh.util.SourceTagsUtil.Companion.getLocaleSourceUtil
import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.date_posted import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.date_posted
import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.genre import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.genre
import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.language import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.language
@ -82,11 +81,11 @@ class SourceEnhancedEHentaiListHolder(private val view: View, adapter: FlexibleA
genre.text = view.context.getString(pair.second) genre.text = view.context.getString(pair.second)
} else genre.text = metadata.genre } else genre.text = metadata.genre
metadata.datePosted?.let { date_posted.text = EX_DATE_FORMAT.format(Date(it)) } metadata.datePosted?.let { date_posted.text = MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
metadata.averageRating?.let { rating_bar.rating = it.toFloat() } metadata.averageRating?.let { rating_bar.rating = it.toFloat() }
val locale = getLocaleSourceUtil(metadata.tags.firstOrNull { it.namespace == "language" }?.name) val locale = SourceTagsUtil.getLocaleSourceUtil(metadata.tags.firstOrNull { it.namespace == "language" }?.name)
val pageCount = metadata.length val pageCount = metadata.length
language.text = if (locale != null && pageCount != null) { language.text = if (locale != null && pageCount != null) {

View File

@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader
import eu.kanade.tachiyomi.widget.SimpleNavigationView import eu.kanade.tachiyomi.widget.SimpleNavigationView
import exh.savedsearches.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
class SourceFilterSheet( class SourceFilterSheet(
activity: Activity, activity: Activity,

View File

@ -21,10 +21,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.AutofitRecyclerView
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.SourceTagsUtil.Companion.TAG_TYPE_EXCLUDE import exh.util.SourceTagsUtil
import exh.util.SourceTagsUtil.Companion.getRaisedTags import exh.util.getRaisedTags
import exh.util.SourceTagsUtil.Companion.parseTag
import kotlinx.android.synthetic.main.source_compact_grid_item.view.card import kotlinx.android.synthetic.main.source_compact_grid_item.view.card
import kotlinx.android.synthetic.main.source_compact_grid_item.view.gradient import kotlinx.android.synthetic.main.source_compact_grid_item.view.gradient
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -160,14 +159,14 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
} }
cleanConstraint.split(",").all { cleanConstraint.split(",").all {
if (raisedTags == null) containsGenre(it.trim(), genres) else containsRaisedGenre( if (raisedTags == null) containsGenre(it.trim(), genres) else containsRaisedGenre(
parseTag(it.trim()), SourceTagsUtil.parseTag(it.trim()),
raisedTags raisedTags
) )
} }
} else if (raisedTags == null) { } else if (raisedTags == null) {
containsGenre(constraint, genres) containsGenre(constraint, genres)
} else { } else {
containsRaisedGenre(parseTag(constraint), raisedTags) containsRaisedGenre(SourceTagsUtil.parseTag(constraint), raisedTags)
} }
} }
@ -175,7 +174,7 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
val genre = genres.find { val genre = genres.find {
(it.namespace?.toLowerCase() == tag.namespace?.toLowerCase() && it.name.toLowerCase() == tag.name.toLowerCase()) (it.namespace?.toLowerCase() == tag.namespace?.toLowerCase() && it.name.toLowerCase() == tag.name.toLowerCase())
} }
return if (tag.type == TAG_TYPE_EXCLUDE) { return if (tag.type == SourceTagsUtil.TAG_TYPE_EXCLUDE) {
genre == null genre == null
} else { } else {
genre != null genre != null

View File

@ -103,7 +103,7 @@ import exh.isEhBasedSource
import exh.md.similar.ui.MangaDexSimilarController import exh.md.similar.ui.MangaDexSimilarController
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.recs.RecommendsController import exh.recs.RecommendsController
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import kotlinx.android.synthetic.main.main_activity.root_coordinator import kotlinx.android.synthetic.main.main_activity.root_coordinator
import kotlinx.android.synthetic.main.main_activity.toolbar import kotlinx.android.synthetic.main.main_activity.toolbar
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException

View File

@ -50,7 +50,7 @@ import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.asObservable import exh.util.asObservable
import exh.util.await import exh.util.await
import exh.util.shouldDeleteChapters import exh.util.shouldDeleteChapters

View File

@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import kotlinx.android.synthetic.main.chapters_item.bookmark_icon import kotlinx.android.synthetic.main.chapters_item.bookmark_icon
import kotlinx.android.synthetic.main.chapters_item.chapter_description import kotlinx.android.synthetic.main.chapters_item.chapter_description
import kotlinx.android.synthetic.main.chapters_item.chapter_title import kotlinx.android.synthetic.main.chapters_item.chapter_title
@ -56,7 +56,7 @@ class ChapterHolder(
if (chapter.date_upload > 0) { if (chapter.date_upload > 0) {
// SY --> // SY -->
if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) { if (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID) {
descriptions.add(EX_DATE_FORMAT.format(Date(chapter.date_upload))) descriptions.add(MetadataUtil.EX_DATE_FORMAT.format(Date(chapter.date_upload)))
} else /* SY <-- */ descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload))) } else /* SY <-- */ descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload)))
} }
if ((!chapter.read || (adapter.preserveReadingPosition && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID))) && chapter.last_page_read > 0) { if ((!chapter.read || (adapter.preserveReadingPosition && (manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID))) && chapter.last_page_read > 0) {

View File

@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Stat
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
class ChaptersSettingsSheet( class ChaptersSettingsSheet(
private val router: Router, private val router: Router,

View File

@ -22,7 +22,7 @@ import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.MERGED_SOURCE_ID import exh.MERGED_SOURCE_ID
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -107,7 +107,7 @@ class MangaInfoHeaderAdapter(
with(binding.btnTracking) { with(binding.btnTracking) {
// SY --> // SY -->
val sourceIsMangaDex = source.let { it.getMainSource() is MangaDex } val sourceIsMangaDex = source.getMainSource() is MangaDex
// SY <-- // SY <--
if (trackManager.hasLoggedServices(/* SY --> */sourceIsMangaDex/* SY <-- */)) { if (trackManager.hasLoggedServices(/* SY --> */sourceIsMangaDex/* SY <-- */)) {
isVisible = true isVisible = true
@ -169,7 +169,7 @@ class MangaInfoHeaderAdapter(
val author = binding.mangaAuthor.text.toString() val author = binding.mangaAuthor.text.toString()
controller.activity?.copyToClipboard( controller.activity?.copyToClipboard(
author, author,
SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = author) ?: author SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = author) ?: author
) )
// SY <-- // SY <--
} }
@ -179,7 +179,7 @@ class MangaInfoHeaderAdapter(
.onEach { .onEach {
// SY --> // SY -->
val author = binding.mangaAuthor.text.toString() val author = binding.mangaAuthor.text.toString()
controller.performGlobalSearch(SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = author) ?: author) controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = author) ?: author)
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(scope)
@ -190,7 +190,7 @@ class MangaInfoHeaderAdapter(
val artist = binding.mangaArtist.text.toString() val artist = binding.mangaArtist.text.toString()
controller.activity?.copyToClipboard( controller.activity?.copyToClipboard(
artist, artist,
SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist
) )
// SY <-- // SY <--
} }
@ -200,7 +200,7 @@ class MangaInfoHeaderAdapter(
.onEach { .onEach {
// SY --> // SY -->
val artist = binding.mangaArtist.text.toString() val artist = binding.mangaArtist.text.toString()
controller.performGlobalSearch(SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist) controller.performGlobalSearch(SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist)
// SY <-- // SY <--
} }
.launchIn(scope) .launchIn(scope)

View File

@ -20,8 +20,8 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.isEhBasedSource import exh.isEhBasedSource
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.SourceTagsUtil.Companion.getRaisedTags import exh.util.getRaisedTags
import exh.util.makeSearchChip import exh.util.makeSearchChip
import exh.util.setChipsExtended import exh.util.setChipsExtended
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope

View File

@ -40,7 +40,7 @@ import exh.md.utils.MdUtil
import exh.md.utils.scanlatorList import exh.md.utils.scanlatorList
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import exh.util.awaitSingleOrNull import exh.util.awaitSingleOrNull
import exh.util.defaultReaderType import exh.util.defaultReaderType
import exh.util.shouldDeleteChapters import exh.util.shouldDeleteChapters

View File

@ -34,12 +34,12 @@ class SettingsMangaDexController :
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply { override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.mangadex_specific_settings titleRes = R.string.mangadex_specific_settings
if (mdex == null) return@apply val mdex = mdex ?: return@apply
val sourcePreference = MangaDexLoginPreference(context, mdex!!).apply { val sourcePreference = MangaDexLoginPreference(context, mdex).apply {
title = mdex!!.name + " Login" title = mdex.name + " Login"
key = getSourceKey(source.id) key = getSourceKey(source.id)
setOnLoginClickListener { setOnLoginClickListener {
if (mdex!!.isLogged()) { if (mdex.isLogged()) {
val dialog = MangadexLogoutDialog(source) val dialog = MangadexLogoutDialog(source)
dialog.targetController = this@SettingsMangaDexController dialog.targetController = this@SettingsMangaDexController
dialog.showDialog(router) dialog.showDialog(router)

View File

@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.Date import java.util.Date

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.SourceManager.Companion.currentDelegatedSources
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXHMigrations import exh.EXHMigrations
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
@ -69,7 +68,7 @@ object DebugFunctions {
} }
private val throttleManager = EHentaiThrottleManager() private val throttleManager = EHentaiThrottleManager()
fun getDelegatedSourceList(): String = currentDelegatedSources.map { it.value.sourceName + " : " + it.value.sourceId + " : " + it.value.factory }.joinToString(separator = "\n") fun getDelegatedSourceList(): String = SourceManager.currentDelegatedSources.map { it.value.sourceName + " : " + it.value.sourceId + " : " + it.value.factory }.joinToString(separator = "\n")
fun resetEHGalleriesForUpdater() { fun resetEHGalleriesForUpdater() {
throttleManager.resetThrottle() throttleManager.resetThrottle()

View File

@ -4,7 +4,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
/** /**
* Presenter of [MangaDexFollowsController]. Inherit BrowseCataloguePresenter. * Presenter of [MangaDexFollowsController]. Inherit BrowseCataloguePresenter.

View File

@ -15,6 +15,7 @@ import exh.metadata.metadata.base.RaisedTag
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.floor import exh.util.floor
import exh.util.nullIfZero
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -126,7 +127,7 @@ class ApiMangaParser(private val langs: List<String>) {
genres.add("Hentai") genres.add("Hentai")
} }
if (tags.size != 0) tags.clear() if (tags.isNotEmpty()) tags.clear()
tags += genres.map { RaisedTag(null, it, MangaDexSearchMetadata.TAG_TYPE_DEFAULT) } tags += genres.map { RaisedTag(null, it, MangaDexSearchMetadata.TAG_TYPE_DEFAULT) }
} catch (e: Exception) { } catch (e: Exception) {
XLog.e(e) XLog.e(e)
@ -157,10 +158,8 @@ class ApiMangaParser(private val langs: List<String>) {
} }
} }
val removeOneshots = filteredChapters.asSequence() val removeOneshots = filteredChapters.asSequence()
.map { it.value.chapter!!.toDoubleOrNull() } .map { it.value.chapter?.toDoubleOrNull()?.floor()?.nullIfZero() }
.filter { it != null } .filterNotNull()
.map { it!!.floor() }
.filter { it != 0 }
.toList().distinctBy { it } .toList().distinctBy { it }
return removeOneshots.toList().size == finalChapterNumber.toDouble().floor() return removeOneshots.toList().size == finalChapterNumber.toDouble().floor()
} }

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -14,8 +15,6 @@ import exh.md.handlers.serializers.FollowsPageResult
import exh.md.handlers.serializers.Result import exh.md.handlers.serializers.Result
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.md.utils.MdUtil.Companion.baseUrl
import exh.md.utils.MdUtil.Companion.getMangaId
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.util.floor import exh.util.floor
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -101,7 +100,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
if (result[0].chapter.isNotBlank()) { if (result[0].chapter.isNotBlank()) {
track.last_chapter_read = follow.chapter.toFloat().floor() track.last_chapter_read = follow.chapter.toFloat().floor()
} }
track.tracking_url = baseUrl + follow.manga_id.toString() track.tracking_url = MdUtil.baseUrl + follow.manga_id.toString()
track.title = follow.title track.title = follow.title
} }
return track return track
@ -111,7 +110,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
* *
*/ */
private fun followsListRequest(): Request { private fun followsListRequest(): Request {
return GET("$baseUrl${MdUtil.followsAllApi}", headers, CacheControl.FORCE_NETWORK) return GET("${MdUtil.baseUrl}${MdUtil.followsAllApi}", headers, CacheControl.FORCE_NETWORK)
} }
/** /**
@ -139,66 +138,66 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
if (followStatus == FollowStatus.UNFOLLOWED) { if (followStatus == FollowStatus.UNFOLLOWED) {
client.newCall( client.newCall(
GET( GET(
"$baseUrl/ajax/actions.ajax.php?function=manga_unfollow&id=$mangaID&type=$mangaID", "${MdUtil.baseUrl}/ajax/actions.ajax.php?function=manga_unfollow&id=$mangaID&type=$mangaID",
headers, headers,
CacheControl.FORCE_NETWORK CacheControl.FORCE_NETWORK
) )
) )
.execute() .await()
} else { } else {
val status = followStatus.int val status = followStatus.int
client.newCall( client.newCall(
GET( GET(
"$baseUrl/ajax/actions.ajax.php?function=manga_follow&id=$mangaID&type=$status", "${MdUtil.baseUrl}/ajax/actions.ajax.php?function=manga_follow&id=$mangaID&type=$status",
headers, headers,
CacheControl.FORCE_NETWORK CacheControl.FORCE_NETWORK
) )
) )
.execute() .await()
} }
response.body!!.string().isEmpty() withContext(Dispatchers.IO) { response.body!!.string().isEmpty() }
} }
} }
suspend fun updateReadingProgress(track: Track): Boolean { suspend fun updateReadingProgress(track: Track): Boolean {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val mangaID = getMangaId(track.tracking_url) val mangaID = MdUtil.getMangaId(track.tracking_url)
val formBody = FormBody.Builder() val formBody = FormBody.Builder()
.add("chapter", track.last_chapter_read.toString()) .add("chapter", track.last_chapter_read.toString())
XLog.d("chapter to update %s", track.last_chapter_read.toString()) XLog.d("chapter to update %s", track.last_chapter_read.toString())
val response = client.newCall( val response = client.newCall(
POST( POST(
"$baseUrl/ajax/actions.ajax.php?function=edit_progress&id=$mangaID", "${MdUtil.baseUrl}/ajax/actions.ajax.php?function=edit_progress&id=$mangaID",
headers, headers,
formBody.build() formBody.build()
) )
).execute() ).await()
val response2 = client.newCall( client.newCall(
GET( GET(
"$baseUrl/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}", "${MdUtil.baseUrl}/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}",
headers headers
) )
) )
.execute() .await()
response.body!!.string().isEmpty() withContext(Dispatchers.IO) { response.body!!.string().isEmpty() }
} }
} }
suspend fun updateRating(track: Track): Boolean { suspend fun updateRating(track: Track): Boolean {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val mangaID = getMangaId(track.tracking_url) val mangaID = MdUtil.getMangaId(track.tracking_url)
val response = client.newCall( val response = client.newCall(
GET( GET(
"$baseUrl/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}", "${MdUtil.baseUrl}/ajax/actions.ajax.php?function=manga_rating&id=$mangaID&rating=${track.score.toInt()}",
headers headers
) )
) )
.execute() .await()
response.body!!.string().isEmpty() withContext(Dispatchers.IO) { response.body!!.string().isEmpty() }
} }
} }
@ -208,7 +207,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
suspend fun fetchAllFollows(forceHd: Boolean): List<Pair<SManga, MangaDexSearchMetadata>> { suspend fun fetchAllFollows(forceHd: Boolean): List<Pair<SManga, MangaDexSearchMetadata>> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val listManga = mutableListOf<Pair<SManga, MangaDexSearchMetadata>>() val listManga = mutableListOf<Pair<SManga, MangaDexSearchMetadata>>()
val response = client.newCall(followsListRequest()).execute() val response = client.newCall(followsListRequest()).await()
val mangasPage = followsParseMangaPage(response, forceHd) val mangasPage = followsParseMangaPage(response, forceHd)
listManga.addAll( listManga.addAll(
mangasPage.mangas.mapIndexed { index, sManga -> mangasPage.mangas.mapIndexed { index, sManga ->
@ -222,11 +221,11 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
suspend fun fetchTrackingInfo(url: String): Track { suspend fun fetchTrackingInfo(url: String): Track {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val request = GET( val request = GET(
"$baseUrl${MdUtil.followsMangaApi}" + getMangaId(url), "${MdUtil.baseUrl}${MdUtil.followsMangaApi}" + MdUtil.getMangaId(url),
headers, headers,
CacheControl.FORCE_NETWORK CacheControl.FORCE_NETWORK
) )
val response = client.newCall(request).execute() val response = client.newCall(request).await()
val track = followStatusParse(response) val track = followStatusParse(response)
track track

View File

@ -22,10 +22,10 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
// TODO make use of this // TODO make use of this
suspend fun fetchMangaAndChapterDetails(manga: SManga): Pair<SManga, List<SChapter>> { suspend fun fetchMangaAndChapterDetails(manga: SManga): Pair<SManga, List<SChapter>> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga)).execute() val response = client.newCall(apiRequest(manga)).await()
val parser = ApiMangaParser(langs) val parser = ApiMangaParser(langs)
val jsonData = response.body!!.string() val jsonData = withContext(Dispatchers.IO) { response.body!!.string() }
if (response.code != 200) { if (response.code != 200) {
XLog.e("error from MangaDex with response code ${response.code} \n body: \n$jsonData") XLog.e("error from MangaDex with response code ${response.code} \n body: \n$jsonData")
throw Exception("Error from MangaDex Response code ${response.code} ") throw Exception("Error from MangaDex Response code ${response.code} ")
@ -43,14 +43,14 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
suspend fun getMangaIdFromChapterId(urlChapterId: String): Int { suspend fun getMangaIdFromChapterId(urlChapterId: String): Int {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val request = GET(MdUtil.baseUrl + MdUtil.apiChapter + urlChapterId + MdUtil.apiChapterSuffix, headers, CacheControl.FORCE_NETWORK) val request = GET(MdUtil.baseUrl + MdUtil.apiChapter + urlChapterId + MdUtil.apiChapterSuffix, headers, CacheControl.FORCE_NETWORK)
val response = client.newCall(request).execute() val response = client.newCall(request).await()
ApiMangaParser(langs).chapterParseForMangaId(response) ApiMangaParser(langs).chapterParseForMangaId(response)
} }
} }
suspend fun fetchMangaDetails(manga: SManga): SManga { suspend fun fetchMangaDetails(manga: SManga): SManga {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga)).execute() val response = client.newCall(apiRequest(manga)).await()
ApiMangaParser(langs).parseToManga(manga, response, forceLatestCovers).await() ApiMangaParser(langs).parseToManga(manga, response, forceLatestCovers).await()
manga.apply { manga.apply {
initialized = true initialized = true
@ -82,7 +82,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
suspend fun fetchChapterList(manga: SManga): List<SChapter> { suspend fun fetchChapterList(manga: SManga): List<SChapter> {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga)).execute() val response = client.newCall(apiRequest(manga)).await()
ApiMangaParser(langs).chapterListParse(response) ApiMangaParser(langs).chapterListParse(response)
} }
} }

View File

@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
/** /**

View File

@ -6,7 +6,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import exh.source.getMainSource
import exh.util.floor import exh.util.floor
import exh.util.nullIfZero
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.jsoup.parser.Parser import org.jsoup.parser.Parser
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -14,6 +16,7 @@ import uy.kohesive.injekt.api.get
import java.net.URI import java.net.URI
import java.net.URISyntaxException import java.net.URISyntaxException
@Suppress("unused")
class MdUtil { class MdUtil {
companion object { companion object {
@ -201,8 +204,8 @@ class MdUtil {
/*}*/ /*}*/
}.sortedByDescending { it.chapter_number } }.sortedByDescending { it.chapter_number }
remove0ChaptersFromCount.firstOrNull()?.let { remove0ChaptersFromCount.firstOrNull()?.let { chapter ->
val chpNumber = it.chapter_number.floor() val chpNumber = chapter.chapter_number.floor()
val allChapters = (1..chpNumber).toMutableSet() val allChapters = (1..chpNumber).toMutableSet()
remove0ChaptersFromCount.forEach { remove0ChaptersFromCount.forEach {
@ -217,8 +220,9 @@ class MdUtil {
fun getEnabledMangaDex(preferences: PreferencesHelper = Injekt.get(), sourceManager: SourceManager = Injekt.get()): MangaDex? { fun getEnabledMangaDex(preferences: PreferencesHelper = Injekt.get(), sourceManager: SourceManager = Injekt.get()): MangaDex? {
return getEnabledMangaDexs(preferences, sourceManager).let { mangadexs -> return getEnabledMangaDexs(preferences, sourceManager).let { mangadexs ->
val preferredMangaDexId = preferences.preferredMangaDexId().get().toLongOrNull() preferences.preferredMangaDexId().get().toLongOrNull()?.nullIfZero()?.let { preferredMangaDexId ->
mangadexs.firstOrNull { preferredMangaDexId != null && preferredMangaDexId != 0L && it.id == preferredMangaDexId } ?: mangadexs.firstOrNull() mangadexs.firstOrNull { it.id == preferredMangaDexId }
} ?: mangadexs.firstOrNull()
} }
} }
@ -226,10 +230,11 @@ class MdUtil {
val languages = preferences.enabledLanguages().get() val languages = preferences.enabledLanguages().get()
val disabledSourceIds = preferences.disabledSources().get() val disabledSourceIds = preferences.disabledSources().get()
return sourceManager.getDelegatedCatalogueSources() return sourceManager.getVisibleOnlineSources()
.map { it.getMainSource() }
.filterIsInstance<MangaDex>()
.filter { it.lang in languages } .filter { it.lang in languages }
.filterNot { it.id.toString() in disabledSourceIds } .filterNot { it.id.toString() in disabledSourceIds }
.filterIsInstance(MangaDex::class.java)
} }
fun mapMdIdToMangaUrl(id: Int) = "/manga/$id/" fun mapMdIdToMangaUrl(id: Int) = "/manga/$id/"

View File

@ -1,13 +1,25 @@
package exh.metadata package exh.metadata
import android.content.Context
import android.graphics.Color
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange
import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.util.SourceTagsUtil
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import kotlin.math.ln import kotlin.math.ln
import kotlin.math.pow import kotlin.math.pow
import kotlin.math.roundToInt
/** /**
* Metadata utils * Metadata utils
*/ */
object MetadataUtil {
fun humanReadableByteCount(bytes: Long, si: Boolean): String { fun humanReadableByteCount(bytes: Long, si: Boolean): String {
val unit = if (si) 1000 else 1024 val unit = if (si) 1000 else 1024
if (bytes < unit) return "$bytes B" if (bytes < unit) return "$bytes B"
@ -37,10 +49,6 @@ fun parseHumanReadableByteCount(arg0: String): Double? {
return null return null
} }
fun <K, V> Set<Map.Entry<K, V>>.forEach(action: (K, V) -> Unit) {
forEach { action(it.key, it.value) }
}
val ONGOING_SUFFIX = arrayOf( val ONGOING_SUFFIX = arrayOf(
"[ongoing]", "[ongoing]",
"(ongoing)", "(ongoing)",
@ -60,3 +68,51 @@ val ONGOING_SUFFIX = arrayOf(
) )
val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US) val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
fun getRatingString(context: Context, @FloatRange(from = 0.0, to = 10.0) rating: Float?) = when ((rating ?: 100F).roundToInt()) {
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}.let { context.getString(it) }
fun getGenreAndColour(context: Context, genre: String) = when (genre) {
"doujinshi", "Doujinshi" -> SourceTagsUtil.DOUJINSHI_COLOR to R.string.doujinshi
"manga", "Japanese Manga", "Manga" -> SourceTagsUtil.MANGA_COLOR to R.string.manga
"artistcg", "artist CG", "artist-cg", "Artist CG" -> SourceTagsUtil.ARTIST_CG_COLOR to R.string.artist_cg
"gamecg", "game CG", "game-cg", "Game CG" -> SourceTagsUtil.GAME_CG_COLOR to R.string.game_cg
"western" -> SourceTagsUtil.WESTERN_COLOR to R.string.western
"non-h", "non-H" -> SourceTagsUtil.NON_H_COLOR to R.string.non_h
"imageset", "image Set" -> SourceTagsUtil.IMAGE_SET_COLOR to R.string.image_set
"cosplay" -> SourceTagsUtil.COSPLAY_COLOR to R.string.cosplay
"asianporn", "asian Porn" -> SourceTagsUtil.ASIAN_PORN_COLOR to R.string.asian_porn
"misc" -> SourceTagsUtil.MISC_COLOR to R.string.misc
"Korean Manhwa" -> SourceTagsUtil.ARTIST_CG_COLOR to R.string.manhwa
"Chinese Manhua" -> SourceTagsUtil.GAME_CG_COLOR to R.string.manhua
"Comic" -> SourceTagsUtil.WESTERN_COLOR to R.string.comic
"artbook" -> SourceTagsUtil.IMAGE_SET_COLOR to R.string.artbook
"webtoon" -> SourceTagsUtil.NON_H_COLOR to R.string.webtoon
"Video" -> SourceTagsUtil.WESTERN_COLOR to R.string.video
else -> "" to 0
}.let { if (it.second == 0) null else Color.parseColor(it.first) to context.getString(it.second) }
}
fun <K, V> Set<Map.Entry<K, V>>.forEach(action: (K, V) -> Unit) {
forEach { action(it.key, it.value) }
}
fun TextView.bindDrawable(context: Context, @DrawableRes drawable: Int) {
ContextCompat.getDrawable(context, drawable)?.apply {
setTint(context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
setCompoundDrawables(this, null, null, null)
}
}

View File

@ -5,9 +5,7 @@ import android.net.Uri
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.humanReadableByteCount
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -71,7 +69,7 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
// We default to completed // We default to completed
manga.status = SManga.COMPLETED manga.status = SManga.COMPLETED
title?.let { t -> title?.let { t ->
ONGOING_SUFFIX.find { MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true) t.endsWith(it, ignoreCase = true)
}?.let { }?.let {
manga.status = SManga.ONGOING manga.status = SManga.ONGOING
@ -111,25 +109,26 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
gId?.let { pairs += Pair(context.getString(R.string.id), it) }
gToken?.let { pairs += Pair(context.getString(R.string.token), it) } gId?.let { pairs += context.getString(R.string.id) to it }
exh?.let { pairs += Pair(context.getString(R.string.is_exhentai_gallery), context.getString(if (it) android.R.string.yes else android.R.string.no)) } gToken?.let { pairs += context.getString(R.string.token) to it }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } exh?.let { pairs += context.getString(R.string.is_exhentai_gallery) to context.getString(if (it) android.R.string.yes else android.R.string.no) }
title?.let { pairs += Pair(context.getString(R.string.title), it) } thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
altTitle?.let { pairs += Pair(context.getString(R.string.alt_title), it) } title?.let { pairs += context.getString(R.string.title) to it }
genre?.let { pairs += Pair(context.getString(R.string.genre), it) } altTitle?.let { pairs += context.getString(R.string.alt_title) to it }
datePosted?.let { pairs += Pair(context.getString(R.string.date_posted), EX_DATE_FORMAT.format(Date(it))) } genre?.let { pairs += context.getString(R.string.genre) to it }
parent?.let { pairs += Pair(context.getString(R.string.parent), it) } datePosted?.let { pairs += context.getString(R.string.date_posted) to MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
visible?.let { pairs += Pair(context.getString(R.string.visible), it) } parent?.let { pairs += context.getString(R.string.parent) to it }
language?.let { pairs += Pair(context.getString(R.string.language), it) } visible?.let { pairs += context.getString(R.string.visible) to it }
translated?.let { pairs += Pair("Translated", context.getString(if (it) android.R.string.yes else android.R.string.no)) } language?.let { pairs += context.getString(R.string.language) to it }
size?.let { pairs += Pair(context.getString(R.string.gallery_size), humanReadableByteCount(it, true)) } translated?.let { pairs += "Translated" to context.getString(if (it) android.R.string.yes else android.R.string.no) }
length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } size?.let { pairs += context.getString(R.string.gallery_size) to MetadataUtil.humanReadableByteCount(it, true) }
favorites?.let { pairs += Pair(context.getString(R.string.total_favorites), it.toString()) } length?.let { pairs += context.getString(R.string.page_count) to it.toString() }
ratingCount?.let { pairs += Pair(context.getString(R.string.total_ratings), it.toString()) } favorites?.let { pairs += context.getString(R.string.total_favorites) to it.toString() }
averageRating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) } ratingCount?.let { pairs += context.getString(R.string.total_ratings) to it.toString() }
aged.let { pairs += Pair(context.getString(R.string.aged), context.getString(if (it) android.R.string.yes else android.R.string.no)) } averageRating?.let { pairs += context.getString(R.string.average_rating) to it.toString() }
lastUpdateCheck.let { pairs += Pair(context.getString(R.string.last_update_check), EX_DATE_FORMAT.format(Date(it))) } aged.let { pairs += context.getString(R.string.aged) to context.getString(if (it) android.R.string.yes else android.R.string.no) }
lastUpdateCheck.let { pairs += context.getString(R.string.last_update_check) to MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
return pairs return pairs
} }

View File

@ -41,12 +41,12 @@ class EightMusesSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
val path = path.joinToString("/", prefix = "/") val path = path.joinToString("/", prefix = "/")
if (path.isNotBlank()) { if (path.isNotBlank()) {
pairs += Pair(context.getString(R.string.path), path) pairs += context.getString(R.string.path) to path
} }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
return pairs return pairs
} }

View File

@ -3,7 +3,6 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.EightMusesSearchMetadata.Companion.ARTIST_NAMESPACE
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -51,11 +50,11 @@ class HBrowseSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
hbId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } hbId?.let { pairs += context.getString(R.string.id) to it.toString() }
hbUrl?.let { pairs += Pair(context.getString(R.string.url), it) } hbUrl?.let { pairs += context.getString(R.string.url) to it }
thumbnail?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnail?.let { pairs += context.getString(R.string.thumbnail_url) to it }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } length?.let { pairs += context.getString(R.string.page_count) to it.toString() }
return pairs return pairs
} }
@ -65,6 +64,7 @@ class HBrowseSearchMetadata : RaisedSearchMetadata() {
private const val TITLE_TYPE_MAIN = 0 private const val TITLE_TYPE_MAIN = 0
const val TAG_TYPE_DEFAULT = 0 const val TAG_TYPE_DEFAULT = 0
const val ARTIST_NAMESPACE = "artist"
fun guessThumbnailUrl(hbid: String): String { fun guessThumbnailUrl(hbid: String): String {
return "$BASE_URL/thumbnails/${hbid}_1.jpg#guessed" return "$BASE_URL/thumbnails/${hbid}_1.jpg#guessed"

View File

@ -48,11 +48,11 @@ class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
hcId?.let { pairs += Pair(context.getString(R.string.id), it) } hcId?.let { pairs += context.getString(R.string.id) to it }
readerId?.let { pairs += Pair(context.getString(R.string.reader_id), it) } readerId?.let { pairs += context.getString(R.string.reader_id) to it }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
artist?.let { pairs += Pair(context.getString(R.string.artist), it) } artist?.let { pairs += context.getString(R.string.artist) to it }
return pairs return pairs
} }

View File

@ -3,7 +3,7 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.Date import java.util.Date
@ -94,25 +94,27 @@ class HitomiSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
hlId?.let { pairs += Pair(context.getString(R.string.id), it) } with(context) {
title?.let { pairs += Pair(context.getString(R.string.title), it) } hlId?.let { pairs += getString(R.string.id) to it }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } title?.let { pairs += getString(R.string.title) to it }
thumbnailUrl?.let { pairs += getString(R.string.thumbnail_url) to it }
val artists = artists.joinToString() val artists = artists.joinToString()
if (artists.isNotBlank()) { if (artists.isNotBlank()) {
pairs += Pair(context.getString(R.string.artist), artists) pairs += getString(R.string.artist) to artists
} }
group?.let { pairs += Pair(context.getString(R.string.group), it) } group?.let { pairs += getString(R.string.group) to it }
genre?.let { pairs += Pair(context.getString(R.string.genre), it) } genre?.let { pairs += getString(R.string.genre) to it }
language?.let { pairs += Pair(context.getString(R.string.language), it) } language?.let { pairs += getString(R.string.language) to it }
val series = series.joinToString() val series = series.joinToString()
if (series.isNotBlank()) { if (series.isNotBlank()) {
pairs += Pair(context.getString(R.string.series), series) pairs += getString(R.string.series) to series
} }
val characters = characters.joinToString() val characters = characters.joinToString()
if (characters.isNotBlank()) { if (characters.isNotBlank()) {
pairs += Pair(context.getString(R.string.characters), characters) pairs += getString(R.string.characters) to characters
}
uploadDate?.let { pairs += getString(R.string.date_posted) to MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
} }
uploadDate?.let { pairs += Pair(context.getString(R.string.date_posted), EX_DATE_FORMAT.format(Date(it))) }
return pairs return pairs
} }

View File

@ -78,24 +78,24 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
mdId?.let { pairs += Pair(context.getString(R.string.id), it) } mdId?.let { pairs += context.getString(R.string.id) to it }
mdUrl?.let { pairs += Pair(context.getString(R.string.url), it) } mdUrl?.let { pairs += context.getString(R.string.url) to it }
thumbnail_url?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnail_url?.let { pairs += context.getString(R.string.thumbnail_url) to it }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
author?.let { pairs += Pair(context.getString(R.string.author), it) } author?.let { pairs += context.getString(R.string.author) to it }
artist?.let { pairs += Pair(context.getString(R.string.artist), it) } artist?.let { pairs += context.getString(R.string.artist) to it }
lang_flag?.let { pairs += Pair(context.getString(R.string.language), it) } lang_flag?.let { pairs += context.getString(R.string.language) to it }
last_chapter_number?.let { pairs += Pair(context.getString(R.string.last_chapter_number), it.toString()) } last_chapter_number?.let { pairs += context.getString(R.string.last_chapter_number) to it.toString() }
rating?.let { pairs += Pair(context.getString(R.string.average_rating), it) } rating?.let { pairs += context.getString(R.string.average_rating) to it }
users?.let { pairs += Pair(context.getString(R.string.total_ratings), it) } users?.let { pairs += context.getString(R.string.total_ratings) to it }
status?.let { pairs += Pair(context.getString(R.string.status), it.toString()) } status?.let { pairs += context.getString(R.string.status) to it.toString() }
missing_chapters?.let { pairs += Pair(context.getString(R.string.missing_chapters), it) } missing_chapters?.let { pairs += context.getString(R.string.missing_chapters) to it }
follow_status?.let { pairs += Pair(context.getString(R.string.follow_status), it.toString()) } follow_status?.let { pairs += context.getString(R.string.follow_status) to it.toString() }
anilist_id?.let { pairs += Pair(context.getString(R.string.anilist_id), it) } anilist_id?.let { pairs += context.getString(R.string.anilist_id) to it }
kitsu_id?.let { pairs += Pair(context.getString(R.string.kitsu_id), it) } kitsu_id?.let { pairs += context.getString(R.string.kitsu_id) to it }
my_anime_list_id?.let { pairs += Pair(context.getString(R.string.mal_id), it) } my_anime_list_id?.let { pairs += context.getString(R.string.mal_id) to it }
manga_updates_id?.let { pairs += Pair(context.getString(R.string.manga_updates_id), it) } manga_updates_id?.let { pairs += context.getString(R.string.manga_updates_id) to it }
anime_planet_id?.let { pairs += Pair(context.getString(R.string.anime_planet_id), it) } anime_planet_id?.let { pairs += context.getString(R.string.anime_planet_id) to it }
return pairs return pairs
} }

View File

@ -3,8 +3,7 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.Date import java.util.Date
@ -65,7 +64,7 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
// We default to completed // We default to completed
manga.status = SManga.COMPLETED manga.status = SManga.COMPLETED
englishTitle?.let { t -> englishTitle?.let { t ->
ONGOING_SUFFIX.find { MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true) t.endsWith(it, ignoreCase = true)
}?.let { }?.let {
manga.status = SManga.ONGOING manga.status = SManga.ONGOING
@ -77,17 +76,17 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
nhId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } nhId?.let { pairs += context.getString(R.string.id) to it.toString() }
uploadDate?.let { pairs += Pair(context.getString(R.string.date_posted), EX_DATE_FORMAT.format(Date(it * 1000))) } uploadDate?.let { pairs += context.getString(R.string.date_posted) to MetadataUtil.EX_DATE_FORMAT.format(Date(it * 1000)) }
favoritesCount?.let { pairs += Pair(context.getString(R.string.total_favorites), it.toString()) } favoritesCount?.let { pairs += context.getString(R.string.total_favorites) to it.toString() }
mediaId?.let { pairs += Pair(context.getString(R.string.media_id), it) } mediaId?.let { pairs += context.getString(R.string.media_id) to it }
japaneseTitle?.let { pairs += Pair(context.getString(R.string.japanese_title), it) } japaneseTitle?.let { pairs += context.getString(R.string.japanese_title) to it }
englishTitle?.let { pairs += Pair(context.getString(R.string.english_title), it) } englishTitle?.let { pairs += context.getString(R.string.english_title) to it }
shortTitle?.let { pairs += Pair(context.getString(R.string.short_title), it) } shortTitle?.let { pairs += context.getString(R.string.short_title) to it }
coverImageType?.let { pairs += Pair(context.getString(R.string.cover_image_file_type), it) } coverImageType?.let { pairs += context.getString(R.string.cover_image_file_type) to it }
pageImageTypes.size.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } pageImageTypes.size.let { pairs += context.getString(R.string.page_count) to it.toString() }
thumbnailImageType?.let { pairs += Pair(context.getString(R.string.thumbnail_image_file_type), it) } thumbnailImageType?.let { pairs += context.getString(R.string.thumbnail_image_file_type) to it }
scanlator?.let { pairs += Pair(context.getString(R.string.scanlator), it) } scanlator?.let { pairs += context.getString(R.string.scanlator) to it }
return pairs return pairs
} }

View File

@ -95,19 +95,19 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
pvId?.let { pairs += Pair(context.getString(R.string.id), it) } pvId?.let { pairs += context.getString(R.string.id) to it }
url?.let { pairs += Pair(context.getString(R.string.url), it) } url?.let { pairs += context.getString(R.string.url) to it }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
val altTitles = altTitles.joinToString() val altTitles = altTitles.joinToString()
if (altTitles.isNotBlank()) { if (altTitles.isNotBlank()) {
pairs += Pair(context.getString(R.string.alt_titles), altTitles) pairs += context.getString(R.string.alt_titles) to altTitles
} }
artist?.let { pairs += Pair(context.getString(R.string.artist), it) } artist?.let { pairs += context.getString(R.string.artist) to it }
genre?.let { pairs += Pair(context.getString(R.string.genre), it) } genre?.let { pairs += context.getString(R.string.genre) to it }
rating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) } rating?.let { pairs += context.getString(R.string.average_rating) to it.toString() }
status?.let { pairs += Pair(context.getString(R.string.status), it) } status?.let { pairs += context.getString(R.string.status) to it }
lang?.let { pairs += Pair(context.getString(R.string.language), it) } lang?.let { pairs += context.getString(R.string.language) to it }
return pairs return pairs
} }

View File

@ -64,16 +64,16 @@ class PururinSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
prId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } prId?.let { pairs += context.getString(R.string.id) to it.toString() }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
altTitle?.let { pairs += Pair(context.getString(R.string.alt_title), it) } altTitle?.let { pairs += context.getString(R.string.alt_title) to it }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) } thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
uploaderDisp?.let { pairs += Pair(context.getString(R.string.uploader_capital), it) } uploaderDisp?.let { pairs += context.getString(R.string.uploader_capital) to it }
uploader?.let { pairs += Pair(context.getString(R.string.uploader), it) } uploader?.let { pairs += context.getString(R.string.uploader) to it }
pages?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } pages?.let { pairs += context.getString(R.string.page_count) to it.toString() }
fileSize?.let { pairs += Pair(context.getString(R.string.gallery_size), it) } fileSize?.let { pairs += context.getString(R.string.gallery_size) to it }
ratingCount?.let { pairs += Pair(context.getString(R.string.total_ratings), it.toString()) } ratingCount?.let { pairs += context.getString(R.string.total_ratings) to it.toString() }
averageRating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) } averageRating?.let { pairs += context.getString(R.string.average_rating) to it.toString() }
return pairs return pairs
} }

View File

@ -4,7 +4,7 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -82,25 +82,25 @@ class TsuminoSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> { override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>() val pairs = mutableListOf<Pair<String, String>>()
tmId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } tmId?.let { pairs += context.getString(R.string.id) to it.toString() }
title?.let { pairs += Pair(context.getString(R.string.title), it) } title?.let { pairs += context.getString(R.string.title) to it }
uploader?.let { pairs += Pair(context.getString(R.string.uploader), it) } uploader?.let { pairs += context.getString(R.string.uploader) to it }
uploadDate?.let { pairs += Pair(context.getString(R.string.date_posted), EX_DATE_FORMAT.format(Date(it))) } uploadDate?.let { pairs += context.getString(R.string.date_posted) to MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } length?.let { pairs += context.getString(R.string.page_count) to it.toString() }
ratingString?.let { pairs += Pair(context.getString(R.string.rating_string), it) } ratingString?.let { pairs += context.getString(R.string.rating_string) to it }
averageRating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) } averageRating?.let { pairs += context.getString(R.string.average_rating) to it.toString() }
userRatings?.let { pairs += Pair(context.getString(R.string.total_ratings), it.toString()) } userRatings?.let { pairs += context.getString(R.string.total_ratings) to it.toString() }
favorites?.let { pairs += Pair(context.getString(R.string.total_favorites), it.toString()) } favorites?.let { pairs += context.getString(R.string.total_favorites) to it.toString() }
category?.let { pairs += Pair(context.getString(R.string.genre), it) } category?.let { pairs += context.getString(R.string.genre) to it }
collection?.let { pairs += Pair(context.getString(R.string.collection), it) } collection?.let { pairs += context.getString(R.string.collection) to it }
group?.let { pairs += Pair(context.getString(R.string.group), it) } group?.let { pairs += context.getString(R.string.group) to it }
val parodiesString = parody.joinToString() val parodiesString = parody.joinToString()
if (parodiesString.isNotEmpty()) { if (parodiesString.isNotEmpty()) {
pairs += Pair(context.getString(R.string.parodies), parodiesString) pairs += context.getString(R.string.parodies) to parodiesString
} }
val charactersString = character.joinToString() val charactersString = character.joinToString()
if (charactersString.isNotEmpty()) { if (charactersString.isNotEmpty()) {
pairs += Pair(context.getString(R.string.characters), charactersString) pairs += context.getString(R.string.characters) to charactersString
} }
return pairs return pairs
} }

View File

@ -1,7 +1,6 @@
package exh.search package exh.search
import exh.plusAssign import exh.plusAssign
import exh.search.SearchEngine.Companion.escapeLike
class Text : QueryComponent() { class Text : QueryComponent() {
val components = mutableListOf<TextComponent>() val components = mutableListOf<TextComponent>()
@ -44,7 +43,7 @@ class Text : QueryComponent() {
val builder = StringBuilder() val builder = StringBuilder()
for (component in components) { for (component in components) {
when (component) { when (component) {
is StringTextComponent -> builder += escapeLike(component.value) is StringTextComponent -> builder += SearchEngine.escapeLike(component.value)
is SingleWildcard -> builder += "_" is SingleWildcard -> builder += "_"
is MultiWildcard -> builder += "%" is MultiWildcard -> builder += "%"
} }
@ -60,5 +59,5 @@ class Text : QueryComponent() {
rawText!! rawText!!
} }
fun rawTextEscapedForLike() = escapeLike(rawTextOnly()) fun rawTextEscapedForLike() = SearchEngine.escapeLike(rawTextOnly())
} }

View File

@ -235,21 +235,22 @@ class EnhancedHttpSource(
originalSource originalSource
} }
} }
}
companion object { fun Source.getMainSource(): Source = if (this is EnhancedHttpSource) {
fun Source.getMainSource(): Source {
return if (this is EnhancedHttpSource) {
this.source() this.source()
} else { } else {
this this
} }
}
fun Source.getOriginalSource(): Source { fun Source.getOriginalSource(): Source = if (this is EnhancedHttpSource) {
return if (this is EnhancedHttpSource) {
this.originalSource this.originalSource
} else { } else {
this this
} }
}
} fun Source.getEnhancedSource(): Source = if (this is EnhancedHttpSource) {
this.enhancedSource
} else {
this
} }

View File

@ -4,7 +4,6 @@ import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER
import org.jsoup.nodes.DataNode import org.jsoup.nodes.DataNode
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.nio.charset.Charset import java.nio.charset.Charset
@ -24,7 +23,7 @@ class AutoSolvingWebViewClient(
val oReq = request.toOkHttpRequest() val oReq = request.toOkHttpRequest()
val response = activity.httpClient.newCall(oReq).execute() val response = activity.httpClient.newCall(oReq).execute()
val doc = response.asJsoup() val doc = response.asJsoup()
doc.body().appendChild(Element("script").appendChild(DataNode(CROSS_WINDOW_SCRIPT_INNER))) doc.body().appendChild(Element("script").appendChild(DataNode(BrowserActionActivity.CROSS_WINDOW_SCRIPT_INNER)))
return WebResourceResponse( return WebResourceResponse(
"text/html", "text/html",
"UTF-8", "UTF-8",

View File

@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource import exh.source.getMainSource
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterEhBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterEhBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.humanReadableByteCount
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -25,7 +21,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.roundToInt
class EHentaiDescriptionAdapter( class EHentaiDescriptionAdapter(
private val controller: MangaController private val controller: MangaController
@ -51,52 +46,23 @@ class EHentaiDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is EHentaiSearchMetadata) return if (meta == null || meta !is EHentaiSearchMetadata) return
val genre = meta.genre binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
if (genre != null) { binding.genre.setBackgroundColor(it.first)
val pair = when (genre) { it.second
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) } ?: meta.genre ?: itemView.context.getString(R.string.unknown)
"manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga)
"artistcg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg)
"gamecg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg)
"western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western)
"non-h" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h)
"imageset" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set)
"cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay)
"asianporn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn)
"misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc)
else -> Pair("", 0)
}
if (pair.first.isNotBlank()) {
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = genre
} else binding.genre.setText(R.string.unknown)
binding.visible.text = itemView.context.getString(R.string.is_visible, meta.visible ?: itemView.context.getString(R.string.unknown)) binding.visible.text = itemView.context.getString(R.string.is_visible, meta.visible ?: itemView.context.getString(R.string.unknown))
binding.favorites.text = (meta.favorites ?: 0).toString() binding.favorites.text = (meta.favorites ?: 0).toString()
ContextCompat.getDrawable(itemView.context, R.drawable.ic_book_24dp)?.apply { binding.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.favorites.setCompoundDrawables(this, null, null, null)
}
binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown) binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown)
binding.size.text = humanReadableByteCount(meta.size ?: 0, true) binding.size.text = MetadataUtil.humanReadableByteCount(meta.size ?: 0, true)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)?.apply { binding.size.bindDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.size.setCompoundDrawables(this, null, null, null)
}
binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply { binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
val language = meta.language ?: itemView.context.getString(R.string.unknown) val language = meta.language ?: itemView.context.getString(R.string.unknown)
binding.language.text = if (meta.translated == true) { binding.language.text = if (meta.translated == true) {
@ -106,29 +72,11 @@ class EHentaiDescriptionAdapter(
} }
val ratingFloat = meta.averageRating?.toFloat() val ratingFloat = meta.averageRating?.toFloat()
val name = when (((ratingFloat ?: 100F) * 2).roundToInt()) {
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}
binding.ratingBar.rating = ratingFloat ?: 0F binding.ratingBar.rating = ratingFloat ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (ratingFloat ?: 0F).toString() + " - " + itemView.context.getString(name) binding.rating.text = (ratingFloat ?: 0F).toString() + " - " + MetadataUtil.getRatingString(itemView.context, ratingFloat?.times(2))
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.favorites, binding.favorites,

View File

@ -3,15 +3,13 @@ package exh.ui.metadata.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapter8mBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapter8mBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.bindDrawable
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.metadata.EightMusesSearchMetadata import exh.metadata.metadata.EightMusesSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -48,11 +46,7 @@ class EightMusesDescriptionAdapter(
binding.title.text = meta.title ?: itemView.context.getString(R.string.unknown) binding.title.text = meta.title ?: itemView.context.getString(R.string.unknown)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.title.longClicks() binding.title.longClicks()
.onEach { .onEach {

View File

@ -3,15 +3,13 @@ package exh.ui.metadata.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHbBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterHbBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.bindDrawable
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.metadata.HBrowseSearchMetadata import exh.metadata.metadata.HBrowseSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -47,17 +45,9 @@ class HBrowseDescriptionAdapter(
if (meta == null || meta !is HBrowseSearchMetadata) return if (meta == null || meta !is HBrowseSearchMetadata) return
binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply { binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.pages.longClicks() binding.pages.longClicks()
.onEach { .onEach {

View File

@ -3,15 +3,13 @@ package exh.ui.metadata.adapters
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHcBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterHcBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.bindDrawable
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.metadata.HentaiCafeSearchMetadata import exh.metadata.metadata.HentaiCafeSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -48,11 +46,7 @@ class HentaiCafeDescriptionAdapter(
binding.artist.text = meta.artist ?: itemView.context.getString(R.string.unknown) binding.artist.text = meta.artist ?: itemView.context.getString(R.string.unknown)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.artist.longClicks() binding.artist.longClicks()
.onEach { .onEach {

View File

@ -1,22 +1,18 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHiBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterHiBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.HitomiSearchMetadata import exh.metadata.metadata.HitomiSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -50,37 +46,16 @@ class HitomiDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is HitomiSearchMetadata) return if (meta == null || meta !is HitomiSearchMetadata) return
val genre = meta.genre binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
if (genre != null) { binding.genre.setBackgroundColor(it.first)
val pair = when (genre) { it.second
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) } ?: meta.genre ?: itemView.context.getString(R.string.unknown)
"manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga)
"artist CG" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg)
"game CG" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg)
"western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western)
"non-H" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h)
"image Set" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set)
"cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay)
"asian Porn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn)
"misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc)
else -> Pair("", 0)
}
if (pair.first.isNotBlank()) { binding.whenPosted.text = MetadataUtil.EX_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = genre
} else binding.genre.setText(R.string.unknown)
binding.whenPosted.text = EX_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
binding.group.text = meta.group ?: itemView.context.getString(R.string.unknown) binding.group.text = meta.group ?: itemView.context.getString(R.string.unknown)
binding.language.text = meta.language ?: itemView.context.getString(R.string.unknown) binding.language.text = meta.language ?: itemView.context.getString(R.string.unknown)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.genre, binding.genre,

View File

@ -4,15 +4,14 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil.getRatingString
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -23,7 +22,6 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.round import kotlin.math.round
import kotlin.math.roundToInt
class MangaDexDescriptionAdapter( class MangaDexDescriptionAdapter(
private val controller: MangaController private val controller: MangaController
@ -50,31 +48,12 @@ class MangaDexDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is MangaDexSearchMetadata) return if (meta == null || meta !is MangaDexSearchMetadata) return
val ratingFloat = meta.rating?.toFloatOrNull()?.div(2F) val ratingFloat = meta.rating?.toFloatOrNull()
val name = when (((ratingFloat ?: 100F) * 2).roundToInt()) { binding.ratingBar.rating = ratingFloat?.div(2F) ?: 0F
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}
binding.ratingBar.rating = ratingFloat ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((meta.rating?.toFloatOrNull() ?: 0F) * 100.0) / 100.0).toString() + " - " + itemView.context.getString(name) binding.rating.text = (round((meta.rating?.toFloatOrNull() ?: 0F) * 100.0) / 100.0).toString() + " - " + getRatingString(itemView.context, ratingFloat)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.rating.longClicks() binding.rating.longClicks()
.onEach { .onEach {

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterNhBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterNhBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -52,60 +48,30 @@ class NHentaiDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is NHentaiSearchMetadata) return if (meta == null || meta !is NHentaiSearchMetadata) return
var category: String? = null binding.genre.text = meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags ->
meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags -> if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
if (tags.isNotEmpty()) category = tags.joinToString(transform = { it.name }) }.let { categoriesString ->
categoriesString?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: categoriesString ?: itemView.context.getString(R.string.unknown)
} }
if (category != null) {
val pair = when (category) {
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)
"manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga)
"artistcg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg)
"gamecg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg)
"western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western)
"non-h" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h)
"imageset" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set)
"cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay)
"asianporn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn)
"misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc)
else -> Pair("", 0)
}
if (pair.first.isNotBlank()) {
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = category
} else binding.genre.setText(R.string.unknown)
meta.favoritesCount?.let { meta.favoritesCount?.let {
if (it == 0L) return@let if (it == 0L) return@let
binding.favorites.text = it.toString() binding.favorites.text = it.toString()
binding.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_book_24dp)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.favorites.setCompoundDrawables(this, null, null, null)
}
} }
binding.whenPosted.text = EX_DATE_FORMAT.format(Date((meta.uploadDate ?: 0) * 1000)) binding.whenPosted.text = MetadataUtil.EX_DATE_FORMAT.format(Date((meta.uploadDate ?: 0) * 1000))
binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pageImageTypes.size, meta.pageImageTypes.size) binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pageImageTypes.size, meta.pageImageTypes.size)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply { binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.id.text = "#" + (meta.nhId ?: 0) binding.id.text = "#" + (meta.nhId ?: 0)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.favorites, binding.favorites,

View File

@ -1,22 +1,19 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPeBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterPeBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.metadata.PervEdenSearchMetadata import exh.metadata.metadata.PervEdenSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -26,7 +23,6 @@ import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import java.util.Locale import java.util.Locale
import kotlin.math.round import kotlin.math.round
import kotlin.math.roundToInt
class PervEdenDescriptionAdapter( class PervEdenDescriptionAdapter(
private val controller: MangaController private val controller: MangaController
@ -52,22 +48,10 @@ class PervEdenDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is PervEdenSearchMetadata) return if (meta == null || meta !is PervEdenSearchMetadata) return
val genre = meta.genre binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
if (genre != null) { binding.genre.setBackgroundColor(it.first)
val pair = when (genre) { it.second
"Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) } ?: meta.genre ?: itemView.context.getString(R.string.unknown)
"Japanese Manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga)
"Korean Manhwa" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.manhwa)
"Chinese Manhua" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.manhua)
"Comic" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.comic)
else -> Pair("", 0)
}
if (pair.first.isNotBlank()) {
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = genre
} else binding.genre.setText(R.string.unknown)
val language = meta.lang val language = meta.lang
binding.language.text = if (language != null) { binding.language.text = if (language != null) {
@ -75,29 +59,11 @@ class PervEdenDescriptionAdapter(
local.displayName local.displayName
} else itemView.context.getString(R.string.unknown) } else itemView.context.getString(R.string.unknown)
val name = when (((meta.rating ?: 100F) * 2).roundToInt()) {
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}
binding.ratingBar.rating = meta.rating ?: 0F binding.ratingBar.rating = meta.rating ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((meta.rating ?: 0F) * 100.0) / 100.0).toString() + " - " + itemView.context.getString(name) binding.rating.text = (round((meta.rating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(itemView.context, meta.rating?.times(2))
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.genre, binding.genre,

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPuBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterPuBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.metadata.PururinSearchMetadata import exh.metadata.metadata.PururinSearchMetadata
import exh.metadata.metadata.PururinSearchMetadata.Companion.TAG_NAMESPACE_CATEGORY
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -26,7 +22,6 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.round import kotlin.math.round
import kotlin.math.roundToInt
class PururinDescriptionAdapter( class PururinDescriptionAdapter(
private val controller: MangaController private val controller: MangaController
@ -52,64 +47,28 @@ class PururinDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is PururinSearchMetadata) return if (meta == null || meta !is PururinSearchMetadata) return
val genre = meta.tags.find { it.namespace == TAG_NAMESPACE_CATEGORY } binding.genre.text = meta.tags.find { it.namespace == PururinSearchMetadata.TAG_NAMESPACE_CATEGORY }.let { genre ->
if (genre != null) { genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it.name) }?.let {
val pair = when (genre.name) { binding.genre.setBackgroundColor(it.first)
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) it.second
"manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) } ?: genre?.name ?: itemView.context.getString(R.string.unknown)
"artist-cg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg)
"game-cg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg)
"artbook" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.artbook)
"webtoon" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.webtoon)
else -> Pair("", 0)
} }
if (pair.first.isNotBlank()) {
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = genre.name
} else binding.genre.setText(R.string.unknown)
binding.uploader.text = meta.uploaderDisp ?: meta.uploader ?: "" binding.uploader.text = meta.uploaderDisp ?: meta.uploader ?: ""
binding.size.text = meta.fileSize ?: itemView.context.getString(R.string.unknown) binding.size.text = meta.fileSize ?: itemView.context.getString(R.string.unknown)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)?.apply { binding.size.bindDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.size.setCompoundDrawables(this, null, null, null)
}
binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pages ?: 0, meta.pages ?: 0) binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pages ?: 0, meta.pages ?: 0)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply { binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
val ratingFloat = meta.averageRating?.toFloat() val ratingFloat = meta.averageRating?.toFloat()
val name = when (((ratingFloat ?: 100F) * 2).roundToInt()) {
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}
binding.ratingBar.rating = ratingFloat ?: 0F binding.ratingBar.rating = ratingFloat ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((ratingFloat ?: 0F) * 100.0) / 100.0).toString() + " - " + itemView.context.getString(name) binding.rating.text = (round((ratingFloat ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(itemView.context, ratingFloat?.times(2))
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.genre, binding.genre,
binding.pages, binding.pages,

View File

@ -1,22 +1,19 @@
package exh.ui.metadata.adapters package exh.ui.metadata.adapters
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx import exh.metadata.MetadataUtil
import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.bindDrawable
import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata
import exh.ui.metadata.MetadataViewController import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -26,7 +23,6 @@ import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import java.util.Date import java.util.Date
import kotlin.math.round import kotlin.math.round
import kotlin.math.roundToInt
class TsuminoDescriptionAdapter( class TsuminoDescriptionAdapter(
private val controller: MangaController private val controller: MangaController
@ -52,64 +48,26 @@ class TsuminoDescriptionAdapter(
val meta = controller.presenter.meta val meta = controller.presenter.meta
if (meta == null || meta !is TsuminoSearchMetadata) return if (meta == null || meta !is TsuminoSearchMetadata) return
val genre = meta.category binding.genre.text = meta.category?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
if (genre != null) { binding.genre.setBackgroundColor(it.first)
val pair = when (genre) { it.second
"Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) } ?: meta.category ?: itemView.context.getString(R.string.unknown)
"Manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga)
"Artist CG" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg)
"Game CG" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg)
"Video" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.video)
else -> Pair("", 0)
}
if (pair.first.isNotBlank()) {
binding.genre.setBackgroundColor(Color.parseColor(pair.first))
binding.genre.text = itemView.context.getString(pair.second)
} else binding.genre.text = genre
} else binding.genre.setText(R.string.unknown)
binding.favorites.text = (meta.favorites ?: 0).toString() binding.favorites.text = (meta.favorites ?: 0).toString()
ContextCompat.getDrawable(itemView.context, R.drawable.ic_book_24dp)?.apply { binding.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.favorites.setCompoundDrawables(this, null, null, null)
}
binding.whenPosted.text = TsuminoSearchMetadata.TSUMINO_DATE_FORMAT.format(Date(meta.uploadDate ?: 0)) binding.whenPosted.text = TsuminoSearchMetadata.TSUMINO_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown) binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown)
binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply { binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
val name = when (((meta.averageRating ?: 100F) * 2).roundToInt()) {
0 -> R.string.rating0
1 -> R.string.rating1
2 -> R.string.rating2
3 -> R.string.rating3
4 -> R.string.rating4
5 -> R.string.rating5
6 -> R.string.rating6
7 -> R.string.rating7
8 -> R.string.rating8
9 -> R.string.rating9
10 -> R.string.rating10
else -> R.string.no_rating
}
binding.ratingBar.rating = meta.averageRating ?: 0F binding.ratingBar.rating = meta.averageRating ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((meta.averageRating ?: 0F) * 100.0) / 100.0).toString() + " - " + itemView.context.getString(name) binding.rating.text = (round((meta.averageRating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(itemView.context, meta.averageRating?.times(2))
ContextCompat.getDrawable(itemView.context, R.drawable.ic_info_24dp)?.apply { binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
listOf( listOf(
binding.favorites, binding.favorites,

View File

@ -1,24 +0,0 @@
package exh.util
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class CachedField<T>(private val expiresAfterMs: Long) {
@Volatile
private var initTime: Long = -1
@Volatile
private var content: T? = null
private val mutex = Mutex()
suspend fun obtain(producer: suspend () -> T): T {
return mutex.withLock {
if (initTime < 0 || System.currentTimeMillis() - initTime > expiresAfterMs) {
content = producer()
}
content!!
}
}
}

View File

@ -1,66 +0,0 @@
package exh.util
/**
* Reads entire `JsonObject`s and `JsonArray`s from `JsonReader`s
*
* @author nulldev
*/
import com.google.gson.JsonArray
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import java.math.BigDecimal
fun JsonReader.nextJsonObject(): JsonObject {
beginObject()
val obj = JsonObject()
while (hasNext()) {
val name = nextName()
when (peek()) {
JsonToken.BEGIN_ARRAY -> obj.add(name, nextJsonArray())
JsonToken.BEGIN_OBJECT -> obj.add(name, nextJsonObject())
JsonToken.NULL -> {
nextNull()
obj.add(name, JsonNull.INSTANCE)
}
JsonToken.BOOLEAN -> obj.addProperty(name, nextBoolean())
JsonToken.NUMBER -> obj.addProperty(name, BigDecimal(nextString()))
JsonToken.STRING -> obj.addProperty(name, nextString())
else -> skipValue()
}
}
endObject()
return obj
}
fun JsonReader.nextJsonArray(): JsonArray {
beginArray()
val arr = JsonArray()
while (hasNext()) {
when (peek()) {
JsonToken.BEGIN_ARRAY -> arr.add(nextJsonArray())
JsonToken.BEGIN_OBJECT -> arr.add(nextJsonObject())
JsonToken.NULL -> {
nextNull()
arr.add(JsonNull.INSTANCE)
}
JsonToken.BOOLEAN -> arr.add(nextBoolean())
JsonToken.NUMBER -> arr.add(BigDecimal(nextString()))
JsonToken.STRING -> arr.add(nextString())
else -> skipValue()
}
}
endArray()
return arr
}

View File

@ -1,5 +1,11 @@
package exh.util package exh.util
fun Float.floor(): Int = kotlin.math.floor(this).toInt() import kotlin.math.floor
fun Double.floor(): Int = kotlin.math.floor(this).toInt() fun Float.floor(): Int = floor(this).toInt()
fun Double.floor(): Int = floor(this).toInt()
fun Int.nullIfZero() = if (this == 0) null else this
fun Long.nullIfZero() = if (this == 0L) null else this

View File

@ -1,368 +0,0 @@
package exh.util
import android.util.SparseArray
import java.util.AbstractMap
import java.util.LinkedList
class NakedTrieNode<T>(val key: Int, var parent: NakedTrieNode<T>?) {
val children = SparseArray<NakedTrieNode<T>>(1)
var hasData: Boolean = false
var data: T? = null
// Walks in ascending order
// Consumer should return true to continue walking, false to stop walking
inline fun walk(prefix: String, consumer: (String, T) -> Boolean, leavesOnly: Boolean) {
// Special case root
if (hasData && (!leavesOnly || children.size() <= 0)) {
if (!consumer(prefix, data!! as T)) return
}
val stack = LinkedList<Pair<String, NakedTrieNode<T>>>()
SparseArrayValueCollection(children, true).forEach {
stack += prefix + it.key.toChar() to it
}
while (!stack.isEmpty()) {
val (key, bottom) = stack.removeLast()
SparseArrayValueCollection(bottom.children, true).forEach {
stack += key + it.key.toChar() to it
}
if (bottom.hasData && (!leavesOnly || bottom.children.size() <= 0)) {
if (!consumer(key, bottom.data!! as T)) return
}
}
}
fun getAsNode(key: String): NakedTrieNode<T>? {
var current = this
for (c in key) {
current = current.children.get(c.toInt()) ?: return null
if (!current.hasData) return null
}
return current
}
}
/**
* Fast, memory efficient and flexible trie implementation with implementation details exposed
*/
class NakedTrie<T> : MutableMap<String, T> {
/**
* Returns the number of key/value pairs in the map.
*/
override var size: Int = 0
private set
/**
* Returns `true` if the map is empty (contains no elements), `false` otherwise.
*/
override fun isEmpty() = size <= 0
/**
* Removes all elements from this map.
*/
override fun clear() {
root.children.clear()
root.hasData = false
root.data = null
size = 0
}
val root = NakedTrieNode<T>(-1, null)
private var version: Long = 0
override fun put(key: String, value: T): T? {
// Traverse to node location in tree, making parent nodes if required
var current = root
for (c in key) {
val castedC = c.toInt()
var node = current.children.get(castedC)
if (node == null) {
node = NakedTrieNode(castedC, current)
current.children.put(castedC, node)
}
current = node
}
// Add data to node or replace existing data
val previous = if (current.hasData) {
current.data
} else {
current.hasData = true
size++
null
}
current.data = value
version++
return previous
}
override fun get(key: String): T? {
val current = getAsNode(key) ?: return null
return if (current.hasData) current.data else null
}
fun getAsNode(key: String): NakedTrieNode<T>? {
return root.getAsNode(key)
}
override fun containsKey(key: String): Boolean {
var current = root
for (c in key) {
current = current.children.get(c.toInt()) ?: return false
if (!current.hasData) return false
}
return current.hasData
}
/**
* Removes the specified key and its corresponding value from this map.
*
* @return the previous value associated with the key, or `null` if the key was not present in the map.
*/
override fun remove(key: String): T? {
// Traverse node tree while keeping track of the nodes we have visited
val nodeStack = LinkedList<NakedTrieNode<T>>()
for (c in key) {
val bottomOfStack = nodeStack.last
val current = bottomOfStack.children.get(c.toInt()) ?: return null
if (!current.hasData) return null
nodeStack.add(bottomOfStack)
}
// Mark node as having no data
val bottomOfStack = nodeStack.last
bottomOfStack.hasData = false
val oldData = bottomOfStack.data
bottomOfStack.data = null // Clear data field for GC
// Remove nodes that we visited that are useless
for (curBottom in nodeStack.descendingIterator()) {
val parent = curBottom.parent ?: break
if (!curBottom.hasData && curBottom.children.size() <= 0) {
// No data or child nodes, this node is useless, discard
parent.children.remove(curBottom.key)
} else break
}
version++
size--
return oldData
}
/**
* Updates this map with key/value pairs from the specified map [from].
*/
override fun putAll(from: Map<out String, T>) {
// No way to optimize this so yeah...
from.forEach { (s, u) ->
put(s, u)
}
}
// Walks in ascending order
// Consumer should return true to continue walking, false to stop walking
inline fun walk(consumer: (String, T) -> Boolean) {
walk(consumer, false)
}
// Walks in ascending order
// Consumer should return true to continue walking, false to stop walking
inline fun walk(consumer: (String, T) -> Boolean, leavesOnly: Boolean) {
root.walk("", consumer, leavesOnly)
}
fun getOrPut(key: String, producer: () -> T): T {
// Traverse to node location in tree, making parent nodes if required
var current = root
for (c in key) {
val castedC = c.toInt()
var node = current.children.get(castedC)
if (node == null) {
node = NakedTrieNode(castedC, current)
current.children.put(castedC, node)
}
current = node
}
// Add data to node or replace existing data
if (!current.hasData) {
current.hasData = true
current.data = producer()
size++
version++
}
return current.data!!
}
// Includes root
fun subMap(prefix: String, leavesOnly: Boolean = false): Map<String, T> {
val node = getAsNode(prefix) ?: return emptyMap()
return object : Map<String, T> {
/**
* Returns a read-only [Set] of all key/value pairs in this map.
*/
override val entries: Set<Map.Entry<String, T>>
get() {
val out = mutableSetOf<Map.Entry<String, T>>()
node.walk(
"",
{ k, v ->
out.add(AbstractMap.SimpleImmutableEntry(k, v))
true
},
leavesOnly
)
return out
}
/**
* Returns a read-only [Set] of all keys in this map.
*/
override val keys: Set<String>
get() {
val out = mutableSetOf<String>()
node.walk(
"",
{ k, _ ->
out.add(k)
true
},
leavesOnly
)
return out
}
/**
* Returns the number of key/value pairs in the map.
*/
override val size: Int get() {
var s = 0
node.walk("", { _, _ -> s++; true }, leavesOnly)
return s
}
/**
* Returns a read-only [Collection] of all values in this map. Note that this collection may contain duplicate values.
*/
override val values: Collection<T>
get() {
val out = mutableSetOf<T>()
node.walk(
"",
{ _, v ->
out.add(v)
true
},
leavesOnly
)
return out
}
/**
* Returns `true` if the map contains the specified [key].
*/
override fun containsKey(key: String): Boolean {
if (!key.startsWith(prefix)) return false
val childNode = node.getAsNode(key.removePrefix(prefix)) ?: return false
return childNode.hasData && (!leavesOnly || childNode.children.size() <= 0)
}
/**
* Returns `true` if the map maps one or more keys to the specified [value].
*/
override fun containsValue(value: T): Boolean {
node.walk(
"",
{ _, v ->
if (v == value) return true
true
},
leavesOnly
)
return false
}
/**
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
*/
override fun get(key: String): T? {
if (!key.startsWith(prefix)) return null
val childNode = node.getAsNode(key.removePrefix(prefix)) ?: return null
if (!childNode.hasData || (leavesOnly && childNode.children.size() > 0)) return null
return childNode.data
}
/**
* Returns `true` if the map is empty (contains no elements), `false` otherwise.
*/
override fun isEmpty(): Boolean {
if (node.children.size() <= 0 && !root.hasData) return true
if (!leavesOnly) return false
node.walk("", { _, _ -> return false }, leavesOnly)
return true
}
}
}
// Slow methods below
/**
* Returns `true` if the map maps one or more keys to the specified [value].
*/
override fun containsValue(value: T): Boolean {
walk { _, t ->
if (t == value) {
return true
}
true
}
return false
}
/**
* Returns a [MutableSet] of all key/value pairs in this map.
*/
override val entries: MutableSet<MutableMap.MutableEntry<String, T>>
get() = FakeMutableSet.fromSet(
mutableSetOf<MutableMap.MutableEntry<String, T>>().apply {
walk { k, v ->
this += FakeMutableEntry.fromPair(k, v)
true
}
}
)
/**
* Returns a [MutableSet] of all keys in this map.
*/
override val keys: MutableSet<String>
get() = FakeMutableSet.fromSet(
mutableSetOf<String>().apply {
walk { k, _ ->
this += k
true
}
}
)
/**
* Returns a [MutableCollection] of all values in this map. Note that this collection may contain duplicate values.
*/
override val values: MutableCollection<T>
get() = FakeMutableCollection.fromCollection(
mutableListOf<T>().apply {
walk { _, v ->
this += v
true
}
}
)
}

View File

@ -14,7 +14,7 @@ private val galleryAdder by lazy {
/** /**
* A version of fetchSearchManga that supports URL importing * A version of fetchSearchManga that supports URL importing
*/ */
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>) = fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when { when {
query.startsWith("http://") || query.startsWith("https://") -> { query.startsWith("http://") || query.startsWith("https://") -> {
Observable.fromCallable { Observable.fromCallable {

View File

@ -10,7 +10,7 @@ import exh.metadata.metadata.base.RaisedTag
import exh.nHentaiSourceIds import exh.nHentaiSourceIds
import java.util.Locale import java.util.Locale
class SourceTagsUtil { object SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? { fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? {
return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId in nHentaiSourceIds || sourceId in hitomiSourceIds) { return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId in nHentaiSourceIds || sourceId in hitomiSourceIds) {
val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) RaisedTag(namespace, tag, TAG_TYPE_DEFAULT) else null val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) RaisedTag(namespace, tag, TAG_TYPE_DEFAULT) else null
@ -47,8 +47,6 @@ class SourceTagsUtil {
} else { } else {
"$namespace:$tag" "$namespace:$tag"
} }
companion object {
fun Manga.getRaisedTags(genres: List<String>? = null): List<RaisedTag>? = (genres ?: this.getGenres())?.map { parseTag(it) }
fun parseTag(tag: String) = RaisedTag( fun parseTag(tag: String) = RaisedTag(
( (
@ -95,4 +93,7 @@ class SourceTagsUtil {
private const val TAG_TYPE_DEFAULT = 1 private const val TAG_TYPE_DEFAULT = 1
} }
fun Manga.getRaisedTags(genres: List<String>? = null): List<RaisedTag>? = (genres ?: this.getGenres())?.map {
SourceTagsUtil.parseTag(it)
} }

View File

@ -1,73 +0,0 @@
package exh.util
import android.util.SparseArray
import java.util.AbstractMap
class SparseArrayKeyCollection(val sparseArray: SparseArray<out Any?>, var reverse: Boolean = false) : AbstractCollection<Int>() {
override val size get() = sparseArray.size()
override fun iterator() = object : Iterator<Int> {
private var index: Int = 0
/**
* Returns `true` if the iteration has more elements.
*/
override fun hasNext() = index < sparseArray.size()
/**
* Returns the next element in the iteration.
*/
override fun next(): Int {
var idx = index++
if (reverse) idx = sparseArray.size() - 1 - idx
return sparseArray.keyAt(idx)
}
}
}
class SparseArrayValueCollection<E>(val sparseArray: SparseArray<E>, var reverse: Boolean = false) : AbstractCollection<E>() {
override val size get() = sparseArray.size()
override fun iterator() = object : Iterator<E> {
private var index: Int = 0
/**
* Returns `true` if the iteration has more elements.
*/
override fun hasNext() = index < sparseArray.size()
/**
* Returns the next element in the iteration.
*/
override fun next(): E {
var idx = index++
if (reverse) idx = sparseArray.size() - 1 - idx
return sparseArray.valueAt(idx)
}
}
}
class SparseArrayCollection<E>(val sparseArray: SparseArray<E>, var reverse: Boolean = false) : AbstractCollection<Map.Entry<Int, E>>() {
override val size get() = sparseArray.size()
override fun iterator() = object : Iterator<Map.Entry<Int, E>> {
private var index: Int = 0
/**
* Returns `true` if the iteration has more elements.
*/
override fun hasNext() = index < sparseArray.size()
/**
* Returns the next element in the iteration.
*/
override fun next(): Map.Entry<Int, E> {
var idx = index++
if (reverse) idx = sparseArray.size() - 1 - idx
return AbstractMap.SimpleImmutableEntry(
sparseArray.keyAt(idx),
sparseArray.valueAt(idx)
)
}
}
}

View File

@ -5,9 +5,7 @@ import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import exh.EH_SOURCE_ID import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID import exh.EXH_SOURCE_ID
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT import exh.metadata.metadata.EHentaiSearchMetadata
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_NORMAL
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_WEAK
/** /**
* Replaces chips in a ChipGroup. * Replaces chips in a ChipGroup.
@ -29,7 +27,7 @@ fun ChipGroup.setChipsExtended(items: List<String>?, onClick: (item: String) ->
fun makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongClick: (item: String) -> Unit = {}, sourceId: Long, context: Context, namespace: String? = null, type: Int? = null): Chip { fun makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongClick: (item: String) -> Unit = {}, sourceId: Long, context: Context, namespace: String? = null, type: Int? = null): Chip {
return Chip(context).apply { return Chip(context).apply {
text = item text = item
val search = (if (namespace != null) SourceTagsUtil().getWrappedTag(sourceId, namespace = namespace, tag = item) else SourceTagsUtil().getWrappedTag(sourceId, fullTag = item)) ?: item val search = (if (namespace != null) SourceTagsUtil.getWrappedTag(sourceId, namespace = namespace, tag = item) else SourceTagsUtil.getWrappedTag(sourceId, fullTag = item)) ?: item
setOnClickListener { onClick(search) } setOnClickListener { onClick(search) }
setOnLongClickListener { setOnLongClickListener {
onLongClick(search) onLongClick(search)
@ -37,9 +35,9 @@ fun makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongCli
} }
if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID) { if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID) {
chipStrokeWidth = when (type) { chipStrokeWidth = when (type) {
TAG_TYPE_NORMAL -> 5F EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 5F
TAG_TYPE_LIGHT -> 3F EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 3F
TAG_TYPE_WEAK -> 0F EHentaiSearchMetadata.TAG_TYPE_WEAK -> 0F
else -> chipStrokeWidth else -> chipStrokeWidth
} }
} }

View File

@ -1,8 +0,0 @@
package exh.util
import android.content.Context
fun dpToPx(context: Context, dp: Int): Int {
val scale = context.resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}

View File

@ -10,10 +10,11 @@ import com.afollestad.materialdialogs.customview.customView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference
import exh.md.utils.MdUtil import exh.source.getMainSource
import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.login import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.login
import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.password import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.password
import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.two_factor_check import kotlinx.android.synthetic.main.pref_site_login_two_factor_auth.view.two_factor_check
@ -29,7 +30,7 @@ import uy.kohesive.injekt.api.get
class MangadexLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle = bundle) { class MangadexLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle = bundle) {
val source by lazy { MdUtil.getEnabledMangaDex() } val source = Injekt.get<SourceManager>().get(args.getLong("key", 0))?.getMainSource() as? MangaDex
val service = Injekt.get<TrackManager>().mdList val service = Injekt.get<TrackManager>().mdList

View File

@ -7,17 +7,21 @@ import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import exh.md.utils.MdUtil import exh.source.getMainSource
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class MangadexLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) { class MangadexLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) {
val source by lazy { MdUtil.getEnabledMangaDex() } val source = Injekt.get<SourceManager>().get(args.getLong("key", 0))?.getMainSource() as? MangaDex
val trackManager: TrackManager by injectLazy() val trackManager: TrackManager by injectLazy()

View File

@ -1,93 +0,0 @@
package org.vepta.vdm
import java.nio.ByteBuffer
/**
* Simple cursor for use on byte arrays
* @author nulldev
*/
class ByteCursor(val content: ByteArray) {
var index = -1
private set
private var mark = -1
fun mark() {
mark = index
}
fun jumpToMark() {
index = mark
}
fun jumpToIndex(index: Int) {
this.index = index
}
fun next(): Byte {
return content[++index]
}
fun next(count: Int): ByteArray {
val res = content.sliceArray(index + 1..index + count)
skip(count)
return res
}
// Used to perform conversions
private fun byteBuffer(count: Int): ByteBuffer {
return ByteBuffer.wrap(next(count))
}
// Epic hack to get an unsigned short properly...
fun fakeNextShortInt(): Int = ByteBuffer
.wrap(arrayOf(0x00, 0x00, *next(2).toTypedArray()).toByteArray())
.getInt(0)
// fun nextShort(): Short = byteBuffer(2).getShort(0)
fun nextInt(): Int = byteBuffer(4).getInt(0)
fun nextLong(): Long = byteBuffer(8).getLong(0)
fun nextFloat(): Float = byteBuffer(4).getFloat(0)
fun nextDouble(): Double = byteBuffer(8).getDouble(0)
fun skip(count: Int) {
index += count
}
fun expect(vararg bytes: Byte) {
if (bytes.size > remaining()) {
throw IllegalStateException("Unexpected end of content!")
}
for (i in 0..bytes.lastIndex) {
val expected = bytes[i]
val actual = content[index + i + 1]
if (expected != actual) {
throw IllegalStateException("Unexpected content (expected: $expected, actual: $actual)!")
}
}
index += bytes.size
}
fun checkEqual(vararg bytes: Byte): Boolean {
if (bytes.size > remaining()) {
return false
}
for (i in 0..bytes.lastIndex) {
val expected = bytes[i]
val actual = content[index + i + 1]
if (expected != actual) {
return false
}
}
return true
}
fun atEnd() = index >= content.size - 1
fun remaining() = content.size - index - 1
}

View File

@ -34,20 +34,18 @@ class FilterSerializer {
) )
fun serialize(filters: FilterList) = buildJsonArray { fun serialize(filters: FilterList) = buildJsonArray {
filters.forEach { filters.filterIsInstance<Filter<Any?>>().forEach {
@Suppress("UNCHECKED_CAST") add(serialize(it))
add(serialize(it as Filter<Any?>))
} }
} }
fun serialize(filter: Filter<Any?>): JsonObject { fun serialize(filter: Filter<Any?>): JsonObject {
for (serializer in serializers) { return serializers
if (filter::class.isSubclassOf(serializer.clazz)) { .filterIsInstance<Serializer<Filter<Any?>>>()
// TODO Not sure how to deal with the mess of types here .firstOrNull {
@Suppress("UNCHECKED_CAST") filter::class.isSubclassOf(it.clazz)
serializer as Serializer<Filter<Any?>> }?.let { serializer ->
buildJsonObject {
return buildJsonObject {
with(serializer) { serialize(filter) } with(serializer) { serialize(filter) }
val classMappings = mutableListOf<Pair<String, Any>>() val classMappings = mutableListOf<Pair<String, Any>>()
@ -66,27 +64,22 @@ class FilterSerializer {
put(TYPE, serializer.type) put(TYPE, serializer.type)
} }
} } ?: throw IllegalArgumentException("Cannot serialize this Filter object!")
}
throw IllegalArgumentException("Cannot serialize this Filter object!")
} }
fun deserialize(filters: FilterList, json: JsonArray) { fun deserialize(filters: FilterList, json: JsonArray) {
filters.zip(json).forEach { (filter, obj) -> filters.filterIsInstance<Filter<Any?>>().zip(json).forEach { (filter, obj) ->
@Suppress("UNCHECKED_CAST") deserialize(filter, obj.jsonObject)
deserialize(filter as Filter<Any?>, obj.jsonObject)
} }
} }
fun deserialize(filter: Filter<Any?>, json: JsonObject) { fun deserialize(filter: Filter<Any?>, json: JsonObject) {
val serializer = serializers.find { val serializer = serializers
.filterIsInstance<Serializer<Filter<Any?>>>()
.firstOrNull {
it.type == json[TYPE]!!.jsonPrimitive.content it.type == json[TYPE]!!.jsonPrimitive.content
} ?: throw IllegalArgumentException("Cannot deserialize this type!") } ?: throw IllegalArgumentException("Cannot deserialize this type!")
// TODO Not sure how to deal with the mess of types here
@Suppress("UNCHECKED_CAST")
serializer as Serializer<Filter<Any?>>
serializer.deserialize(json, filter) serializer.deserialize(json, filter)
serializer.mappings().forEach { serializer.mappings().forEach {