Make internal and delegated sources able to use getMangaDetails, getChapterList, and getPageList properly

This commit is contained in:
Jobobby04 2021-01-02 03:11:20 -05:00
parent 23ac4b271c
commit 28fca8c839
26 changed files with 576 additions and 7 deletions

View File

@ -11,8 +11,10 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await
import rx.Completable import rx.Completable
import rx.Single import rx.Single
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -33,6 +35,8 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
*/ */
fun parseIntoMetadata(metadata: M, input: I) fun parseIntoMetadata(metadata: M, input: I)
suspend fun parseInfoIntoMetadata(metadata: M, input: I) = parseIntoMetadata(metadata, input)
/** /**
* Use reflection to create a new instance of metadata * Use reflection to create a new instance of metadata
*/ */
@ -46,6 +50,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
* *
* Will also save the metadata to the DB if possible * Will also save the metadata to the DB if possible
*/ */
@Deprecated("Use the MangaInfo variant")
fun parseToManga(manga: SManga, input: I): Completable { fun parseToManga(manga: SManga, input: I): Completable {
val mangaId = manga.id val mangaId = manga.id
val metaObservable = if (mangaId != null) { val metaObservable = if (mangaId != null) {
@ -71,6 +76,22 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
} }
} }
suspend fun parseToManga(manga: MangaInfo, input: I): MangaInfo {
val mangaId = manga.id()
val metadata = if (mangaId != null) {
val flatMetadata = db.getFlatMetadataForManga(mangaId).await()
flatMetadata?.raise(metaClass) ?: newMetaInstance()
} else newMetaInstance()
parseInfoIntoMetadata(metadata, input)
if (mangaId != null) {
metadata.mangaId = mangaId
db.insertFlatMetadata(metadata.flatten()).await()
}
return metadata.createMangaInfo(manga)
}
/** /**
* Try to first get the metadata from the DB. If the metadata is not in the DB, calls the input * Try to first get the metadata from the DB. If the metadata is not in the DB, calls the input
* producer and parses the metadata from the input * producer and parses the metadata from the input
@ -78,6 +99,7 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
* If the metadata needs to be parsed from the input producer, the resulting parsed metadata will * If the metadata needs to be parsed from the input producer, the resulting parsed metadata will
* also be saved to the DB. * also be saved to the DB.
*/ */
@Deprecated("use fetchOrLoadMetadata made for MangaInfo")
fun getOrLoadMetadata(mangaId: Long?, inputProducer: () -> Single<I>): Single<M> { fun getOrLoadMetadata(mangaId: Long?, inputProducer: () -> Single<I>): Single<M> {
val metaObservable = if (mangaId != null) { val metaObservable = if (mangaId != null) {
// We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions // We have to use fromCallable because StorIO messes up the thread scheduling if we use their rx functions
@ -103,8 +125,34 @@ interface MetadataSource<M : RaisedSearchMetadata, I> : CatalogueSource {
} }
} }
/**
* Try to first get the metadata from the DB. If the metadata is not in the DB, calls the input
* producer and parses the metadata from the input
*
* If the metadata needs to be parsed from the input producer, the resulting parsed metadata will
* also be saved to the DB.
*/
suspend fun fetchOrLoadMetadata(mangaId: Long?, inputProducer: suspend () -> I): M {
val meta = if (mangaId != null) {
val flatMetadata = db.getFlatMetadataForManga(mangaId).await()
flatMetadata?.raise(metaClass)
} else {
null
}
return meta ?: inputProducer().let { input ->
val newMeta = newMetaInstance()
parseInfoIntoMetadata(newMeta, input)
if (mangaId != null) {
newMeta.mangaId = mangaId
db.insertFlatMetadata(newMeta.flatten()).let { newMeta }
} else newMeta
}
}
fun getDescriptionAdapter(controller: MangaController): RecyclerView.Adapter<*>? fun getDescriptionAdapter(controller: MangaController): RecyclerView.Adapter<*>?
suspend fun MangaInfo.id() = db.getManga(key, id).await()?.id
val SManga.id get() = (this as? Manga)?.id val SManga.id get() = (this as? Manga)?.id
val SChapter.mangaId get() = (this as? Chapter)?.manga_id val SChapter.mangaId get() = (this as? Chapter)?.manga_id
} }

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
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
@ -16,6 +17,8 @@ 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.model.toChapterInfo
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -41,6 +44,7 @@ import exh.ui.metadata.adapters.EHentaiDescriptionAdapter
import exh.util.UriFilter import exh.util.UriFilter
import exh.util.UriGroup import exh.util.UriGroup
import exh.util.asObservableWithAsyncStacktrace import exh.util.asObservableWithAsyncStacktrace
import exh.util.awaitSingle
import exh.util.dropBlank import exh.util.dropBlank
import exh.util.ignore import exh.util.ignore
import exh.util.nullIfBlank import exh.util.nullIfBlank
@ -72,6 +76,8 @@ import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode import org.jsoup.nodes.TextNode
import rx.Observable import rx.Observable
import rx.Single import rx.Single
import tachiyomi.source.model.ChapterInfo
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder import java.net.URLEncoder
import java.util.ArrayList import java.util.ArrayList
@ -276,8 +282,13 @@ class EHentai(
MetadataMangasPage(mangaFromSource.first.map { it.manga }, mangaFromSource.second, mangaFromSource.first.map { it.metadata }) MetadataMangasPage(mangaFromSource.first.map { it.manga }, mangaFromSource.second, mangaFromSource.first.map { it.metadata })
} }
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> = getChapterList(manga) {}
suspend fun getChapterList(manga: MangaInfo, throttleFunc: () -> Unit) = fetchChapterList(manga.toSManga(), throttleFunc).awaitSingle().map { it.toChapterInfo() }
override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {} override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {}
@Deprecated("Use getChapterList instead")
fun fetchChapterList(manga: SManga, throttleFunc: () -> Unit): Observable<List<SChapter>> { fun fetchChapterList(manga: SManga, throttleFunc: () -> Unit): Observable<List<SChapter>> {
return Single.fromCallable { return Single.fromCallable {
// Pull all the way to the root gallery // Pull all the way to the root gallery
@ -516,6 +527,31 @@ class EHentai(
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val exception = Exception("Async stacktrace")
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
if (response.isSuccessful) {
// Pull to most recent
val doc = response.asJsoup()
val newerGallery = doc.select("#gnd a").lastOrNull()
val pre = if (newerGallery != null && DebugToggles.PULL_TO_ROOT_WHEN_LOADING_EXH_MANGA_DETAILS.enabled) {
val sManga = manga.toSManga().apply {
url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href"))
}
client.newCall(mangaDetailsRequest(sManga)).await().asJsoup()
} else doc
return parseToManga(manga, pre)
} else {
response.close()
if (response.code == 404) {
throw GalleryNotFoundException(exception)
} else {
throw Exception("HTTP error ${response.code}", exception)
}
}
}
/** /**
* Parse gallery page to metadata model * Parse gallery page to metadata model
*/ */

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -21,6 +23,7 @@ import exh.ui.metadata.adapters.HitomiDescriptionAdapter
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -46,6 +49,11 @@ class Hitomi(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
override fun parseIntoMetadata(metadata: HitomiSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: HitomiSearchMetadata, input: Document) {
with(metadata) { with(metadata) {
url = input.location() url = input.location()

View File

@ -57,6 +57,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -126,6 +127,10 @@ class MangaDex(delegate: HttpSource, val context: Context) :
return MangaHandler(client, headers, listOf(mdLang), preferences.mangaDexForceLatestCovers().get()).fetchMangaDetailsObservable(manga) return MangaHandler(client, headers, listOf(mdLang), preferences.mangaDexForceLatestCovers().get()).fetchMangaDetailsObservable(manga)
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
return MangaHandler(client, headers, listOf(mdLang), preferences.mangaDexForceLatestCovers().get()).getMangaDetails(manga, id)
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return MangaHandler(client, headers, listOf(mdLang), preferences.mangaDexForceLatestCovers().get()).fetchChapterListObservable(manga) return MangaHandler(client, headers, listOf(mdLang), preferences.mangaDexForceLatestCovers().get()).fetchChapterListObservable(manga)
} }

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -24,6 +26,7 @@ import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class NHentai(delegate: HttpSource, val context: Context) : class NHentai(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -63,6 +66,11 @@ class NHentai(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response)
}
override fun parseIntoMetadata(metadata: NHentaiSearchMetadata, input: Response) { override fun parseIntoMetadata(metadata: NHentaiSearchMetadata, input: Response) {
val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace( val json = GALLERY_JSON_REGEX.find(input.body!!.string())!!.groupValues[1].replace(
UNICODE_ESCAPE_REGEX UNICODE_ESCAPE_REGEX

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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
@ -22,6 +24,7 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode import org.jsoup.nodes.TextNode
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class PervEden(delegate: HttpSource, val context: Context) : class PervEden(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -44,6 +47,11 @@ class PervEden(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
override fun parseIntoMetadata(metadata: PervEdenSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: PervEdenSearchMetadata, input: Document) {
with(metadata) { with(metadata) {
url = input.location().toUri().path url = input.location().toUri().path

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -21,6 +23,7 @@ import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class EightMuses(delegate: HttpSource, val context: Context) : class EightMuses(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -44,6 +47,11 @@ class EightMuses(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
data class SelfContents(val albums: List<Element>, val images: List<Element>) data class SelfContents(val albums: List<Element>, val images: List<Element>)
private fun parseSelf(doc: Document): SelfContents { private fun parseSelf(doc: Document): SelfContents {

View File

@ -3,9 +3,11 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -20,6 +22,7 @@ import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class HBrowse(delegate: HttpSource, val context: Context) : class HBrowse(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -43,6 +46,11 @@ class HBrowse(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
override fun parseIntoMetadata(metadata: HBrowseSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: HBrowseSearchMetadata, input: Document) {
val tables = parseIntoTables(input) val tables = parseIntoTables(input)
with(metadata) { with(metadata) {

View File

@ -3,14 +3,17 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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.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.model.toSManga
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.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.lang.runAsObservable
import exh.metadata.metadata.HentaiCafeSearchMetadata import exh.metadata.metadata.HentaiCafeSearchMetadata
import exh.metadata.metadata.HentaiCafeSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.HentaiCafeSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
@ -21,6 +24,8 @@ import exh.util.urlImportFetchSearchManga
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import tachiyomi.source.model.ChapterInfo
import tachiyomi.source.model.MangaInfo
class HentaiCafe(delegate: HttpSource, val context: Context) : class HentaiCafe(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -56,6 +61,11 @@ class HentaiCafe(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
/** /**
* Parse the supplied input into the supplied metadata object * Parse the supplied input into the supplied metadata object
*/ */
@ -86,12 +96,12 @@ class HentaiCafe(delegate: HttpSource, val context: Context) :
} }
} }
override fun fetchChapterList(manga: SManga) = getOrLoadMetadata(manga.id) { override fun fetchChapterList(manga: SManga) = runAsObservable({
client.newCall(mangaDetailsRequest(manga)) fetchOrLoadMetadata(manga.id) {
.asObservableSuccess() val response = client.newCall(mangaDetailsRequest(manga)).await()
.map { it.asJsoup() } response.asJsoup()
.toSingle() }
}.map { }).map {
listOf( listOf(
SChapter.create().apply { SChapter.create().apply {
url = "/manga/read/${it.readerId}/en/0/1/" url = "/manga/read/${it.readerId}/en/0/1/"
@ -99,7 +109,21 @@ class HentaiCafe(delegate: HttpSource, val context: Context) :
chapter_number = 0.0f chapter_number = 0.0f
} }
) )
}.toObservable() }
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> {
val metadata = fetchOrLoadMetadata(manga.id()) {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
response.asJsoup()
}
return listOf(
ChapterInfo(
key = "/manga/read/${metadata.readerId}/en/0/1/",
name = "Chapter",
number = 0F
)
)
}
override val matchingHosts = listOf( override val matchingHosts = listOf(
"hentai.cafe" "hentai.cafe"

View File

@ -4,9 +4,11 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.net.toUri import androidx.core.net.toUri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -23,6 +25,7 @@ import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class Pururin(delegate: HttpSource, val context: Context) : class Pururin(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate), DelegatedHttpSource(delegate),
@ -60,6 +63,11 @@ class Pururin(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
override fun parseIntoMetadata(metadata: PururinSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: PururinSearchMetadata, input: Document) {
val selfLink = input.select("[itemprop=name]").last().parent() val selfLink = input.select("[itemprop=name]").last().parent()
val parsedSelfLink = selfLink.attr("href").toUri().pathSegments val parsedSelfLink = selfLink.attr("href").toUri().pathSegments

View File

@ -3,9 +3,11 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.toSManga
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.NamespaceSource import eu.kanade.tachiyomi.source.online.NamespaceSource
@ -23,6 +25,7 @@ import exh.util.trimAll
import exh.util.urlImportFetchSearchManga import exh.util.urlImportFetchSearchManga
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -56,6 +59,11 @@ class Tsumino(delegate: HttpSource, val context: Context) :
} }
} }
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
val response = client.newCall(mangaDetailsRequest(manga.toSManga())).await()
return parseToManga(manga, response.asJsoup())
}
override fun parseIntoMetadata(metadata: TsuminoSearchMetadata, input: Document) { override fun parseIntoMetadata(metadata: TsuminoSearchMetadata, input: Document) {
with(metadata) { with(metadata) {
tmId = TsuminoSearchMetadata.tmIdFromUrl(input.location())!!.toInt() tmId = TsuminoSearchMetadata.tmIdFromUrl(input.location())!!.toInt()

View File

@ -14,6 +14,7 @@ import exh.metadata.metadata.MangaDexSearchMetadata
import exh.metadata.metadata.base.RaisedTag import exh.metadata.metadata.base.RaisedTag
import exh.metadata.metadata.base.getFlatMetadataForManga import exh.metadata.metadata.base.getFlatMetadataForManga
import exh.metadata.metadata.base.insertFlatMetadata import exh.metadata.metadata.base.insertFlatMetadata
import exh.util.await
import exh.util.floor import exh.util.floor
import exh.util.nullIfZero import exh.util.nullIfZero
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
@ -24,6 +25,7 @@ import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Response import okhttp3.Response
import rx.Completable import rx.Completable
import rx.Single import rx.Single
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -70,6 +72,24 @@ class ApiMangaParser(private val langs: List<String>) {
} }
} }
suspend fun parseToManga(manga: MangaInfo, input: Response, forceLatestCover: Boolean, sourceId: Long): MangaInfo {
val mangaId = db.getManga(manga.key, sourceId).await()?.id
val metadata = if (mangaId != null) {
val flatMetadata = db.getFlatMetadataForManga(mangaId).await()
flatMetadata?.raise(metaClass) ?: newMetaInstance()
} else newMetaInstance()
parseInfoIntoMetadata(metadata, input, forceLatestCover)
if (mangaId != null) {
metadata.mangaId = mangaId
db.insertFlatMetadata(metadata.flatten()).await()
}
return metadata.createMangaInfo(manga)
}
fun parseInfoIntoMetadata(metadata: MangaDexSearchMetadata, input: Response, forceLatestCover: Boolean) = parseIntoMetadata(metadata, input, forceLatestCover)
fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Response, forceLatestCover: Boolean) { fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Response, forceLatestCover: Boolean) {
with(metadata) { with(metadata) {
try { try {

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
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.model.toSManga
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -14,6 +15,7 @@ import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import rx.Observable import rx.Observable
import tachiyomi.source.model.MangaInfo
class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: List<String>, val forceLatestCovers: Boolean = false) { class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: List<String>, val forceLatestCovers: Boolean = false) {
@ -56,6 +58,13 @@ class MangaHandler(val client: OkHttpClient, val headers: Headers, val langs: Li
} }
} }
suspend fun getMangaDetails(manga: MangaInfo, sourceId: Long): MangaInfo {
return withContext(Dispatchers.IO) {
val response = client.newCall(apiRequest(manga.toSManga())).await()
ApiMangaParser(langs).parseToManga(manga, response, forceLatestCovers, sourceId)
}
}
fun fetchMangaDetailsObservable(manga: SManga): Observable<SManga> { fun fetchMangaDetailsObservable(manga: SManga): Observable<SManga> {
return client.newCall(apiRequest(manga)) return client.newCall(apiRequest(manga))
.asObservableSuccess() .asObservableSuccess()

View File

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.util.Date import java.util.Date
@ -42,6 +43,53 @@ class EHentaiSearchMetadata : RaisedSearchMetadata() {
var aged: Boolean = false var aged: Boolean = false
var lastUpdateCheck: Long = 0 var lastUpdateCheck: Long = 0
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = gId?.let { gId ->
gToken?.let { gToken ->
idAndTokenToUrl(gId, gToken)
}
}
val cover = thumbnailUrl
// No title bug?
val title = if (Injekt.get<PreferencesHelper>().useJapaneseTitle().get()) {
altTitle ?: title
} else {
title
}
// Set artist (if we can find one)
val artist = tags.filter { it.namespace == EH_ARTIST_NAMESPACE }.let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}
// Copy tags -> genres
val genres = tagsToGenreList()
// Try to automatically identify if it is ongoing, we try not to be too lenient here to avoid making mistakes
// We default to completed
var status = MangaInfo.COMPLETED
title?.let { t ->
MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true)
}?.let {
status = MangaInfo.ONGOING
}
}
val description = "meta"
return manga.copy(
key = key ?: manga.key,
title = title ?: manga.title,
artist = artist ?: manga.artist,
description = description,
genres = genres,
status = status,
cover = cover ?: manga.cover
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
gId?.let { gId -> gId?.let { gId ->
gToken?.let { gToken -> gToken?.let { gToken ->

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class EightMusesSearchMetadata : RaisedSearchMetadata() { class EightMusesSearchMetadata : RaisedSearchMetadata() {
@ -14,6 +15,29 @@ class EightMusesSearchMetadata : RaisedSearchMetadata() {
var thumbnailUrl: String? = null var thumbnailUrl: String? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = path.joinToString("/", prefix = "/")
val title = title
val cover = thumbnailUrl
val artist = tags.ofNamespace(ARTIST_NAMESPACE).joinToString { it.name }
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
key = key,
title = title ?: manga.title,
cover = cover ?: manga.cover,
artist = artist,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
manga.url = path.joinToString("/", prefix = "/") manga.url = path.joinToString("/", prefix = "/")

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class HBrowseSearchMetadata : RaisedSearchMetadata() { class HBrowseSearchMetadata : RaisedSearchMetadata() {
@ -19,6 +20,32 @@ class HBrowseSearchMetadata : RaisedSearchMetadata() {
// Length in pages // Length in pages
var length: Int? = null var length: Int? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = hbUrl
val title = title
// Guess thumbnail URL if manga does not have thumbnail URL
val cover = if (manga.cover.isBlank()) {
guessThumbnailUrl(hbId.toString())
} else null
val artist = tags.ofNamespace(ARTIST_NAMESPACE).joinToString { it.name }
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
key = key ?: manga.key,
title = title ?: manga.title,
cover = cover ?: manga.cover,
artist = artist,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
hbUrl?.let { hbUrl?.let {
manga.url = it manga.url = it

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class HentaiCafeSearchMetadata : RaisedSearchMetadata() { class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
@ -24,6 +25,31 @@ class HentaiCafeSearchMetadata : RaisedSearchMetadata() {
var artist: String? = null var artist: String? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val cover = thumbnailUrl
val title = title
val artist = artist
val author = artist
// Not available
val status = MangaInfo.UNKNOWN
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
cover = cover ?: manga.cover,
title = title ?: manga.title,
artist = artist ?: manga.artist,
author = author ?: manga.author,
status = status,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
thumbnailUrl?.let { manga.thumbnail_url = it } thumbnailUrl?.let { manga.thumbnail_url = it }

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
import java.util.Date import java.util.Date
@Serializable @Serializable
@ -37,6 +38,30 @@ class HitomiSearchMetadata : RaisedSearchMetadata() {
var uploadDate: Long? = null var uploadDate: Long? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val cover = thumbnailUrl
val title = title
// Copy tags -> genres
val genres = tagsToGenreList()
val artist = artists.joinToString()
val status = MangaInfo.UNKNOWN
val description = "meta"
return manga.copy(
cover = cover ?: manga.cover,
title = title ?: manga.title,
genres = genres,
artist = artist,
status = status,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
thumbnailUrl?.let { manga.thumbnail_url = it } thumbnailUrl?.let { manga.thumbnail_url = it }

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class MangaDexSearchMetadata : RaisedSearchMetadata() { class MangaDexSearchMetadata : RaisedSearchMetadata() {
@ -40,6 +41,43 @@ class MangaDexSearchMetadata : RaisedSearchMetadata() {
var follow_status: Int? = null var follow_status: Int? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = mdUrl?.let {
try {
val uri = it.toUri()
val out = uri.path!!.removePrefix("/api")
out + if (out.endsWith("/")) "" else "/"
} catch (e: Exception) {
it
}
}
val title = title
val cover = thumbnail_url
val author = author
val artist = artist
val status = status
val genres = tagsToGenreList()
val description = description
return manga.copy(
key = key ?: manga.key,
title = title ?: manga.title,
cover = cover ?: manga.cover,
author = author ?: manga.author,
artist = artist ?: manga.artist,
status = status ?: manga.status,
genres = genres,
description = description ?: manga.description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
mdUrl?.let { mdUrl?.let {
manga.url = try { manga.url = try {

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
import java.util.Date import java.util.Date
@Serializable @Serializable
@ -37,6 +38,53 @@ class NHentaiSearchMetadata : RaisedSearchMetadata() {
var preferredTitle: Int? = null var preferredTitle: Int? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = nhId?.let { nhIdToPath(it) }
val cover = if (mediaId != null) {
typeToExtension(coverImageType)?.let {
"https://t.nhentai.net/galleries/$mediaId/cover.$it"
}
} else null
val title = when (preferredTitle) {
TITLE_TYPE_SHORT -> shortTitle ?: englishTitle ?: japaneseTitle ?: manga.title
0, TITLE_TYPE_ENGLISH -> englishTitle ?: japaneseTitle ?: shortTitle ?: manga.title
else -> englishTitle ?: japaneseTitle ?: shortTitle ?: manga.title
}
// Set artist (if we can find one)
val artist = tags.filter { it.namespace == NHENTAI_ARTIST_NAMESPACE }.let { tags ->
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
}
// Copy tags -> genres
val genres = tagsToGenreList()
// Try to automatically identify if it is ongoing, we try not to be too lenient here to avoid making mistakes
// We default to completed
var status = SManga.COMPLETED
englishTitle?.let { t ->
MetadataUtil.ONGOING_SUFFIX.find {
t.endsWith(it, ignoreCase = true)
}?.let {
status = SManga.ONGOING
}
}
val description = "meta"
return manga.copy(
key = key ?: manga.key,
cover = cover ?: manga.cover,
title = title,
artist = artist ?: manga.artist,
genres = genres,
status = status,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
nhId?.let { manga.url = nhIdToPath(it) } nhId?.let { manga.url = nhIdToPath(it) }

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedTitle import exh.metadata.metadata.base.RaisedTitle
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class PervEdenSearchMetadata : RaisedSearchMetadata() { class PervEdenSearchMetadata : RaisedSearchMetadata() {
@ -33,6 +34,36 @@ class PervEdenSearchMetadata : RaisedSearchMetadata() {
var lang: String? = null var lang: String? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = url
val cover = thumbnailUrl
val title = title
val artist = artist
val status = when (status) {
"Ongoing" -> MangaInfo.ONGOING
"Completed", "Suspended" -> MangaInfo.COMPLETED
else -> MangaInfo.UNKNOWN
}
// Copy tags -> genres
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
key = key ?: manga.key,
cover = cover ?: manga.cover,
title = title ?: manga.title,
artist = artist ?: manga.artist,
status = status,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
url?.let { manga.url = it } url?.let { manga.url = it }
thumbnailUrl?.let { manga.thumbnail_url = it } thumbnailUrl?.let { manga.thumbnail_url = it }

View File

@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
@Serializable @Serializable
class PururinSearchMetadata : RaisedSearchMetadata() { class PururinSearchMetadata : RaisedSearchMetadata() {
@ -26,6 +27,33 @@ class PururinSearchMetadata : RaisedSearchMetadata() {
var ratingCount: Int? = null var ratingCount: Int? = null
var averageRating: Double? = null var averageRating: Double? = null
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val key = prId?.let { prId ->
prShortLink?.let { prShortLink ->
"/gallery/$prId/$prShortLink"
}
}
val title = title ?: altTitle
val cover = thumbnailUrl
val artist = tags.ofNamespace(TAG_NAMESPACE_ARTIST).joinToString { it.name }
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
key = key ?: manga.key,
title = title ?: manga.title,
cover = cover ?: manga.cover,
artist = artist,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
prId?.let { prId -> prId?.let { prId ->
prShortLink?.let { prShortLink -> prShortLink?.let { prShortLink ->

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.model.SManga
import exh.metadata.MetadataUtil import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import tachiyomi.source.model.MangaInfo
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@ -41,6 +42,29 @@ class TsuminoSearchMetadata : RaisedSearchMetadata() {
var character: List<String> = emptyList() var character: List<String> = emptyList()
override fun createMangaInfo(manga: MangaInfo): MangaInfo {
val title = title
val cover = tmId?.let { BASE_URL.replace("www", "content") + thumbUrlFromId(it.toString()) }
val artist = artist
val status = SManga.UNKNOWN
// Copy tags -> genres
val genres = tagsToGenreList()
val description = "meta"
return manga.copy(
title = title ?: manga.title,
cover = cover ?: manga.cover,
artist = artist ?: manga.artist,
status = status,
genres = genres,
description = description
)
}
override fun copyTo(manga: SManga) { override fun copyTo(manga: SManga) {
title?.let { manga.title = it } title?.let { manga.title = it }
manga.thumbnail_url = BASE_URL.replace("www", "content") + thumbUrlFromId(tmId.toString()) manga.thumbnail_url = BASE_URL.replace("www", "content") + thumbUrlFromId(tmId.toString())

View File

@ -25,6 +25,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass import kotlinx.serialization.modules.subclass
import tachiyomi.source.model.MangaInfo
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@ -57,8 +58,12 @@ abstract class RaisedSearchMetadata {
abstract fun copyTo(manga: SManga) abstract fun copyTo(manga: SManga)
abstract fun createMangaInfo(manga: MangaInfo): MangaInfo
fun tagsToGenreString() = tags.toGenreString() fun tagsToGenreString() = tags.toGenreString()
fun tagsToGenreList() = tags.toGenreList()
fun tagsToDescription() = fun tagsToDescription() =
StringBuilder("Tags:\n").apply { StringBuilder("Tags:\n").apply {
// BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags' // BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
@ -142,6 +147,10 @@ abstract class RaisedSearchMetadata {
(this).filter { it.type != TAG_TYPE_VIRTUAL } (this).filter { it.type != TAG_TYPE_VIRTUAL }
.joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name } .joinToString { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
fun MutableList<RaisedTag>.toGenreList() =
(this).filter { it.type != TAG_TYPE_VIRTUAL }
.map { (if (it.namespace != null) "${it.namespace}: " else "") + it.name }
private val module = SerializersModule { private val module = SerializersModule {
polymorphic(RaisedSearchMetadata::class) { polymorphic(RaisedSearchMetadata::class) {
subclass(EHentaiSearchMetadata::class) subclass(EHentaiSearchMetadata::class)

View File

@ -10,6 +10,8 @@ import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import tachiyomi.source.model.ChapterInfo
import tachiyomi.source.model.MangaInfo
abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() { abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
/** /**
@ -185,6 +187,14 @@ abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
return delegate.fetchMangaDetails(manga) return delegate.fetchMangaDetails(manga)
} }
/**
* [1.x API] Get the updated details for a manga.
*/
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo {
ensureDelegateCompatible()
return delegate.getMangaDetails(manga)
}
/** /**
* Returns the request for the details of a manga. Override only if it's needed to change the * Returns the request for the details of a manga. Override only if it's needed to change the
* url, send different headers or request method like POST. * url, send different headers or request method like POST.
@ -207,6 +217,14 @@ abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
return delegate.fetchChapterList(manga) return delegate.fetchChapterList(manga)
} }
/**
* [1.x API] Get all the available chapters for a manga.
*/
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> {
ensureDelegateCompatible()
return delegate.getChapterList(manga)
}
/** /**
* Returns an observable with the page list for a chapter. * Returns an observable with the page list for a chapter.
* *
@ -217,6 +235,14 @@ abstract class DelegatedHttpSource(val delegate: HttpSource) : HttpSource() {
return delegate.fetchPageList(chapter) return delegate.fetchPageList(chapter)
} }
/**
* [1.x API] Get the list of pages a chapter has.
*/
override suspend fun getPageList(chapter: ChapterInfo): List<tachiyomi.source.model.Page> {
ensureDelegateCompatible()
return delegate.getPageList(chapter)
}
/** /**
* Returns an observable with the page containing the source url of the image. If there's any * Returns an observable with the page containing the source url of the image. If there's any
* error, it will return null instead of throwing an exception. * error, it will return null instead of throwing an exception.

View File

@ -9,6 +9,8 @@ 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.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Response import okhttp3.Response
import tachiyomi.source.model.ChapterInfo
import tachiyomi.source.model.MangaInfo
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class EnhancedHttpSource( class EnhancedHttpSource(
@ -177,6 +179,11 @@ class EnhancedHttpSource(
*/ */
override fun fetchMangaDetails(manga: SManga) = source().fetchMangaDetails(manga) override fun fetchMangaDetails(manga: SManga) = source().fetchMangaDetails(manga)
/**
* [1.x API] Get the updated details for a manga.
*/
override suspend fun getMangaDetails(manga: MangaInfo): MangaInfo = source().getMangaDetails(manga)
/** /**
* Returns the request for the details of a manga. Override only if it's needed to change the * Returns the request for the details of a manga. Override only if it's needed to change the
* url, send different headers or request method like POST. * url, send different headers or request method like POST.
@ -193,6 +200,11 @@ class EnhancedHttpSource(
*/ */
override fun fetchChapterList(manga: SManga) = source().fetchChapterList(manga) override fun fetchChapterList(manga: SManga) = source().fetchChapterList(manga)
/**
* [1.x API] Get all the available chapters for a manga.
*/
override suspend fun getChapterList(manga: MangaInfo): List<ChapterInfo> = source().getChapterList(manga)
/** /**
* Returns an observable with the page list for a chapter. * Returns an observable with the page list for a chapter.
* *
@ -200,6 +212,11 @@ class EnhancedHttpSource(
*/ */
override fun fetchPageList(chapter: SChapter) = source().fetchPageList(chapter) override fun fetchPageList(chapter: SChapter) = source().fetchPageList(chapter)
/**
* [1.x API] Get the list of pages a chapter has.
*/
override suspend fun getPageList(chapter: ChapterInfo): List<tachiyomi.source.model.Page> = source().getPageList(chapter)
/** /**
* Returns an observable with the page containing the source url of the image. If there's any * Returns an observable with the page containing the source url of the image. If there's any
* error, it will return null instead of throwing an exception. * error, it will return null instead of throwing an exception.