diff --git a/multisrc/overrides/masonry/elitebabes/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/elitebabes/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..7d96dc788 Binary files /dev/null and b/multisrc/overrides/masonry/elitebabes/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/elitebabes/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/elitebabes/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..9a1190d48 Binary files /dev/null and b/multisrc/overrides/masonry/elitebabes/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/elitebabes/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/elitebabes/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..a0fcbe79b Binary files /dev/null and b/multisrc/overrides/masonry/elitebabes/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/elitebabes/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/elitebabes/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..91444cacb Binary files /dev/null and b/multisrc/overrides/masonry/elitebabes/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/elitebabes/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/elitebabes/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..a98573b01 Binary files /dev/null and b/multisrc/overrides/masonry/elitebabes/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/femjoyhunter/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..edc162340 Binary files /dev/null and b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/femjoyhunter/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..d5ecdf596 Binary files /dev/null and b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..ddf933abf Binary files /dev/null and b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..85f5ed5f8 Binary files /dev/null and b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..cb4781400 Binary files /dev/null and b/multisrc/overrides/masonry/femjoyhunter/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/ftvhunter/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/ftvhunter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..529e0f6af Binary files /dev/null and b/multisrc/overrides/masonry/ftvhunter/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/ftvhunter/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/ftvhunter/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..cd93668b6 Binary files /dev/null and b/multisrc/overrides/masonry/ftvhunter/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/ftvhunter/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..113eecd8c Binary files /dev/null and b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d47de8764 Binary files /dev/null and b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4a8c3a79c Binary files /dev/null and b/multisrc/overrides/masonry/ftvhunter/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/joymiihub/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/joymiihub/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..cc28642f5 Binary files /dev/null and b/multisrc/overrides/masonry/joymiihub/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/joymiihub/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/joymiihub/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..e9d479ee7 Binary files /dev/null and b/multisrc/overrides/masonry/joymiihub/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/joymiihub/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/joymiihub/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..59166a320 Binary files /dev/null and b/multisrc/overrides/masonry/joymiihub/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/joymiihub/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/joymiihub/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..a00e64ef3 Binary files /dev/null and b/multisrc/overrides/masonry/joymiihub/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/joymiihub/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/joymiihub/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..120704f9d Binary files /dev/null and b/multisrc/overrides/masonry/joymiihub/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/metarthunter/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/metarthunter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..5c6d94c0f Binary files /dev/null and b/multisrc/overrides/masonry/metarthunter/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/metarthunter/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/metarthunter/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..719306a73 Binary files /dev/null and b/multisrc/overrides/masonry/metarthunter/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/metarthunter/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/metarthunter/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..e54b92fb3 Binary files /dev/null and b/multisrc/overrides/masonry/metarthunter/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/metarthunter/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/metarthunter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..8e8176947 Binary files /dev/null and b/multisrc/overrides/masonry/metarthunter/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/metarthunter/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/metarthunter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..31c263f9f Binary files /dev/null and b/multisrc/overrides/masonry/metarthunter/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/playmatehunter/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/playmatehunter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..4373d4116 Binary files /dev/null and b/multisrc/overrides/masonry/playmatehunter/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/playmatehunter/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/playmatehunter/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..08020ef52 Binary files /dev/null and b/multisrc/overrides/masonry/playmatehunter/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/playmatehunter/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..4f269de43 Binary files /dev/null and b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..910675f74 Binary files /dev/null and b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..bfe549090 Binary files /dev/null and b/multisrc/overrides/masonry/playmatehunter/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/xarthunter/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/masonry/xarthunter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..1367e1f85 Binary files /dev/null and b/multisrc/overrides/masonry/xarthunter/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/xarthunter/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/masonry/xarthunter/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..84276a027 Binary files /dev/null and b/multisrc/overrides/masonry/xarthunter/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/xarthunter/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/masonry/xarthunter/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..445a51b64 Binary files /dev/null and b/multisrc/overrides/masonry/xarthunter/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/xarthunter/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/masonry/xarthunter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..bfa6982ca Binary files /dev/null and b/multisrc/overrides/masonry/xarthunter/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/masonry/xarthunter/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/masonry/xarthunter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..dd955b743 Binary files /dev/null and b/multisrc/overrides/masonry/xarthunter/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/Masonry.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/Masonry.kt new file mode 100644 index 000000000..4559aa888 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/Masonry.kt @@ -0,0 +1,199 @@ +package eu.kanade.tachiyomi.multisrc.masonry + +import eu.kanade.tachiyomi.network.GET +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.model.UpdateStrategy +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable +import java.lang.UnsupportedOperationException + +abstract class Masonry( + override val name: String, + override val baseUrl: String, + override val lang: String, +) : ParsedHttpSource() { + + override val supportsLatest = true + + override val client = network.cloudflareClient + + override fun headersBuilder() = super.headersBuilder() + .set("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int): Request { + val url = when (page) { + 1 -> baseUrl + 2 -> "$baseUrl/archive/" + else -> "$baseUrl/archive/page/${page - 1}/" + } + + return GET(url, headers) + } + + override fun popularMangaParse(response: Response): MangasPage { + getTags() + return super.popularMangaParse(response) + } + + override fun popularMangaSelector() = ".list-gallery:not(.static) figure:not(:has(a[href*=/video/]))" + override fun popularMangaNextPageSelector() = ".pagination-a li.next" + + override fun popularMangaFromElement(element: Element) = SManga.create().apply { + element.selectFirst("a")!!.also { + setUrlWithoutDomain(it.absUrl("href")) + title = it.attr("title") + } + thumbnail_url = element.selectFirst("img")?.imgAttr() + } + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/updates/sort/newest/mpage/$page/", headers) + } + + override fun latestUpdatesParse(response: Response) = popularMangaParse(response) + override fun latestUpdatesSelector() = popularMangaSelector() + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return if (query.isNotEmpty()) { + val url = "$baseUrl/search/post/".toHttpUrl().newBuilder() + .addPathSegment(query.trim()) + .addEncodedPathSegments("mpage/$page/") + .build() + + GET(url, headers) + } else { + val tagFilter = filters.filterIsInstance().firstOrNull() + val sortFilter = filters.filterIsInstance().first() + + val url = baseUrl.toHttpUrl().newBuilder().apply { + if (tagFilter == null || tagFilter.selected == "") { + addPathSegment("updates") + sortFilter.getUriPartIfNeeded("search").also { + if (it.isBlank()) { + addEncodedPathSegments("page/$page/") + } else { + addEncodedPathSegments(it) + addEncodedPathSegments("mpage/$page/") + } + } + } else { + addPathSegment("tag") + addPathSegment(tagFilter.selected) + sortFilter.getUriPartIfNeeded("tag").also { + if (it.isBlank()) { + addEncodedPathSegments("page/$page/") + } else { + addEncodedPathSegments(it) + addEncodedPathSegments("mpage/$page/") + } + } + } + }.build() + + GET(url, headers) + } + } + + private var tags = emptyList>() + private var tagsFetchAttempt = 0 + + private fun getTags() { + if (tags.isEmpty() && tagsFetchAttempt < 3) { + runCatching { + tags = client.newCall(GET("$baseUrl/updates/sort/newest/", headers)) + .execute().asJsoup() + .select("#filter-a span:has(> input)") + .mapNotNull { + Pair( + it.select("label").text(), + it.select("input").attr("value"), + ) + }.let { + listOf(Pair("", "")) + it + } + } + tagsFetchAttempt++ + } + } + + override fun getFilterList(): FilterList { + val filters = mutableListOf( + Filter.Header("Filters ignored with text search"), + Filter.Separator(), + SortFilter(), + ) + + if (tags.isEmpty()) { + filters.add( + Filter.Header("Press 'reset' to attempt to load tags"), + ) + } else { + filters.add( + TagFilter(tags), + ) + } + + return FilterList(filters) + } + + override fun searchMangaParse(response: Response) = popularMangaParse(response) + override fun searchMangaSelector() = popularMangaSelector() + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + document.selectFirst("p.link-btn")?.run { + artist = select("a[href*=/model/]").eachText().joinToString() + genre = select("a[href*=/tag/]").eachText().joinToString() + author = selectFirst("a")?.text() + } + description = document.selectFirst("#content > p")?.text() + status = SManga.COMPLETED + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + } + + override fun fetchChapterList(manga: SManga): Observable> { + return Observable.just( + listOf( + SChapter.create().apply { + name = "Gallery" + url = manga.url + }, + ), + ) + } + + override fun chapterListSelector() = throw UnsupportedOperationException() + override fun chapterFromElement(element: Element) = throw UnsupportedOperationException() + + override fun pageListParse(document: Document): List { + return document.select(".list-gallery a[href^=https://cdn.]").mapIndexed { idx, img -> + Page(idx, imageUrl = img.absUrl("href")) + } + } + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() + + protected fun Element.imgAttr(): String? { + return when { + hasAttr("srcset") -> attr("abs:srcset").substringBefore(" ") + hasAttr("data-cfsrc") -> attr("abs:data-cfsrc") + hasAttr("data-src") -> attr("abs:data-src") + hasAttr("data-lazy-src") -> attr("abs:data-lazy-src") + else -> attr("abs:src") + } + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryFilters.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryFilters.kt new file mode 100644 index 000000000..8e4a184e9 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryFilters.kt @@ -0,0 +1,40 @@ +package eu.kanade.tachiyomi.multisrc.masonry + +import eu.kanade.tachiyomi.source.model.Filter + +abstract class SelectFilter( + name: String, + private val options: List>, +) : Filter.Select( + name, + options.map { it.first }.toTypedArray(), +) { + val selected get() = options[state].second +} + +class SortFilter : SelectFilter("Sort by", sortFilterOptions) { + fun getUriPartIfNeeded(part: String) = + when (part) { + "search" -> { + when (state) { + 2 -> "" + else -> selected + } + } + "tag" -> { + when (state) { + 0 -> "" + else -> selected + } + } + else -> "" + } +} + +private val sortFilterOptions = listOf( + Pair("Trending", "sort/trending"), + Pair("Newest", "sort/newest"), + Pair("Popular", "sort/popular"), +) + +class TagFilter(options: List>) : SelectFilter("Tags", options) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryGenerator.kt new file mode 100644 index 000000000..6ed89e406 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/masonry/MasonryGenerator.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.multisrc.masonry + +import generator.ThemeSourceData.SingleLang +import generator.ThemeSourceGenerator + +class MasonryGenerator : ThemeSourceGenerator { + + override val themePkg = "masonry" + + override val themeClass = "Masonry" + + override val baseVersionCode = 1 + + override val sources = listOf( + SingleLang("Elite Babes", "https://www.elitebabes.com", "all", isNsfw = true), + SingleLang("Femjoy Hunter", "https://www.femjoyhunter.com", "all", isNsfw = true), + SingleLang("FTV Hunter", "https://www.ftvhunter.com", "all", isNsfw = true), + SingleLang("Joymii Hub", "https://www.joymiihub.com", "all", isNsfw = true), + SingleLang("Metart Hunter", "https://www.metarthunter.com", "all", isNsfw = true), + SingleLang("Playmate Hunter", "https://pmatehunter.com", "all", isNsfw = true), + SingleLang("XArt Hunter", "https://www.xarthunter.com", "all", isNsfw = true), + ) + + companion object { + @JvmStatic + fun main(args: Array) { + MasonryGenerator().createAll() + } + } +}