diff --git a/src/en/myhentaicomics/build.gradle b/src/en/myhentaicomics/build.gradle new file mode 100644 index 000000000..4a26e3d61 --- /dev/null +++ b/src/en/myhentaicomics/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: MyHentaiComics' + pkgNameSuffix = 'en.myhentaicomics' + extClass = '.MyHentaiComics' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/myhentaicomics/res/mipmap-hdpi/ic_launcher.png b/src/en/myhentaicomics/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..16350c947 Binary files /dev/null and b/src/en/myhentaicomics/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/myhentaicomics/res/mipmap-mdpi/ic_launcher.png b/src/en/myhentaicomics/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..415d73fd2 Binary files /dev/null and b/src/en/myhentaicomics/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/myhentaicomics/res/mipmap-xhdpi/ic_launcher.png b/src/en/myhentaicomics/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..079706390 Binary files /dev/null and b/src/en/myhentaicomics/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/myhentaicomics/res/mipmap-xxhdpi/ic_launcher.png b/src/en/myhentaicomics/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..979c8e170 Binary files /dev/null and b/src/en/myhentaicomics/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/myhentaicomics/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/myhentaicomics/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..e8bae1710 Binary files /dev/null and b/src/en/myhentaicomics/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/myhentaicomics/res/web_hi_res_512.png b/src/en/myhentaicomics/res/web_hi_res_512.png new file mode 100644 index 000000000..0dc13af62 Binary files /dev/null and b/src/en/myhentaicomics/res/web_hi_res_512.png differ diff --git a/src/en/myhentaicomics/src/eu/kanade/tachiyomi/extension/en/myhentaicomics/MyHentaiComics.kt b/src/en/myhentaicomics/src/eu/kanade/tachiyomi/extension/en/myhentaicomics/MyHentaiComics.kt new file mode 100644 index 000000000..6db3bca66 --- /dev/null +++ b/src/en/myhentaicomics/src/eu/kanade/tachiyomi/extension/en/myhentaicomics/MyHentaiComics.kt @@ -0,0 +1,176 @@ +package eu.kanade.tachiyomi.extension.en.myhentaicomics + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.* +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import okhttp3.OkHttpClient +import okhttp3.Request +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable + +class MyHentaiComics : ParsedHttpSource() { + + override val name = "MyHentaiComics" + + override val baseUrl = "https://myhentaicomics.com" + + override val lang = "en" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + // Popular + + override fun popularMangaRequest(page: Int): Request { + return GET("$baseUrl/index.php/tag/2402?page=$page", headers) + } + + override fun popularMangaSelector() = "li.g-item" + + override fun popularMangaFromElement(element: Element): SManga { + return SManga.create().apply { + title = element.select("h2").text() + setUrlWithoutDomain(element.select("a").attr("href")) + thumbnail_url = element.select("img").attr("abs:src") + } + } + + override fun popularMangaNextPageSelector() = "a.ui-state-default span.ui-icon-seek-next" + + // Latest + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/index.php/?page=$page", headers) + } + + override fun latestUpdatesSelector() = popularMangaSelector() + + override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element) + + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return if (query.isNotBlank()) { + GET("$baseUrl/index.php/search?q=$query&page=$page", headers) + } else { + var url = baseUrl + for (filter in if (filters.isEmpty()) getFilterList() else filters) { + when (filter) { + is GenreFilter -> url += filter.toUriPart() + "?page=$page" + } + } + GET(url, headers) + } + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaFromElement(element: Element): SManga { + return SManga.create().apply { + title = element.select("h2, p").text() + setUrlWithoutDomain(element.select("a").attr("href")) + thumbnail_url = element.select("img").attr("abs:src") + } + } + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + // Details + + override fun mangaDetailsParse(document: Document): SManga { + val tags = document.select("div.g-description a").partition { tag -> + tag.text().startsWith("Artist: ") + } + return SManga.create().apply { + artist = tags.first.joinToString { it.text().substringAfter(" ") } + author = artist + genre = tags.second.joinToString { it.text() } + thumbnail_url = document.select("img.g-thumbnail").first().attr("abs:src").replace("/thumbs/", "/resizes/") + } + } + + // Chapters + + override fun fetchChapterList(manga: SManga): Observable> { + return Observable.just( + listOf( + SChapter.create().apply { + name = "Chapter" + url = manga.url + } + ) + ) + } + + override fun chapterListSelector() = throw UnsupportedOperationException("Not used") + + override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used") + + // Pages + + override fun pageListParse(document: Document): List { + return document.select("img.g-thumbnail").mapIndexed { i, img -> + Page(i, "", img.attr("abs:src").replace("/thumbs/", "/resizes/")) + } + } + + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") + + // Filters + + override fun getFilterList() = FilterList( + Filter.Header("Cannot combine search types!"), + Filter.Separator("-----------------"), + GenreFilter () + ) + + private class GenreFilter: UriPartFilter("Genres", + arrayOf( + Pair("", ""), + Pair("3D", "/index.php/tag/2403"), + Pair("Asian", "/index.php/tag/2404"), + Pair("Ass Expansion", "/index.php/tag/2405"), + Pair("BBW", "/index.php/tag/2406"), + Pair("Beastiality", "/index.php/tag/2407"), + Pair("Bisexual", "/index.php/tag/2408"), + Pair("Body Swap", "/index.php/tag/2410"), + Pair("Breast Expansion", "/index.php/tag/2413"), + Pair("Bukakke", "/index.php/tag/2412"), + Pair("Cheating", "/index.php/tag/2414"), + Pair("Crossdressing", "/index.php/tag/2415"), + Pair("Femdom", "/index.php/tag/2417"), + Pair("Furry", "/index.php/tag/2418"), + Pair("Futanari", "/index.php/tag/2419"), + Pair("Futanari On Male", "/index.php/tag/2430"), + Pair("Gangbang", "/index.php/tag/2421"), + Pair("Gay", "/index.php/tag/2422"), + Pair("Gender Bending", "/index.php/tag/2423"), + Pair("Giantess", "/index.php/tag/2424"), + Pair("Gloryhole", "/index.php/tag/2425"), + Pair("Hardcore", "/index.php/tag/2426"), + Pair("Harem", "/index.php/tag/2427"), + Pair("Incest", "/index.php/tag/2450"), + Pair("Interracial", "/index.php/tag/2409"), + Pair("Lactation", "/index.php/tag/2428"), + Pair("Lesbian", "/index.php/tag/3167"), + Pair("Milf", "/index.php/tag/2431"), + Pair("Mind Control & Hypnosis", "/index.php/tag/2432"), + Pair("Muscle Girl", "/index.php/tag/2434"), + Pair("Pegging", "/index.php/tag/2437"), + Pair("Pregnant", "/index.php/tag/2438"), + Pair("Rape", "/index.php/tag/2433"), + Pair("Strap-On", "/index.php/tag/2441"), + Pair("Superheroes", "/index.php/tag/2443"), + Pair("Tentacles", "/index.php/tag/2444") + ) + ) + + private open class UriPartFilter(displayName: String, val vals: Array>) : + Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { + fun toUriPart() = vals[state].second + } +}