diff --git a/src/all/holonometria/AndroidManifest.xml b/src/all/holonometria/AndroidManifest.xml new file mode 100644 index 000000000..cc947c567 --- /dev/null +++ b/src/all/holonometria/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/src/all/holonometria/build.gradle b/src/all/holonometria/build.gradle new file mode 100644 index 000000000..fe9ec48fa --- /dev/null +++ b/src/all/holonometria/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'HOLONOMETRIA' + pkgNameSuffix = 'all.holonometria' + extClass = '.HolonometriaFactory' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..bef1d2b85 Binary files /dev/null and b/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..caaa59fad Binary files /dev/null and b/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..fc8894dbd Binary files /dev/null and b/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..5f6f59ea8 Binary files /dev/null and b/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..f0f185cea Binary files /dev/null and b/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/web_hi_res_512.png b/src/all/holonometria/res/web_hi_res_512.png new file mode 100644 index 000000000..b3bd72e7b Binary files /dev/null and b/src/all/holonometria/res/web_hi_res_512.png differ diff --git a/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt new file mode 100644 index 000000000..4c9058328 --- /dev/null +++ b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt @@ -0,0 +1,150 @@ +package eu.kanade.tachiyomi.extension.all.holonometria + +import eu.kanade.tachiyomi.network.GET +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.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class Holonometria( + override val lang: String, + private val langPath: String = "$lang/", +) : ParsedHttpSource() { + + override val name = "HOLONOMETRIA" + + override val baseUrl = "https://alt.hololive.tv" + + override val supportsLatest = false + + override val client = network.client.newBuilder() + .readTimeout(60, TimeUnit.SECONDS) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int) = + GET("$baseUrl/holonometria/$langPath", headers) + + override fun popularMangaSelector() = "#Story article:has(a[href*=/manga/])" + override fun popularMangaNextPageSelector() = null + + override fun popularMangaFromElement(element: Element) = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + title = element.select(".ttl").text() + thumbnail_url = element.selectFirst("img")?.attr("abs:src") + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = + GET("$baseUrl/holonometria/$langPath#${query.trim()}", headers) + + override fun searchMangaParse(response: Response): MangasPage { + val document = response.asJsoup() + val search = response.request.url.fragment!! + + val entries = document.select(searchMangaSelector()) + .map(::searchMangaFromElement) + .filter { it.title.contains(search, true) } + + return MangasPage(entries, false) + } + + override fun searchMangaSelector() = popularMangaSelector() + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.select(".md-ttl__pages").text() + thumbnail_url = document.select(".mangainfo img").attr("abs:src") + description = document.select(".mangainfo aside").text() + val info = document.select(".mangainfo footer").html().split("
") + author = info.firstOrNull { desc -> manga.any { desc.contains(it, true) } } + ?.substringAfter(":") + ?.substringAfter(":") + ?.trim() + ?.replace("&", "&") + artist = info.firstOrNull { desc -> script.any { desc.contains(it, true) } } + ?.substringAfter(":") + ?.substringAfter(":") + ?.trim() + ?.replace("&", "&") + } + + override fun chapterListRequest(manga: SManga) = + paginatedChapterListRequest(manga.url, 1) + + private fun paginatedChapterListRequest(mangaUrl: String, page: Int) = + GET("$baseUrl$mangaUrl".removeSuffix("/") + if (page == 1) "/" else "/page/$page/", headers) + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val mangaUrl = response.request.url.toString() + .substringAfter(baseUrl) + .substringBefore("page/") + + val chapters = document.select(chapterListSelector()) + .map(::chapterFromElement) + .toMutableList() + + val lastPage = document.select(".pagenation-list a").last() + ?.text()?.toIntOrNull() ?: return chapters + + for (page in 2..lastPage) { + val request = paginatedChapterListRequest(mangaUrl, page) + val newDocument = client.newCall(request).execute().asJsoup() + + val moreChapters = newDocument.select(chapterListSelector()) + .map(::chapterFromElement) + + chapters.addAll(moreChapters) + } + + return chapters + } + + override fun chapterListSelector() = "#Archive article" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + name = element.select(".ttl").text() + date_upload = element.selectFirst(".data--date")?.text().parseDate() + scanlator = element.selectFirst(".data--category")?.text() + } + + private fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!)!!.time + }.getOrDefault(0L) + } + + override fun pageListParse(document: Document): List { + return document.select("#js-mangaviewer img").mapIndexed { idx, img -> + Page(idx, "", img.attr("abs:src")) + } + } + + companion object { + private val manga = listOf("manga", "gambar", "漫画") + private val script = listOf("script", "naskah", "脚本") + + private val dateFormat by lazy { + SimpleDateFormat("yy.MM.dd", Locale.ENGLISH) + } + } + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used") + override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException("Not used") + override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException("Not used") + override fun latestUpdatesSelector() = throw UnsupportedOperationException("Not used") + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used") +} diff --git a/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt new file mode 100644 index 000000000..08d64ab98 --- /dev/null +++ b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.all.holonometria + +import eu.kanade.tachiyomi.source.SourceFactory + +class HolonometriaFactory : SourceFactory { + override fun createSources() = listOf( + Holonometria("ja", ""), + Holonometria("en"), + Holonometria("id"), + ) +}