diff --git a/src/pl/desuonline/AndroidManifest.xml b/src/pl/desuonline/AndroidManifest.xml new file mode 100644 index 000000000..0978de1ae --- /dev/null +++ b/src/pl/desuonline/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/src/pl/desuonline/build.gradle b/src/pl/desuonline/build.gradle new file mode 100644 index 000000000..21169db53 --- /dev/null +++ b/src/pl/desuonline/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'desu-online' + pkgNameSuffix = 'pl.desuonline' + extClass = '.DesuOnline' + extVersionCode = 1 + libVersion = '13' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/pl/desuonline/res/mipmap-hdpi/ic_launcher.png b/src/pl/desuonline/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..53298291e Binary files /dev/null and b/src/pl/desuonline/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/pl/desuonline/res/mipmap-mdpi/ic_launcher.png b/src/pl/desuonline/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..38c510159 Binary files /dev/null and b/src/pl/desuonline/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/pl/desuonline/res/mipmap-xhdpi/ic_launcher.png b/src/pl/desuonline/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..5e9ffea12 Binary files /dev/null and b/src/pl/desuonline/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/pl/desuonline/res/mipmap-xxhdpi/ic_launcher.png b/src/pl/desuonline/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..987160c05 Binary files /dev/null and b/src/pl/desuonline/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/pl/desuonline/res/mipmap-xxxhdpi/ic_launcher.png b/src/pl/desuonline/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..f3a06cc82 Binary files /dev/null and b/src/pl/desuonline/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/pl/desuonline/res/web_hi_res_512.png b/src/pl/desuonline/res/web_hi_res_512.png new file mode 100644 index 000000000..641868dd6 Binary files /dev/null and b/src/pl/desuonline/res/web_hi_res_512.png differ diff --git a/src/pl/desuonline/src/eu/kanade/tachiyomi/animeextension/pl/desuonline/DesuOnline.kt b/src/pl/desuonline/src/eu/kanade/tachiyomi/animeextension/pl/desuonline/DesuOnline.kt new file mode 100644 index 000000000..b52c3eda6 --- /dev/null +++ b/src/pl/desuonline/src/eu/kanade/tachiyomi/animeextension/pl/desuonline/DesuOnline.kt @@ -0,0 +1,238 @@ +package eu.kanade.tachiyomi.animeextension.pl.desuonline + +import android.app.Application +import android.content.SharedPreferences +import android.util.Base64 +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilterList +import eu.kanade.tachiyomi.animesource.model.SAnime +import eu.kanade.tachiyomi.animesource.model.SEpisode +import eu.kanade.tachiyomi.animesource.model.Video +import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.text.SimpleDateFormat +import java.util.Locale + +class DesuOnline : ConfigurableAnimeSource, ParsedAnimeHttpSource() { + + override val name = "desu-online" + + override val baseUrl = "https://desu-online.pl" + + override val lang = "pl" + + override val supportsLatest = true + + override val client = network.cloudflareClient + + private val json = Json { + ignoreUnknownKeys = true + } + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + // ============================== Popular =============================== + + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime/?page=$page&order=popular") + + override fun popularAnimeSelector() = "div.listupd div.bsx > a" + + override fun popularAnimeNextPageSelector() = "div.pagination > a.next, div.hpage > a.r" + + override fun popularAnimeFromElement(element: Element): SAnime { + val animeTitle = element.select("div.tt > h2").text().trim() + val img = element.select("div.limit > img") + return SAnime.create().apply { + setUrlWithoutDomain(element.attr("href")) + title = animeTitle + thumbnail_url = img.attr("data-src") + } + } + + // =========================== Anime Details ============================ + + override fun animeDetailsParse(document: Document): SAnime { + val img = document.select("div.thumb > img").ifEmpty { null } + val studio = document.select("div.info-content > div.spe > span:contains(Studio:)").ifEmpty { null } + val statusSpan = document.select("div.info-content > div.spe > span:contains(Status:)").ifEmpty { null } + val desc = document.select("div[itemprop=description] > p:last-child").ifEmpty { null } + val director = document.select("div.info-content > div.spe > span:contains(Reżyser:)").ifEmpty { null } + val genres = document.select("div.genxed > a") + return SAnime.create().apply { + title = document.select("h1.entry-title").text() + thumbnail_url = img?.attr("data-src") + author = studio?.text()?.substringAfter("Studio: ") + status = parseStatus(statusSpan?.text()?.substringAfter("Status: ")) + description = desc?.text()?.trim() + artist = director?.text()?.substringAfter("Reżyser: ") + genre = genres.joinToString { it.text() } + } + } + + // ============================== Episodes ============================== + + override fun episodeListSelector() = "div.eplister > ul > li" + + override fun episodeFromElement(element: Element): SEpisode { + val a = element.select("a") + val epNum = a.select("div.epl-num").text() + val epTitle = a.select("div.epl-title").text() + val date = a.select("div.epl-date").text() + return SEpisode.create().apply { + setUrlWithoutDomain(a.attr("href")) + name = "Odcinek $epNum: $epTitle" + date_upload = parseDate(date) + episode_number = epNum.substringBefore(" ").toFloatOrNull() ?: 0F + } + } + + // ============================ Video Links ============================= + + override fun videoListParse(response: Response): List