feat(pt/animesdrive): New source pt/AnimesDrive (close #228) #521
7 changed files with 157 additions and 0 deletions
14
src/pt/animesdrive/build.gradle
Normal file
14
src/pt/animesdrive/build.gradle
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
ext {
|
||||||
|
extName = 'AnimesDrive'
|
||||||
|
extClass = '.AnimesDrive'
|
||||||
|
themePkg = 'dooplay'
|
||||||
|
baseUrl = 'https://animesdrive.blog'
|
||||||
|
overrideVersionCode = 1
|
||||||
|
isNsfw = true
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib:blogger-extractor"))
|
||||||
|
}
|
BIN
src/pt/animesdrive/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/pt/animesdrive/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
src/pt/animesdrive/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/pt/animesdrive/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
src/pt/animesdrive/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/pt/animesdrive/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
BIN
src/pt/animesdrive/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/pt/animesdrive/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
BIN
src/pt/animesdrive/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/pt/animesdrive/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,143 @@
|
||||||
|
package eu.kanade.tachiyomi.animeextension.pt.animesdrive
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||||
|
import eu.kanade.tachiyomi.animesource.model.Video
|
||||||
|
import eu.kanade.tachiyomi.lib.bloggerextractor.BloggerExtractor
|
||||||
|
import eu.kanade.tachiyomi.multisrc.dooplay.DooPlay
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
|
class AnimesDrive : DooPlay(
|
||||||
|
"pt-BR",
|
||||||
|
"Animes Drive",
|
||||||
|
"https://animesdrive.blog",
|
||||||
|
) {
|
||||||
|
|
||||||
|
// ============================== Popular ===============================
|
||||||
|
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/anime", headers)
|
||||||
|
|
||||||
|
// =============================== Latest ===============================
|
||||||
|
override fun latestUpdatesNextPageSelector() = "div.pagination > a.arrow_pag > i.fa-caret-right"
|
||||||
|
|
||||||
|
// =============================== Search ===============================
|
||||||
|
|
||||||
|
// =========================== Anime Details ============================
|
||||||
|
override val additionalInfoSelector = "div.wp-content"
|
||||||
|
|
||||||
|
override fun Document.getDescription(): String {
|
||||||
|
return select("$additionalInfoSelector p")
|
||||||
|
.first { !it.text().contains("Título Alternativo") }
|
||||||
|
?.let { it.text() + "\n" }
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Document.getAlternativeTitle(): String {
|
||||||
|
return select("$additionalInfoSelector p")
|
||||||
|
.first { it.text().contains("Título Alternativo") }
|
||||||
|
?.let { it.text() + "\n" }
|
||||||
|
?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun animeDetailsParse(document: Document): SAnime {
|
||||||
|
val doc = getRealAnimeDoc(document)
|
||||||
|
val sheader = doc.selectFirst("div.sheader")!!
|
||||||
|
return SAnime.create().apply {
|
||||||
|
setUrlWithoutDomain(doc.location())
|
||||||
|
sheader.selectFirst("div.poster > img")!!.let {
|
||||||
|
thumbnail_url = it.getImageUrl()
|
||||||
|
title = it.attr("alt").ifEmpty {
|
||||||
|
sheader.selectFirst("div.data > h1")!!.text()
|
||||||
|
}.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
genre = sheader.select("div.data div.sgeneros > a")
|
||||||
|
.eachText()
|
||||||
|
.joinToString()
|
||||||
|
|
||||||
|
// description = doc.getDescription()
|
||||||
|
doc.selectFirst("div#info")?.let { info ->
|
||||||
|
description = buildString {
|
||||||
|
append(doc.getDescription())
|
||||||
|
append(doc.getAlternativeTitle())
|
||||||
|
additionalInfoItems.forEach {
|
||||||
|
info.getInfo(it)?.let(::append)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================ Video Links =============================
|
||||||
|
override fun videoListParse(response: Response): List<Video> {
|
||||||
|
val document = response.asJsoup()
|
||||||
|
val players = document.select("ul#playeroptionsul li")
|
||||||
|
return players.parallelCatchingFlatMapBlocking(::getPlayerVideos)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val prefQualityValues = arrayOf("360p", "720p", "1080p")
|
||||||
|
override val prefQualityEntries = prefQualityValues
|
||||||
|
|
||||||
|
private val bloggerExtractor by lazy { BloggerExtractor(client) }
|
||||||
|
|
||||||
|
private fun getPlayerVideos(player: Element): List<Video> {
|
||||||
|
val name = player.selectFirst("span.title")!!.text()
|
||||||
|
.run {
|
||||||
|
when (this) {
|
||||||
|
"SD" -> "360p"
|
||||||
|
"HD" -> "720p"
|
||||||
|
"SD/HD" -> "720p"
|
||||||
|
"FHD", "FULLHD" -> "1080p"
|
||||||
|
else -> this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val url = getPlayerUrl(player)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
"blogger.com" in url -> bloggerExtractor.videosFromUrl(url, headers)
|
||||||
|
"jwplayer?source=" in url -> {
|
||||||
|
val videoUrl = url.toHttpUrl().queryParameter("source") ?: return emptyList()
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
Video(videoUrl, name, videoUrl, headers),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPlayerUrl(player: Element): String {
|
||||||
|
val type = player.attr("data-type")
|
||||||
|
val id = player.attr("data-post")
|
||||||
|
val num = player.attr("data-nume")
|
||||||
|
return client.newCall(GET("$baseUrl/wp-json/dooplayer/v2/$id/$type/$num"))
|
||||||
|
.execute()
|
||||||
|
.let { response ->
|
||||||
|
response.body.string()
|
||||||
|
.substringAfter("\"embed_url\":\"")
|
||||||
|
.substringBefore("\",")
|
||||||
|
.replace("\\", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================= Utilities ==============================
|
||||||
|
override fun List<Video>.sort(): List<Video> {
|
||||||
|
val quality = preferences.getString(videoSortPrefKey, videoSortPrefDefault)!!
|
||||||
|
return sortedWith(
|
||||||
|
compareBy(
|
||||||
|
{ it.quality.lowercase().contains(quality.lowercase()) },
|
||||||
|
{ REGEX_QUALITY.find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
|
||||||
|
),
|
||||||
|
).reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val REGEX_QUALITY by lazy { Regex("""(\d+)p""") }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue