diff --git a/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt index d832fa54..39864aaa 100644 --- a/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt +++ b/src/all/newgrounds/src/eu/kanade/tachiyomi/animeextension/all/newgrounds/NewGrounds.kt @@ -6,22 +6,20 @@ import android.widget.Toast import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource +import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList -import eu.kanade.tachiyomi.animesource.model.AnimesPage 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.awaitSuccess import eu.kanade.tachiyomi.util.asJsoup -import eu.kanade.tachiyomi.util.parseAs +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import okhttp3.Response import org.json.JSONObject import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import rx.Observable import tryParse import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -29,6 +27,8 @@ import java.text.SimpleDateFormat import java.util.Locale import java.util.regex.Pattern +private const val SEARCH_PAGE_SIZE = 20 + class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { override val lang = "all" @@ -50,81 +50,35 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { .build() } - private fun creatorUrl(username: String) = baseUrl.replaceFirst("www", username) - - private fun animeFromElement(element: Element, section: String): SAnime { - return if (section == PREF_SECTIONS["Your Feed"]) { - SAnime.create().apply { - title = element.selectFirst(".detail-title h4")!!.text() - author = element.selectFirst(".detail-title strong")?.text() - description = element.selectFirst(".detail-description")?.text() - thumbnail_url = element.selectFirst(".item-icon img")?.absUrl("src") - setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href")) - } - } else { - SAnime.create().apply { - title = element.selectFirst(".card-title h4")!!.text() - author = element.selectFirst(".card-title span")?.text()?.replace("By ", "") - description = element.selectFirst("a")?.attr("title") - thumbnail_url = element.selectFirst("img")?.absUrl("src") - setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href")) - } - } - } - // Latest private val latestSection = preferences.getString("LATEST", PREF_SECTIONS["Latest"])!! - override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/$latestSection", headers) - - override fun latestUpdatesNextPageSelector(): String? = null - - override fun latestUpdatesSelector(): String { - return if (latestSection == PREF_SECTIONS["Your Feed"]) { - "a.item-portalsubmission" - } else { - "a.inline-card-portalsubmission" - } + override fun latestUpdatesRequest(page: Int): Request { + val offset = (page - 1) * SEARCH_PAGE_SIZE + return GET("$baseUrl/$latestSection?offset=$offset", headers) } + override fun latestUpdatesNextPageSelector(): String = "#load-more-items a" + + override fun latestUpdatesSelector(): String = animeSelector(latestSection) + override fun latestUpdatesFromElement(element: Element): SAnime { return animeFromElement(element, latestSection) } -// override suspend fun getLatestUpdates(page: Int): AnimesPage { -// val data = client.newCall(GET("$baseUrl")).awaitSuccess() -// val document = data.parseAs() -// -// val animeList = document.select(latestUpdatesSelector()).map { element -> -// animeFromElement(element, latestSection) -// } -// -// return AnimesPage(animeList, hasNextPage = true) -// -// } - // Browse private val popularSection = preferences.getString("POPULAR", PREF_SECTIONS["Popular"])!! - override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/$popularSection", headers) - - override fun popularAnimeNextPageSelector(): String? = null - - override suspend fun getPopularAnime(page: Int): AnimesPage { - val offset = 20 - //TODO - return super.getPopularAnime(page) + override fun popularAnimeRequest(page: Int): Request { + val offset = (page - 1) * SEARCH_PAGE_SIZE + return GET("$baseUrl/$popularSection?offset=$offset", headers) } - override fun popularAnimeSelector(): String { - return if (latestSection == PREF_SECTIONS["Your Feed"]) { - "a.item-portalsubmission" - } else { - "a.inline-card-portalsubmission" - } - } + override fun popularAnimeNextPageSelector(): String = "#load-more-items a" + + override fun popularAnimeSelector(): String = animeSelector(popularSection) override fun popularAnimeFromElement(element: Element): SAnime { return animeFromElement(element, popularSection) @@ -133,27 +87,108 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { // Search override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - TODO("Not yet implemented") + val searchUrl = "$baseUrl/search/conduct/movies".toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + + if (query.isNotEmpty()) searchUrl.addQueryParameter("terms", query) + + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("match", MATCH_AGAINST.values.elementAt(it.state)) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("exact", "1") + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("any", "1") + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("user", it.state) + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("genre", GENRE.values.elementAt(it.state)) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("min_length", it.state) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("max_length", it.state) + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("frontpaged", "1") + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("after", it.state) + } + filters.findInstance()?.state + ?.findInstance().ifFilterSet { + searchUrl.addQueryParameter("before", it.state) + } +// filters.findInstance().ifFilterSet { +// searchUrl.addQueryParameter("", "") +// } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("sort", SORTING.values.elementAt(it.state)) + } + filters.findInstance().ifFilterSet { + searchUrl.addQueryParameter("tags", it.state) + } + + Log.d("Tst", "$searchUrl") + + return GET(searchUrl.build(), headers) } - override fun searchAnimeNextPageSelector(): String? { - TODO("Not yet implemented") - } + override fun searchAnimeNextPageSelector(): String = "#results-load-more" - override fun searchAnimeSelector(): String { - TODO("Not yet implemented") - } + override fun searchAnimeSelector(): String = "ul.itemlist li a" - override fun searchAnimeFromElement(element: Element): SAnime { - TODO("Not yet implemented") - } + override fun searchAnimeFromElement(element: Element): SAnime = animeFromListElement(element) // Etc. override fun animeDetailsParse(document: Document): SAnime { + fun getStarRating(): String { + val score: Double = document.selectFirst("#score_number")?.text()?.toDouble() ?: 0.0 + val fullStars = score.toInt() + val hasHalfStar = (score % 1) >= 0.5 + val totalStars = if (hasHalfStar) fullStars + 1 else fullStars + val emptyStars = 5 - totalStars + + return "✪".repeat(fullStars) + (if (hasHalfStar) "✪" else "") + "⬤".repeat(emptyStars) + " ($score)" + } + + fun getAdultRating(): String { + val rating = document.selectFirst("#embed_header h2")!!.className().substringAfter("rated-") + return when (rating) { + "e" -> "🟩 Everyone" + "t" -> "🟦 Ages 13+" + "m" -> "🟪 Ages 17+" + "a" -> "🟥 Adults Only" + else -> "❓" + } + } + + fun getStats(): String { + val statsElement = document.selectFirst("#sidestats > dl:first-of-type") + val views = statsElement?.selectFirst("dd:first-of-type")?.text() ?: "?" + val faves = statsElement?.selectFirst("dd:nth-of-type(2)")?.text() ?: "?" + val votes = statsElement?.selectFirst("dd:nth-of-type(3)")?.text() ?: "?" + + return "👀 $views | ❤️ $faves | 👍 $votes" + } + return SAnime.create().apply { title = document.selectFirst("h2[itemprop=\"name\"]")!!.text() - description = document.selectFirst("meta[itemprop=\"description\"]")?.attr("content") + description = """ + ${document.selectFirst("meta[itemprop=\"description\"]")?.attr("content")} + + ${getAdultRating()} | ${getStarRating()} | ${getStats()} + """.trimIndent() author = document.selectFirst(".authorlinks > div:first-of-type .item-details-main")?.text() artist = document.select(".authorlinks > div:not(:first-of-type) .item-details-main").joinToString { it.text() @@ -170,7 +205,7 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { override fun episodeFromElement(element: Element): SEpisode = throw UnsupportedOperationException("Not Used") private fun extractEpisodeIdFromScript(element: Element?): String? { - val regex = """data-movie-id=\\\"(\d+)\\\"""" + val regex = """data-movie-id=\\"(\d+)\\"""" val scriptContent = element!!.html().toString() val pattern = Pattern.compile(regex, Pattern.MULTILINE) @@ -199,13 +234,9 @@ class NewGrounds : ParsedAnimeHttpSource(), ConfigurableAnimeSource { ) } - override fun videoListRequest(episode: SEpisode): Request { - Log.d("Tst", videoListHeaders.toString()) - return GET("$baseUrl${episode.url}", videoListHeaders) - } + override fun videoListRequest(episode: SEpisode): Request = GET("$baseUrl${episode.url}", videoListHeaders) override fun videoListParse(response: Response): List