From e346d95b0eacb3129751a46ad4bd569c0a64559b Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Sun, 2 Aug 2020 00:50:52 -0400 Subject: [PATCH] Delegate HBrowse --- app/build.gradle | 2 +- .../database/resolvers/MangaUrlPutResolver.kt | 32 + .../tachiyomi/extension/ExtensionManager.kt | 2 - .../kanade/tachiyomi/source/SourceManager.kt | 9 +- .../source/online/english/HBrowse.kt | 937 +----------------- .../kanade/tachiyomi/ui/main/MainActivity.kt | 4 - .../ui/setting/SettingsAdvancedController.kt | 7 - app/src/main/java/exh/EHSourceHelpers.kt | 6 +- app/src/main/java/exh/EXHMigrations.kt | 46 + .../metadata/HBrowseSearchMetadata.kt | 8 +- 10 files changed, 119 insertions(+), 934 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaUrlPutResolver.kt diff --git a/app/build.gradle b/app/build.gradle index d0f4dcc37..9398b9013 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,7 @@ android { minSdkVersion AndroidConfig.minSdk targetSdkVersion AndroidConfig.targetSdk testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - versionCode 3 + versionCode 4 versionName "1.1.0" buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\"" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaUrlPutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaUrlPutResolver.kt new file mode 100644 index 000000000..9cacff754 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/MangaUrlPutResolver.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.data.database.resolvers + +import android.content.ContentValues +import com.pushtorefresh.storio.sqlite.StorIOSQLite +import com.pushtorefresh.storio.sqlite.operations.put.PutResolver +import com.pushtorefresh.storio.sqlite.operations.put.PutResult +import com.pushtorefresh.storio.sqlite.queries.UpdateQuery +import eu.kanade.tachiyomi.data.database.inTransactionReturn +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.database.tables.MangaTable + +// [EXH] +class MangaUrlPutResolver : PutResolver() { + + override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn { + val updateQuery = mapToUpdateQuery(manga) + val contentValues = mapToContentValues(manga) + + val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues) + PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table()) + } + + fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder() + .table(MangaTable.TABLE) + .where("${MangaTable.COL_ID} = ?") + .whereArgs(manga.id) + .build() + + fun mapToContentValues(manga: Manga) = ContentValues(1).apply { + put(MangaTable.COL_URL, manga.url) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 5b4ed8617..0bf25899f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.util.system.toast import exh.EH_SOURCE_ID import exh.EIGHTMUSES_SOURCE_ID import exh.EXH_SOURCE_ID -import exh.HBROWSE_SOURCE_ID import exh.HITOMI_SOURCE_ID import exh.MERGED_SOURCE_ID import exh.NHENTAI_SOURCE_ID @@ -89,7 +88,6 @@ class ExtensionManager( NHENTAI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_nhentai_source) HITOMI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_hitomi_source) EIGHTMUSES_SOURCE_ID -> context.getDrawable(R.mipmap.ic_8muses_source) - HBROWSE_SOURCE_ID -> context.getDrawable(R.mipmap.ic_hbrowse_source) MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source) else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt index ed5409eb9..4c59fd1ac 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt @@ -141,7 +141,6 @@ open class SourceManager(private val context: Context) { exSrcs += NHentai(context) exSrcs += Hitomi(context) exSrcs += EightMuses(context) - exSrcs += HBrowse(context) return exSrcs } // SY <-- @@ -200,7 +199,13 @@ open class SourceManager(private val context: Context) { "eu.kanade.tachiyomi.extension.all.mangadex", MangaDex::class, true - )*/ + )*/, + DelegatedSource( + "HBrowse", + 1401584337232758222, + "eu.kanade.tachiyomi.extension.en.hbrowse.HBrowse", + HBrowse::class + ) ).associateBy { it.originalSourceQualifiedClassName } var currentDelegatedSources = mutableMapOf() diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt index 06881868f..02149932f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HBrowse.kt @@ -2,330 +2,56 @@ package eu.kanade.tachiyomi.source.online.english import android.content.Context import android.net.Uri -import com.github.salomonbrys.kotson.array -import com.github.salomonbrys.kotson.string -import com.google.gson.JsonParser -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.asObservable +import androidx.core.net.toUri import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.asJsoup -import exh.HBROWSE_SOURCE_ID import exh.metadata.metadata.HBrowseSearchMetadata import exh.metadata.metadata.base.RaisedTag -import exh.search.Namespace -import exh.search.SearchEngine -import exh.search.Text +import exh.source.DelegatedHttpSource import exh.ui.metadata.adapters.HBrowseDescriptionAdapter -import exh.util.await -import exh.util.dropBlank import exh.util.urlImportFetchSearchManga -import hu.akarnokd.rxjava.interop.RxJavaInterop -import info.debatty.java.stringsimilarity.Levenshtein -import kotlin.math.ceil -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import kotlinx.coroutines.rx2.asSingle -import okhttp3.CookieJar -import okhttp3.FormBody -import okhttp3.Headers -import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -import rx.schedulers.Schedulers - -class HBrowse(val context: Context) : HttpSource(), LewdSource, UrlImportableSource { - /** - * An ISO 639-1 compliant language code (two letters in lower case). - */ - override val lang: String = "en" - /** - * Base url of the website without the trailing slash, like: http://mysite.com - */ - override val baseUrl = HBrowseSearchMetadata.BASE_URL - - override val name: String = "HBrowse" - - override val supportsLatest = true +class HBrowse(delegate: HttpSource, val context: Context) : + DelegatedHttpSource(delegate), + LewdSource, + UrlImportableSource { override val metaClass = HBrowseSearchMetadata::class + override val lang = "en" - override val id: Long = HBROWSE_SOURCE_ID + // Support direct URL importing + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable = + urlImportFetchSearchManga(context, query) { + super.fetchSearchManga(page, query, filters) + } - override fun headersBuilder() = Headers.Builder() - .add("Cookie", BASE_COOKIES) - - private val clientWithoutCookies = client.newBuilder() - .cookieJar(CookieJar.NO_COOKIES) - .build() - - private val nonRedirectingClientWithoutCookies = clientWithoutCookies.newBuilder() - .followRedirects(false) - .build() - - private val searchEngine = SearchEngine() - - /** - * Returns the request for the popular manga given the page. - * - * @param page the page number to retrieve. - */ - override fun popularMangaRequest(page: Int) = GET("$baseUrl/browse/title/rank/DESC/$page", headers) - - private fun parseListing(response: Response): MangasPage { - val doc = response.asJsoup() - val main = doc.selectFirst("#main") - val items = main.select(".thumbTable > tbody") - val manga = items.map { mangaEle -> - SManga.create().apply { - val thumbElement = mangaEle.selectFirst(".thumbImg") - url = "/" + thumbElement.parent().attr("href").split("/").dropBlank().first() - title = thumbElement.parent().attr("title").substringAfter('\'').substringBeforeLast('\'') - thumbnail_url = baseUrl + thumbElement.attr("src") + override fun fetchMangaDetails(manga: SManga): Observable { + return client.newCall(mangaDetailsRequest(manga)) + .asObservableSuccess() + .flatMap { + parseToManga(manga, it.asJsoup()).andThen(Observable.just(manga)) } - } - - val hasNextPage = doc.selectFirst("#main > p > a[title~=jump]:nth-last-child(1)") != null - return MangasPage( - manga, - hasNextPage - ) - } - - /** - * Returns an observable containing a page with a list of manga. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return urlImportFetchSearchManga(context, query) { - fetchSearchMangaInternal(page, query, filters) - } - } - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - override fun popularMangaParse(response: Response) = parseListing(response) - - /** - * Returns the request for the search manga given the page. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Should not be called!") - - private fun fetchSearchMangaInternal(page: Int, query: String, filters: FilterList): Observable { - return RxJavaInterop.toV1Single( - GlobalScope.async(Dispatchers.IO) { - val modeFilter = filters.filterIsInstance().firstOrNull() - val sortFilter = filters.filterIsInstance().firstOrNull() - - var base: String? = null - var isSortFilter = false - // - var tagQuery: List>? = null - - if (sortFilter != null) { - sortFilter.state?.let { state -> - if (query.isNotBlank()) { - throw IllegalArgumentException("Cannot use sorting while text/tag search is active!") - } - - isSortFilter = true - base = "/browse/title/${SortFilter.SORT_OPTIONS[state.index].first}/${if (state.ascending) "ASC" else "DESC"}" - } - } - - if (base == null) { - base = if (modeFilter != null && modeFilter.state == 1) { - tagQuery = searchEngine.parseQuery(query, false).map { - when (it) { - is Text -> { - var minDist = Int.MAX_VALUE.toDouble() - // ns, value - var minContent: Pair = "" to "" - for (ns in ALL_TAGS) { - val (v, d) = ns.value.nearest(it.rawTextOnly(), minDist) - if (d < minDist) { - minDist = d - minContent = ns.key to v - } - } - minContent - } - is Namespace -> { - // Map ns aliases - val mappedNs = NS_MAPPINGS[it.namespace] ?: it.namespace - - var key = mappedNs - if (!ALL_TAGS.containsKey(key)) key = ALL_TAGS.keys.sorted().nearest(mappedNs).first - - // Find nearest NS - val nsContents = ALL_TAGS[key] - - key to nsContents!!.nearest(it.tag?.rawTextOnly() ?: "").first - } - else -> error("Unknown type!") - }.let { p -> - Triple(p.first, p.second, it.excluded) - } - } - - "/result" - } else { - "/search" - } - } - - base += "/$page" - - if (isSortFilter) { - parseListing( - client.newCall(GET(baseUrl + base, headers)) - .asObservableSuccess() - .toSingle() - .await(Schedulers.io()) - ) - } else { - val body = if (tagQuery != null) { - FormBody.Builder() - .add("type", "advance") - .apply { - tagQuery.forEach { - add(it.first + "_" + it.second, if (it.third) "n" else "y") - } - } - } else { - FormBody.Builder() - .add("type", "search") - .add("needle", query) - } - val processRequest = POST( - "$baseUrl/content/process.php", - headers, - body = body.build() - ) - val processResponse = nonRedirectingClientWithoutCookies.newCall(processRequest) - .asObservable() - .toSingle() - .await(Schedulers.io()) - - if (!processResponse.isRedirect) { - throw IllegalStateException("Unexpected process response code!") - } - - val sessId = processResponse.headers("Set-Cookie").find { - it.startsWith("PHPSESSID") - } ?: throw IllegalStateException("Missing server session cookie!") - - val response = clientWithoutCookies.newCall( - GET( - baseUrl + base, - headersBuilder() - .set("Cookie", BASE_COOKIES + " " + sessId.substringBefore(';')) - .build() - ) - ) - .asObservableSuccess() - .toSingle() - .await(Schedulers.io()) - - val doc = response.asJsoup() - val manga = doc.select(".browseDescription").map { - SManga.create().apply { - val first = it.child(0) - url = first.attr("href") - title = first.attr("title").substringAfter('\'').removeSuffix("'").replace('_', ' ') - thumbnail_url = HBrowseSearchMetadata.guessThumbnailUrl(url.substring(1)) - } - } - val hasNextPage = doc.selectFirst("#main > p > a[title~=jump]:nth-last-child(1)") != null - MangasPage( - manga, - hasNextPage - ) - } - }.asSingle(GlobalScope.coroutineContext) - ).toObservable() - } - - // Collection must be sorted and cannot be sorted - private fun List.nearest(string: String, maxDist: Double = Int.MAX_VALUE.toDouble()): Pair { - val idx = binarySearch(string) - return if (idx < 0) { - val l = Levenshtein() - var minSoFar = maxDist - var minIndexSoFar = 0 - forEachIndexed { index, s -> - val d = l.distance(string, s, ceil(minSoFar).toInt()) - if (d < minSoFar) { - minSoFar = d - minIndexSoFar = index - } - } - get(minIndexSoFar) to minSoFar - } else { - get(idx) to 0.0 - } - } - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - override fun searchMangaParse(response: Response) = parseListing(response) - - /** - * Returns the request for latest manga given the page. - * - * @param page the page number to retrieve. - */ - override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/browse/title/date/DESC/$page", headers) - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - override fun latestUpdatesParse(response: Response) = parseListing(response) - - /** - * Parses the response from the site and returns the details of a manga. - * - * @param response the response from the site. - */ - override fun mangaDetailsParse(response: Response): SManga { - throw UnsupportedOperationException("Should not be called!") } override fun parseIntoMetadata(metadata: HBrowseSearchMetadata, input: Document) { val tables = parseIntoTables(input) with(metadata) { - hbId = Uri.parse(input.location()).pathSegments.first().toLong() + val uri = input.location().toUri() + hbId = uri.pathSegments[1].toLong() + + hbUrlExtra = uri.pathSegments[2] tags.clear() - (tables[""]!! + tables["categories"]!!).forEach { (k, v) -> + ((tables[""] ?: error("")) + (tables["categories"] ?: error(""))).forEach { (k, v) -> when (val lowercaseNs = k.toLowerCase()) { "title" -> title = v.text() "length" -> length = v.text().substringBefore(" ").toInt() @@ -343,35 +69,6 @@ class HBrowse(val context: Context) : HttpSource(), LewdSource { - return client.newCall(mangaDetailsRequest(manga)) - .asObservableSuccess() - .flatMap { - parseToManga(manga, it.asJsoup()).andThen(Observable.just(manga)) - } - } - - /** - * Parses the response from the site and returns a list of chapters. - * - * @param response the response from the site. - */ - override fun chapterListParse(response: Response): List { - return parseIntoTables(response.asJsoup())["read manga online"]?.map { (key, value) -> - SChapter.create().apply { - url = value.selectFirst(".listLink").attr("href") - - name = key - } - } ?: emptyList() - } - private fun parseIntoTables(doc: Document): Map> { return doc.select("#main > .listTable").map { ele -> val tableName = ele.previousElementSibling()?.text()?.toLowerCase() ?: "" @@ -381,93 +78,6 @@ class HBrowse(val context: Context) : HttpSource(), LewdSource { - val doc = response.asJsoup() - val basePath = listOf("data") + response.request.url.pathSegments - val scripts = doc.getElementsByTag("script").map { it.data() } - for (script in scripts) { - val totalPages = TOTAL_PAGES_REGEX.find(script)?.groupValues?.getOrNull(1)?.toIntOrNull() - ?: continue - val pageList = PAGE_LIST_REGEX.find(script)?.groupValues?.getOrNull(1) ?: continue - - return JsonParser.parseString(pageList).array.take(totalPages).map { - it.string - }.mapIndexed { index, pageName -> - Page( - index, - pageName, - "$baseUrl/${basePath.joinToString("/")}/$pageName" - ) - } - } - - return emptyList() - } - - class HelpFilter : Filter.HelpDialog( - "Usage instructions", - markdown = - """ - ### Modes - There are three available filter modes: - - Text search - - Tag search - - Sort mode - - You can only use a single mode at a time. Switch between the text and tag search modes using the dropdown menu. Switch to sorting mode by selecting a sorting option. - - ### Text search - Search for galleries by title, artist or origin. - - ### Tag search - Search for galleries by tag (e.g. search for a specific genre, type, setting, etc). Uses nhentai/e-hentai syntax. Refer to the "Search" section on [this page](https://nhentai.net/info/) for more information. - - ### Sort mode - View a list of all galleries sorted by a specific parameter. Exit sorting mode by resetting the filters using the reset button near the bottom of the screen. - - ### Tag list - """.trimIndent() + "\n$TAGS_AS_MARKDOWN" - ) - - class ModeFilter : Filter.Select( - "Mode", - arrayOf( - "Text search", - "Tag search" - ) - ) - - class SortFilter : Filter.Sort("Sort", SORT_OPTIONS.map { it.second }.toTypedArray()) { - companion object { - // internal to display - val SORT_OPTIONS = listOf( - "length" to "Length", - "date" to "Date added", - "rank" to "Rank" - ) - } - } - - override fun getFilterList() = FilterList( - HelpFilter(), - ModeFilter(), - SortFilter() - ) - - /** - * Parses the response from the site and returns the absolute url to the source image. - * - * @param response the response from the site. - */ - override fun imageUrlParse(response: Response): String { - throw UnsupportedOperationException("Should not be called!") - } - override val matchingHosts = listOf( "www.hbrowse.com", "hbrowse.com" @@ -480,507 +90,4 @@ class HBrowse(val context: Context) : HttpSource(), LewdSource - "#### $ns\n" + values.map { "- $it" }.joinToString("\n") - }.joinToString("\n\n") - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index a63d701fd..ae71281f7 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -50,7 +50,6 @@ import exh.EH_SOURCE_ID import exh.EIGHTMUSES_SOURCE_ID import exh.EXHMigrations import exh.EXH_SOURCE_ID -import exh.HBROWSE_SOURCE_ID import exh.HITOMI_SOURCE_ID import exh.NHENTAI_SOURCE_ID import exh.PERV_EDEN_EN_SOURCE_ID @@ -235,9 +234,6 @@ class MainActivity : BaseActivity() { if (EIGHTMUSES_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { BlacklistedSources.HIDDEN_SOURCES += EIGHTMUSES_SOURCE_ID } - if (HBROWSE_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { - BlacklistedSources.HIDDEN_SOURCES += HBROWSE_SOURCE_ID - } } // SY --> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index 6881f65c7..96ad689cc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -35,7 +35,6 @@ import eu.kanade.tachiyomi.util.system.toast import exh.EH_SOURCE_ID import exh.EIGHTMUSES_SOURCE_ID import exh.EXH_SOURCE_ID -import exh.HBROWSE_SOURCE_ID import exh.HITOMI_SOURCE_ID import exh.NHENTAI_SOURCE_ID import exh.PERV_EDEN_EN_SOURCE_ID @@ -175,9 +174,6 @@ class SettingsAdvancedController : SettingsController() { if (EIGHTMUSES_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { BlacklistedSources.HIDDEN_SOURCES += EIGHTMUSES_SOURCE_ID } - if (HBROWSE_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) { - BlacklistedSources.HIDDEN_SOURCES += HBROWSE_SOURCE_ID - } } else { if (EH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) { BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID @@ -200,9 +196,6 @@ class SettingsAdvancedController : SettingsController() { if (EIGHTMUSES_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) { BlacklistedSources.HIDDEN_SOURCES -= EIGHTMUSES_SOURCE_ID } - if (HBROWSE_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) { - BlacklistedSources.HIDDEN_SOURCES -= HBROWSE_SOURCE_ID - } } true } diff --git a/app/src/main/java/exh/EHSourceHelpers.kt b/app/src/main/java/exh/EHSourceHelpers.kt index 8d5e8b622..380b5ccdd 100755 --- a/app/src/main/java/exh/EHSourceHelpers.kt +++ b/app/src/main/java/exh/EHSourceHelpers.kt @@ -2,6 +2,7 @@ package exh import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.online.english.HBrowse import eu.kanade.tachiyomi.source.online.english.HentaiCafe import eu.kanade.tachiyomi.source.online.english.Pururin import eu.kanade.tachiyomi.source.online.english.Tsumino @@ -22,13 +23,14 @@ val PURURIN_SOURCE_ID = delegatedSourceId() val TSUMINO_SOURCE_ID = delegatedSourceId() const val HITOMI_SOURCE_ID = LEWD_SOURCE_SERIES + 10 const val EIGHTMUSES_SOURCE_ID = LEWD_SOURCE_SERIES + 11 -const val HBROWSE_SOURCE_ID = LEWD_SOURCE_SERIES + 12 +val HBROWSE_SOURCE_ID = delegatedSourceId() const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69 private val DELEGATED_LEWD_SOURCES = listOf( HentaiCafe::class, Pururin::class, - Tsumino::class + Tsumino::class, + HBrowse::class ) val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf( diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index e67d2739a..2de6b323a 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -2,6 +2,8 @@ package exh import android.content.Context import com.elvishew.xlog.XLog +import com.pushtorefresh.storio.sqlite.queries.Query +import com.pushtorefresh.storio.sqlite.queries.RawQuery import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.data.backup.models.DHistory import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -9,6 +11,8 @@ import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaImpl import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.database.resolvers.MangaUrlPutResolver +import eu.kanade.tachiyomi.data.database.tables.MangaTable import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.updater.UpdaterJob @@ -43,6 +47,44 @@ object EXHMigrations { LibraryUpdateJob.setupTask(context) return false } + if (oldVersion < 4) { + db.inTransaction { + // Migrate Tsumino source IDs + db.lowLevel().executeSQL( + RawQuery.builder() + .query( + """ + UPDATE ${MangaTable.TABLE} + SET ${MangaTable.COL_SOURCE} = $HBROWSE_SOURCE_ID + WHERE ${MangaTable.COL_SOURCE} = 6912 + """.trimIndent() + ) + .affectsTables(MangaTable.TABLE) + .build() + ) + // Migrate BHrowse URLs + val hBrowseManga = db.db.get() + .listOfObjects(Manga::class.java) + .withQuery( + Query.builder() + .table(MangaTable.TABLE) + .where("${MangaTable.COL_SOURCE} = $HBROWSE_SOURCE_ID") + .build() + ) + .prepare() + .executeAsBlocking() + hBrowseManga.forEach { + it.url = it.url + "/c00001" + } + + db.db.put() + .objects(hBrowseManga) + // Extremely slow without the resolver :/ + .withPutResolver(MangaUrlPutResolver()) + .prepare() + .executeAsBlocking() + } + } // if (oldVersion < 1) { } // do stuff here when releasing changed crap @@ -70,6 +112,10 @@ object EXHMigrations { manga.source = TSUMINO_SOURCE_ID!! } + if (manga.source == 6912L) { + manga.source = HBROWSE_SOURCE_ID!! + } + // Migrate nhentai URLs if (manga.source == NHENTAI_SOURCE_ID) { manga.url = getUrlWithoutDomain(manga.url) diff --git a/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt b/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt index 1ac752b20..fbc53cd06 100644 --- a/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt +++ b/app/src/main/java/exh/metadata/metadata/HBrowseSearchMetadata.kt @@ -9,13 +9,17 @@ import exh.metadata.metadata.base.RaisedSearchMetadata class HBrowseSearchMetadata : RaisedSearchMetadata() { var hbId: Long? = null + var hbUrlExtra: String? = null + + var thumbnail: String? = null + var title: String? by titleDelegate(TITLE_TYPE_MAIN) // Length in pages var length: Int? = null override fun copyTo(manga: SManga) { - manga.url = "/$hbId" + manga.url = "/$hbId/$hbUrlExtra" title?.let { manga.title = it @@ -44,6 +48,8 @@ class HBrowseSearchMetadata : RaisedSearchMetadata() { override fun getExtraInfoPairs(context: Context): List> { val pairs = mutableListOf>() hbId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } + hbUrlExtra?.let { pairs += Pair(context.getString(R.string.id), it.toString()) } + thumbnail?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it.toString()) } title?.let { pairs += Pair(context.getString(R.string.title), it) } length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) } return pairs