diff --git a/src/en/keenspot/build.gradle b/src/en/keenspot/build.gradle new file mode 100644 index 000000000..94e50979f --- /dev/null +++ b/src/en/keenspot/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'keenspot' + pkgNameSuffix = 'en.keenspot' + extClass = '.KeenspotFactory' + extVersionCode = 1 + libVersion = '1.2' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/keenspot/res/mipmap-hdpi/ic_launcher.png b/src/en/keenspot/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..81fca9506 Binary files /dev/null and b/src/en/keenspot/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/keenspot/res/mipmap-mdpi/ic_launcher.png b/src/en/keenspot/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..ef37699b6 Binary files /dev/null and b/src/en/keenspot/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/keenspot/res/mipmap-xhdpi/ic_launcher.png b/src/en/keenspot/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..14c9df990 Binary files /dev/null and b/src/en/keenspot/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/keenspot/res/mipmap-xxhdpi/ic_launcher.png b/src/en/keenspot/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..c6154cf32 Binary files /dev/null and b/src/en/keenspot/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/keenspot/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/keenspot/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..46fd3a27f Binary files /dev/null and b/src/en/keenspot/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/keenspot/res/web_hi_res_512.png b/src/en/keenspot/res/web_hi_res_512.png new file mode 100644 index 000000000..1f167d6cd Binary files /dev/null and b/src/en/keenspot/res/web_hi_res_512.png differ diff --git a/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/KeenspotFactory.kt b/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/KeenspotFactory.kt new file mode 100644 index 000000000..62cdcf73f --- /dev/null +++ b/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/KeenspotFactory.kt @@ -0,0 +1,8 @@ +package eu.kanade.tachiyomi.extension.en.keenspot + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class KeenspotFactory : SourceFactory { + override fun createSources(): List = listOf(TwoKinds()) +} diff --git a/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/TwoKinds.kt b/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/TwoKinds.kt new file mode 100644 index 000000000..5ee5f1588 --- /dev/null +++ b/src/en/keenspot/src/eu/kanade/tachiyomi/extension/en/keenspot/TwoKinds.kt @@ -0,0 +1,155 @@ +package eu.kanade.tachiyomi.extension.en.keenspot + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess +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.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import rx.Observable + +class TwoKinds : HttpSource() { + + override val name = "Keenspot: TwoKinds" + + override val baseUrl = "https://twokinds.keenspot.com" + + override val lang = "en" + + override val supportsLatest: Boolean = false + + // the one and only manga entry + fun mangaSinglePages(): SManga { + return SManga.create().apply { + title = "TwoKinds (1 page per chapter)" + thumbnail_url = "https://dummyimage.com/768x994/000/ffffff.jpg&text=$title" + artist = "Tom Fischbach" + author = "Tom Fischbach" + status = SManga.UNKNOWN + url = "1" + } + } + + fun manga20Pages(): SManga { + return SManga.create().apply { + title = "TwoKinds (20 pages per chapter)" + thumbnail_url = "https://dummyimage.com/768x994/000/ffffff.jpg&text=$title" + artist = "Tom Fischbach" + author = "Tom Fischbach" + status = SManga.UNKNOWN + url = "20" + } + } + + override fun fetchPopularManga(page: Int): Observable { + return Observable.just(MangasPage(listOf(mangaSinglePages(), manga20Pages()), false)) + } + + override fun popularMangaRequest(page: Int): Request = throw Exception("Not used") + + override fun popularMangaParse(response: Response): MangasPage = throw Exception("Not used") + + // latest Updates not used + + override fun latestUpdatesParse(response: Response): MangasPage = throw Exception("Not used") + + override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used") + + // the manga is one and only, but still write the data again to avoid bugs in backup restore + override fun fetchMangaDetails(manga: SManga): Observable { + if (manga.url == "1") + return Observable.just(mangaSinglePages()) + else + return Observable.just(manga20Pages()) + } + + override fun mangaDetailsParse(response: Response): SManga = throw Exception("Not used") + + // chapter list + + override fun fetchChapterList(manga: SManga): Observable> { + return client.newCall(chapterListRequest(manga)) + .asObservableSuccess() + .map { response -> + chapterListParse(response, manga) + } + } + + override fun chapterListRequest(manga: SManga): Request { + return GET(baseUrl, headers) + } + + override fun chapterListParse(response: Response): List = throw Exception("Not used") + + fun chapterListParse(response: Response, manga: SManga): List { + val document = response.asJsoup() + + val lastPage = document.select(".navprev").first().attr("href").split("/")[2].toInt() + 1 + + val chapters = mutableListOf() + + if (manga.url == "1") { + for (i in 1..lastPage) { + chapters.add( + SChapter.create().apply() { + url = "1-$i" + name = "Page $i" + } + ) + } + } else { + for (i in 1..lastPage step 20) { + chapters.add( + SChapter.create().apply() { + url = "20-$i" + if (i + 20 > lastPage) + name = "Pages $i-$lastPage" + else + name = "Pages $i-${i + 20}" + } + ) + } + } + + return chapters.reversed() + } + + override fun fetchPageList(chapter: SChapter): Observable> { + if (chapter.url.startsWith("1")) { + return Observable.just( + listOf( + Page(0, baseUrl + "/comic/${chapter.url.substringAfter("-")}/") + ) + ) + } else { + val pages = mutableListOf() + val firstPage = chapter.url.substringAfter("-").toInt() + + for (i in firstPage..firstPage + 19) { + pages.add( + Page(i - firstPage, baseUrl + "/comic/$i/") + ) + } + return Observable.just(pages) + } + } + + override fun pageListParse(response: Response): List = throw Exception("Not used") + + override fun imageUrlParse(response: Response): String { + val document = response.asJsoup() + + return document.select("#content article img").first().attr("src") + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable = throw Exception("Search functionality is not available.") + + override fun searchMangaParse(response: Response): MangasPage = throw Exception("Not used") + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw Exception("Not used") +}