Extract source api from app module (#8014)

* Extract source api from app module

* Extract source online api from app module

(cherry picked from commit 86fe85079413f8ed6e1109b46e6131a9b788b988)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt
#	core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt
#	source-api/src/main/java/eu/kanade/tachiyomi/source/Source.kt
#	source-api/src/main/java/eu/kanade/tachiyomi/source/model/SManga.kt
This commit is contained in:
Andreas 2022-09-16 00:12:27 +02:00 committed by Jobobby04
parent b975b9b86f
commit 8a322ea28e
117 changed files with 547 additions and 422 deletions

View File

@ -133,6 +133,8 @@ android {
dependencies { dependencies {
implementation(project(":i18n")) implementation(project(":i18n"))
implementation(project(":core"))
implementation(project(":source-api"))
// Compose // Compose
implementation(compose.activity) implementation(compose.activity)

View File

@ -20,6 +20,7 @@ import eu.kanade.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetFavoriteEntries import eu.kanade.domain.manga.interactor.GetFavoriteEntries
import eu.kanade.domain.manga.interactor.GetFlatMetadataById import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetIdsOfFavoriteMangaWithMetadata import eu.kanade.domain.manga.interactor.GetIdsOfFavoriteMangaWithMetadata
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.GetMangaBySource import eu.kanade.domain.manga.interactor.GetMangaBySource
import eu.kanade.domain.manga.interactor.GetMergedManga import eu.kanade.domain.manga.interactor.GetMergedManga
import eu.kanade.domain.manga.interactor.GetMergedMangaById import eu.kanade.domain.manga.interactor.GetMergedMangaById
@ -65,6 +66,7 @@ import eu.kanade.domain.source.interactor.ToggleExcludeFromDataSaver
import eu.kanade.domain.source.interactor.ToggleSources import eu.kanade.domain.source.interactor.ToggleSources
import eu.kanade.domain.source.repository.FeedSavedSearchRepository import eu.kanade.domain.source.repository.FeedSavedSearchRepository
import eu.kanade.domain.source.repository.SavedSearchRepository import eu.kanade.domain.source.repository.SavedSearchRepository
import eu.kanade.tachiyomi.source.online.MetadataSource
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addFactory import uy.kohesive.injekt.api.addFactory
@ -100,6 +102,11 @@ class SYDomainModule : InjektModule {
addFactory { ReorderSortTag(get(), get()) } addFactory { ReorderSortTag(get(), get()) }
addFactory { GetPagePreviews(get()) } addFactory { GetPagePreviews(get()) }
// Required for [MetadataSource]
addFactory<MetadataSource.GetMangaId> { GetManga(get()) }
addFactory<MetadataSource.GetFlatMetadataById> { GetFlatMetadataById(get()) }
addFactory<MetadataSource.InsertFlatMetadata> { InsertFlatMetadata(get()) }
addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) } addSingletonFactory<MangaMetadataRepository> { MangaMetadataRepositoryImpl(get()) }
addFactory { GetFlatMetadataById(get()) } addFactory { GetFlatMetadataById(get()) }
addFactory { InsertFlatMetadata(get()) } addFactory { InsertFlatMetadata(get()) }

View File

@ -1,6 +1,7 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository import eu.kanade.domain.manga.repository.MangaMetadataRepository
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -9,9 +10,9 @@ import logcat.LogPriority
class GetFlatMetadataById( class GetFlatMetadataById(
private val mangaMetadataRepository: MangaMetadataRepository, private val mangaMetadataRepository: MangaMetadataRepository,
) { ) : MetadataSource.GetFlatMetadataById {
suspend fun await(id: Long): FlatMetadata? { override suspend fun await(id: Long): FlatMetadata? {
return try { return try {
val meta = mangaMetadataRepository.getMetadataById(id) val meta = mangaMetadataRepository.getMetadataById(id)
return if (meta != null) { return if (meta != null) {

View File

@ -2,13 +2,14 @@ package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.repository.MangaRepository import eu.kanade.domain.manga.repository.MangaRepository
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import logcat.LogPriority import logcat.LogPriority
class GetManga( class GetManga(
private val mangaRepository: MangaRepository, private val mangaRepository: MangaRepository,
) { ) : MetadataSource.GetMangaId {
suspend fun await(id: Long): Manga? { suspend fun await(id: Long): Manga? {
return try { return try {
@ -30,4 +31,10 @@ class GetManga(
fun subscribe(url: String, sourceId: Long): Flow<Manga?> { fun subscribe(url: String, sourceId: Long): Flow<Manga?> {
return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId) return mangaRepository.getMangaByUrlAndSourceIdAsFlow(url, sourceId)
} }
// SY -->
override suspend fun awaitId(url: String, sourceId: Long): Long? {
return await(url, sourceId)?.id
}
// SY <--
} }

View File

@ -1,6 +1,7 @@
package eu.kanade.domain.manga.interactor package eu.kanade.domain.manga.interactor
import eu.kanade.domain.manga.repository.MangaMetadataRepository import eu.kanade.domain.manga.repository.MangaMetadataRepository
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
@ -8,7 +9,7 @@ import logcat.LogPriority
class InsertFlatMetadata( class InsertFlatMetadata(
private val mangaMetadataRepository: MangaMetadataRepository, private val mangaMetadataRepository: MangaMetadataRepository,
) { ) : MetadataSource.InsertFlatMetadata {
suspend fun await(flatMetadata: FlatMetadata) { suspend fun await(flatMetadata: FlatMetadata) {
try { try {
@ -18,7 +19,7 @@ class InsertFlatMetadata(
} }
} }
suspend fun await(metadata: RaisedSearchMetadata) { override suspend fun await(metadata: RaisedSearchMetadata) {
try { try {
mangaMetadataRepository.insertMetadata(metadata) mangaMetadataRepository.insertMetadata(metadata)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -72,14 +72,33 @@ import eu.kanade.presentation.util.isScrollingUp
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.getNameForMangaInfo import eu.kanade.tachiyomi.source.getNameForMangaInfo
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.source.online.all.Hitomi
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.all.PervEden
import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino
import eu.kanade.tachiyomi.ui.manga.ChapterItem import eu.kanade.tachiyomi.ui.manga.ChapterItem
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
import exh.ui.metadata.adapters.EHentaiDescription
import exh.ui.metadata.adapters.EightMusesDescription
import exh.ui.metadata.adapters.HBrowseDescription
import exh.ui.metadata.adapters.HitomiDescription
import exh.ui.metadata.adapters.MangaDexDescription
import exh.ui.metadata.adapters.NHentaiDescription
import exh.ui.metadata.adapters.PervEdenDescription
import exh.ui.metadata.adapters.PururinDescription
import exh.ui.metadata.adapters.TsuminoDescription
@Composable @Composable
fun MangaScreen( fun MangaScreen(
@ -255,7 +274,7 @@ private fun MangaScreenSmallImpl(
val chapters = remember(state) { state.processedChapters.toList() } val chapters = remember(state) { state.processedChapters.toList() }
// SY --> // SY -->
val metadataSource = remember(state.source.id) { state.source.getMainSource<MetadataSource<*, *>>() } val metadataDescription = metadataDescription(state.source)
// SY <-- // SY <--
val internalOnBackPressed = { val internalOnBackPressed = {
@ -408,12 +427,12 @@ private fun MangaScreenSmallImpl(
} }
// SY --> // SY -->
if (metadataSource != null) { if (metadataDescription != null) {
item( item(
key = MangaScreenItem.METADATA_INFO, key = MangaScreenItem.METADATA_INFO,
contentType = MangaScreenItem.METADATA_INFO, contentType = MangaScreenItem.METADATA_INFO,
) { ) {
metadataSource.DescriptionComposable( metadataDescription(
state = state, state = state,
openMetadataViewer = onMetadataViewerClicked, openMetadataViewer = onMetadataViewerClicked,
search = { onSearch(it, false) }, search = { onSearch(it, false) },
@ -540,7 +559,7 @@ fun MangaScreenLargeImpl(
val chapters = remember(state) { state.processedChapters.toList() } val chapters = remember(state) { state.processedChapters.toList() }
// SY --> // SY -->
val metadataSource = remember(state.source.id) { state.source.getMainSource<MetadataSource<*, *>>() } val metadataDescription = metadataDescription(state.source)
// SY <-- // SY <--
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues() val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
@ -681,7 +700,7 @@ fun MangaScreenLargeImpl(
// SY <-- // SY <--
) )
// SY --> // SY -->
metadataSource?.DescriptionComposable( metadataDescription?.invoke(
state = state, state = state,
openMetadataViewer = onMetadataViewerClicked, openMetadataViewer = onMetadataViewerClicked,
search = { onSearch(it, false) }, search = { onSearch(it, false) },
@ -847,3 +866,42 @@ private fun onChapterItemClick(
else -> onChapterClicked(chapterItem.chapter) else -> onChapterClicked(chapterItem.chapter)
} }
} }
typealias MetadataDescriptionComposable = @Composable (state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) -> Unit
@Composable
fun metadataDescription(source: Source): MetadataDescriptionComposable? {
val metadataSource = remember(source.id) { source.getMainSource<MetadataSource<*, *>>() }
return remember(metadataSource) {
when (metadataSource) {
is EHentai -> { state, openMetadataViewer, search ->
EHentaiDescription(state, openMetadataViewer, search)
}
is Hitomi -> { state, openMetadataViewer, _ ->
HitomiDescription(state, openMetadataViewer)
}
is MangaDex -> { state, openMetadataViewer, _ ->
MangaDexDescription(state, openMetadataViewer)
}
is NHentai -> { state, openMetadataViewer, _ ->
NHentaiDescription(state, openMetadataViewer)
}
is PervEden -> { state, openMetadataViewer, _ ->
PervEdenDescription(state, openMetadataViewer)
}
is EightMuses -> { state, openMetadataViewer, _ ->
EightMusesDescription(state, openMetadataViewer)
}
is HBrowse -> { state, openMetadataViewer, _ ->
HBrowseDescription(state, openMetadataViewer)
}
is Pururin -> { state, openMetadataViewer, _ ->
PururinDescription(state, openMetadataViewer)
}
is Tsumino -> { state, openMetadataViewer, _ ->
TsuminoDescription(state, openMetadataViewer)
}
else -> null
}
}
}

View File

@ -48,6 +48,7 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.library.CustomMangaManager import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.copyFrom
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import eu.kanade.tachiyomi.util.system.toLong import eu.kanade.tachiyomi.util.system.toLong

View File

@ -1,6 +1,10 @@
package eu.kanade.tachiyomi.data.database.models package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.library.CustomMangaManager import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.source.model.SManga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
open class MangaImpl : Manga { open class MangaImpl : Manga {
@ -77,6 +81,19 @@ open class MangaImpl : Manga {
private set private set
var ogStatus: Int = 0 var ogStatus: Int = 0
private set private set
override val originalTitle: String
get() = ogTitle
override val originalAuthor: String?
get() = ogAuthor ?: author
override val originalArtist: String?
get() = ogArtist ?: artist
override val originalDescription: String?
get() = ogDesc ?: description
override val originalGenre: String?
get() = ogGenre ?: genre
override val originalStatus: Int
get() = ogStatus
// SY <-- // SY <--
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@ -93,6 +110,18 @@ open class MangaImpl : Manga {
} }
// SY --> // SY -->
override fun copyFrom(other: SManga) {
// EXH -->
if (other.title.isNotBlank() && originalTitle != other.title) {
val source = (this as? Manga)?.source
if (source != null) {
Injekt.get<DownloadManager>().renameMangaDir(originalTitle, other.originalTitle, source)
}
}
// EXH <--
super.copyFrom(other)
}
companion object { companion object {
private val customMangaManager: CustomMangaManager by injectLazy() private val customMangaManager: CustomMangaManager by injectLazy()
} }

View File

@ -22,6 +22,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.LocaleHelper import eu.kanade.tachiyomi.util.system.LocaleHelper
import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isDevFlavor
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import eu.kanade.tachiyomi.widget.ExtendedNavigationView import eu.kanade.tachiyomi.widget.ExtendedNavigationView
import java.io.File import java.io.File
import java.text.DateFormat import java.text.DateFormat

View File

@ -0,0 +1,58 @@
package eu.kanade.tachiyomi.source
import android.graphics.drawable.Drawable
import eu.kanade.domain.source.model.SourceData
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.extension.ExtensionManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
fun Source.getPreferenceKey(): String = "source_$id"
fun Source.toSourceData(): SourceData = SourceData(id = id, lang = lang, name = name)
fun Source.getNameForMangaInfo(mergeSources: List<Source>?): String {
val preferences = Injekt.get<PreferencesHelper>()
val enabledLanguages = preferences.enabledLanguages().get()
.filterNot { it in listOf("all", "other") }
val hasOneActiveLanguages = enabledLanguages.size == 1
val isInEnabledLanguages = lang in enabledLanguages
return when {
// SY -->
!mergeSources.isNullOrEmpty() -> getMergedSourcesString(
mergeSources,
enabledLanguages,
hasOneActiveLanguages,
)
// SY <--
// For edge cases where user disables a source they got manga of in their library.
hasOneActiveLanguages && !isInEnabledLanguages -> toString()
// Hide the language tag when only one language is used.
hasOneActiveLanguages && isInEnabledLanguages -> name
else -> toString()
}
}
// SY -->
private fun getMergedSourcesString(
mergeSources: List<Source>,
enabledLangs: List<String>,
onlyName: Boolean,
): String {
return if (onlyName) {
mergeSources.joinToString { source ->
if (source.lang !in enabledLangs) {
source.toString()
} else {
source.name
}
}
} else {
mergeSources.joinToString()
}
}
// SY <--
fun Source.isLocalOrStub(): Boolean = id == LocalSource.ID || this is SourceManager.StubSource

View File

@ -133,6 +133,7 @@ class SourceManager(
val enhancedSource = EnhancedHttpSource( val enhancedSource = EnhancedHttpSource(
this, this,
delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(this, context), delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(this, context),
::delegateSources,
) )
currentDelegatedSources[enhancedSource.originalSource.id] = DelegatedSource( currentDelegatedSources[enhancedSource.originalSource.id] = DelegatedSource(
@ -156,6 +157,8 @@ class SourceManager(
// EXH <-- // EXH <--
} }
private fun delegateSources() = preferences.delegateSources().get()
fun get(sourceKey: Long): Source? { fun get(sourceKey: Long): Source? {
return sourcesMap[sourceKey] return sourcesMap[sourceKey]
} }

View File

@ -0,0 +1,11 @@
package eu.kanade.tachiyomi.source.model
import data.Chapters
fun SChapter.copyFrom(other: Chapters) {
name = other.name
url = other.url
date_upload = other.date_upload
chapter_number = other.chapter_number
scanlator = other.scanlator
}

View File

@ -0,0 +1,46 @@
package eu.kanade.tachiyomi.source.model
import data.Mangas
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
fun SManga.copyFrom(other: Mangas) {
// EXH -->
if (other.title.isNotBlank() && originalTitle != other.title) {
val oldTitle = originalTitle
title = other.title
val source = (this as? Manga)?.source
if (source != null) {
Injekt.get<DownloadManager>().renameMangaDir(oldTitle, other.title, source)
}
}
// EXH <--
if (other.author != null) {
author = other.author
}
if (other.artist != null) {
artist = other.artist
}
if (other.description != null) {
description = other.description
}
if (other.genre != null) {
genre = other.genre.joinToString(separator = ", ")
}
if (other.thumbnail_url != null) {
thumbnail_url = other.thumbnail_url
}
status = other.status.toInt()
if (!initialized) {
initialized = other.initialized
}
}

View File

@ -1,9 +0,0 @@
package eu.kanade.tachiyomi.source.online
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.BaseController
interface BrowseSourceFilterHeader : CatalogueSource {
fun getFilterHeader(controller: BaseController<*>, onClick: () -> Unit): RecyclerView.Adapter<*>
}

View File

@ -4,7 +4,6 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.MangaImpl
@ -28,7 +27,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
@ -51,7 +49,6 @@ import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUA
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.toGenreString import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.toGenreString
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.ui.login.EhLoginActivity import exh.ui.login.EhLoginActivity
import exh.ui.metadata.adapters.EHentaiDescription
import exh.util.UriFilter import exh.util.UriFilter
import exh.util.UriGroup import exh.util.UriGroup
import exh.util.asObservableWithAsyncStacktrace import exh.util.asObservableWithAsyncStacktrace
@ -1127,11 +1124,6 @@ class EHentai(
return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${obj["token"]!!.jsonPrimitive.content}/" return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${obj["token"]!!.jsonPrimitive.content}/"
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
EHentaiDescription(state, openMetadataViewer, search)
}
override suspend fun getPagePreviewList( override suspend fun getPagePreviewList(
manga: SManga, manga: SManga,
page: Int, page: Int,

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import androidx.compose.runtime.Composable
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -11,13 +10,11 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.HitomiSearchMetadata import exh.metadata.metadata.HitomiSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.HitomiDescription
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -136,11 +133,6 @@ class Hitomi(delegate: HttpSource, val context: Context) :
return "https://hitomi.la/manga/${uri.pathSegments[1].substringBefore('.')}.html" return "https://hitomi.la/manga/${uri.pathSegments[1].substringBefore('.')}.html"
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
HitomiDescription(state, openMetadataViewer)
}
companion object { companion object {
const val otherId = 2703068117101782422L const val otherId = 2703068117101782422L
private val DATE_FORMAT by lazy { private val DATE_FORMAT by lazy {

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
@ -14,7 +13,6 @@ import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader
import eu.kanade.tachiyomi.source.online.FollowsSource import eu.kanade.tachiyomi.source.online.FollowsSource
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LoginSource import eu.kanade.tachiyomi.source.online.LoginSource
@ -22,10 +20,7 @@ import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.RandomMangaSource import eu.kanade.tachiyomi.source.online.RandomMangaSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.md.MangaDexFabHeaderAdapter
import exh.md.dto.MangaDto import exh.md.dto.MangaDto
import exh.md.dto.StatisticsMangaDto import exh.md.dto.StatisticsMangaDto
import exh.md.handlers.ApiMangaParser import exh.md.handlers.ApiMangaParser
@ -49,7 +44,6 @@ import exh.md.utils.MdLang
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.MangaDexDescription
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
@ -64,7 +58,6 @@ class MangaDex(delegate: HttpSource, val context: Context) :
UrlImportableSource, UrlImportableSource,
FollowsSource, FollowsSource,
LoginSource, LoginSource,
BrowseSourceFilterHeader,
RandomMangaSource, RandomMangaSource,
NamespaceSource { NamespaceSource {
override val lang: String = delegate.lang override val lang: String = delegate.lang
@ -217,11 +210,6 @@ class MangaDex(delegate: HttpSource, val context: Context) :
// MetadataSource methods // MetadataSource methods
override val metaClass: KClass<MangaDexSearchMetadata> = MangaDexSearchMetadata::class override val metaClass: KClass<MangaDexSearchMetadata> = MangaDexSearchMetadata::class
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
MangaDexDescription(state, openMetadataViewer)
}
override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Triple<MangaDto, List<String>, StatisticsMangaDto>) { override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Triple<MangaDto, List<String>, StatisticsMangaDto>) {
apiMangaParser.parseIntoMetadata(metadata, input.first, input.second, input.third) apiMangaParser.parseIntoMetadata(metadata, input.first, input.second, input.third)
} }
@ -272,11 +260,11 @@ class MangaDex(delegate: HttpSource, val context: Context) :
return followsHandler.fetchAllFollows() return followsHandler.fetchAllFollows()
} }
override suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean { suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean {
return followsHandler.updateFollowStatus(mangaID, followStatus) return followsHandler.updateFollowStatus(mangaID, followStatus)
} }
override suspend fun fetchTrackingInfo(url: String): Track { suspend fun fetchTrackingInfo(url: String): Track {
return followsHandler.fetchTrackingInfo(url) return followsHandler.fetchTrackingInfo(url)
} }
@ -293,11 +281,6 @@ class MangaDex(delegate: HttpSource, val context: Context) :
return mangaHandler.getTrackingInfo(track) return mangaHandler.getTrackingInfo(track)
} }
// BrowseSourceFilterHeader method
override fun getFilterHeader(controller: BaseController<*>, onClick: () -> Unit): MangaDexFabHeaderAdapter {
return MangaDexFabHeaderAdapter(controller, this, onClick)
}
// RandomMangaSource method // RandomMangaSource method
override suspend fun fetchRandomMangaUrl(): String { override suspend fun fetchRandomMangaUrl(): String {
return mangaHandler.fetchRandomMangaId() return mangaHandler.fetchRandomMangaId()

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.newCallWithProgress import eu.kanade.tachiyomi.network.newCallWithProgress
@ -16,12 +15,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.NHentaiDescription
import exh.util.trimOrNull import exh.util.trimOrNull
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
@ -175,11 +172,6 @@ class NHentai(delegate: HttpSource, val context: Context) :
return "$baseUrl/g/${uri.pathSegments[1]}/" return "$baseUrl/g/${uri.pathSegments[1]}/"
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
NHentaiDescription(state, openMetadataViewer)
}
override suspend fun getPagePreviewList(manga: SManga, page: Int): PagePreviewPage { override suspend fun getPagePreviewList(manga: SManga, page: Int): PagePreviewPage {
val metadata = fetchOrLoadMetadata(manga.id()) { val metadata = fetchOrLoadMetadata(manga.id()) {
client.newCall(mangaDetailsRequest(manga)).await() client.newCall(mangaDetailsRequest(manga)).await()

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -10,13 +9,11 @@ import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.PervEdenSearchMetadata import exh.metadata.metadata.PervEdenSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.PervEdenDescription
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -132,9 +129,4 @@ class PervEden(delegate: HttpSource, val context: Context) :
} }
return newUri.toString() return newUri.toString()
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
PervEdenDescription(state, openMetadataViewer)
}
} }

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -11,12 +10,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.EightMusesSearchMetadata import exh.metadata.metadata.EightMusesSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.EightMusesDescription
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -96,9 +93,4 @@ class EightMuses(delegate: HttpSource, val context: Context) :
} }
return "/comics/album/${path.joinToString("/")}" return "/comics/album/${path.joinToString("/")}"
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
EightMusesDescription(state, openMetadataViewer)
}
} }

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -10,12 +9,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.HBrowseSearchMetadata import exh.metadata.metadata.HBrowseSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.HBrowseDescription
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
@ -84,9 +81,4 @@ class HBrowse(delegate: HttpSource, val context: Context) :
override suspend fun mapUrlToMangaUrl(uri: Uri): String? { override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
return uri.pathSegments.firstOrNull()?.let { "/$it/c00001/" } return uri.pathSegments.firstOrNull()?.let { "/$it/c00001/" }
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
HBrowseDescription(state, openMetadataViewer)
}
} }

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -12,13 +11,11 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.PururinSearchMetadata import exh.metadata.metadata.PururinSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.PururinDescription
import exh.util.dropBlank import exh.util.dropBlank
import exh.util.trimAll import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
@ -116,9 +113,4 @@ class Pururin(delegate: HttpSource, val context: Context) :
override suspend fun mapUrlToMangaUrl(uri: Uri): String { override suspend fun mapUrlToMangaUrl(uri: Uri): String {
return "${PururinSearchMetadata.BASE_URL}/gallery/${uri.pathSegments.getOrNull(1)}/${uri.lastPathSegment}" return "${PururinSearchMetadata.BASE_URL}/gallery/${uri.pathSegments.getOrNull(1)}/${uri.lastPathSegment}"
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
PururinDescription(state, openMetadataViewer)
}
} }

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.compose.runtime.Composable
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
@ -11,14 +10,12 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.TsuminoDescription
import exh.util.dropBlank import exh.util.dropBlank
import exh.util.trimAll import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
@ -141,9 +138,4 @@ class Tsumino(delegate: HttpSource, val context: Context) :
val RATING_USERS_REGEX = "\\(([0-9].*) users".toRegex() val RATING_USERS_REGEX = "\\(([0-9].*) users".toRegex()
val RATING_FAVORITES_REGEX = "/ ([0-9].*) favs".toRegex() val RATING_FAVORITES_REGEX = "/ ([0-9].*) favs".toRegex()
} }
@Composable
override fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
TsuminoDescription(state, openMetadataViewer)
}
} }

View File

@ -13,10 +13,11 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding import eu.kanade.tachiyomi.databinding.SourceFilterSheetBinding
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.online.BrowseSourceFilterHeader import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.widget.SimpleNavigationView import eu.kanade.tachiyomi.widget.SimpleNavigationView
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
import exh.md.MangaDexFabHeaderAdapter
import exh.savedsearches.EXHSavedSearch import exh.savedsearches.EXHSavedSearch
import exh.source.getMainSource import exh.source.getMainSource
@ -117,8 +118,12 @@ class SourceFilterSheet(
recycler.adapter = ConcatAdapter( recycler.adapter = ConcatAdapter(
listOfNotNull( listOfNotNull(
controller?.let { controller?.let {
source?.getMainSource<BrowseSourceFilterHeader>() source?.getMainSource<MangaDex>()
?.getFilterHeader(it) { dismissSheet?.invoke() } ?.let {
MangaDexFabHeaderAdapter(controller, it) {
dismissSheet?.invoke()
}
}
}, },
savedSearchesAdapter, savedSearchesAdapter,
adapter, adapter,

View File

@ -46,7 +46,7 @@ open class AutoComplete(val filter: Filter.AutoComplete) : AbstractFlexibleItem<
// select from auto complete // select from auto complete
holder.autoComplete.setOnItemClickListener { adapterView, _, chipPosition, _ -> holder.autoComplete.setOnItemClickListener { adapterView, _, chipPosition, _ ->
val name = adapterView.getItemAtPosition(chipPosition) as String val name = adapterView.getItemAtPosition(chipPosition) as String
if (name !in if (filter.excludePrefix != null && name.startsWith(filter.excludePrefix)) filter.skipAutoFillTags.map { filter.excludePrefix + it } else filter.skipAutoFillTags) { if (name !in if (filter.excludePrefix != null && name.startsWith(filter.excludePrefix!!)) filter.skipAutoFillTags.map { filter.excludePrefix + it } else filter.skipAutoFillTags) {
holder.autoComplete.text = null holder.autoComplete.text = null
addTag(name, holder) addTag(name, holder)
} }
@ -54,7 +54,7 @@ open class AutoComplete(val filter: Filter.AutoComplete) : AbstractFlexibleItem<
// done keyboard button is pressed // done keyboard button is pressed
holder.autoComplete.setOnEditorActionListener { textView, actionId, _ -> holder.autoComplete.setOnEditorActionListener { textView, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE && textView.text.toString() !in if (filter.excludePrefix != null && textView.text.toString().startsWith(filter.excludePrefix)) filter.skipAutoFillTags.map { filter.excludePrefix + it } else filter.skipAutoFillTags) { if (actionId == EditorInfo.IME_ACTION_DONE && textView.text.toString() !in if (filter.excludePrefix != null && textView.text.toString().startsWith(filter.excludePrefix!!)) filter.skipAutoFillTags.map { filter.excludePrefix + it } else filter.skipAutoFillTags) {
textView.text = null textView.text = null
addTag(textView.text.toString(), holder) addTag(textView.text.toString(), holder)
return@setOnEditorActionListener true return@setOnEditorActionListener true

View File

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.preference.preferenceCategory
import eu.kanade.tachiyomi.util.preference.switchPreference import eu.kanade.tachiyomi.util.preference.switchPreference
import eu.kanade.tachiyomi.util.preference.titleRes import eu.kanade.tachiyomi.util.preference.titleRes
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
import eu.kanade.tachiyomi.util.system.isTablet import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.widget.preference.ThemesPreference import eu.kanade.tachiyomi.widget.preference.ThemesPreference
import java.util.Date import java.util.Date

View File

@ -24,10 +24,8 @@ import android.util.TypedValue
import android.view.Display import android.view.Display
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.StringRes
import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
@ -52,29 +50,6 @@ import kotlin.math.roundToInt
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720 private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
/**
* Display a toast in this context.
*
* @param resource the text resource.
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
return toast(getString(resource), duration, block)
}
/**
* Display a toast in this context.
*
* @param text the text to display.
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
block(it)
it.show()
}
}
/** /**
* Copies a string to clipboard * Copies a string to clipboard
* *

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.util.system
import android.os.Build
import com.google.android.material.color.DynamicColors
val DeviceUtil.isDynamicColorAvailable by lazy {
DynamicColors.isDynamicColorAvailable() || (DeviceUtil.isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
}

View File

@ -12,8 +12,8 @@ import eu.kanade.tachiyomi.databinding.DescriptionAdapterEhBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
@Composable @Composable
fun EHentaiDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) { fun EHentaiDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) {
@ -29,7 +29,7 @@ fun EHentaiDescription(state: MangaScreenState.Success, openMetadataViewer: () -
val binding = DescriptionAdapterEhBinding.bind(it) val binding = DescriptionAdapterEhBinding.bind(it)
binding.genre.text = binding.genre.text =
meta.genre?.let { MetadataUtil.getGenreAndColour(context, it) } meta.genre?.let { MetadataUIUtil.getGenreAndColour(context, it) }
?.let { ?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
@ -61,7 +61,7 @@ fun EHentaiDescription(state: MangaScreenState.Success, openMetadataViewer: () -
val ratingFloat = meta.averageRating?.toFloat() val ratingFloat = meta.averageRating?.toFloat()
binding.ratingBar.rating = ratingFloat ?: 0F binding.ratingBar.rating = ratingFloat ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (ratingFloat ?: 0F).toString() + " - " + MetadataUtil.getRatingString(context, ratingFloat?.times(2)) binding.rating.text = (ratingFloat ?: 0F).toString() + " - " + MetadataUIUtil.getRatingString(context, ratingFloat?.times(2))
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp) binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp)

View File

@ -10,8 +10,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapter8mBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapter8mBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.bindDrawable
import exh.metadata.metadata.EightMusesSearchMetadata import exh.metadata.metadata.EightMusesSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
@Composable @Composable
fun EightMusesDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit) { fun EightMusesDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit) {

View File

@ -10,8 +10,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHbBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterHbBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.bindDrawable
import exh.metadata.metadata.HBrowseSearchMetadata import exh.metadata.metadata.HBrowseSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
@Composable @Composable
fun HBrowseDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit) { fun HBrowseDescription(state: MangaScreenState.Success, openMetadataViewer: () -> Unit) {

View File

@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.databinding.DescriptionAdapterHiBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.HitomiSearchMetadata import exh.metadata.metadata.HitomiSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import java.util.Date import java.util.Date
@Composable @Composable
@ -28,7 +28,7 @@ fun HitomiDescription(state: MangaScreenState.Success, openMetadataViewer: () ->
if (meta == null || meta !is HitomiSearchMetadata) return@AndroidView if (meta == null || meta !is HitomiSearchMetadata) return@AndroidView
val binding = DescriptionAdapterHiBinding.bind(it) val binding = DescriptionAdapterHiBinding.bind(it)
binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(context, it) }?.let { binding.genre.text = meta.genre?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
} ?: meta.genre ?: context.getString(R.string.unknown) } ?: meta.genre ?: context.getString(R.string.unknown)

View File

@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterMdBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil.getRatingString
import exh.metadata.bindDrawable
import exh.metadata.metadata.MangaDexSearchMetadata import exh.metadata.metadata.MangaDexSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import exh.ui.metadata.adapters.MetadataUIUtil.getRatingString
import kotlin.math.round import kotlin.math.round
@Composable @Composable

View File

@ -1,72 +1,17 @@
package exh.metadata package exh.ui.metadata.adapters
import android.content.Context import android.content.Context
import android.widget.TextView import android.widget.TextView
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.R
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.ln
import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
/** object MetadataUIUtil {
* Metadata utils
*/
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)
}
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(bytes: String): Double? {
val ret = bytes.substringBefore(' ').toDouble()
return when (bytes.substringAfter(' ')) {
"GB" -> ret * GB_FACTOR
"GiB" -> ret * GIB_FACTOR
"MB" -> ret * MB_FACTOR
"MiB" -> ret * MIB_FACTOR
"KB" -> ret * KB_FACTOR
"KiB" -> ret * KIB_FACTOR
else -> 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? = null) = when (rating?.roundToInt()) { fun getRatingString(context: Context, @FloatRange(from = 0.0, to = 10.0) rating: Float? = null) = when (rating?.roundToInt()) {
0 -> R.string.rating0 0 -> R.string.rating0
1 -> R.string.rating1 1 -> R.string.rating1
@ -103,12 +48,12 @@ object MetadataUtil {
}?.let { (genreColor, stringId) -> }?.let { (genreColor, stringId) ->
genreColor.color to context.getString(stringId) genreColor.color to context.getString(stringId)
} }
}
fun TextView.bindDrawable(context: Context, @DrawableRes drawable: Int) { fun TextView.bindDrawable(context: Context, @DrawableRes drawable: Int) {
ContextCompat.getDrawable(context, drawable)?.apply { ContextCompat.getDrawable(context, drawable)?.apply {
setTint(context.getResourceColor(R.attr.colorAccent)) setTint(context.getResourceColor(R.attr.colorAccent))
setBounds(0, 0, 20.dpToPx, 20.dpToPx) setBounds(0, 0, 20.dpToPx, 20.dpToPx)
setCompoundDrawables(this, null, null, null) setCompoundDrawables(this, null, null, null)
}
} }
} }

View File

@ -12,8 +12,8 @@ import eu.kanade.tachiyomi.databinding.DescriptionAdapterNhBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import java.util.Date import java.util.Date
@Composable @Composable
@ -32,7 +32,7 @@ fun NHentaiDescription(state: MangaScreenState.Success, openMetadataViewer: () -
binding.genre.text = meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags -> binding.genre.text = meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}.let { categoriesString -> }.let { categoriesString ->
categoriesString?.let { MetadataUtil.getGenreAndColour(context, it) }?.let { categoriesString?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
} ?: categoriesString ?: context.getString(R.string.unknown) } ?: categoriesString ?: context.getString(R.string.unknown)

View File

@ -11,9 +11,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPeBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterPeBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.PervEdenSearchMetadata import exh.metadata.metadata.PervEdenSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import java.util.Locale import java.util.Locale
import kotlin.math.round import kotlin.math.round
@ -30,7 +29,7 @@ fun PervEdenDescription(state: MangaScreenState.Success, openMetadataViewer: ()
if (meta == null || meta !is PervEdenSearchMetadata) return@AndroidView if (meta == null || meta !is PervEdenSearchMetadata) return@AndroidView
val binding = DescriptionAdapterPeBinding.bind(it) val binding = DescriptionAdapterPeBinding.bind(it)
binding.genre.text = meta.genre?.let { MetadataUtil.getGenreAndColour(context, it) }?.let { binding.genre.text = meta.genre?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
} ?: meta.genre ?: context.getString(R.string.unknown) } ?: meta.genre ?: context.getString(R.string.unknown)
@ -45,7 +44,7 @@ fun PervEdenDescription(state: MangaScreenState.Success, openMetadataViewer: ()
binding.ratingBar.rating = meta.rating ?: 0F binding.ratingBar.rating = meta.rating ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((meta.rating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(context, meta.rating?.times(2)) binding.rating.text = (round((meta.rating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUIUtil.getRatingString(context, meta.rating?.times(2))
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp) binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp)

View File

@ -11,9 +11,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterPuBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterPuBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.PururinSearchMetadata import exh.metadata.metadata.PururinSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import kotlin.math.round import kotlin.math.round
@Composable @Composable
@ -30,7 +29,7 @@ fun PururinDescription(state: MangaScreenState.Success, openMetadataViewer: () -
val binding = DescriptionAdapterPuBinding.bind(it) val binding = DescriptionAdapterPuBinding.bind(it)
binding.genre.text = meta.tags.find { it.namespace == PururinSearchMetadata.TAG_NAMESPACE_CATEGORY }.let { genre -> binding.genre.text = meta.tags.find { it.namespace == PururinSearchMetadata.TAG_NAMESPACE_CATEGORY }.let { genre ->
genre?.let { MetadataUtil.getGenreAndColour(context, it.name) }?.let { genre?.let { MetadataUIUtil.getGenreAndColour(context, it.name) }?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
} ?: genre?.name ?: context.getString(R.string.unknown) } ?: genre?.name ?: context.getString(R.string.unknown)
@ -47,7 +46,7 @@ fun PururinDescription(state: MangaScreenState.Success, openMetadataViewer: () -
val ratingFloat = meta.averageRating?.toFloat() val ratingFloat = meta.averageRating?.toFloat()
binding.ratingBar.rating = ratingFloat ?: 0F binding.ratingBar.rating = ratingFloat ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((ratingFloat ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(context, ratingFloat?.times(2)) binding.rating.text = (round((ratingFloat ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUIUtil.getRatingString(context, ratingFloat?.times(2))
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp) binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp)

View File

@ -11,9 +11,8 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.MetadataUtil
import exh.metadata.bindDrawable
import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import java.util.Date import java.util.Date
import kotlin.math.round import kotlin.math.round
@ -30,7 +29,7 @@ fun TsuminoDescription(state: MangaScreenState.Success, openMetadataViewer: () -
if (meta == null || meta !is TsuminoSearchMetadata) return@AndroidView if (meta == null || meta !is TsuminoSearchMetadata) return@AndroidView
val binding = DescriptionAdapterTsBinding.bind(it) val binding = DescriptionAdapterTsBinding.bind(it)
binding.genre.text = meta.category?.let { MetadataUtil.getGenreAndColour(context, it) }?.let { binding.genre.text = meta.category?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
binding.genre.setBackgroundColor(it.first) binding.genre.setBackgroundColor(it.first)
it.second it.second
} ?: meta.category ?: context.getString(R.string.unknown) } ?: meta.category ?: context.getString(R.string.unknown)
@ -47,7 +46,7 @@ fun TsuminoDescription(state: MangaScreenState.Success, openMetadataViewer: () -
binding.ratingBar.rating = meta.averageRating ?: 0F binding.ratingBar.rating = meta.averageRating ?: 0F
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.rating.text = (round((meta.averageRating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUtil.getRatingString(context, meta.averageRating?.times(2)) binding.rating.text = (round((meta.averageRating ?: 0F) * 100.0) / 100.0).toString() + " - " + MetadataUIUtil.getRatingString(context, meta.averageRating?.times(2))
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp) binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp)

View File

@ -34,12 +34,12 @@ object SourceTagsUtil {
} }
if (parsed?.namespace != null) { if (parsed?.namespace != null) {
when (sourceId) { when (sourceId) {
in hitomiSourceIds -> wrapTagHitomi(parsed.namespace, parsed.name.substringBefore('|').trim()) in hitomiSourceIds -> wrapTagHitomi(parsed.namespace!!, parsed.name.substringBefore('|').trim())
in nHentaiSourceIds -> wrapTagNHentai(parsed.namespace, parsed.name.substringBefore('|').trim()) in nHentaiSourceIds -> wrapTagNHentai(parsed.namespace!!, parsed.name.substringBefore('|').trim())
in mangaDexSourceIds -> parsed.name in mangaDexSourceIds -> parsed.name
PURURIN_SOURCE_ID -> parsed.name.substringBefore('|').trim() PURURIN_SOURCE_ID -> parsed.name.substringBefore('|').trim()
TSUMINO_SOURCE_ID -> wrapTagTsumino(parsed.namespace, parsed.name.substringBefore('|').trim()) TSUMINO_SOURCE_ID -> wrapTagTsumino(parsed.namespace!!, parsed.name.substringBefore('|').trim())
else -> wrapTag(parsed.namespace, parsed.name.substringBefore('|').trim()) else -> wrapTag(parsed.namespace!!, parsed.name.substringBefore('|').trim())
} }
} else { } else {
null null

View File

@ -1,12 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package exh.util
import java.util.Locale
fun String.capitalize(locale: Locale = Locale.getDefault()) =
replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }

1
core/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

50
core/build.gradle.kts Normal file
View File

@ -0,0 +1,50 @@
plugins {
id("com.android.library")
kotlin("android")
kotlin("plugin.serialization")
}
android {
namespace = "eu.kanade.tachiyomi.core"
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation(project(":i18n"))
api(libs.logcat)
api(libs.rxjava)
api(libs.okhttp.core)
api(libs.okhttp.logging)
api(libs.okhttp.dnsoverhttps)
api(libs.okio)
api(kotlinx.coroutines.core)
api(kotlinx.serialization.json)
api(libs.injekt.core)
api(libs.preferencektx)
implementation(androidx.corektx)
// SY -->
implementation(sylibs.xlog)
// SY <--
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,22 +1,22 @@
package eu.kanade.tachiyomi.network package eu.kanade.tachiyomi.network
import android.content.Context import android.content.Context
import eu.kanade.tachiyomi.BuildConfig import androidx.preference.PreferenceManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.i18n.BuildConfig
import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
import eu.kanade.tachiyomi.network.interceptor.Http103Interceptor import eu.kanade.tachiyomi.network.interceptor.Http103Interceptor
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
import uy.kohesive.injekt.injectLazy
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
/* SY --> */ /* SY --> */
open /* SY <-- */ class NetworkHelper(context: Context) { open /* SY <-- */ class NetworkHelper(context: Context) {
private val preferences: PreferencesHelper by injectLazy() // TODO: Abstract preferences similar to 1.x
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
private val cacheDir = File(context.cacheDir, "network_cache") private val cacheDir = File(context.cacheDir, "network_cache")
private val cacheSize = 5L * 1024 * 1024 // 5 MiB private val cacheSize = 5L * 1024 * 1024 // 5 MiB
@ -46,7 +46,7 @@ open /* SY <-- */ class NetworkHelper(context: Context) {
builder.addNetworkInterceptor(httpLoggingInterceptor) builder.addNetworkInterceptor(httpLoggingInterceptor)
} }
when (preferences.dohProvider()) { when (preferences.getInt("doh_provider", -1)) {
PREF_DOH_CLOUDFLARE -> builder.dohCloudflare() PREF_DOH_CLOUDFLARE -> builder.dohCloudflare()
PREF_DOH_GOOGLE -> builder.dohGoogle() PREF_DOH_GOOGLE -> builder.dohGoogle()
PREF_DOH_ADGUARD -> builder.dohAdGuard() PREF_DOH_ADGUARD -> builder.dohAdGuard()
@ -74,6 +74,6 @@ open /* SY <-- */ class NetworkHelper(context: Context) {
} }
val defaultUserAgent by lazy { val defaultUserAgent by lazy {
preferences.defaultUserAgent().get() preferences.getString("default_user_agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0")!!
} }
} }

View File

@ -10,7 +10,7 @@ import java.util.concurrent.TimeUnit.MINUTES
private val DEFAULT_CACHE_CONTROL = CacheControl.Builder().maxAge(10, MINUTES).build() private val DEFAULT_CACHE_CONTROL = CacheControl.Builder().maxAge(10, MINUTES).build()
private val DEFAULT_HEADERS = Headers.Builder().build() private val DEFAULT_HEADERS = Headers.Builder().build()
private val DEFAULT_BODY: RequestBody = FormBody.Builder().build() private val DEFAULT_BODY: RequestBody = FormBody.Builder().build()
internal val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build() val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build()
fun GET( fun GET(
url: String, url: String,

View File

@ -5,7 +5,7 @@ import android.content.Context
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.R
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.system.WebViewClientCompat import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import eu.kanade.tachiyomi.util.system.isOutdated import eu.kanade.tachiyomi.util.system.isOutdated

View File

@ -5,7 +5,7 @@ import android.os.Build
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast import android.widget.Toast
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.R
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.DeviceUtil

View File

@ -5,6 +5,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import rx.Emitter import rx.Emitter
@ -20,6 +21,7 @@ import kotlin.coroutines.resumeWithException
suspend fun <T> Observable<T>.awaitSingle(): T = single().awaitOne() suspend fun <T> Observable<T>.awaitSingle(): T = single().awaitOne()
@OptIn(InternalCoroutinesApi::class)
private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutine { cont -> private suspend fun <T> Observable<T>.awaitOne(): T = suspendCancellableCoroutine { cont ->
cont.unsubscribeOnCancellation( cont.unsubscribeOnCancellation(
subscribe( subscribe(

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.util.system
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Build import android.os.Build
import com.google.android.material.color.DynamicColors
import logcat.LogPriority import logcat.LogPriority
object DeviceUtil { object DeviceUtil {
@ -31,10 +30,6 @@ object DeviceUtil {
Build.MANUFACTURER.equals("samsung", ignoreCase = true) Build.MANUFACTURER.equals("samsung", ignoreCase = true)
} }
val isDynamicColorAvailable by lazy {
DynamicColors.isDynamicColorAvailable() || (isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
}
val invalidDefaultBrowsers = listOf("android", "com.huawei.android.internal.app") val invalidDefaultBrowsers = listOf("android", "com.huawei.android.internal.app")
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")

View File

@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
/**
* Display a toast in this context.
*
* @param resource the text resource.
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
return toast(getString(resource), duration, block)
}
/**
* Display a toast in this context.
*
* @param text the text to display.
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT, block: (Toast) -> Unit = {}): Toast {
return Toast.makeText(applicationContext, text.orEmpty(), duration).also {
block(it)
it.show()
}
}

View File

@ -3,8 +3,7 @@ package exh.log
import android.content.Context import android.content.Context
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.R
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
enum class EHLogLevel(@StringRes val nameRes: Int, @StringRes val description: Int) { enum class EHLogLevel(@StringRes val nameRes: Int, @StringRes val description: Int) {
MINIMAL(R.string.log_minimal, R.string.log_minimal_desc), MINIMAL(R.string.log_minimal, R.string.log_minimal_desc),
@ -19,7 +18,7 @@ enum class EHLogLevel(@StringRes val nameRes: Int, @StringRes val description: I
fun init(context: Context) { fun init(context: Context) {
curLogLevel = PreferenceManager.getDefaultSharedPreferences(context) curLogLevel = PreferenceManager.getDefaultSharedPreferences(context)
.getInt(PreferenceKeys.eh_logLevel, 0) .getInt("eh_log_level", 0) // todo
} }
fun shouldLog(requiredLogLevel: EHLogLevel): Boolean { fun shouldLog(requiredLogLevel: EHLogLevel): Boolean {

View File

@ -1,5 +1,7 @@
package exh.util package exh.util
import java.util.Locale
fun Collection<String>.trimAll() = map { it.trim() } fun Collection<String>.trimAll() = map { it.trim() }
fun Collection<String>.dropBlank() = filter { it.isNotBlank() } fun Collection<String>.dropBlank() = filter { it.isNotBlank() }
fun Collection<String>.dropEmpty() = filter { it.isNotEmpty() } fun Collection<String>.dropEmpty() = filter { it.isNotEmpty() }
@ -13,3 +15,6 @@ fun String.removeArticles(): String {
fun String.trimOrNull() = trim().nullIfBlank() fun String.trimOrNull() = trim().nullIfBlank()
fun String.nullIfBlank(): String? = ifBlank { null } fun String.nullIfBlank(): String? = ifBlank { null }
fun String.capitalize(locale: Locale = Locale.getDefault()) =
replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }

View File

@ -40,3 +40,5 @@ dependencyResolutionManagement {
rootProject.name = "TachiyomiSY" rootProject.name = "TachiyomiSY"
include(":app") include(":app")
include(":i18n") include(":i18n")
include(":source-api")
include(":core")

1
source-api/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.library")
kotlin("android")
kotlin("plugin.serialization")
}
android {
namespace = "eu.kanade.tachiyomi.source"
compileSdk = AndroidConfig.compileSdk
defaultConfig {
minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
dependencies {
implementation(project(":core"))
api(kotlinx.serialization.json)
api(libs.rxjava)
api(libs.preferencektx)
api(libs.jsoup)
implementation(androidx.corektx)
// SY -->
implementation(project(":i18n"))
implementation(kotlinx.reflect)
// SY <--
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />

View File

@ -1,16 +1,10 @@
package eu.kanade.tachiyomi.source package eu.kanade.tachiyomi.source
import android.graphics.drawable.Drawable
import eu.kanade.domain.source.model.SourceData
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.awaitSingle
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/** /**
* A basic interface for creating a source. It could be an online source, a local source, etc... * A basic interface for creating a source. It could be an online source, a local source, etc...
@ -88,53 +82,3 @@ interface Source {
return fetchPageList(chapter).awaitSingle() return fetchPageList(chapter).awaitSingle()
} }
} }
fun Source.icon(): Drawable? = Injekt.get<ExtensionManager>().getAppIconForSource(this)
fun Source.getPreferenceKey(): String = "source_$id"
fun Source.toSourceData(): SourceData = SourceData(id = id, lang = lang, name = name)
fun Source.getNameForMangaInfo(mergeSources: List<Source>?): String {
val preferences = Injekt.get<PreferencesHelper>()
val enabledLanguages = preferences.enabledLanguages().get()
.filterNot { it in listOf("all", "other") }
val hasOneActiveLanguages = enabledLanguages.size == 1
val isInEnabledLanguages = lang in enabledLanguages
return when {
// SY -->
!mergeSources.isNullOrEmpty() -> getMergedSourcesString(
mergeSources,
enabledLanguages,
hasOneActiveLanguages,
)
// SY <--
// For edge cases where user disables a source they got manga of in their library.
hasOneActiveLanguages && !isInEnabledLanguages -> toString()
// Hide the language tag when only one language is used.
hasOneActiveLanguages && isInEnabledLanguages -> name
else -> toString()
}
}
// SY -->
private fun getMergedSourcesString(
mergeSources: List<Source>,
enabledLangs: List<String>,
onlyName: Boolean,
): String {
return if (onlyName) {
mergeSources.joinToString { source ->
if (source.lang !in enabledLangs) {
source.toString()
} else {
source.name
}
}
} else {
mergeSources.joinToString()
}
}
// SY <--
fun Source.isLocalOrStub(): Boolean = id == LocalSource.ID || this is SourceManager.StubSource

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import data.Chapters
import java.io.Serializable import java.io.Serializable
interface SChapter : Serializable { interface SChapter : Serializable {
@ -23,14 +22,6 @@ interface SChapter : Serializable {
scanlator = other.scanlator scanlator = other.scanlator
} }
fun copyFrom(other: Chapters) {
name = other.name
url = other.url
date_upload = other.date_upload
chapter_number = other.chapter_number
scanlator = other.scanlator
}
companion object { companion object {
fun create(): SChapter { fun create(): SChapter {
return SChapterImpl() return SChapterImpl()

View File

@ -1,11 +1,5 @@
package eu.kanade.tachiyomi.source.model package eu.kanade.tachiyomi.source.model
import data.Mangas
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.download.DownloadManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.Serializable import java.io.Serializable
interface SManga : Serializable { interface SManga : Serializable {
@ -35,28 +29,17 @@ interface SManga : Serializable {
// SY --> // SY -->
val originalTitle: String val originalTitle: String
get() = (this as? MangaImpl)?.ogTitle ?: title
val originalAuthor: String? val originalAuthor: String?
get() = (this as? MangaImpl)?.ogAuthor ?: author
val originalArtist: String? val originalArtist: String?
get() = (this as? MangaImpl)?.ogArtist ?: artist
val originalDescription: String? val originalDescription: String?
get() = (this as? MangaImpl)?.ogDesc ?: description
val originalGenre: String? val originalGenre: String?
get() = (this as? MangaImpl)?.ogGenre ?: genre
val originalStatus: Int val originalStatus: Int
get() = (this as? MangaImpl)?.ogStatus ?: status
// SY <-- // SY <--
fun copyFrom(other: SManga) { fun copyFrom(other: SManga) {
// EXH --> // EXH -->
if (other.title.isNotBlank() && originalTitle != other.title) { if (other.title.isNotBlank() && originalTitle != other.title) {
val oldTitle = originalTitle
title = other.originalTitle title = other.originalTitle
val source = (this as? Manga)?.source
if (source != null) {
Injekt.get<DownloadManager>().renameMangaDir(oldTitle, other.originalTitle, source)
}
} }
// EXH <-- // EXH <--
@ -87,45 +70,6 @@ interface SManga : Serializable {
} }
} }
fun copyFrom(other: Mangas) {
// EXH -->
if (other.title.isNotBlank() && originalTitle != other.title) {
val oldTitle = originalTitle
title = other.title
val source = (this as? Manga)?.source
if (source != null) {
Injekt.get<DownloadManager>().renameMangaDir(oldTitle, other.title, source)
}
}
// EXH <--
if (other.author != null) {
author = other.author
}
if (other.artist != null) {
artist = other.artist
}
if (other.description != null) {
description = other.description
}
if (other.genre != null) {
genre = other.genre.joinToString(separator = ", ")
}
if (other.thumbnail_url != null) {
thumbnail_url = other.thumbnail_url
}
status = other.status.toInt()
if (!initialized) {
initialized = other.initialized
}
}
fun copy() = create().also { fun copy() = create().also {
it.url = url it.url = url
// SY --> // SY -->

View File

@ -21,4 +21,19 @@ class SMangaImpl : SManga {
override var thumbnail_url: String? = null override var thumbnail_url: String? = null
override var initialized: Boolean = false override var initialized: Boolean = false
// SY -->
override val originalTitle: String
get() = title
override val originalAuthor: String?
get() = author
override val originalArtist: String?
get() = artist
override val originalDescription: String?
get() = description
override val originalGenre: String?
get() = genre
override val originalStatus: Int
get() = status
// SY <--
} }

View File

@ -1,10 +1,8 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.md.utils.FollowStatus
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
interface FollowsSource : CatalogueSource { interface FollowsSource : CatalogueSource {
@ -16,14 +14,4 @@ interface FollowsSource : CatalogueSource {
* @param SManga all smanga found for user * @param SManga all smanga found for user
*/ */
suspend fun fetchAllFollows(): List<Pair<SManga, RaisedSearchMetadata>> suspend fun fetchAllFollows(): List<Pair<SManga, RaisedSearchMetadata>>
/**
* updates the follow status for a manga
*/
suspend fun updateFollowStatus(mangaID: String, followStatus: FollowStatus): Boolean
/**
* Get a MdList Track of the manga
*/
suspend fun fetchTrackingInfo(url: String): Track
} }

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
import android.app.Application import android.app.Application
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.AndroidCookieJar import eu.kanade.tachiyomi.network.AndroidCookieJar
import eu.kanade.tachiyomi.network.CACHE_CONTROL_NO_STORE import eu.kanade.tachiyomi.network.CACHE_CONTROL_NO_STORE
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -15,7 +14,6 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.log.maybeInjectEHLogger import exh.log.maybeInjectEHLogger
import exh.patch.injectPatches
import exh.source.DelegatedHttpSource import exh.source.DelegatedHttpSource
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -43,14 +41,14 @@ abstract class HttpSource : CatalogueSource {
override val client: OkHttpClient override val client: OkHttpClient
get() = delegate?.networkHttpClient ?: network.client get() = delegate?.networkHttpClient ?: network.client
.newBuilder() .newBuilder()
.injectPatches { id } //.injectPatches { id } todo
.maybeInjectEHLogger() .maybeInjectEHLogger()
.build() .build()
override val cloudflareClient: OkHttpClient override val cloudflareClient: OkHttpClient
get() = delegate?.networkCloudflareClient ?: network.cloudflareClient get() = delegate?.networkCloudflareClient ?: network.cloudflareClient
.newBuilder() .newBuilder()
.injectPatches { id } //.injectPatches { id } todo
.maybeInjectEHLogger() .maybeInjectEHLogger()
.build() .build()
@ -411,7 +409,7 @@ abstract class HttpSource : CatalogueSource {
// EXH --> // EXH -->
private var delegate: DelegatedHttpSource? = null private var delegate: DelegatedHttpSource? = null
get() = if (Injekt.get<PreferencesHelper>().delegateSources().get()) { get() = if (Injekt.get<NetworkHelper>().preferences.getBoolean("eh_delegate_sources", true)) { // todo
field field
} else { } else {
null null

View File

@ -1,14 +1,10 @@
package eu.kanade.tachiyomi.source.online package eu.kanade.tachiyomi.source.online
import androidx.compose.runtime.Composable
import eu.kanade.domain.manga.interactor.GetFlatMetadataById
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.interactor.InsertFlatMetadata
import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.util.lang.awaitSingle import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.runAsObservable import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import rx.Completable import rx.Completable
import rx.Single import rx.Single
@ -20,7 +16,16 @@ import kotlin.reflect.KClass
* LEWD! * LEWD!
*/ */
interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource { interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
val getManga: GetManga get() = Injekt.get() interface GetMangaId {
suspend fun awaitId(url: String, sourceId: Long): Long?
}
interface InsertFlatMetadata {
suspend fun await(metadata: RaisedSearchMetadata)
}
interface GetFlatMetadataById {
suspend fun await(id: Long): FlatMetadata?
}
val getMangaId: GetMangaId get() = Injekt.get()
val insertFlatMetadata: InsertFlatMetadata get() = Injekt.get() val insertFlatMetadata: InsertFlatMetadata get() = Injekt.get()
val getFlatMetadataById: GetFlatMetadataById get() = Injekt.get() val getFlatMetadataById: GetFlatMetadataById get() = Injekt.get()
@ -111,8 +116,5 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
} }
} }
@Composable suspend fun SManga.id() = getMangaId.awaitId(url, id)
fun DescriptionComposable(state: MangaScreenState.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit)
suspend fun SManga.id() = getManga.await(url, id)?.id
} }

View File

@ -12,6 +12,7 @@ import org.jsoup.nodes.Element
/** /**
* A simple implementation for sources from a website using Jsoup, an HTML parser. * A simple implementation for sources from a website using Jsoup, an HTML parser.
*/ */
@Suppress("unused")
abstract class ParsedHttpSource : HttpSource() { abstract class ParsedHttpSource : HttpSource() {
/** /**

View File

@ -1,7 +1,7 @@
package exh.md.utils package exh.md.utils
import androidx.annotation.StringRes import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.R
enum class MangaDexRelation(@StringRes val resId: Int, val mdString: String?) { enum class MangaDexRelation(@StringRes val resId: Int, val mdString: String?) {
SIMILAR(R.string.relation_similar, null), SIMILAR(R.string.relation_similar, null),

View File

@ -0,0 +1,60 @@
package exh.metadata
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.ln
import kotlin.math.pow
/**
* Metadata utils
*/
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)
}
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(bytes: String): Double? {
val ret = bytes.substringBefore(' ').toDouble()
return when (bytes.substringAfter(' ')) {
"GB" -> ret * GB_FACTOR
"GiB" -> ret * GIB_FACTOR
"MB" -> ret * MB_FACTOR
"MiB" -> ret * MIB_FACTOR
"KB" -> ret * KB_FACTOR
"KiB" -> ret * KIB_FACTOR
else -> 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)
}

View File

@ -2,8 +2,8 @@ package exh.metadata.metadata
import android.content.Context import android.content.Context
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.copy import eu.kanade.tachiyomi.source.model.copy
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
@ -53,7 +53,7 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
// No title bug? // No title bug?
val title = altTitle val title = altTitle
?.takeIf { Injekt.get<PreferencesHelper>().useJapaneseTitle().get() } ?.takeIf { Injekt.get<NetworkHelper>().preferences.getBoolean("use_jp_title", false) } // todo
?: title ?: title
// Set artist (if we can find one) // Set artist (if we can find one)

Some files were not shown because too many files have changed in this diff Show More