Implement Perv Eden source.

This commit is contained in:
NerdNumber9 2017-03-07 22:02:16 -05:00
parent 957c50088d
commit c42f011a05
17 changed files with 505 additions and 76 deletions

View File

@ -13,16 +13,14 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
import eu.kanade.tachiyomi.source.online.all.EHentaiMetadata
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.YamlHttpSource
import eu.kanade.tachiyomi.source.online.all.PervEden
import eu.kanade.tachiyomi.source.online.english.*
import eu.kanade.tachiyomi.source.online.german.WieManga
import eu.kanade.tachiyomi.source.online.russian.Mangachan
import eu.kanade.tachiyomi.source.online.russian.Mintmanga
import eu.kanade.tachiyomi.source.online.russian.Readmanga
import eu.kanade.tachiyomi.util.hasPermission
import exh.EH_METADATA_SOURCE_ID
import exh.EH_SOURCE_ID
import exh.EXH_METADATA_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.*
import org.yaml.snakeyaml.Yaml
import rx.functions.Action1
import timber.log.Timber
@ -99,6 +97,8 @@ open class SourceManager(private val context: Context) {
exSrcs += EHentai(EXH_SOURCE_ID, true, context)
exSrcs += EHentaiMetadata(EXH_METADATA_SOURCE_ID, true, context)
}
exSrcs += PervEden(PERV_EDEN_EN_SOURCE_ID, "en")
exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, "it")
return exSrcs
}

View File

@ -20,6 +20,8 @@ import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.util.*
import exh.ui.login.LoginActivity
import exh.util.UriFilter
import exh.util.UriGroup
import okhttp3.Request
class EHentai(override val id: Long,
@ -158,11 +160,14 @@ class EHentai(override val id: Long,
override fun mangaDetailsParse(response: Response) = with(response.asJsoup()) {
val metdata = ExGalleryMetadata()
with(metdata) {
val manga = SManga.create()
url = response.request().url().toString()
exh = this@EHentai.exh
title = select("#gn").text().nullIfBlank()?.trim()
altTitle = select("#gj").text().nullIfBlank()?.trim()
altTitles.clear()
select("#gj").text().nullIfBlank()?.trim()?.let { newAltTitle ->
altTitles.add(newAltTitle)
}
thumbnailUrl = select("#gd1 img").attr("src").nullIfBlank()?.trim()
@ -227,12 +232,13 @@ class EHentai(override val id: Long,
}
//Save metadata
metadataHelper.writeGallery(this)
metadataHelper.writeGallery(this, id)
//Copy metadata to manga
copyTo(manga)
manga
SManga.create().let {
copyTo(it)
it
}
}
}
@ -333,9 +339,6 @@ class EHentai(override val id: Long,
GenreGroup(),
AdvancedGroup()
)
private interface UriFilter {
fun addToUri(builder: Uri.Builder)
}
class GenreOption(name: String, val genreId: String): Filter.CheckBox(name, false), UriFilter {
override fun addToUri(builder: Uri.Builder) {
@ -386,14 +389,6 @@ class EHentai(override val id: Long,
RatingOption()
))
open class UriGroup<V>(name: String, state: List<V>) : Filter.Group<V>(name, state), UriFilter {
override fun addToUri(builder: Uri.Builder) {
state.forEach {
if(it is UriFilter) it.addToUri(builder)
}
}
}
override val name = if(exh)
"ExHentai"
else

View File

@ -111,7 +111,7 @@ class EHentaiMetadata(override val id: Long,
override fun fetchMangaDetails(manga: SManga) = Observable.fromCallable {
//Hack to convert the gallery into an online gallery when favoriting it or reading it
metadataHelper.fetchMetadata(manga.url, exh)?.copyTo(manga)
metadataHelper.fetchEhMetadata(manga.url, exh)?.copyTo(manga)
manga
}!!

View File

@ -0,0 +1,282 @@
package eu.kanade.tachiyomi.source.online.all
import android.net.Uri
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.ChapterRecognition
import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.MetadataHelper
import exh.metadata.copyTo
import exh.metadata.models.PervEdenGalleryMetadata
import exh.metadata.models.Tag
import exh.util.UriFilter
import exh.util.UriGroup
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
class PervEden(override val id: Long, override val lang: String) : ParsedHttpSource() {
override val supportsLatest = true
override val name = "Perv Eden"
override val baseUrl = "http://www.perveden.com"
val metadataHelper by lazy { MetadataHelper() }
override fun popularMangaSelector() = "#topManga > ul > li"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = "http:" + element.select(".hottestImage > img").attr("data-src")
val titleElement = element.getElementsByClass("hottestInfo").first().child(0)
manga.url = titleElement.attr("href")
manga.title = titleElement.text()
return manga
}
override fun popularMangaNextPageSelector(): String? = null
override fun searchMangaSelector() = "#mangaList > tbody > tr"
override fun searchMangaFromElement(element: Element): SManga {
val manga = SManga.create()
val titleElement = element.child(0).child(0)
manga.url = titleElement.attr("href")
manga.title = titleElement.text().trim()
return manga
}
override fun searchMangaNextPageSelector() = ".next"
override fun popularMangaRequest(page: Int): Request {
val urlLang = if(lang == "en")
"eng"
else "it"
return GET("$baseUrl/$urlLang/")
}
override fun latestUpdatesSelector() = ".newsManga"
override fun latestUpdatesFromElement(element: Element): SManga {
val manga = SManga.create()
val header = element.getElementsByClass("manga_tooltop_header").first()
val titleElement = header.child(0)
manga.url = titleElement.attr("href")
manga.title = titleElement.text().trim()
manga.thumbnail_url = "http:" + titleElement.getElementsByClass("mangaImage").first().attr("tmpsrc")
return manga
}
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select(latestUpdatesSelector()).map { element ->
latestUpdatesFromElement(element)
}
return MangasPage(mangas, mangas.isNotEmpty())
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val uri = Uri.parse("$baseUrl/$lang/$lang-directory/").buildUpon()
uri.appendQueryParameter("page", page.toString())
filters.forEach {
if(it is UriFilter) it.addToUri(uri)
}
return GET(uri.toString())
}
override fun latestUpdatesNextPageSelector(): String? {
throw NotImplementedError("Unused method called!")
}
override fun mangaDetailsParse(document: Document): SManga {
val metadata = PervEdenGalleryMetadata()
with(metadata) {
url = document.location()
lang = this@PervEden.lang
title = document.getElementsByClass("manga-title").first()?.text()
thumbnailUrl = "http:" + document.getElementsByClass("mangaImage2").first()?.child(0)?.attr("src")
val rightBoxElement = document.select(".rightBox:not(.info)").first()
tags.clear()
var inStatus: String? = null
rightBoxElement.childNodes().forEach {
if(it is Element && it.tagName().toLowerCase() == "h4") {
inStatus = it.text().trim()
} else {
when(inStatus) {
"Alternative name(s)" -> {
if(it is TextNode) {
val text = it.text().trim()
if(!text.isBlank())
altTitles.add(text)
}
}
"Artist" -> {
if(it is Element && it.tagName() == "a") {
artist = it.text()
tags.getOrPut("artist", {
ArrayList()
}).add(Tag(it.text().toLowerCase(), false))
}
}
"Genres" -> {
if(it is Element && it.tagName() == "a")
tags.getOrPut("genre", {
ArrayList()
}).add(Tag(it.text().toLowerCase(), false))
}
"Type" -> {
if(it is TextNode) {
val text = it.text().trim()
if(!text.isBlank())
type = text
}
}
"Status" -> {
if(it is TextNode) {
val text = it.text().trim()
if(!text.isBlank())
status = text
}
}
}
}
}
rating = document.getElementById("rating-score")?.attr("value")?.toFloat()
//Save metadata
Timber.d("LNG: " + metadata.lang)
metadataHelper.writeGallery(this, id)
return SManga.create().apply {
copyTo(this)
}
}
}
override fun latestUpdatesRequest(page: Int): Request {
val num = if(lang == "en") "0"
else if(lang == "it") "1"
else throw NotImplementedError("Unimplemented language!")
return GET("$baseUrl/ajax/news/$page/$num/0/")
}
override fun chapterListSelector() = "#leftContent > table > tbody > tr"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
val linkElement = element.getElementsByClass("chapterLink").first()
setUrlWithoutDomain(linkElement.attr("href"))
name = "Chapter " + linkElement.getElementsByTag("b").text()
ChapterRecognition.parseChapterNumber(
this,
SManga.create().apply {
title = ""
})
try {
date_upload = DATE_FORMAT.parse(element.getElementsByClass("chapterDate").first().text().trim()).time
} catch(ignored: Exception) {}
}
override fun pageListParse(document: Document)
= document.getElementById("pageSelect").getElementsByTag("option").map {
Page(it.attr("data-page").toInt() - 1, baseUrl + it.attr("value"))
}
override fun imageUrlParse(document: Document)
= "http:" + document.getElementById("mainImg").attr("src")!!
override fun getFilterList() = FilterList (
AuthorFilter(),
ArtistFilter(),
TypeFilterGroup(),
ReleaseYearGroup(),
StatusFilterGroup()
)
class StatusFilterGroup : UriGroup<StatusFilter>("Status", listOf(
StatusFilter("Ongoing", 1),
StatusFilter("Completed", 2),
StatusFilter("Suspended", 0)
))
class StatusFilter(n: String, val id: Int) : Filter.CheckBox(n, false), UriFilter {
override fun addToUri(builder: Uri.Builder) {
if(state)
builder.appendQueryParameter("status", id.toString())
}
}
//Explicit type arg for listOf() to workaround this: KT-16570
class ReleaseYearGroup : UriGroup<Filter<*>>("Release Year", listOf<Filter<*>>(
ReleaseYearRangeFilter(),
ReleaseYearYearFilter()
))
class ReleaseYearRangeFilter : Filter.Select<String>("Range", arrayOf(
"on",
"after",
"before"
)), UriFilter {
override fun addToUri(builder: Uri.Builder) {
builder.appendQueryParameter("releasedType", state.toString())
}
}
class ReleaseYearYearFilter : Filter.Text("Year"), UriFilter {
override fun addToUri(builder: Uri.Builder) {
builder.appendQueryParameter("released", state)
}
}
class AuthorFilter : Filter.Text("Author"), UriFilter {
override fun addToUri(builder: Uri.Builder) {
builder.appendQueryParameter("author", state)
}
}
class ArtistFilter : Filter.Text("Artist"), UriFilter {
override fun addToUri(builder: Uri.Builder) {
builder.appendQueryParameter("artist", state)
}
}
class TypeFilterGroup : UriGroup<TypeFilter>("Type", listOf(
TypeFilter("Japanese Manga", 0),
TypeFilter("Korean Manhwa", 1),
TypeFilter("Chinese Manhua", 2),
TypeFilter("Comic", 3),
TypeFilter("Doujinshi", 4)
))
class TypeFilter(n: String, val id: Int) : Filter.CheckBox(n, false), UriFilter {
override fun addToUri(builder: Uri.Builder) {
if(state)
builder.appendQueryParameter("type", id.toString())
}
}
companion object {
val DATE_FORMAT = SimpleDateFormat("MMM d, yyyy", Locale.US).apply {
timeZone = TimeZone.getTimeZone("GMT")
}
}
}

View File

@ -101,21 +101,10 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) :
author != null && author!!.toLowerCase().contains(query)
} else {
//Use gallery search engine for EH manga
val source = sourceManager.get(manga.source)
source?.let {
val exh: Boolean
if(source is EHentai)
exh = source.exh
else if(source is EHentaiMetadata)
exh = source.exh
else
return@with false
val metadata = metadataHelper.fetchMetadata(manga.url, exh)
metadata?.let {
searchEngine.matches(metadata, searchEngine.parseQuery(query))
} ?: title.contains(query, ignoreCase = true) //Use regular searching when the metadata is not set up for this gallery
} ?: false
val metadata = metadataHelper.fetchMetadata(manga.url, manga.source)
metadata?.let {
searchEngine.matches(it, searchEngine.parseQuery(query))
} ?: title.contains(query, ignoreCase = true) //Use regular searching when the metadata is not set up for this gallery
}
}

View File

@ -10,8 +10,16 @@ val EXH_SOURCE_ID = LEWD_SOURCE_SERIES + 2
val EH_METADATA_SOURCE_ID = LEWD_SOURCE_SERIES + 3
val EXH_METADATA_SOURCE_ID = LEWD_SOURCE_SERIES + 4
fun isLewdSource(source: Long) = source >= 6900
&& source <= 6999
val PERV_EDEN_EN_SOURCE_ID = LEWD_SOURCE_SERIES + 5
val PERV_EDEN_IT_SOURCE_ID = LEWD_SOURCE_SERIES + 6
fun isLewdSource(source: Long) = source in 6900..6999
fun isEhSource(source: Long) = source == EH_SOURCE_ID
|| source == EH_METADATA_SOURCE_ID
fun isExSource(source: Long) = source == EXH_SOURCE_ID
|| source == EXH_METADATA_SOURCE_ID
fun isPervEdenSource(source: Long) = source == PERV_EDEN_IT_SOURCE_ID
|| source == PERV_EDEN_EN_SOURCE_ID

View File

@ -44,7 +44,7 @@ class GalleryAdder {
manga.copyFrom(sourceObj.fetchMangaDetails(manga).toBlocking().first())
//Apply metadata
metadataHelper.fetchMetadata(url, isExSource(source))?.copyTo(manga)
metadataHelper.fetchEhMetadata(url, isExSource(source))?.copyTo(manga)
if(fav) manga.favorite = true

View File

@ -1,20 +1,46 @@
package exh.metadata
import exh.*
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.PervEdenGalleryMetadata
import exh.metadata.models.SearchableGalleryMetadata
import io.paperdb.Paper
class MetadataHelper {
fun writeGallery(galleryMetadata: ExGalleryMetadata)
= exGalleryBook().write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata)!!
fun writeGallery(galleryMetadata: SearchableGalleryMetadata, source: Long)
= (if(isExSource(source) || isEhSource(source)) exGalleryBook()
else if(isPervEdenSource(source)) pervEdenGalleryBook()
else null)?.write(galleryMetadata.galleryUniqueIdentifier(), galleryMetadata)!!
fun fetchMetadata(url: String, exh: Boolean): ExGalleryMetadata?
fun fetchEhMetadata(url: String, exh: Boolean): ExGalleryMetadata?
= ExGalleryMetadata().let {
it.url = url
it.exh = exh
return exGalleryBook().read<ExGalleryMetadata>(it.galleryUniqueIdentifier())
}
fun fetchPervEdenMetadata(url: String, source: Long): PervEdenGalleryMetadata?
= PervEdenGalleryMetadata().let {
it.url = url
if(source == PERV_EDEN_EN_SOURCE_ID)
it.lang = "en"
else if(source == PERV_EDEN_IT_SOURCE_ID)
it.lang = "it"
else throw IllegalArgumentException("Invalid source id!")
return pervEdenGalleryBook().read<PervEdenGalleryMetadata>(it.galleryUniqueIdentifier())
}
fun fetchMetadata(url: String, source: Long): SearchableGalleryMetadata? {
if(isExSource(source) || isEhSource(source)) {
return fetchEhMetadata(url, isExSource(source))
} else if(isPervEdenSource(source)) {
return fetchPervEdenMetadata(url, source)
} else {
return null
}
}
fun getAllGalleries() = exGalleryBook().allKeys.map {
exGalleryBook().read<ExGalleryMetadata>(it)
}
@ -26,5 +52,9 @@ class MetadataHelper {
return exGalleryBook().exist(it.galleryUniqueIdentifier())
}
//TODO Problem, our new metadata structures are incompatible.
//TODO We will probably just delete the old metadata structures
fun exGalleryBook() = Paper.book("gallery-ex")!!
fun pervEdenGalleryBook() = Paper.book("gallery-perveden")!!
}

View File

@ -3,7 +3,10 @@ package exh.metadata
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.PervEden
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.PervEdenGalleryMetadata
import exh.metadata.models.SearchableGalleryMetadata
import exh.metadata.models.Tag
import exh.plusAssign
import uy.kohesive.injekt.injectLazy
@ -40,7 +43,7 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
//No title bug?
val titleObj = if(prefs.useJapaneseTitle().getOrDefault())
altTitle ?: title
altTitles.firstOrNull() ?: title
else
title
titleObj?.let { manga.title = it }
@ -70,7 +73,7 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
//Build a nice looking description out of what we know
val titleDesc = StringBuilder()
title?.let { titleDesc += "Title: $it\n" }
altTitle?.let { titleDesc += "Japanese Title: $it\n" }
altTitles.firstOrNull()?.let { titleDesc += "Alternate Title: $it\n" }
val detailsDesc = StringBuilder()
uploader?.let { detailsDesc += "Uploader: $it\n" }
@ -90,16 +93,66 @@ fun ExGalleryMetadata.copyTo(manga: SManga) {
detailsDesc += "\n"
}
val tagsDesc = StringBuilder("Tags:\n")
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
tags.entries.forEach { namespace, tags ->
if(tags.isNotEmpty()) {
val joinedTags = tags.joinToString(separator = " ", transform = { "<${it.name}>" })
tagsDesc += "$namespace: $joinedTags\n"
}
}
manga.description = listOf(titleDesc, detailsDesc, tagsDesc)
.filter { it.isNotBlank() }
val tagsDesc = buildTagsDescription(this)
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank)
.joinToString(separator = "\n")
}
fun PervEdenGalleryMetadata.copyTo(manga: SManga) {
url?.let { manga.url = it }
thumbnailUrl?.let { manga.thumbnail_url = it }
val titleDesc = StringBuilder()
title?.let {
manga.title = it
titleDesc += "Title: $it\n"
}
if(altTitles.isNotEmpty())
titleDesc += "Alternate Titles: \n" + altTitles.map {
"$it"
}.joinToString(separator = "\n", postfix = "\n")
val detailsDesc = StringBuilder()
artist?.let {
manga.artist = it
detailsDesc += "Artist: $it\n"
}
type?.let {
manga.genre = it
detailsDesc += "Type: $it\n"
}
status?.let {
manga.status = when(it) {
"Ongoing" -> SManga.ONGOING
"Completed", "Suspended" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
detailsDesc += "Status: $it\n"
}
rating?.let {
detailsDesc += "Rating: %.2\n".format(it)
}
val tagsDesc = buildTagsDescription(this)
manga.description = listOf(titleDesc.toString(), detailsDesc.toString(), tagsDesc.toString())
.filter(String::isNotBlank)
.joinToString(separator = "\n")
}
private fun buildTagsDescription(metadata: SearchableGalleryMetadata)
= StringBuilder("Tags:\n").apply {
//BiConsumer only available in Java 8, don't bother calling forEach directly on 'tags'
metadata.tags.entries.forEach { namespace, tags ->
if (tags.isNotEmpty()) {
val joinedTags = tags.joinToString(separator = " ", transform = { "<${it.name}>" })
this += "$namespace: $joinedTags\n"
}
}
}

View File

@ -7,19 +7,15 @@ import java.util.*
* Gallery metadata storage model
*/
class ExGalleryMetadata {
class ExGalleryMetadata : SearchableGalleryMetadata() {
var url: String? = null
var exh: Boolean? = null
var title: String? = null
var altTitle: String? = null
var thumbnailUrl: String? = null
var genre: String? = null
var uploader: String? = null
var datePosted: Long? = null
var parent: String? = null
var visible: String? = null //Not a boolean
@ -31,8 +27,6 @@ class ExGalleryMetadata {
var ratingCount: Int? = null
var averageRating: Double? = null
//Being specific about which classes are used in generics to make deserialization easier
var tags: HashMap<String, ArrayList<Tag>> = HashMap()
private fun splitGalleryUrl()
= url?.let {
@ -44,8 +38,10 @@ class ExGalleryMetadata {
fun galleryToken() =
splitGalleryUrl()?.last()
fun galleryUniqueIdentifier() = exh?.let { exh ->
override fun galleryUniqueIdentifier() = exh?.let { exh ->
url?.let {
//Fuck, this should be EXH and EH but it's too late to change it now...
//TODO Change this during migration
"${if(exh) "EXH" else "EX"}-${galleryId()}-${galleryToken()}"
}
}

View File

@ -0,0 +1,32 @@
package exh.metadata.models
import android.net.Uri
import timber.log.Timber
//TODO Add artificial artist tag
class PervEdenGalleryMetadata : SearchableGalleryMetadata() {
var url: String? = null
var thumbnailUrl: String? = null
var artist: String? = null
var type: String? = null
var rating: Float? = null
var status: String? = null
var lang: String? = null
private fun splitGalleryUrl()
= url?.let {
Uri.parse(it).pathSegments.filterNot(String::isNullOrBlank)
}
override fun galleryUniqueIdentifier() = splitGalleryUrl()?.let {
Timber.d(
"PERVEDEN-${lang?.toUpperCase()}-${it.last()}"
)
"PERVEDEN-${lang?.toUpperCase()}-${it.last()}"
}
}

View File

@ -0,0 +1,19 @@
package exh.metadata.models
import java.util.ArrayList
import java.util.HashMap
/**
* A gallery that can be searched using the EH search engine
*/
abstract class SearchableGalleryMetadata {
var uploader: String? = null
var title: String? = null
val altTitles: MutableList<String> = mutableListOf()
//Being specific about which classes are used in generics to make deserialization easier
val tags: HashMap<String, ArrayList<Tag>> = HashMap()
abstract fun galleryUniqueIdentifier(): String?
}

View File

@ -1,13 +1,13 @@
package exh.search
import exh.metadata.models.ExGalleryMetadata
import exh.metadata.models.SearchableGalleryMetadata
import exh.metadata.models.Tag
class SearchEngine {
private val queryCache = mutableMapOf<String, List<QueryComponent>>()
fun matches(metadata: ExGalleryMetadata, query: List<QueryComponent>): Boolean {
fun matches(metadata: SearchableGalleryMetadata, query: List<QueryComponent>): Boolean {
fun matchTagList(tags: Sequence<Tag>,
component: Text): Boolean {
@ -29,13 +29,13 @@ class SearchEngine {
}
val cachedLowercaseTitle = metadata.title?.toLowerCase()
val cachedLowercaseAltTitle = metadata.altTitle?.toLowerCase()
val cachedLowercaseAltTitles = metadata.altTitles.map(String::toLowerCase)
for(component in query) {
if(component is Text) {
//Match title
if (component.asRegex().test(cachedLowercaseTitle)
|| component.asRegex().test(cachedLowercaseAltTitle)) {
|| cachedLowercaseAltTitles.find { component.asRegex().test(it) } != null) {
continue
}
//Match tags

View File

@ -63,7 +63,7 @@ class MetadataFetchDialog {
source?.let {
it as EHentai
manga.copyFrom(it.fetchMangaDetails(manga).toBlocking().first())
metadataHelper.fetchMetadata(manga.url, it.exh)?.copyTo(manga)
metadataHelper.fetchEhMetadata(manga.url, it.exh)?.copyTo(manga)
}
} catch(t: Throwable) {
Timber.e(t, "Could not migrate manga!")

View File

@ -39,33 +39,33 @@ class UrlMigrator {
//Sort possible dups so we can use binary search on it
possibleDups.sortBy { it.url }
badMangas.forEach {
badMangas.forEach { manga ->
//Build fixed URL
val urlWithSlash = "/" + it.url
val urlWithSlash = "/" + manga.url
//Fix metadata if required
val metadata = metadataHelper.fetchMetadata(it.url, isExSource(it.source))
val metadata = metadataHelper.fetchEhMetadata(manga.url, isExSource(manga.source))
metadata?.url?.let {
if(it.startsWith("g/")) { //Check if metadata URL has no slash
metadata.url = urlWithSlash //Fix it
metadataHelper.writeGallery(metadata) //Write new metadata to disk
metadataHelper.writeGallery(metadata, manga.source) //Write new metadata to disk
}
}
//If we have a dup (with the fixed url), use the dup instead
val possibleDup = possibleDups.binarySearchBy(urlWithSlash, selector = { it.url })
if(possibleDup >= 0) {
//Make sure it is favorited if we are
if(it.favorite) {
if(manga.favorite) {
val dup = possibleDups[possibleDup]
dup.favorite = true
db.insertManga(dup).executeAsBlocking() //Update DB with changes
}
//Delete ourself (but the dup is still there)
db.deleteManga(it).executeAsBlocking()
db.deleteManga(manga).executeAsBlocking()
return@forEach
}
//No dup, correct URL and reinsert ourselves
it.url = urlWithSlash
db.insertManga(it).executeAsBlocking()
manga.url = urlWithSlash
db.insertManga(manga).executeAsBlocking()
}
}
}

View File

@ -0,0 +1,10 @@
package exh.util
import android.net.Uri
/**
* Uri filter
*/
interface UriFilter {
fun addToUri(builder: Uri.Builder)
}

View File

@ -0,0 +1,15 @@
package exh.util
import android.net.Uri
import eu.kanade.tachiyomi.source.model.Filter
/**
* UriGroup
*/
open class UriGroup<V>(name: String, state: List<V>) : Filter.Group<V>(name, state), UriFilter {
override fun addToUri(builder: Uri.Builder) {
state.forEach {
if(it is UriFilter) it.addToUri(builder)
}
}
}