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.insertFlatMetadata
import exh.savedsearches.JsonSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString

View File

@ -40,7 +40,7 @@ import exh.MERGED_SOURCE_ID
import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil
import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
import exh.util.asObservable
import exh.util.await
import exh.util.awaitSingle
@ -425,7 +425,7 @@ class LibraryUpdateService(
* @return a pair of the inserted and removed chapters.
*/
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
if (preferences.autoUpdateMetadata()) {
@ -447,21 +447,6 @@ class LibraryUpdateService(
.subscribe()
}
// SY -->
if (source.getMainSource() is MangaDex && trackManager.mdList.isLogged) {
try {
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()
}
} catch (e: Exception) {
XLog.e(e)
}
}
// SY <--
return (
/* SY --> */ if (source is MergedSource) runBlocking { source.fetchChaptersAndSync(manga, false).asObservable() }
else /* SY <-- */ source.fetchChapterList(manga)
@ -469,12 +454,16 @@ class LibraryUpdateService(
// 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()
if (source is MangaDex && trackManager.mdList.isLogged) {
try {
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()
}
} catch (e: Exception) {
XLog.e(e)
}
}
}
@ -548,7 +537,7 @@ class LibraryUpdateService(
// filter all follows from Mangadex and only add reading or rereading manga to library
private fun syncFollows(): Observable<LibraryManga> {
val count = AtomicInteger(0)
val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager)!!
val mangaDex = MdUtil.getEnabledMangaDex(preferences, sourceManager) ?: return Observable.empty()
return mangaDex.fetchAllFollows(true)
.asObservable()
.map { listManga ->
@ -582,7 +571,7 @@ class LibraryUpdateService(
.doOnCompleted {
notifier.cancelProgressNotification()
}
.map { LibraryManga() }
.flatMap { Observable.empty() }
}
/**
@ -616,7 +605,7 @@ class LibraryUpdateService(
.doOnCompleted {
notifier.cancelProgressNotification()
}
.map { LibraryManga() }
.flatMap { Observable.empty() }
}
// SY <--

View File

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

View File

@ -104,7 +104,7 @@ class NHentai(delegate: HttpSource, val context: Context) :
tags.clear()
}.forEach {
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.md.similar.ui.EnableMangaDexSimilarDialogController
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.coroutines.Job
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.toMangaThumbnail
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.base.RaisedSearchMetadata
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.genre
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)
} 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() }
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
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.widget.SimpleNavigationView
import exh.savedsearches.EXHSavedSearch
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
class SourceFilterSheet(
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.widget.AutofitRecyclerView
import exh.metadata.metadata.base.RaisedTag
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.util.SourceTagsUtil.Companion.TAG_TYPE_EXCLUDE
import exh.util.SourceTagsUtil.Companion.getRaisedTags
import exh.util.SourceTagsUtil.Companion.parseTag
import exh.source.getMainSource
import exh.util.SourceTagsUtil
import exh.util.getRaisedTags
import kotlinx.android.synthetic.main.source_compact_grid_item.view.card
import kotlinx.android.synthetic.main.source_compact_grid_item.view.gradient
import uy.kohesive.injekt.Injekt
@ -160,14 +159,14 @@ class LibraryItem(val manga: LibraryManga, private val libraryDisplayMode: Prefe
}
cleanConstraint.split(",").all {
if (raisedTags == null) containsGenre(it.trim(), genres) else containsRaisedGenre(
parseTag(it.trim()),
SourceTagsUtil.parseTag(it.trim()),
raisedTags
)
}
} else if (raisedTags == null) {
containsGenre(constraint, genres)
} 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 {
(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
} else {
genre != null

View File

@ -103,7 +103,7 @@ import exh.isEhBasedSource
import exh.md.similar.ui.MangaDexSimilarController
import exh.metadata.metadata.base.FlatMetadata
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.toolbar
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.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
import exh.util.asObservable
import exh.util.await
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 exh.EH_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.chapter_description
import kotlinx.android.synthetic.main.chapters_item.chapter_title
@ -56,7 +56,7 @@ class ChapterHolder(
if (chapter.date_upload > 0) {
// SY -->
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)))
}
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 exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
class ChaptersSettingsSheet(
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.util.system.copyToClipboard
import exh.MERGED_SOURCE_ID
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -107,7 +107,7 @@ class MangaInfoHeaderAdapter(
with(binding.btnTracking) {
// SY -->
val sourceIsMangaDex = source.let { it.getMainSource() is MangaDex }
val sourceIsMangaDex = source.getMainSource() is MangaDex
// SY <--
if (trackManager.hasLoggedServices(/* SY --> */sourceIsMangaDex/* SY <-- */)) {
isVisible = true
@ -169,7 +169,7 @@ class MangaInfoHeaderAdapter(
val author = binding.mangaAuthor.text.toString()
controller.activity?.copyToClipboard(
author,
SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = author) ?: author
SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = author) ?: author
)
// SY <--
}
@ -179,7 +179,7 @@ class MangaInfoHeaderAdapter(
.onEach {
// SY -->
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 <--
}
.launchIn(scope)
@ -190,7 +190,7 @@ class MangaInfoHeaderAdapter(
val artist = binding.mangaArtist.text.toString()
controller.activity?.copyToClipboard(
artist,
SourceTagsUtil().getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist
SourceTagsUtil.getWrappedTag(source.id, namespace = "artist", tag = artist) ?: artist
)
// SY <--
}
@ -200,7 +200,7 @@ class MangaInfoHeaderAdapter(
.onEach {
// SY -->
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 <--
}
.launchIn(scope)

View File

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

View File

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

View File

@ -34,12 +34,12 @@ class SettingsMangaDexController :
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
titleRes = R.string.mangadex_specific_settings
if (mdex == null) return@apply
val sourcePreference = MangaDexLoginPreference(context, mdex!!).apply {
title = mdex!!.name + " Login"
val mdex = mdex ?: return@apply
val sourcePreference = MangaDexLoginPreference(context, mdex).apply {
title = mdex.name + " Login"
key = getSourceKey(source.id)
setOnLoginClickListener {
if (mdex!!.isLogged()) {
if (mdex.isLogged()) {
val dialog = MangadexLogoutDialog(source)
dialog.targetController = this@SettingsMangaDexController
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.all.EHentai
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
import uy.kohesive.injekt.injectLazy
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.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.SourceManager.Companion.currentDelegatedSources
import exh.EH_SOURCE_ID
import exh.EXHMigrations
import exh.EXH_SOURCE_ID
@ -69,7 +68,7 @@ object DebugFunctions {
}
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() {
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.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
/**
* 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.insertFlatMetadata
import exh.util.floor
import exh.util.nullIfZero
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
@ -126,7 +127,7 @@ class ApiMangaParser(private val langs: List<String>) {
genres.add("Hentai")
}
if (tags.size != 0) tags.clear()
if (tags.isNotEmpty()) tags.clear()
tags += genres.map { RaisedTag(null, it, MangaDexSearchMetadata.TAG_TYPE_DEFAULT) }
} catch (e: Exception) {
XLog.e(e)
@ -157,10 +158,8 @@ class ApiMangaParser(private val langs: List<String>) {
}
}
val removeOneshots = filteredChapters.asSequence()
.map { it.value.chapter!!.toDoubleOrNull() }
.filter { it != null }
.map { it!!.floor() }
.filter { it != 0 }
.map { it.value.chapter?.toDoubleOrNull()?.floor()?.nullIfZero() }
.filterNotNull()
.toList().distinctBy { it }
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.POST
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.MetadataMangasPage
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.utils.FollowStatus
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.util.floor
import kotlinx.coroutines.Dispatchers
@ -101,7 +100,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
if (result[0].chapter.isNotBlank()) {
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
}
return track
@ -111,7 +110,7 @@ class FollowsHandler(val client: OkHttpClient, val headers: Headers, val prefere
*
*/
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) {
client.newCall(
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,
CacheControl.FORCE_NETWORK
)
)
.execute()
.await()
} else {
val status = followStatus.int
client.newCall(
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,
CacheControl.FORCE_NETWORK
)
)
.execute()
.await()
}
response.body!!.string().isEmpty()
withContext(Dispatchers.IO) { response.body!!.string().isEmpty() }
}
}
suspend fun updateReadingProgress(track: Track): Boolean {
return withContext(Dispatchers.IO) {
val mangaID = getMangaId(track.tracking_url)
val mangaID = MdUtil.getMangaId(track.tracking_url)
val formBody = FormBody.Builder()
.add("chapter", track.last_chapter_read.toString())
XLog.d("chapter to update %s", track.last_chapter_read.toString())
val response = client.newCall(
POST(
"$baseUrl/ajax/actions.ajax.php?function=edit_progress&id=$mangaID",
"${MdUtil.baseUrl}/ajax/actions.ajax.php?function=edit_progress&id=$mangaID",
headers,
formBody.build()
)
).execute()
).await()
val response2 = client.newCall(
client.newCall(
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
)
)
.execute()
.await()
response.body!!.string().isEmpty()
withContext(Dispatchers.IO) { response.body!!.string().isEmpty() }
}
}
suspend fun updateRating(track: Track): Boolean {
return withContext(Dispatchers.IO) {
val mangaID = getMangaId(track.tracking_url)
val mangaID = MdUtil.getMangaId(track.tracking_url)
val response = client.newCall(
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
)
)
.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>> {
return withContext(Dispatchers.IO) {
val listManga = mutableListOf<Pair<SManga, MangaDexSearchMetadata>>()
val response = client.newCall(followsListRequest()).execute()
val response = client.newCall(followsListRequest()).await()
val mangasPage = followsParseMangaPage(response, forceHd)
listManga.addAll(
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 {
return withContext(Dispatchers.IO) {
val request = GET(
"$baseUrl${MdUtil.followsMangaApi}" + getMangaId(url),
"${MdUtil.baseUrl}${MdUtil.followsMangaApi}" + MdUtil.getMangaId(url),
headers,
CacheControl.FORCE_NETWORK
)
val response = client.newCall(request).execute()
val response = client.newCall(request).await()
val track = followStatusParse(response)
track

View File

@ -22,10 +22,10 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
// TODO make use of this
suspend fun fetchMangaAndChapterDetails(manga: SManga): Pair<SManga, List<SChapter>> {
return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga)).execute()
val response = client.newCall(apiRequest(manga)).await()
val parser = ApiMangaParser(langs)
val jsonData = response.body!!.string()
val jsonData = withContext(Dispatchers.IO) { response.body!!.string() }
if (response.code != 200) {
XLog.e("error from MangaDex with response code ${response.code} \n body: \n$jsonData")
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 {
return withContext(Dispatchers.IO) {
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)
}
}
suspend fun fetchMangaDetails(manga: SManga): SManga {
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()
manga.apply {
initialized = true
@ -82,7 +82,7 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
suspend fun fetchChapterList(manga: SManga): List<SChapter> {
return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga)).execute()
val response = client.newCall(apiRequest(manga)).await()
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.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.source.EnhancedHttpSource.Companion.getMainSource
import exh.source.getMainSource
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.SManga
import eu.kanade.tachiyomi.source.online.all.MangaDex
import exh.source.getMainSource
import exh.util.floor
import exh.util.nullIfZero
import kotlinx.serialization.json.Json
import org.jsoup.parser.Parser
import uy.kohesive.injekt.Injekt
@ -14,6 +16,7 @@ import uy.kohesive.injekt.api.get
import java.net.URI
import java.net.URISyntaxException
@Suppress("unused")
class MdUtil {
companion object {
@ -201,8 +204,8 @@ class MdUtil {
/*}*/
}.sortedByDescending { it.chapter_number }
remove0ChaptersFromCount.firstOrNull()?.let {
val chpNumber = it.chapter_number.floor()
remove0ChaptersFromCount.firstOrNull()?.let { chapter ->
val chpNumber = chapter.chapter_number.floor()
val allChapters = (1..chpNumber).toMutableSet()
remove0ChaptersFromCount.forEach {
@ -217,8 +220,9 @@ class MdUtil {
fun getEnabledMangaDex(preferences: PreferencesHelper = Injekt.get(), sourceManager: SourceManager = Injekt.get()): MangaDex? {
return getEnabledMangaDexs(preferences, sourceManager).let { mangadexs ->
val preferredMangaDexId = preferences.preferredMangaDexId().get().toLongOrNull()
mangadexs.firstOrNull { preferredMangaDexId != null && preferredMangaDexId != 0L && it.id == preferredMangaDexId } ?: mangadexs.firstOrNull()
preferences.preferredMangaDexId().get().toLongOrNull()?.nullIfZero()?.let { preferredMangaDexId ->
mangadexs.firstOrNull { it.id == preferredMangaDexId }
} ?: mangadexs.firstOrNull()
}
}
@ -226,10 +230,11 @@ class MdUtil {
val languages = preferences.enabledLanguages().get()
val disabledSourceIds = preferences.disabledSources().get()
return sourceManager.getDelegatedCatalogueSources()
return sourceManager.getVisibleOnlineSources()
.map { it.getMainSource() }
.filterIsInstance<MangaDex>()
.filter { it.lang in languages }
.filterNot { it.id.toString() in disabledSourceIds }
.filterIsInstance(MangaDex::class.java)
}
fun mapMdIdToMangaUrl(id: Int) = "/manga/$id/"

View File

@ -1,62 +1,118 @@
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.util.Locale
import kotlin.math.ln
import kotlin.math.pow
import kotlin.math.roundToInt
/**
* Metadata utils
*/
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
val unit = if (si) 1000 else 1024
if (bytes < unit) return "$bytes B"
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
}
private const val KB_FACTOR: Long = 1000
private const val KIB_FACTOR: Long = 1024
private const val MB_FACTOR = 1000 * KB_FACTOR
private const val MIB_FACTOR = 1024 * KIB_FACTOR
private const val GB_FACTOR = 1000 * MB_FACTOR
private const val GIB_FACTOR = 1024 * MIB_FACTOR
fun parseHumanReadableByteCount(arg0: String): Double? {
val spaceNdx = arg0.indexOf(" ")
val ret = java.lang.Double.parseDouble(arg0.substring(0, spaceNdx))
when (arg0.substring(spaceNdx + 1)) {
"GB" -> return ret * GB_FACTOR
"GiB" -> return ret * GIB_FACTOR
"MB" -> return ret * MB_FACTOR
"MiB" -> return ret * MIB_FACTOR
"KB" -> return ret * KB_FACTOR
"KiB" -> return ret * KIB_FACTOR
object MetadataUtil {
fun humanReadableByteCount(bytes: Long, si: Boolean): String {
val unit = if (si) 1000 else 1024
if (bytes < unit) return "$bytes B"
val exp = (ln(bytes.toDouble()) / ln(unit.toDouble())).toInt()
val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1] + if (si) "" else "i"
return String.format("%.1f %sB", bytes / unit.toDouble().pow(exp.toDouble()), pre)
}
return null
private const val KB_FACTOR: Long = 1000
private const val KIB_FACTOR: Long = 1024
private const val MB_FACTOR = 1000 * KB_FACTOR
private const val MIB_FACTOR = 1024 * KIB_FACTOR
private const val GB_FACTOR = 1000 * MB_FACTOR
private const val GIB_FACTOR = 1024 * MIB_FACTOR
fun parseHumanReadableByteCount(arg0: String): Double? {
val spaceNdx = arg0.indexOf(" ")
val ret = java.lang.Double.parseDouble(arg0.substring(0, spaceNdx))
when (arg0.substring(spaceNdx + 1)) {
"GB" -> return ret * GB_FACTOR
"GiB" -> return ret * GIB_FACTOR
"MB" -> return ret * MB_FACTOR
"MiB" -> return ret * MIB_FACTOR
"KB" -> return ret * KB_FACTOR
"KiB" -> return ret * KIB_FACTOR
}
return null
}
val ONGOING_SUFFIX = arrayOf(
"[ongoing]",
"(ongoing)",
"{ongoing}",
"<ongoing>",
"ongoing",
"[incomplete]",
"(incomplete)",
"{incomplete}",
"<incomplete>",
"incomplete",
"[wip]",
"(wip)",
"{wip}",
"<wip>",
"wip"
)
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) }
}
val ONGOING_SUFFIX = arrayOf(
"[ongoing]",
"(ongoing)",
"{ongoing}",
"<ongoing>",
"ongoing",
"[incomplete]",
"(incomplete)",
"{incomplete}",
"<incomplete>",
"incomplete",
"[wip]",
"(wip)",
"{wip}",
"<wip>",
"wip"
)
val EX_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US)
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.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.ONGOING_SUFFIX
import exh.metadata.humanReadableByteCount
import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable
import uy.kohesive.injekt.Injekt
@ -71,7 +69,7 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
// We default to completed
manga.status = SManga.COMPLETED
title?.let { t ->
ONGOING_SUFFIX.find {
MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true)
}?.let {
manga.status = SManga.ONGOING
@ -111,25 +109,26 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<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) }
exh?.let { pairs += Pair(context.getString(R.string.is_exhentai_gallery), context.getString(if (it) android.R.string.yes else android.R.string.no)) }
thumbnailUrl?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) }
title?.let { pairs += Pair(context.getString(R.string.title), it) }
altTitle?.let { pairs += Pair(context.getString(R.string.alt_title), it) }
genre?.let { pairs += Pair(context.getString(R.string.genre), it) }
datePosted?.let { pairs += Pair(context.getString(R.string.date_posted), EX_DATE_FORMAT.format(Date(it))) }
parent?.let { pairs += Pair(context.getString(R.string.parent), it) }
visible?.let { pairs += Pair(context.getString(R.string.visible), it) }
language?.let { pairs += Pair(context.getString(R.string.language), it) }
translated?.let { pairs += Pair("Translated", context.getString(if (it) android.R.string.yes else android.R.string.no)) }
size?.let { pairs += Pair(context.getString(R.string.gallery_size), humanReadableByteCount(it, true)) }
length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) }
favorites?.let { pairs += Pair(context.getString(R.string.total_favorites), it.toString()) }
ratingCount?.let { pairs += Pair(context.getString(R.string.total_ratings), it.toString()) }
averageRating?.let { pairs += Pair(context.getString(R.string.average_rating), it.toString()) }
aged.let { pairs += Pair(context.getString(R.string.aged), context.getString(if (it) android.R.string.yes else android.R.string.no)) }
lastUpdateCheck.let { pairs += Pair(context.getString(R.string.last_update_check), EX_DATE_FORMAT.format(Date(it))) }
gId?.let { pairs += context.getString(R.string.id) to it }
gToken?.let { pairs += context.getString(R.string.token) to 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) }
thumbnailUrl?.let { pairs += context.getString(R.string.thumbnail_url) to it }
title?.let { pairs += context.getString(R.string.title) to it }
altTitle?.let { pairs += context.getString(R.string.alt_title) to it }
genre?.let { pairs += context.getString(R.string.genre) to it }
datePosted?.let { pairs += context.getString(R.string.date_posted) to MetadataUtil.EX_DATE_FORMAT.format(Date(it)) }
parent?.let { pairs += context.getString(R.string.parent) to it }
visible?.let { pairs += context.getString(R.string.visible) to it }
language?.let { pairs += context.getString(R.string.language) to it }
translated?.let { pairs += "Translated" to context.getString(if (it) android.R.string.yes else android.R.string.no) }
size?.let { pairs += context.getString(R.string.gallery_size) to MetadataUtil.humanReadableByteCount(it, true) }
length?.let { pairs += context.getString(R.string.page_count) to it.toString() }
favorites?.let { pairs += context.getString(R.string.total_favorites) to it.toString() }
ratingCount?.let { pairs += context.getString(R.string.total_ratings) to it.toString() }
averageRating?.let { pairs += context.getString(R.string.average_rating) to it.toString() }
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
}

View File

@ -41,12 +41,12 @@ class EightMusesSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<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 = "/")
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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -235,21 +235,22 @@ class EnhancedHttpSource(
originalSource
}
}
companion object {
fun Source.getMainSource(): Source {
return if (this is EnhancedHttpSource) {
this.source()
} else {
this
}
}
fun Source.getOriginalSource(): Source {
return if (this is EnhancedHttpSource) {
this.originalSource
} else {
this
}
}
}
}
fun Source.getMainSource(): Source = if (this is EnhancedHttpSource) {
this.source()
} else {
this
}
fun Source.getOriginalSource(): Source = if (this is EnhancedHttpSource) {
this.originalSource
} else {
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.WebView
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.Element
import java.nio.charset.Charset
@ -24,7 +23,7 @@ class AutoSolvingWebViewClient(
val oReq = request.toOkHttpRequest()
val response = activity.httpClient.newCall(oReq).execute()
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(
"text/html",
"UTF-8",

View File

@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.metadata.metadata.base.FlatMetadata
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.api.get

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterEhBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.humanReadableByteCount
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.EHentaiSearchMetadata
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -25,7 +21,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.roundToInt
class EHentaiDescriptionAdapter(
private val controller: MangaController
@ -51,52 +46,23 @@ class EHentaiDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is EHentaiSearchMetadata) return
val genre = meta.genre
if (genre != null) {
val pair = when (genre) {
"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 = genre
} else binding.genre.setText(R.string.unknown)
binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: meta.genre ?: 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()
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.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown)
binding.size.text = humanReadableByteCount(meta.size ?: 0, true)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.size.setCompoundDrawables(this, null, null, null)
}
binding.size.text = MetadataUtil.humanReadableByteCount(meta.size ?: 0, true)
binding.size.bindDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)
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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
val language = meta.language ?: itemView.context.getString(R.string.unknown)
binding.language.text = if (meta.translated == true) {
@ -106,29 +72,11 @@ class EHentaiDescriptionAdapter(
}
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
@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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
listOf(
binding.favorites,

View File

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

View File

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

View File

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

View File

@ -1,22 +1,18 @@
package exh.ui.metadata.adapters
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHiBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.HitomiSearchMetadata
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -50,37 +46,16 @@ class HitomiDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is HitomiSearchMetadata) return
val genre = meta.genre
if (genre != null) {
val pair = when (genre) {
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)
"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)
}
binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: meta.genre ?: itemView.context.getString(R.string.unknown)
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.whenPosted.text = EX_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
binding.whenPosted.text = MetadataUtil.EX_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
binding.group.text = meta.group ?: 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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
listOf(
binding.genre,

View File

@ -4,15 +4,14 @@ import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.MetadataUtil.getRatingString
import exh.metadata.bindDrawable
import exh.metadata.metadata.MangaDexSearchMetadata
import exh.ui.metadata.MetadataViewController
import kotlinx.coroutines.CoroutineScope
@ -23,7 +22,6 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.round
import kotlin.math.roundToInt
class MangaDexDescriptionAdapter(
private val controller: MangaController
@ -50,31 +48,12 @@ class MangaDexDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is MangaDexSearchMetadata) return
val ratingFloat = meta.rating?.toFloatOrNull()?.div(2F)
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
val ratingFloat = meta.rating?.toFloatOrNull()
binding.ratingBar.rating = ratingFloat?.div(2F) ?: 0F
@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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
binding.rating.longClicks()
.onEach {

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterNhBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.EX_DATE_FORMAT
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.NHentaiSearchMetadata
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -52,60 +48,30 @@ class NHentaiDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is NHentaiSearchMetadata) return
var category: String? = null
meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags ->
if (tags.isNotEmpty()) category = tags.joinToString(transform = { it.name })
binding.genre.text = meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}.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 {
if (it == 0L) return@let
binding.favorites.text = it.toString()
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.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
}
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)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
@SuppressLint("SetTextI18n")
binding.id.text = "#" + (meta.nhId ?: 0)
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)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
listOf(
binding.favorites,

View File

@ -1,22 +1,19 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPeBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.PervEdenSearchMetadata
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -26,7 +23,6 @@ import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import java.util.Locale
import kotlin.math.round
import kotlin.math.roundToInt
class PervEdenDescriptionAdapter(
private val controller: MangaController
@ -52,22 +48,10 @@ class PervEdenDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is PervEdenSearchMetadata) return
val genre = meta.genre
if (genre != null) {
val pair = when (genre) {
"Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)
"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)
binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: meta.genre ?: itemView.context.getString(R.string.unknown)
val language = meta.lang
binding.language.text = if (language != null) {
@ -75,29 +59,11 @@ class PervEdenDescriptionAdapter(
local.displayName
} 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
@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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
listOf(
binding.genre,

View File

@ -1,23 +1,19 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPuBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.PururinSearchMetadata
import exh.metadata.metadata.PururinSearchMetadata.Companion.TAG_NAMESPACE_CATEGORY
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -26,7 +22,6 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import kotlin.math.round
import kotlin.math.roundToInt
class PururinDescriptionAdapter(
private val controller: MangaController
@ -52,64 +47,28 @@ class PururinDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is PururinSearchMetadata) return
val genre = meta.tags.find { it.namespace == TAG_NAMESPACE_CATEGORY }
if (genre != null) {
val pair = when (genre.name) {
"doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)
"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)
"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.genre.text = meta.tags.find { it.namespace == PururinSearchMetadata.TAG_NAMESPACE_CATEGORY }.let { genre ->
genre?.let { MetadataUtil.getGenreAndColour(itemView.context, it.name) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: genre?.name ?: itemView.context.getString(R.string.unknown)
}
binding.uploader.text = meta.uploaderDisp ?: meta.uploader ?: ""
binding.size.text = meta.fileSize ?: itemView.context.getString(R.string.unknown)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.size.setCompoundDrawables(this, null, null, null)
}
binding.size.bindDrawable(itemView.context, R.drawable.ic_outline_sd_card_24)
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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
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
@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(
binding.genre,
binding.pages,

View File

@ -1,22 +1,19 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.TsuminoSearchMetadata
import exh.ui.metadata.MetadataViewController
import exh.util.SourceTagsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -26,7 +23,6 @@ import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import java.util.Date
import kotlin.math.round
import kotlin.math.roundToInt
class TsuminoDescriptionAdapter(
private val controller: MangaController
@ -52,64 +48,26 @@ class TsuminoDescriptionAdapter(
val meta = controller.presenter.meta
if (meta == null || meta !is TsuminoSearchMetadata) return
val genre = meta.category
if (genre != null) {
val pair = when (genre) {
"Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi)
"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.genre.text = meta.category?.let { MetadataUtil.getGenreAndColour(itemView.context, it) }?.let {
binding.genre.setBackgroundColor(it.first)
it.second
} ?: meta.category ?: itemView.context.getString(R.string.unknown)
binding.favorites.text = (meta.favorites ?: 0).toString()
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.favorites.bindDrawable(itemView.context, R.drawable.ic_book_24dp)
binding.whenPosted.text = TsuminoSearchMetadata.TSUMINO_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
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)
ContextCompat.getDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)?.apply {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.pages.setCompoundDrawables(this, null, null, null)
}
binding.pages.bindDrawable(itemView.context, R.drawable.ic_baseline_menu_book_24)
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
@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 {
setTint(itemView.context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx)
binding.moreInfo.setCompoundDrawables(this, null, null, null)
}
binding.moreInfo.bindDrawable(itemView.context, R.drawable.ic_info_24dp)
listOf(
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
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
*/
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>) =
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
when {
query.startsWith("http://") || query.startsWith("https://") -> {
Observable.fromCallable {

View File

@ -10,7 +10,7 @@ import exh.metadata.metadata.base.RaisedTag
import exh.nHentaiSourceIds
import java.util.Locale
class SourceTagsUtil {
object SourceTagsUtil {
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) {
val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) RaisedTag(namespace, tag, TAG_TYPE_DEFAULT) else null
@ -47,52 +47,53 @@ class SourceTagsUtil {
} else {
"$namespace:$tag"
}
companion object {
fun Manga.getRaisedTags(genres: List<String>? = null): List<RaisedTag>? = (genres ?: this.getGenres())?.map { parseTag(it) }
fun parseTag(tag: String) = RaisedTag(
(
if (tag.startsWith("-")) {
tag.substringAfter("-")
} else {
tag
}
).substringBefore(':', missingDelimiterValue = "").trimOrNull(),
tag.substringAfter(':', missingDelimiterValue = tag).trim(),
if (tag.startsWith("-")) TAG_TYPE_EXCLUDE else TAG_TYPE_DEFAULT
)
fun parseTag(tag: String) = RaisedTag(
(
if (tag.startsWith("-")) {
tag.substringAfter("-")
} else {
tag
}
).substringBefore(':', missingDelimiterValue = "").trimOrNull(),
tag.substringAfter(':', missingDelimiterValue = tag).trim(),
if (tag.startsWith("-")) TAG_TYPE_EXCLUDE else TAG_TYPE_DEFAULT
)
const val TAG_TYPE_EXCLUDE = 69 // why not
const val TAG_TYPE_EXCLUDE = 69 // why not
const val DOUJINSHI_COLOR = "#f44336"
const val MANGA_COLOR = "#ff9800"
const val ARTIST_CG_COLOR = "#fbc02d"
const val GAME_CG_COLOR = "#4caf50"
const val WESTERN_COLOR = "#8bc34a"
const val NON_H_COLOR = "#2196f3"
const val IMAGE_SET_COLOR = "#3f51b5"
const val COSPLAY_COLOR = "#9c27b0"
const val ASIAN_PORN_COLOR = "#9575cd"
const val MISC_COLOR = "#f06292"
const val DOUJINSHI_COLOR = "#f44336"
const val MANGA_COLOR = "#ff9800"
const val ARTIST_CG_COLOR = "#fbc02d"
const val GAME_CG_COLOR = "#4caf50"
const val WESTERN_COLOR = "#8bc34a"
const val NON_H_COLOR = "#2196f3"
const val IMAGE_SET_COLOR = "#3f51b5"
const val COSPLAY_COLOR = "#9c27b0"
const val ASIAN_PORN_COLOR = "#9575cd"
const val MISC_COLOR = "#f06292"
fun getLocaleSourceUtil(language: String?) = when (language) {
"english", "eng" -> Locale("en")
"chinese" -> Locale("zh")
"spanish" -> Locale("es")
"korean" -> Locale("ko")
"russian" -> Locale("ru")
"french" -> Locale("fr")
"portuguese" -> Locale("pt")
"thai" -> Locale("th")
"german" -> Locale("de")
"italian" -> Locale("it")
"vietnamese" -> Locale("vi")
"polish" -> Locale("pl")
"hungarian" -> Locale("hu")
"dutch" -> Locale("nl")
else -> null
}
private const val TAG_TYPE_DEFAULT = 1
fun getLocaleSourceUtil(language: String?) = when (language) {
"english", "eng" -> Locale("en")
"chinese" -> Locale("zh")
"spanish" -> Locale("es")
"korean" -> Locale("ko")
"russian" -> Locale("ru")
"french" -> Locale("fr")
"portuguese" -> Locale("pt")
"thai" -> Locale("th")
"german" -> Locale("de")
"italian" -> Locale("it")
"vietnamese" -> Locale("vi")
"polish" -> Locale("pl")
"hungarian" -> Locale("hu")
"dutch" -> Locale("nl")
else -> null
}
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 exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_NORMAL
import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_WEAK
import exh.metadata.metadata.EHentaiSearchMetadata
/**
* 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 {
return Chip(context).apply {
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) }
setOnLongClickListener {
onLongClick(search)
@ -37,9 +35,9 @@ fun makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongCli
}
if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID) {
chipStrokeWidth = when (type) {
TAG_TYPE_NORMAL -> 5F
TAG_TYPE_LIGHT -> 3F
TAG_TYPE_WEAK -> 0F
EHentaiSearchMetadata.TAG_TYPE_NORMAL -> 5F
EHentaiSearchMetadata.TAG_TYPE_LIGHT -> 3F
EHentaiSearchMetadata.TAG_TYPE_WEAK -> 0F
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.data.track.TrackManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.util.system.toast
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.password
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) {
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

View File

@ -7,17 +7,21 @@ import com.afollestad.materialdialogs.MaterialDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager
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.util.lang.launchNow
import eu.kanade.tachiyomi.util.system.toast
import exh.md.utils.MdUtil
import exh.source.getMainSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
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()

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