mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	See CHANGELOG.md for this commit
This commit is contained in:
		| @@ -9,9 +9,9 @@ import dalvik.system.PathClassLoader | ||||
| import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.data.preference.getOrDefault | ||||
| import eu.kanade.tachiyomi.source.online.all.EHentai | ||||
| import eu.kanade.tachiyomi.source.online.HttpSource | ||||
| import eu.kanade.tachiyomi.source.online.YamlHttpSource | ||||
| import eu.kanade.tachiyomi.source.online.all.EHentai | ||||
| import eu.kanade.tachiyomi.source.online.all.NHentai | ||||
| import eu.kanade.tachiyomi.source.online.all.PervEden | ||||
| import eu.kanade.tachiyomi.source.online.english.* | ||||
| @@ -20,7 +20,10 @@ 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.* | ||||
| import exh.EH_SOURCE_ID | ||||
| import exh.EXH_SOURCE_ID | ||||
| import exh.PERV_EDEN_EN_SOURCE_ID | ||||
| import exh.PERV_EDEN_IT_SOURCE_ID | ||||
| import exh.metadata.models.PervEdenLang | ||||
| import org.yaml.snakeyaml.Yaml | ||||
| import rx.Observable | ||||
| @@ -98,6 +101,7 @@ open class SourceManager(private val context: Context) { | ||||
|         exSrcs += PervEden(PERV_EDEN_IT_SOURCE_ID, PervEdenLang.it) | ||||
|         exSrcs += NHentai(context) | ||||
|         exSrcs += HentaiCafe() | ||||
|         exSrcs += Tsumino() | ||||
|         return exSrcs | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import exh.HENTAI_CAFE_SOURCE_ID | ||||
| import exh.metadata.models.HentaiCafeMetadata | ||||
| import exh.metadata.models.HentaiCafeMetadata.Companion.BASE_URL | ||||
| import exh.metadata.models.Tag | ||||
| import exh.util.urlImportFetchSearchManga | ||||
| import okhttp3.Request | ||||
| import org.jsoup.nodes.Document | ||||
| import org.jsoup.nodes.Element | ||||
| @@ -34,8 +35,13 @@ class HentaiCafe : ParsedHttpSource(), LewdSource<HentaiCafeMetadata, Document> | ||||
|     override fun popularMangaNextPageSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun fetchPopularManga(page: Int) = fetchLatestUpdates(page) | ||||
|  | ||||
|     override fun searchMangaSelector() = "article.post" | ||||
|      | ||||
|     //Support direct URL importing | ||||
|     override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = | ||||
|             urlImportFetchSearchManga(query, { | ||||
|                 super.fetchSearchManga(page, query, filters) | ||||
|             }) | ||||
|     override fun searchMangaSelector() = "article.post:not(#post-0)" | ||||
|     override fun searchMangaFromElement(element: Element): SManga { | ||||
|         val thumb = element.select(".entry-thumb > img") | ||||
|         val title = element.select(".entry-title > a") | ||||
| @@ -111,6 +117,8 @@ class HentaiCafe : ParsedHttpSource(), LewdSource<HentaiCafeMetadata, Document> | ||||
|  | ||||
|         url = Uri.decode(it.location()) | ||||
|         title = eTitle.text() | ||||
|          | ||||
|         thumbnailUrl = content.select("img").attr("src") | ||||
|  | ||||
|         tags.clear() | ||||
|         val eDetails = content.select("p > a[rel=tag]") | ||||
|   | ||||
| @@ -0,0 +1,304 @@ | ||||
| package eu.kanade.tachiyomi.source.online.english | ||||
|  | ||||
| import android.net.Uri | ||||
| import com.github.salomonbrys.kotson.* | ||||
| import com.google.gson.JsonParser | ||||
| import eu.kanade.tachiyomi.network.POST | ||||
| import eu.kanade.tachiyomi.network.asObservableSuccess | ||||
| import eu.kanade.tachiyomi.source.model.* | ||||
| import eu.kanade.tachiyomi.source.online.LewdSource | ||||
| import eu.kanade.tachiyomi.source.online.ParsedHttpSource | ||||
| import eu.kanade.tachiyomi.util.asJsoup | ||||
| import exh.TSUMINO_SOURCE_ID | ||||
| import exh.metadata.models.Tag | ||||
| import exh.metadata.models.TsuminoMetadata | ||||
| import exh.metadata.models.TsuminoMetadata.Companion.BASE_URL | ||||
| import exh.util.urlImportFetchSearchManga | ||||
| import okhttp3.FormBody | ||||
| import okhttp3.Request | ||||
| import okhttp3.Response | ||||
| import org.jsoup.nodes.Document | ||||
| import org.jsoup.nodes.Element | ||||
| import rx.Observable | ||||
| import java.text.SimpleDateFormat | ||||
| import java.util.* | ||||
|  | ||||
| class Tsumino: ParsedHttpSource(), LewdSource<TsuminoMetadata, Document> { | ||||
|     override val id = TSUMINO_SOURCE_ID | ||||
|      | ||||
|     override val lang = "en" | ||||
|     override val supportsLatest = true | ||||
|     override val name = "Tsumino" | ||||
|      | ||||
|     override fun queryAll() = TsuminoMetadata.EmptyQuery() | ||||
|      | ||||
|     override fun queryFromUrl(url: String) = TsuminoMetadata.UrlQuery(url) | ||||
|      | ||||
|     override val baseUrl = BASE_URL | ||||
|      | ||||
|     override val metaParser: TsuminoMetadata.(Document) -> Unit = { | ||||
|         url = it.location() | ||||
|         tags.clear() | ||||
|          | ||||
|         it.getElementById("Title")?.text()?.let { | ||||
|             title = it.trim() | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Artist")?.children()?.first()?.text()?.trim()?.let { | ||||
|             tags.add(Tag("artist", it, false)) | ||||
|             artist = it | ||||
|         } | ||||
|      | ||||
|         it.getElementById("Uploader")?.children()?.first()?.text()?.trim()?.let { | ||||
|             tags.add(Tag("uploader", it, false)) | ||||
|             uploader = it | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Uploaded")?.text()?.let { | ||||
|             uploadDate = TM_DATE_FORMAT.parse(it.trim()).time | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Pages")?.text()?.let { | ||||
|             length = it.trim().toIntOrNull() | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Rating")?.text()?.let { | ||||
|             ratingString = it.trim() | ||||
|         } | ||||
|      | ||||
|         it.getElementById("Category")?.children()?.first()?.text()?.let { | ||||
|             category = it.trim() | ||||
|             tags.add(Tag("genre", it, false)) | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Collection")?.children()?.first()?.text()?.let { | ||||
|             collection = it.trim() | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Group")?.children()?.first()?.text()?.let { | ||||
|             group = it.trim() | ||||
|             tags.add(Tag("group", it, false)) | ||||
|         } | ||||
|          | ||||
|         parody.clear() | ||||
|         it.getElementById("Parody")?.children()?.forEach { | ||||
|             val entry = it.text().trim() | ||||
|             parody.add(entry) | ||||
|             tags.add(Tag("parody", entry, false)) | ||||
|         } | ||||
|          | ||||
|         character.clear() | ||||
|         it.getElementById("Character")?.children()?.forEach { | ||||
|             val entry = it.text().trim() | ||||
|             character.add(entry) | ||||
|             tags.add(Tag("character", entry, false)) | ||||
|         } | ||||
|          | ||||
|         it.getElementById("Tag")?.children()?.let { | ||||
|             tags.addAll(it.map { | ||||
|                 Tag("tag", it.text().trim(), false) | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     fun genericMangaParse(response: Response): MangasPage { | ||||
|         val json = jsonParser.parse(response.body()!!.string()!!).asJsonObject | ||||
|         val hasNextPage = json["PageNumber"].int < json["PageCount"].int | ||||
|          | ||||
|         val manga = json["Data"].array.map { | ||||
|             val obj = it.obj["Entry"].obj | ||||
|              | ||||
|             SManga.create().apply { | ||||
|                 val id = obj["Id"].long | ||||
|                 setUrlWithoutDomain(TsuminoMetadata.mangaUrlFromId(id.toString())) | ||||
|                 thumbnail_url = TsuminoMetadata.thumbUrlFromId(id.toString()) | ||||
|                  | ||||
|                 title = obj["Title"].string | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return MangasPage(manga, hasNextPage) | ||||
|     } | ||||
|      | ||||
|     fun genericMangaRequest(page: Int, | ||||
|                             query: String, | ||||
|                             sort: SortType, | ||||
|                             length: LengthType, | ||||
|                             minRating: Int, | ||||
|                             excludeParodies: Boolean = false, | ||||
|                             advSearch: List<AdvSearchEntry> = emptyList()) | ||||
|         = POST("$BASE_URL/Books/Operate", body = FormBody.Builder() | ||||
|             .add("PageNumber", (page + 1).toString()) | ||||
|             .add("Text", query) | ||||
|             .add("Sort", sort.name) | ||||
|             .add("List", "0") | ||||
|             .add("Length", length.id.toString()) | ||||
|             .add("MinimumRating", minRating.toString()) | ||||
|             .apply { | ||||
|                 advSearch.forEachIndexed { index, entry -> | ||||
|                     add("Tags[$index][Type]", entry.type.toString()) | ||||
|                     add("Tags[$index][Text]", entry.text) | ||||
|                     add("Tags[$index][Exclude]", entry.exclude.toString()) | ||||
|                 } | ||||
|                  | ||||
|                 if(excludeParodies) | ||||
|                     add("Exclude[]", "6") | ||||
|             } | ||||
|             .build()) | ||||
|      | ||||
|     enum class SortType { | ||||
|         Newest, | ||||
|         Oldest, | ||||
|         Alphabetical, | ||||
|         Rating, | ||||
|         Pages, | ||||
|         Views, | ||||
|         Random, | ||||
|         Comments, | ||||
|         Popularity | ||||
|     } | ||||
|      | ||||
|     enum class LengthType(val id: Int) { | ||||
|         Any(0), | ||||
|         Short(1), | ||||
|         Medium(2), | ||||
|         Long(3) | ||||
|     } | ||||
|      | ||||
|     override fun popularMangaSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun popularMangaNextPageSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun popularMangaRequest(page: Int) = genericMangaRequest(page, | ||||
|             "", | ||||
|             SortType.Random, | ||||
|             LengthType.Any, | ||||
|             0) | ||||
|      | ||||
|     override fun popularMangaParse(response: Response) = genericMangaParse(response) | ||||
|      | ||||
|     override fun latestUpdatesSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun latestUpdatesRequest(page: Int) = genericMangaRequest(page, | ||||
|             "", | ||||
|             SortType.Newest, | ||||
|             LengthType.Any, | ||||
|             0) | ||||
|     override fun latestUpdatesParse(response: Response) = genericMangaParse(response) | ||||
|      | ||||
|     //Support direct URL importing | ||||
|     override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = | ||||
|             urlImportFetchSearchManga(query, { | ||||
|                 super.fetchSearchManga(page, query, filters) | ||||
|             }) | ||||
|     override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { | ||||
|         // Append filters again, to provide fallback in case a filter is not provided | ||||
|         // Since we only work with the first filter when building the result, if the filter is provided, | ||||
|         // the original filter is ignored | ||||
|         val f = filters + getFilterList() | ||||
|          | ||||
|         return genericMangaRequest( | ||||
|                 page, | ||||
|                 query, | ||||
|                 SortType.values()[f.filterIsInstance<SortFilter>().first().state], | ||||
|                 LengthType.values()[f.filterIsInstance<LengthFilter>().first().state], | ||||
|                 f.filterIsInstance<MinimumRatingFilter>().first().state, | ||||
|                 f.filterIsInstance<ExcludeParodiesFilter>().first().state, | ||||
|                 f.filterIsInstance<AdvSearchEntryFilter>().flatMap { filter -> | ||||
|                     val splitState = filter.state.split(",").map(String::trim).filterNot(String::isBlank) | ||||
|                      | ||||
|                     splitState.map { | ||||
|                         AdvSearchEntry(filter.type, it.removePrefix("-"), it.startsWith("-")) | ||||
|                     } | ||||
|                 } | ||||
|         ) | ||||
|     } | ||||
|      | ||||
|     override fun searchMangaSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun searchMangaParse(response: Response) = genericMangaParse(response) | ||||
|      | ||||
|     override fun mangaDetailsParse(document: Document) | ||||
|             = parseToManga(queryFromUrl(document.location()), document) | ||||
|      | ||||
|     override fun chapterListSelector() = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun chapterFromElement(element: Element) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun fetchChapterList(manga: SManga) = lazyLoadMeta(queryFromUrl(manga.url), | ||||
|             client.newCall(mangaDetailsRequest(manga)).asObservableSuccess().map { it.asJsoup() } | ||||
|     ).map { | ||||
|         listOf( | ||||
|                 SChapter.create().apply { | ||||
|                     url = "/Read/View/${it.tmId}" | ||||
|                     name = "Chapter" | ||||
|                      | ||||
|                     it.uploadDate?.let { date_upload = it } | ||||
|                      | ||||
|                     chapter_number = 1f | ||||
|                 } | ||||
|         ) | ||||
|     } | ||||
|      | ||||
|     override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { | ||||
|         val id = chapter.url.substringAfterLast('/') | ||||
|         val call = POST("$BASE_URL/Read/Load", body = FormBody.Builder().add("q", id).build()) | ||||
|         return client.newCall(call).asObservableSuccess().map { | ||||
|             val parsed = jsonParser.parse(it.body()!!.string()).obj | ||||
|             val pageUrls = parsed["reader_page_urls"].array | ||||
|              | ||||
|             val imageUrl = Uri.parse("$BASE_URL/Image/Object") | ||||
|             pageUrls.mapIndexed { index, obj -> | ||||
|                 val newImageUrl = imageUrl.buildUpon().appendQueryParameter("name", obj.string) | ||||
|                 Page(index, chapter.url + "#${index + 1}", newImageUrl.toString()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     override fun pageListParse(document: Document) = throw UnsupportedOperationException("Unused method called!") | ||||
|     override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Unused method called!") | ||||
|      | ||||
|     data class AdvSearchEntry(val type: Int, val text: String, val exclude: Boolean) | ||||
|      | ||||
|     override fun getFilterList() = FilterList( | ||||
|             Filter.Header("Separate tags with commas"), | ||||
|             Filter.Header("Prepend with dash to exclude"), | ||||
|             TagFilter(), | ||||
|             CategoryFilter(), | ||||
|             CollectionFilter(), | ||||
|             GroupFilter(), | ||||
|             ArtistFilter(), | ||||
|             ParodyFilter(), | ||||
|             CharactersFilter(), | ||||
|             UploaderFilter(), | ||||
|              | ||||
|             Filter.Separator(), | ||||
|              | ||||
|             SortFilter(), | ||||
|             LengthFilter(), | ||||
|             MinimumRatingFilter(), | ||||
|             ExcludeParodiesFilter() | ||||
|     ) | ||||
|      | ||||
|     class TagFilter : AdvSearchEntryFilter("Tags", 1) | ||||
|     class CategoryFilter : AdvSearchEntryFilter("Categories", 2) | ||||
|     class CollectionFilter : AdvSearchEntryFilter("Collections", 3) | ||||
|     class GroupFilter : AdvSearchEntryFilter("Groups", 4) | ||||
|     class ArtistFilter : AdvSearchEntryFilter("Artists", 5) | ||||
|     class ParodyFilter : AdvSearchEntryFilter("Parodies", 6) | ||||
|     class CharactersFilter : AdvSearchEntryFilter("Characters", 7) | ||||
|     class UploaderFilter : AdvSearchEntryFilter("Uploaders", 8) | ||||
|     open class AdvSearchEntryFilter(name: String, val type: Int) : Filter.Text(name) | ||||
|      | ||||
|     class SortFilter : Filter.Select<SortType>("Sort by", SortType.values()) | ||||
|     class LengthFilter : Filter.Select<LengthType>("Length", LengthType.values()) | ||||
|     class MinimumRatingFilter : Filter.Select<String>("Minimum rating", (0 .. 5).map { "$it stars" }.toTypedArray()) | ||||
|     class ExcludeParodiesFilter : Filter.CheckBox("Exclude parodies") | ||||
|      | ||||
|     companion object { | ||||
|         val jsonParser by lazy { | ||||
|             JsonParser() | ||||
|         } | ||||
|          | ||||
|         val TM_DATE_FORMAT = SimpleDateFormat("yyyy MMM dd", Locale.US) | ||||
|     } | ||||
| } | ||||
| @@ -17,6 +17,8 @@ val NHENTAI_SOURCE_ID = LEWD_SOURCE_SERIES + 7 | ||||
|  | ||||
| val HENTAI_CAFE_SOURCE_ID = LEWD_SOURCE_SERIES + 8 | ||||
|  | ||||
| val TSUMINO_SOURCE_ID = LEWD_SOURCE_SERIES + 9 | ||||
|  | ||||
| fun isLewdSource(source: Long) = source in 6900..6999 | ||||
|  | ||||
| fun isEhSource(source: Long) = source == EH_SOURCE_ID | ||||
|   | ||||
| @@ -10,10 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.network.NetworkHelper | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.util.syncChaptersWithSource | ||||
| import exh.metadata.models.ExGalleryMetadata | ||||
| import exh.metadata.models.NHentaiMetadata | ||||
| import exh.metadata.models.PervEdenGalleryMetadata | ||||
| import exh.metadata.models.PervEdenLang | ||||
| import exh.metadata.models.* | ||||
| import exh.util.defRealm | ||||
| import okhttp3.MediaType | ||||
| import okhttp3.Request | ||||
| @@ -68,7 +65,7 @@ class GalleryAdder { | ||||
|         try { | ||||
|             val urlObj = Uri.parse(url) | ||||
|             val lowercasePs = urlObj.pathSegments.map(String::toLowerCase) | ||||
|             val firstPathSegment = lowercasePs[0] | ||||
|             val lcFirstPathSegment = lowercasePs[0] | ||||
|             val source = when (urlObj.host.toLowerCase()) { | ||||
|                 "g.e-hentai.org", "e-hentai.org" -> EH_SOURCE_ID | ||||
|                 "exhentai.org" -> EXH_SOURCE_ID | ||||
| @@ -80,6 +77,8 @@ class GalleryAdder { | ||||
|                         else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|                     } | ||||
|                 } | ||||
|                 "hentai.cafe" -> HENTAI_CAFE_SOURCE_ID | ||||
|                 "www.tsumino.com" -> TSUMINO_SOURCE_ID | ||||
|                 else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|             } | ||||
|  | ||||
| @@ -88,7 +87,7 @@ class GalleryAdder { | ||||
|             } | ||||
|  | ||||
|             val realUrl = when(source) { | ||||
|                 EH_SOURCE_ID, EXH_SOURCE_ID -> when (firstPathSegment) { | ||||
|                 EH_SOURCE_ID, EXH_SOURCE_ID -> when (lcFirstPathSegment) { | ||||
|                     "g" -> { | ||||
|                         //Is already gallery page, do nothing | ||||
|                         url | ||||
| @@ -100,7 +99,7 @@ class GalleryAdder { | ||||
|                     else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|                 } | ||||
|                 NHENTAI_SOURCE_ID -> { | ||||
|                     if(firstPathSegment != "g") | ||||
|                     if(lcFirstPathSegment != "g") | ||||
|                         return GalleryAddEvent.Fail.UnknownType(url) | ||||
|  | ||||
|                     "https://nhentai.net/g/${urlObj.pathSegments[1]}/" | ||||
| @@ -113,6 +112,18 @@ class GalleryAdder { | ||||
|                     } | ||||
|                     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]}" | ||||
|                 } | ||||
|                 else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|             } | ||||
|  | ||||
| @@ -124,6 +135,8 @@ class GalleryAdder { | ||||
|                 NHENTAI_SOURCE_ID -> realUrl //nhentai uses URLs directly (oops, my bad when implementing this source) | ||||
|                 PERV_EDEN_EN_SOURCE_ID, | ||||
|                 PERV_EDEN_IT_SOURCE_ID -> getUrlWithoutDomain(realUrl) | ||||
|                 HENTAI_CAFE_SOURCE_ID -> getUrlWithoutDomain(realUrl) | ||||
|                 TSUMINO_SOURCE_ID -> getUrlWithoutDomain(realUrl) | ||||
|                 else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|             } | ||||
|  | ||||
| @@ -142,23 +155,14 @@ class GalleryAdder { | ||||
|             //Apply metadata | ||||
|             defRealm { realm -> | ||||
|                 when (source) { | ||||
|                     EH_SOURCE_ID, EXH_SOURCE_ID -> | ||||
|                         ExGalleryMetadata.UrlQuery(realUrl, isExSource(source)) | ||||
|                                 .query(realm) | ||||
|                                 .findFirst()?.copyTo(manga) | ||||
|                     NHENTAI_SOURCE_ID -> | ||||
|                         NHentaiMetadata.UrlQuery(realUrl) | ||||
|                                 .query(realm) | ||||
|                                 .findFirst() | ||||
|                                 ?.copyTo(manga) | ||||
|                     EH_SOURCE_ID, EXH_SOURCE_ID -> ExGalleryMetadata.UrlQuery(realUrl, isExSource(source)) | ||||
|                     NHENTAI_SOURCE_ID -> NHentaiMetadata.UrlQuery(realUrl) | ||||
|                     PERV_EDEN_EN_SOURCE_ID, | ||||
|                     PERV_EDEN_IT_SOURCE_ID -> | ||||
|                         PervEdenGalleryMetadata.UrlQuery(realUrl, PervEdenLang.source(source)) | ||||
|                                 .query(realm) | ||||
|                                 .findFirst() | ||||
|                                 ?.copyTo(manga) | ||||
|                     PERV_EDEN_IT_SOURCE_ID -> PervEdenGalleryMetadata.UrlQuery(realUrl, PervEdenLang.source(source)) | ||||
|                     HENTAI_CAFE_SOURCE_ID -> HentaiCafeMetadata.UrlQuery(realUrl) | ||||
|                     TSUMINO_SOURCE_ID -> TsuminoMetadata.UrlQuery(realUrl) | ||||
|                     else -> return GalleryAddEvent.Fail.UnknownType(url) | ||||
|                 } | ||||
|                 }.query(realm).findFirst() | ||||
|             } | ||||
|  | ||||
|             if (fav) manga.favorite = true | ||||
|   | ||||
| @@ -25,16 +25,18 @@ open class HentaiCafeMetadata : RealmObject(), SearchableGalleryMetadata { | ||||
|                 hcId = hcIdFromUrl(a) | ||||
|             } | ||||
|         } | ||||
|      | ||||
|     var thumbnailUrl: String? = null | ||||
|  | ||||
|     var title: String? = null | ||||
|  | ||||
|     var artist: String? = null | ||||
|  | ||||
|     override var uploader: String? = null | ||||
|     override var uploader: String? = null //Always will be null as this is unknown | ||||
|  | ||||
|     override var tags: RealmList<Tag> = RealmList() | ||||
|  | ||||
|     override fun getTitles() = listOf(title).filterNotNull() | ||||
|     override fun getTitles() = listOfNotNull(title) | ||||
|  | ||||
|     @Ignore | ||||
|     override val titleFields = listOf( | ||||
| @@ -45,6 +47,8 @@ open class HentaiCafeMetadata : RealmObject(), SearchableGalleryMetadata { | ||||
|     override var mangaId: Long? = null | ||||
|  | ||||
|     override fun copyTo(manga: SManga) { | ||||
|         thumbnailUrl?.let { manga.thumbnail_url = it } | ||||
|          | ||||
|         manga.title = title!! | ||||
|         manga.artist = artist | ||||
|         manga.author = artist | ||||
|   | ||||
							
								
								
									
										130
									
								
								app/src/main/java/exh/metadata/models/TsuminoMetadata.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								app/src/main/java/exh/metadata/models/TsuminoMetadata.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| package exh.metadata.models | ||||
|  | ||||
| import android.net.Uri | ||||
| import eu.kanade.tachiyomi.source.model.SManga | ||||
| import exh.metadata.EX_DATE_FORMAT | ||||
| import exh.metadata.buildTagsDescription | ||||
| import exh.plusAssign | ||||
| import io.realm.RealmList | ||||
| import io.realm.RealmObject | ||||
| import io.realm.annotations.Ignore | ||||
| import io.realm.annotations.Index | ||||
| import io.realm.annotations.PrimaryKey | ||||
| import io.realm.annotations.RealmClass | ||||
| import java.util.* | ||||
|  | ||||
| @RealmClass | ||||
| open class TsuminoMetadata : RealmObject(), SearchableGalleryMetadata { | ||||
|     @PrimaryKey | ||||
|     override var uuid: String = UUID.randomUUID().toString() | ||||
|      | ||||
|     @Index | ||||
|     var tmId: String? = null | ||||
|      | ||||
|     var url get() = tmId?.let { mangaUrlFromId(it) } | ||||
|         set(a) { | ||||
|             a?.let { | ||||
|                 tmId = tmIdFromUrl(a) | ||||
|             } | ||||
|         } | ||||
|      | ||||
|     var title: String? = null | ||||
|      | ||||
|     var artist: String? = null | ||||
|      | ||||
|     override var uploader: String? = null | ||||
|      | ||||
|     var uploadDate: Long? = null | ||||
|      | ||||
|     var length: Int? = null | ||||
|      | ||||
|     var ratingString: String? = null | ||||
|      | ||||
|     var category: String? = null | ||||
|      | ||||
|     var collection: String? = null | ||||
|      | ||||
|     var group: String? = null | ||||
|      | ||||
|     var parody: RealmList<String> = RealmList() | ||||
|      | ||||
|     var character: RealmList<String> = RealmList() | ||||
|      | ||||
|     override var tags: RealmList<Tag> = RealmList() | ||||
|      | ||||
|     override fun getTitles() = listOfNotNull(title) | ||||
|      | ||||
|     @Ignore | ||||
|     override val titleFields = listOf( | ||||
|             TsuminoMetadata::title.name | ||||
|     ) | ||||
|      | ||||
|     @Index | ||||
|     override var mangaId: Long? = null | ||||
|      | ||||
|     class EmptyQuery : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class) | ||||
|      | ||||
|     class UrlQuery( | ||||
|             val url: String | ||||
|     ) : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class) { | ||||
|         override fun transform() = Query( | ||||
|                 tmIdFromUrl(url) | ||||
|         ) | ||||
|     } | ||||
|      | ||||
|     class Query( | ||||
|             val tmId: String | ||||
|     ) : GalleryQuery<TsuminoMetadata>(TsuminoMetadata::class) { | ||||
|         override fun map() = mapOf( | ||||
|                 TsuminoMetadata::tmId to Query::tmId | ||||
|         ) | ||||
|     } | ||||
|      | ||||
|     override fun copyTo(manga: SManga) { | ||||
|         title?.let { manga.title = it } | ||||
|         manga.thumbnail_url = thumbUrlFromId(tmId.toString()) | ||||
|          | ||||
|         artist?.let { manga.artist = it } | ||||
|          | ||||
|         manga.status = SManga.UNKNOWN | ||||
|          | ||||
|         val titleDesc = "Title: $title\n" | ||||
|      | ||||
|         val detailsDesc = StringBuilder() | ||||
|         uploader?.let { detailsDesc += "Uploader: $it\n" } | ||||
|         uploadDate?.let { detailsDesc += "Uploaded: ${EX_DATE_FORMAT.format(Date(it))}\n" } | ||||
|         length?.let { detailsDesc += "Length: $it pages\n" } | ||||
|         ratingString?.let { detailsDesc += "Rating: $it\n" } | ||||
|         category?.let { | ||||
|             manga.genre = it | ||||
|             detailsDesc += "Category: $it\n" | ||||
|         } | ||||
|         collection?.let { detailsDesc += "Collection: $it\n" } | ||||
|         group?.let { detailsDesc += "Group: $it\n" } | ||||
|         val parodiesString = parody.joinToString() | ||||
|         if(parodiesString.isNotEmpty()) { | ||||
|             detailsDesc += "Parody: $parodiesString\n" | ||||
|         } | ||||
|         val charactersString = character.joinToString() | ||||
|         if(charactersString.isNotEmpty()) { | ||||
|             detailsDesc += "Character: $charactersString\n" | ||||
|         } | ||||
|          | ||||
|         val tagsDesc = buildTagsDescription(this) | ||||
|          | ||||
|         manga.description = listOf(titleDesc, detailsDesc.toString(), tagsDesc.toString()) | ||||
|                 .filter(String::isNotBlank) | ||||
|                 .joinToString(separator = "\n") | ||||
|     } | ||||
|      | ||||
|     companion object { | ||||
|         val BASE_URL = "https://www.tsumino.com" | ||||
|          | ||||
|         fun tmIdFromUrl(url: String) | ||||
|             = Uri.parse(url).pathSegments[2] | ||||
|          | ||||
|         fun mangaUrlFromId(id: String) = "$BASE_URL/Book/Info/$id" | ||||
|          | ||||
|         fun thumbUrlFromId(id: String) = "$BASE_URL/Image/Thumb/$id" | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user