Migrate to new URL import system

This commit is contained in:
NerdNumber9 2019-08-07 10:21:18 -04:00
parent 46636b537c
commit e915fd28cb
10 changed files with 231 additions and 168 deletions

View File

@ -0,0 +1,31 @@
package eu.kanade.tachiyomi.source.online
import android.net.Uri
import eu.kanade.tachiyomi.source.Source
import java.net.URI
import java.net.URISyntaxException
interface UrlImportableSource : Source {
val matchingHosts: List<String>
fun matchesUri(uri: Uri): Boolean {
return (uri.host ?: "").toLowerCase() in matchingHosts
}
// This method is allowed to block for IO if necessary
fun mapUrlToMangaUrl(uri: Uri): String?
fun cleanMangaUrl(url: String): String {
return try {
val uri = URI(url)
var out = uri.path
if (uri.query != null)
out += "?" + uri.query
if (uri.fragment != null)
out += "#" + uri.fragment
out
} catch (e: URISyntaxException) {
url
}
}
}

View File

@ -3,6 +3,10 @@ package eu.kanade.tachiyomi.source.online.all
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.*
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.data.database.models.Manga 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.data.preference.getOrDefault import eu.kanade.tachiyomi.data.preference.getOrDefault
@ -12,6 +16,7 @@ import eu.kanade.tachiyomi.network.asObservableWithAsyncStacktrace
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.eh.EHentaiUpdateHelper import exh.eh.EHentaiUpdateHelper
import exh.metadata.EX_DATE_FORMAT import exh.metadata.EX_DATE_FORMAT
@ -46,7 +51,7 @@ import java.lang.RuntimeException
// TODO Consider gallery updating when doing tabbed browsing // TODO Consider gallery updating when doing tabbed browsing
class EHentai(override val id: Long, class EHentai(override val id: Long,
val exh: Boolean, val exh: Boolean,
val context: Context) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document> { val context: Context) : HttpSource(), LewdSource<EHentaiSearchMetadata, Document>, UrlImportableSource {
override val metaClass = EHentaiSearchMetadata::class override val metaClass = EHentaiSearchMetadata::class
val schema: String val schema: String
@ -636,11 +641,67 @@ class EHentai(override val id: Long,
class GalleryNotFoundException(cause: Throwable): RuntimeException("Gallery not found!", cause) class GalleryNotFoundException(cause: Throwable): RuntimeException("Gallery not found!", cause)
// === URL IMPORT STUFF
override val matchingHosts: List<String> = if(exh) listOf(
"exhentai.org"
) else listOf(
"g.e-hentai.org",
"e-hentai.org"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
return when (uri.pathSegments.firstOrNull()) {
"g" -> {
//Is already gallery page, do nothing
uri.toString()
}
"s" -> {
//Is page, fetch gallery token and use that
getGalleryUrlFromPage(uri)
}
else -> null
}
}
override fun cleanMangaUrl(url: String): String {
return EHentaiSearchMetadata.normalizeUrl(super.cleanMangaUrl(url))
}
private fun getGalleryUrlFromPage(uri: Uri): String {
val lastSplit = uri.pathSegments.last().split("-")
val pageNum = lastSplit.last()
val gallery = lastSplit.first()
val pageToken = uri.pathSegments.elementAt(1)
val json = JsonObject()
json["method"] = "gtoken"
json["pagelist"] = JsonArray().apply {
add(JsonArray().apply {
add(gallery.toInt())
add(pageToken)
add(pageNum.toInt())
})
}
val outJson = JsonParser().parse(client.newCall(Request.Builder()
.url(EH_API_BASE)
.post(RequestBody.create(JSON, json.toString()))
.build()).execute().body()!!.string()).obj
val obj = outJson["tokenlist"].array.first()
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
}
companion object { companion object {
private const val QUERY_PREFIX = "?f_apply=Apply+Filter" private const val QUERY_PREFIX = "?f_apply=Apply+Filter"
private const val TR_SUFFIX = "TR" private const val TR_SUFFIX = "TR"
private const val REVERSE_PARAM = "TEH_REVERSE" private const val REVERSE_PARAM = "TEH_REVERSE"
private const val EH_API_BASE = "https://api.e-hentai.org/api.php"
private val JSON = MediaType.parse("application/json; charset=utf-8")!!
private val FAVORITES_BORDER_HEX_COLORS = listOf( private val FAVORITES_BORDER_HEX_COLORS = listOf(
"000", "000",
"f00", "f00",

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.source.online.all package eu.kanade.tachiyomi.source.online.all
import android.net.Uri
import android.os.Build import android.os.Build
import com.github.salomonbrys.kotson.array import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.get
@ -12,7 +13,9 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.GalleryAddEvent
import exh.HITOMI_SOURCE_ID import exh.HITOMI_SOURCE_ID
import exh.hitomi.HitomiNozomi import exh.hitomi.HitomiNozomi
import exh.metadata.metadata.HitomiSearchMetadata import exh.metadata.metadata.HitomiSearchMetadata
@ -36,7 +39,7 @@ import java.util.*
/** /**
* Man, I hate this source :( * Man, I hate this source :(
*/ */
class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document> { class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document>, UrlImportableSource {
private val prefs: PreferencesHelper by injectLazy() private val prefs: PreferencesHelper by injectLazy()
private val jsonParser by lazy { JsonParser() } private val jsonParser by lazy { JsonParser() }
@ -390,6 +393,19 @@ class Hitomi : HttpSource(), LewdSource<HitomiSearchMetadata, Document> {
.build() .build()
} }
override val matchingHosts = listOf(
"hitomi.la"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
if(lcFirstPathSegment != "galleries" && lcFirstPathSegment != "reader")
return null
return "https://hitomi.la/galleries/${uri.pathSegments[1].substringBefore('.')}.html"
}
companion object { companion object {
private val INDEX_VERSION_CACHE_TIME_MS = 1000 * 60 * 10 private val INDEX_VERSION_CACHE_TIME_MS = 1000 * 60 * 10
private val PAGE_SIZE = 25 private val PAGE_SIZE = 25

View File

@ -13,7 +13,9 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.GalleryAddEvent
import exh.NHENTAI_SOURCE_ID import exh.NHENTAI_SOURCE_ID
import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata
import exh.metadata.metadata.NHentaiSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.NHentaiSearchMetadata.Companion.TAG_TYPE_DEFAULT
@ -27,7 +29,7 @@ import rx.Observable
* NHentai source * NHentai source
*/ */
class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata, Response> { class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata, Response>, UrlImportableSource {
override val metaClass = NHentaiSearchMetadata::class override val metaClass = NHentaiSearchMetadata::class
override fun fetchPopularManga(page: Int): Observable<MangasPage> { override fun fetchPopularManga(page: Int): Observable<MangasPage> {
@ -127,9 +129,6 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
override fun mangaDetailsRequest(manga: SManga) override fun mangaDetailsRequest(manga: SManga)
= nhGet(baseUrl + manga.url) = nhGet(baseUrl + manga.url)
fun urlToDetailsRequest(url: String)
= nhGet(baseUrl + "/api/gallery/" + url.split("/").last { it.isNotBlank() })
fun parseResultPage(response: Response): MangasPage { fun parseResultPage(response: Response): MangasPage {
val doc = response.asJsoup() val doc = response.asJsoup()
@ -250,7 +249,7 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
) )
val appName by lazy { val appName by lazy {
context.getString(R.string.app_name)!! context.getString(R.string.app_name)
} }
fun nhGet(url: String, tag: Any? = null) = GET(url) fun nhGet(url: String, tag: Any? = null) = GET(url)
.newBuilder() .newBuilder()
@ -260,7 +259,7 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
"Chrome/56.0.2924.87 " + "Chrome/56.0.2924.87 " +
"Safari/537.36 " + "Safari/537.36 " +
"$appName/${BuildConfig.VERSION_CODE}") "$appName/${BuildConfig.VERSION_CODE}")
.tag(tag).build()!! .tag(tag).build()
override val id = NHENTAI_SOURCE_ID override val id = NHENTAI_SOURCE_ID
@ -272,6 +271,19 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
override val supportsLatest = true override val supportsLatest = true
// === URL IMPORT STUFF
override val matchingHosts = listOf(
"nhentai.net"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
if(uri.pathSegments.firstOrNull()?.toLowerCase() != "g")
return null
return "https://nhentai.net/g/${uri.pathSegments[1]}/"
}
companion object { companion object {
private val GALLERY_JSON_REGEX = Regex("new N.gallery\\((.*)\\);") private val GALLERY_JSON_REGEX = Regex("new N.gallery\\((.*)\\);")
private const val REVERSE_PARAM = "TEH_REVERSE" private const val REVERSE_PARAM = "TEH_REVERSE"
@ -282,9 +294,4 @@ class NHentai(context: Context) : HttpSource(), LewdSource<NHentaiSearchMetadata
JsonParser() JsonParser()
} }
} }
fun JsonElement.notNull() =
if(this is JsonNull)
null
else this
} }

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.ChapterRecognition import eu.kanade.tachiyomi.util.ChapterRecognition
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.PervEdenLang import exh.metadata.metadata.PervEdenLang
@ -27,7 +28,7 @@ import java.util.*
// TODO Transform into delegated source // TODO Transform into delegated source
class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSource(), class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSource(),
LewdSource<PervEdenSearchMetadata, Document> { LewdSource<PervEdenSearchMetadata, Document>, UrlImportableSource {
/** /**
* The class of the metadata used by this source * The class of the metadata used by this source
*/ */
@ -306,6 +307,23 @@ class PervEden(override val id: Long, val pvLang: PervEdenLang) : ParsedHttpSour
} }
} }
override val matchingHosts = listOf("www.perveden.com")
override fun matchesUri(uri: Uri): Boolean {
return super.matchesUri(uri) && uri.pathSegments.firstOrNull()?.toLowerCase() == when(pvLang) {
PervEdenLang.en -> "en-manga"
PervEdenLang.it -> "it-manga"
}
}
override fun mapUrlToMangaUrl(uri: Uri): String? {
val newUri = Uri.parse("http://www.perveden.com/").buildUpon()
uri.pathSegments.take(3).forEach {
newUri.appendPath(it)
}
return newUri.toString()
}
companion object { companion object {
val DATE_FORMAT = SimpleDateFormat("MMM d, yyyy", Locale.US).apply { val DATE_FORMAT = SimpleDateFormat("MMM d, yyyy", Locale.US).apply {
timeZone = TimeZone.getTimeZone("GMT") timeZone = TimeZone.getTimeZone("GMT")

View File

@ -1,11 +1,13 @@
package eu.kanade.tachiyomi.source.online.english package eu.kanade.tachiyomi.source.online.english
import android.net.Uri
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
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.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
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
@ -18,7 +20,7 @@ import org.jsoup.nodes.Document
import rx.Observable import rx.Observable
class HentaiCafe(delegate: HttpSource) : DelegatedHttpSource(delegate), class HentaiCafe(delegate: HttpSource) : DelegatedHttpSource(delegate),
LewdSource<HentaiCafeSearchMetadata, Document> { LewdSource<HentaiCafeSearchMetadata, Document>, UrlImportableSource {
/** /**
* An ISO 639-1 compliant language code (two letters in lower case). * An ISO 639-1 compliant language code (two letters in lower case).
*/ */
@ -88,4 +90,17 @@ class HentaiCafe(delegate: HttpSource) : DelegatedHttpSource(delegate),
} }
) )
}.toObservable() }.toObservable()
override val matchingHosts = listOf(
"hentai.cafe"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
return if(lcFirstPathSegment == "manga")
"https://hentai.cafe/${uri.pathSegments[2]}"
else
"https://hentai.cafe/$lcFirstPathSegment"
}
} }

View File

@ -13,8 +13,10 @@ import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.toast
import exh.GalleryAddEvent
import exh.TSUMINO_SOURCE_ID import exh.TSUMINO_SOURCE_ID
import exh.ui.captcha.ActionCompletionVerifier import exh.ui.captcha.ActionCompletionVerifier
import exh.ui.captcha.BrowserActionActivity import exh.ui.captcha.BrowserActionActivity
@ -33,7 +35,10 @@ import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource<TsuminoSearchMetadata, Document>, ActionCompletionVerifier { class Tsumino(private val context: Context): ParsedHttpSource(),
LewdSource<TsuminoSearchMetadata, Document>,
ActionCompletionVerifier,
UrlImportableSource {
override val metaClass = TsuminoSearchMetadata::class override val metaClass = TsuminoSearchMetadata::class
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
@ -400,6 +405,19 @@ class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource<Tsum
class MinimumRatingFilter : Filter.Select<String>("Minimum rating", (0 .. 5).map { "$it stars" }.toTypedArray()) class MinimumRatingFilter : Filter.Select<String>("Minimum rating", (0 .. 5).map { "$it stars" }.toTypedArray())
class ExcludeParodiesFilter : Filter.CheckBox("Exclude parodies") class ExcludeParodiesFilter : Filter.CheckBox("Exclude parodies")
override val matchingHosts = listOf(
"www.tsumino.com"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.toLowerCase() ?: return null
if(lcFirstPathSegment != "read" && lcFirstPathSegment != "book")
return null
return "https://tsumino.com/Book/Info/${uri.pathSegments[2]}"
}
companion object { companion object {
val jsonParser by lazy { val jsonParser by lazy {
JsonParser() JsonParser()

View File

@ -2,23 +2,13 @@ package exh
import android.net.Uri import android.net.Uri
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.github.salomonbrys.kotson.*
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.source.online.all.EHentai import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.util.syncChaptersWithSource import eu.kanade.tachiyomi.util.syncChaptersWithSource
import exh.metadata.metadata.EHentaiSearchMetadata
import okhttp3.MediaType
import okhttp3.Request
import okhttp3.RequestBody
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URI
import java.net.URISyntaxException
class GalleryAdder { class GalleryAdder {
@ -26,133 +16,55 @@ class GalleryAdder {
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private val networkHelper: NetworkHelper by injectLazy()
companion object {
const val EH_API_BASE = "https://api.e-hentai.org/api.php"
val JSON = MediaType.parse("application/json; charset=utf-8")!!
}
fun getGalleryUrlFromPage(url: String): String {
val uri = Uri.parse(url)
val lastSplit = uri.pathSegments.last().split("-")
val pageNum = lastSplit.last()
val gallery = lastSplit.first()
val pageToken = uri.pathSegments.elementAt(1)
val json = JsonObject()
json["method"] = "gtoken"
json["pagelist"] = JsonArray().apply {
add(JsonArray().apply {
add(gallery.toInt())
add(pageToken)
add(pageNum.toInt())
})
}
val outJson = JsonParser().parse(networkHelper.client.newCall(Request.Builder()
.url(EH_API_BASE)
.post(RequestBody.create(JSON, json.toString()))
.build()).execute().body()!!.string()).obj
val obj = outJson["tokenlist"].array.first()
return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/"
}
fun addGallery(url: String, fun addGallery(url: String,
fav: Boolean = false, fav: Boolean = false,
forceSource: Long? = null, forceSource: UrlImportableSource? = null,
throttleFunc: () -> Unit = {}): GalleryAddEvent { throttleFunc: () -> Unit = {}): GalleryAddEvent {
XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource) XLog.d("Importing gallery (url: %s, fav: %s, forceSource: %s)...", url, fav, forceSource)
try { try {
val urlObj = Uri.parse(url) val uri = Uri.parse(url)
val lowercasePs = urlObj.pathSegments.map(String::toLowerCase)
val lcFirstPathSegment = lowercasePs[0] // Find matching source
val source = when (urlObj.host.toLowerCase()) { val source = if(forceSource != null) {
"g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID try {
"exhentai.org" -> EXH_SOURCE_ID if (forceSource.matchesUri(uri)) forceSource
"nhentai.net" -> NHENTAI_SOURCE_ID else return GalleryAddEvent.Fail.UnknownType(url)
"www.perveden.com" -> { } catch(e: Exception) {
when(lowercasePs[1]) { XLog.e("Source URI match check error!", e)
"en-manga" -> PERV_EDEN_EN_SOURCE_ID return GalleryAddEvent.Fail.UnknownType(url)
"it-manga" -> PERV_EDEN_IT_SOURCE_ID
else -> return GalleryAddEvent.Fail.UnknownType(url)
}
} }
"hentai.cafe" -> HENTAI_CAFE_SOURCE_ID } else {
"www.tsumino.com" -> TSUMINO_SOURCE_ID sourceManager.getVisibleCatalogueSources()
"hitomi.la" -> HITOMI_SOURCE_ID .filterIsInstance<UrlImportableSource>()
else -> return GalleryAddEvent.Fail.UnknownType(url) .find {
try {
it.matchesUri(uri)
} catch(e: Exception) {
XLog.e("Source URI match check error!", e)
false
}
} ?: return GalleryAddEvent.Fail.UnknownType(url)
} }
if(forceSource != null && source != forceSource) { // Map URL to manga URL
return GalleryAddEvent.Fail.UnknownType(url) val realUrl = try {
} source.mapUrlToMangaUrl(uri)
} catch(e: Exception) {
XLog.e("Source URI map-to-manga error!", e)
null
} ?: return GalleryAddEvent.Fail.UnknownType(url)
val sourceObj = sourceManager.get(source) // Clean URL
?: return GalleryAddEvent.Fail.Error(url, "Source not installed!") val cleanedUrl = try {
source.cleanMangaUrl(realUrl)
val realUrl = when(source) { } catch(e: Exception) {
EH_SOURCE_ID, EXH_SOURCE_ID -> when (lcFirstPathSegment) { XLog.e("Source URI clean error!", e)
"g" -> { null
//Is already gallery page, do nothing } ?: return GalleryAddEvent.Fail.UnknownType(url)
url
}
"s" -> {
//Is page, fetch gallery token and use that
getGalleryUrlFromPage(url)
}
else -> return GalleryAddEvent.Fail.UnknownType(url)
}
NHENTAI_SOURCE_ID -> {
if(lcFirstPathSegment != "g")
return GalleryAddEvent.Fail.UnknownType(url)
"https://nhentai.net/g/${urlObj.pathSegments[1]}/"
}
PERV_EDEN_EN_SOURCE_ID,
PERV_EDEN_IT_SOURCE_ID -> {
val uri = Uri.parse("http://www.perveden.com/").buildUpon()
urlObj.pathSegments.take(3).forEach {
uri.appendPath(it)
}
uri.toString()
}
HENTAI_CAFE_SOURCE_ID -> {
if(lcFirstPathSegment == "manga")
"https://hentai.cafe/${urlObj.pathSegments[2]}"
"https://hentai.cafe/$lcFirstPathSegment"
}
TSUMINO_SOURCE_ID -> {
if(lcFirstPathSegment != "read" && lcFirstPathSegment != "book")
return GalleryAddEvent.Fail.UnknownType(url)
"https://tsumino.com/Book/Info/${urlObj.pathSegments[2]}"
}
HITOMI_SOURCE_ID -> {
if(lcFirstPathSegment != "galleries" && lcFirstPathSegment != "reader")
return GalleryAddEvent.Fail.UnknownType(url)
"https://hitomi.la/galleries/${urlObj.pathSegments[1].substringBefore('.')}.html"
}
else -> return GalleryAddEvent.Fail.UnknownType(url)
}
val cleanedUrl = when(source) {
EH_SOURCE_ID, EXH_SOURCE_ID -> EHentaiSearchMetadata.normalizeUrl(getUrlWithoutDomain(realUrl))
NHENTAI_SOURCE_ID -> getUrlWithoutDomain(realUrl)
PERV_EDEN_EN_SOURCE_ID,
PERV_EDEN_IT_SOURCE_ID -> getUrlWithoutDomain(realUrl)
HENTAI_CAFE_SOURCE_ID -> getUrlWithoutDomain(realUrl)
TSUMINO_SOURCE_ID -> getUrlWithoutDomain(realUrl)
HITOMI_SOURCE_ID -> getUrlWithoutDomain(realUrl)
else -> return GalleryAddEvent.Fail.UnknownType(url)
}
//Use manga in DB if possible, otherwise, make a new manga //Use manga in DB if possible, otherwise, make a new manga
val manga = db.getManga(cleanedUrl, source).executeAsBlocking() val manga = db.getManga(cleanedUrl, source.id).executeAsBlocking()
?: Manga.create(source).apply { ?: Manga.create(source.id).apply {
this.url = cleanedUrl this.url = cleanedUrl
title = realUrl title = realUrl
} }
@ -166,7 +78,7 @@ class GalleryAdder {
} }
// Fetch and copy details // Fetch and copy details
val newManga = sourceObj.fetchMangaDetails(manga).toBlocking().first() val newManga = source.fetchMangaDetails(manga).toBlocking().first()
manga.copyFrom(newManga) manga.copyFrom(newManga)
manga.initialized = true manga.initialized = true
@ -176,13 +88,13 @@ class GalleryAdder {
//Fetch and copy chapters //Fetch and copy chapters
try { try {
val chapterListObs = if(sourceObj is EHentai) { val chapterListObs = if(source is EHentai) {
sourceObj.fetchChapterList(manga, throttleFunc) source.fetchChapterList(manga, throttleFunc)
} else { } else {
sourceObj.fetchChapterList(manga) source.fetchChapterList(manga)
} }
chapterListObs.map { chapterListObs.map {
syncChaptersWithSource(db, it, manga, sourceObj) syncChaptersWithSource(db, it, manga, source)
}.toBlocking().first() }.toBlocking().first()
} catch (e: Exception) { } catch (e: Exception) {
XLog.w("Failed to update chapters for gallery: ${manga.title}!", e) XLog.w("Failed to update chapters for gallery: ${manga.title}!", e)
@ -201,20 +113,6 @@ class GalleryAdder {
((e.message ?: "Unknown error!") + " (Gallery: $url)").trim()) ((e.message ?: "Unknown error!") + " (Gallery: $url)").trim())
} }
} }
private fun getUrlWithoutDomain(orig: String): String {
return try {
val uri = URI(orig)
var out = uri.path
if (uri.query != null)
out += "?" + uri.query
if (uri.fragment != null)
out += "#" + uri.fragment
out
} catch (e: URISyntaxException) {
orig
}
}
} }
sealed class GalleryAddEvent { sealed class GalleryAddEvent {
@ -224,9 +122,8 @@ sealed class GalleryAddEvent {
class Success(override val galleryUrl: String, class Success(override val galleryUrl: String,
val manga: Manga): GalleryAddEvent() { val manga: Manga): GalleryAddEvent() {
override val galleryTitle = manga.title
override val logMessage = "Added gallery: $galleryTitle" override val logMessage = "Added gallery: $galleryTitle"
override val galleryTitle: String
get() = manga.title
} }
sealed class Fail: GalleryAddEvent() { sealed class Fail: GalleryAddEvent() {

View File

@ -350,7 +350,7 @@ class FavoritesSyncHelper(val context: Context) {
//Import using gallery adder //Import using gallery adder
val result = galleryAdder.addGallery("${exh.baseUrl}${it.getUrl()}", val result = galleryAdder.addGallery("${exh.baseUrl}${it.getUrl()}",
true, true,
EXH_SOURCE_ID, exh,
throttleManager::throttle) throttleManager::throttle)
if(result is GalleryAddEvent.Fail) { if(result is GalleryAddEvent.Fail) {

View File

@ -1,7 +1,7 @@
package exh.util package exh.util
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import exh.GalleryAddEvent import exh.GalleryAddEvent
import exh.GalleryAdder import exh.GalleryAdder
import rx.Observable import rx.Observable
@ -13,11 +13,11 @@ private val galleryAdder by lazy {
/** /**
* A version of fetchSearchManga that supports URL importing * A version of fetchSearchManga that supports URL importing
*/ */
fun Source.urlImportFetchSearchManga(query: String, fail: () -> Observable<MangasPage>) = fun UrlImportableSource.urlImportFetchSearchManga(query: String, fail: () -> Observable<MangasPage>) =
when { when {
query.startsWith("http://") || query.startsWith("https://") -> { query.startsWith("http://") || query.startsWith("https://") -> {
Observable.fromCallable { Observable.fromCallable {
val res = galleryAdder.addGallery(query, false, id) val res = galleryAdder.addGallery(query, false, this)
MangasPage((if(res is GalleryAddEvent.Success) MangasPage((if(res is GalleryAddEvent.Success)
listOf(res.manga) listOf(res.manga)
else else