Initial commit
This commit is contained in:
commit
98ed7e8839
2263 changed files with 108711 additions and 0 deletions
22
src/pt/hentaistube/AndroidManifest.xml
Normal file
22
src/pt/hentaistube/AndroidManifest.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application>
|
||||
<activity
|
||||
android:name=".pt.hentaistube.HentaisTubeUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="www.hentaistube.com"
|
||||
android:pathPattern="/..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
12
src/pt/hentaistube/build.gradle
Normal file
12
src/pt/hentaistube/build.gradle
Normal file
|
@ -0,0 +1,12 @@
|
|||
ext {
|
||||
extName = 'HentaisTube'
|
||||
extClass = '.HentaisTube'
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
||||
dependencies {
|
||||
implementation(project(':lib:blogger-extractor'))
|
||||
}
|
BIN
src/pt/hentaistube/res/mipmap-hdpi/ic_launcher.png
Normal file
BIN
src/pt/hentaistube/res/mipmap-hdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
BIN
src/pt/hentaistube/res/mipmap-mdpi/ic_launcher.png
Normal file
BIN
src/pt/hentaistube/res/mipmap-mdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
BIN
src/pt/hentaistube/res/mipmap-xhdpi/ic_launcher.png
Normal file
BIN
src/pt/hentaistube/res/mipmap-xhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7 KiB |
BIN
src/pt/hentaistube/res/mipmap-xxhdpi/ic_launcher.png
Normal file
BIN
src/pt/hentaistube/res/mipmap-xxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
src/pt/hentaistube/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
BIN
src/pt/hentaistube/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
|
@ -0,0 +1,234 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.hentaistube
|
||||
|
||||
import android.app.Application
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.animeextension.pt.hentaistube.HentaisTubeFilters.applyFilterParams
|
||||
import eu.kanade.tachiyomi.animeextension.pt.hentaistube.dto.ItemsListDto
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
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.lib.bloggerextractor.BloggerExtractor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
|
||||
import eu.kanade.tachiyomi.util.parseAs
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class HentaisTube : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
|
||||
|
||||
override val name = "HentaisTube"
|
||||
|
||||
override val baseUrl = "https://www.hentaistube.com"
|
||||
|
||||
override val lang = "pt-BR"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override fun headersBuilder() = super.headersBuilder()
|
||||
.add("Referer", baseUrl)
|
||||
.add("Origin", baseUrl)
|
||||
|
||||
private val preferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int) = GET("$baseUrl/ranking-hentais?paginacao=$page", headers)
|
||||
|
||||
override fun popularAnimeSelector() = "ul.ul_sidebar > li"
|
||||
|
||||
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
|
||||
thumbnail_url = element.selectFirst("img")?.attr("src")
|
||||
element.selectFirst("div.rt a.series")!!.run {
|
||||
setUrlWithoutDomain(attr("href"))
|
||||
title = text().substringBefore(" - Episódios")
|
||||
}
|
||||
}
|
||||
|
||||
override fun popularAnimeNextPageSelector() = "div.paginacao > a:contains(»)"
|
||||
|
||||
// =============================== Latest ===============================
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/page/$page/", headers)
|
||||
|
||||
override fun latestUpdatesSelector() = "div.epiContainer:first-child div.epiItem > a"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href").substringBeforeLast("-") + "s")
|
||||
title = element.attr("title")
|
||||
thumbnail_url = element.selectFirst("img")?.attr("src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularAnimeNextPageSelector()
|
||||
|
||||
// =============================== Search ===============================
|
||||
private val animeList by lazy {
|
||||
val headers = headersBuilder().add("X-Requested-With", "XMLHttpRequest").build()
|
||||
client.newCall(GET("$baseUrl/json-lista-capas.php", headers)).execute()
|
||||
.parseAs<ItemsListDto>().items
|
||||
.asSequence()
|
||||
}
|
||||
|
||||
override suspend fun getSearchAnime(page: Int, query: String, filters: AnimeFilterList): AnimesPage {
|
||||
return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
|
||||
val id = query.removePrefix(PREFIX_SEARCH)
|
||||
client.newCall(GET("$baseUrl/$id"))
|
||||
.awaitSuccess()
|
||||
.use(::searchAnimeByIdParse)
|
||||
} else {
|
||||
val params = HentaisTubeFilters.getSearchParameters(filters).apply {
|
||||
animeName = query
|
||||
}
|
||||
val filtered = animeList.applyFilterParams(params)
|
||||
val results = filtered.chunked(30).toList()
|
||||
val hasNextPage = results.size > page
|
||||
val currentPage = if (results.size == 0) {
|
||||
emptyList<SAnime>()
|
||||
} else {
|
||||
results.get(page - 1).map {
|
||||
SAnime.create().apply {
|
||||
title = it.title.substringBefore("- Episódios")
|
||||
url = "/" + it.url
|
||||
thumbnail_url = it.thumbnail
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimesPage(currentPage, hasNextPage)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = HentaisTubeFilters.FILTER_LIST
|
||||
|
||||
private fun searchAnimeByIdParse(response: Response): AnimesPage {
|
||||
val details = animeDetailsParse(response.asJsoup())
|
||||
return AnimesPage(listOf(details), false)
|
||||
}
|
||||
|
||||
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun searchAnimeSelector(): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun searchAnimeFromElement(element: Element): SAnime {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun searchAnimeNextPageSelector(): String? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
// =========================== Anime Details ============================
|
||||
override fun animeDetailsParse(document: Document) = SAnime.create().apply {
|
||||
setUrlWithoutDomain(document.location())
|
||||
val infos = document.selectFirst("div#anime")!!
|
||||
thumbnail_url = infos.selectFirst("img")?.attr("src")
|
||||
title = infos.getInfo("Hentai:")
|
||||
genre = infos.getInfo("Tags")
|
||||
artist = infos.getInfo("Estúdio")
|
||||
description = infos.selectFirst("div#sinopse2")?.text().orEmpty()
|
||||
}
|
||||
|
||||
// ============================== Episodes ==============================
|
||||
override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed()
|
||||
|
||||
override fun episodeListSelector() = "ul.pagAniListaContainer > li > a"
|
||||
|
||||
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
|
||||
setUrlWithoutDomain(element.attr("href"))
|
||||
name = element.text()
|
||||
episode_number = element.text().substringAfter(" ").toFloatOrNull() ?: 1F
|
||||
}
|
||||
|
||||
// ============================ Video Links =============================
|
||||
override fun videoListParse(response: Response): List<Video> {
|
||||
return response.asJsoup().select(videoListSelector())
|
||||
.parallelCatchingFlatMapBlocking {
|
||||
client.newCall(GET(it.attr("src"), headers)).await().let { res ->
|
||||
extractVideosFromIframe(res.asJsoup())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val bloggerExtractor by lazy { BloggerExtractor(client) }
|
||||
|
||||
private fun extractVideosFromIframe(iframe: Document): List<Video> {
|
||||
val url = iframe.location()
|
||||
return when {
|
||||
url.contains("/hd.php") -> {
|
||||
val video = iframe.selectFirst("video > source")!!
|
||||
val videoUrl = video.attr("src")
|
||||
val quality = video.attr("label").ifEmpty { "Unknown" }
|
||||
listOf(Video(videoUrl, "Principal - $quality", videoUrl, headers))
|
||||
}
|
||||
url.contains("/index.php") -> {
|
||||
val bloggerUrl = iframe.selectFirst("iframe")!!.attr("src")
|
||||
bloggerExtractor.videosFromUrl(bloggerUrl, headers)
|
||||
}
|
||||
url.contains("/player.php") -> {
|
||||
val ahref = iframe.selectFirst("a")!!.attr("href")
|
||||
val internal = client.newCall(GET(ahref, headers)).execute().asJsoup()
|
||||
val videoUrl = internal.selectFirst("video > source")!!.attr("src")
|
||||
listOf(Video(videoUrl, "Alternativo", videoUrl, headers))
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
override fun videoListSelector() = "iframe.meu-player"
|
||||
|
||||
override fun videoFromElement(element: Element): Video {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun videoUrlParse(document: Document): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
// ============================== Settings ==============================
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_QUALITY_KEY
|
||||
title = PREF_QUALITY_TITLE
|
||||
entries = PREF_QUALITY_ENTRIES
|
||||
entryValues = PREF_QUALITY_ENTRIES
|
||||
setDefaultValue(PREF_QUALITY_DEFAULT)
|
||||
summary = "%s"
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
// ============================= Utilities ==============================
|
||||
private fun Element.getInfo(key: String): String =
|
||||
select("div.boxAnimeSobreLinha:has(b:contains($key)) > a")
|
||||
.eachText()
|
||||
.joinToString()
|
||||
|
||||
override fun List<Video>.sort(): List<Video> {
|
||||
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
|
||||
return sortedWith(
|
||||
compareBy { it.quality.contains(quality) },
|
||||
).reversed()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PREFIX_SEARCH = "id:"
|
||||
|
||||
private const val PREF_QUALITY_KEY = "preferred_quality"
|
||||
private const val PREF_QUALITY_TITLE = "Qualidade preferida"
|
||||
private const val PREF_QUALITY_DEFAULT = "720p"
|
||||
private val PREF_QUALITY_ENTRIES = arrayOf("360p", "720p")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,349 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.hentaistube
|
||||
|
||||
import eu.kanade.tachiyomi.animeextension.pt.hentaistube.dto.SearchItemDto
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter.TriState
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
|
||||
object HentaisTubeFilters {
|
||||
|
||||
open class QueryPartFilter(
|
||||
displayName: String,
|
||||
val vals: Array<Pair<String, String>>,
|
||||
) : AnimeFilter.Select<String>(
|
||||
displayName,
|
||||
vals.map { it.first }.toTypedArray(),
|
||||
) {
|
||||
fun toQueryPart() = vals[state].second
|
||||
}
|
||||
|
||||
open class TriStateFilterList(name: String, val vals: Array<String>) :
|
||||
AnimeFilter.Group<TriState>(name, vals.map(::TriStateVal))
|
||||
|
||||
private class TriStateVal(name: String) : TriState(name)
|
||||
|
||||
private inline fun <reified R> AnimeFilterList.getFirst(): R {
|
||||
return first { it is R } as R
|
||||
}
|
||||
|
||||
private inline fun <reified R> AnimeFilterList.asQueryPart(): String {
|
||||
return (getFirst<R>() as QueryPartFilter).toQueryPart()
|
||||
}
|
||||
|
||||
private inline fun <reified R> AnimeFilterList.parseTriFilter(): List<List<String>> {
|
||||
return (getFirst<R>() as TriStateFilterList).state
|
||||
.filterNot { it.isIgnored() }
|
||||
.map { filter -> filter.state to filter.name }
|
||||
.groupBy { it.first } // group by state
|
||||
.let { dict ->
|
||||
val included = dict.get(TriState.STATE_INCLUDE)?.map { it.second }.orEmpty()
|
||||
val excluded = dict.get(TriState.STATE_EXCLUDE)?.map { it.second }.orEmpty()
|
||||
listOf(included, excluded)
|
||||
}
|
||||
}
|
||||
|
||||
class InitialLetterFilter : QueryPartFilter("Primeira letra", HentaisTubeFiltersData.INITIAL_LETTER)
|
||||
|
||||
class SortFilter : AnimeFilter.Sort(
|
||||
"Ordem",
|
||||
arrayOf("Alfabética"),
|
||||
Selection(0, true),
|
||||
)
|
||||
|
||||
class GenresFilter : TriStateFilterList("Gêneros", HentaisTubeFiltersData.GENRES)
|
||||
class StudiosFilter : TriStateFilterList("Estúdios", HentaisTubeFiltersData.STUDIOS)
|
||||
|
||||
val FILTER_LIST get() = AnimeFilterList(
|
||||
InitialLetterFilter(),
|
||||
SortFilter(),
|
||||
|
||||
AnimeFilter.Separator(),
|
||||
GenresFilter(),
|
||||
StudiosFilter(),
|
||||
)
|
||||
|
||||
data class FilterSearchParams(
|
||||
val initialLetter: String = "",
|
||||
val orderAscending: Boolean = true,
|
||||
val blackListedGenres: List<String> = emptyList(),
|
||||
val includedGenres: List<String> = emptyList(),
|
||||
val blackListedStudios: List<String> = emptyList(),
|
||||
val includedStudios: List<String> = emptyList(),
|
||||
var animeName: String = "",
|
||||
)
|
||||
|
||||
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
|
||||
if (filters.isEmpty()) return FilterSearchParams()
|
||||
|
||||
val isAscending = filters.getFirst<SortFilter>().state?.ascending ?: false
|
||||
|
||||
val (includedGenres, excludedGenres) = filters.parseTriFilter<GenresFilter>()
|
||||
val (includedStudios, excludedStudios) = filters.parseTriFilter<StudiosFilter>()
|
||||
|
||||
return FilterSearchParams(
|
||||
initialLetter = filters.asQueryPart<InitialLetterFilter>(),
|
||||
orderAscending = isAscending,
|
||||
blackListedGenres = excludedGenres,
|
||||
includedGenres = includedGenres,
|
||||
blackListedStudios = excludedStudios,
|
||||
includedStudios = includedStudios,
|
||||
)
|
||||
}
|
||||
|
||||
private fun mustRemove(anime: SearchItemDto, params: FilterSearchParams): Boolean {
|
||||
return when {
|
||||
params.animeName != "" && !anime.title.contains(params.animeName, true) -> true
|
||||
params.initialLetter != "" && !anime.title.lowercase().startsWith(params.initialLetter) -> true
|
||||
params.blackListedGenres.size > 0 && params.blackListedGenres.any {
|
||||
anime.tags.contains(it, true)
|
||||
} -> true
|
||||
params.includedGenres.size > 0 && params.includedGenres.any {
|
||||
!anime.tags.contains(it, true)
|
||||
} -> true
|
||||
params.blackListedStudios.size > 0 && params.blackListedStudios.any {
|
||||
anime.studios.contains(it, true)
|
||||
} -> true
|
||||
params.includedStudios.size > 0 && params.includedStudios.any {
|
||||
!anime.studios.contains(it, true)
|
||||
} -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun <T, R : Comparable<R>> Sequence<T>.sortedByIf(
|
||||
isAscending: Boolean,
|
||||
crossinline selector: (T) -> R,
|
||||
): Sequence<T> {
|
||||
return when {
|
||||
isAscending -> sortedBy(selector)
|
||||
else -> sortedByDescending(selector)
|
||||
}
|
||||
}
|
||||
|
||||
fun Sequence<SearchItemDto>.applyFilterParams(params: FilterSearchParams): Sequence<SearchItemDto> {
|
||||
return filterNot { mustRemove(it, params) }
|
||||
.sortedByIf(params.orderAscending) { it.title.lowercase() }
|
||||
}
|
||||
|
||||
private object HentaisTubeFiltersData {
|
||||
val INITIAL_LETTER = arrayOf(Pair("Selecione", "")) + ('A'..'Z').map {
|
||||
Pair(it.toString(), it.toString().lowercase())
|
||||
}.toTypedArray()
|
||||
|
||||
val GENRES = arrayOf(
|
||||
"Anal",
|
||||
"Aventura",
|
||||
"Boquete",
|
||||
"Brinquedos",
|
||||
"Comédia",
|
||||
"Dark Skin",
|
||||
"Demônios",
|
||||
"Ecchi",
|
||||
"Elfos",
|
||||
"Empregada",
|
||||
"Enfermeira",
|
||||
"Esporte",
|
||||
"Estupro",
|
||||
"Ficção",
|
||||
"Futanari",
|
||||
"Gay",
|
||||
"Harém",
|
||||
"Hospital",
|
||||
"Incesto",
|
||||
"Lactante",
|
||||
"Lolicon",
|
||||
"Magia",
|
||||
"Masturbação",
|
||||
"Milf",
|
||||
"Mistério",
|
||||
"Monstros",
|
||||
"Médico",
|
||||
"Netorare",
|
||||
"Ninjas",
|
||||
"Orgia",
|
||||
"Peitões",
|
||||
"Policial",
|
||||
"Professora",
|
||||
"Romance",
|
||||
"Shotacon",
|
||||
"Submissão",
|
||||
"Super Poderes",
|
||||
"Tentáculos",
|
||||
"Terror",
|
||||
"Tetas",
|
||||
"Travesti",
|
||||
"Vampiros",
|
||||
"Vida Escolar",
|
||||
"Virgem",
|
||||
"Yaoi",
|
||||
"Yuri",
|
||||
)
|
||||
|
||||
val STUDIOS = arrayOf(
|
||||
"3D Pix",
|
||||
"A1",
|
||||
"AIC",
|
||||
"APPP",
|
||||
"AT2",
|
||||
"Actas",
|
||||
"Active",
|
||||
"Agent 21",
|
||||
"Alice Soft",
|
||||
"Amam",
|
||||
"Amumo",
|
||||
"Angelfish",
|
||||
"AniMan",
|
||||
"Animac",
|
||||
"Animate Film",
|
||||
"Anime Antenna Iinkai",
|
||||
"Animopron",
|
||||
"Antechinus",
|
||||
"Arms",
|
||||
"Asahi Production",
|
||||
"BOMB! CUTE! BOMB!",
|
||||
"BOOTLEG",
|
||||
"Blue Cat",
|
||||
"Blue Eyes",
|
||||
"Bootleg",
|
||||
"BreakBottle",
|
||||
"Bunnywalker",
|
||||
"CLOCKUP",
|
||||
"Central Park Media",
|
||||
"Chaos Project",
|
||||
"Cherry Lips",
|
||||
"ChiChinoya",
|
||||
"Chippai",
|
||||
"Chocolat",
|
||||
"ChuChu",
|
||||
"Cinema Paradise",
|
||||
"Circle Tribute",
|
||||
"Collaboration Works",
|
||||
"Comic Media",
|
||||
"Cosmo",
|
||||
"Cotton Doll",
|
||||
"Cranberry",
|
||||
"Crimson",
|
||||
"D3",
|
||||
"Daiei",
|
||||
"Deep Forest",
|
||||
"Digital Works",
|
||||
"Discovery",
|
||||
"Dream Force",
|
||||
"Dreamroom",
|
||||
"EDGE",
|
||||
"Easy Film",
|
||||
"Echo",
|
||||
"Erozuki",
|
||||
"Fan",
|
||||
"Fans",
|
||||
"Filmlink International",
|
||||
"Five Ways",
|
||||
"Flavors Soft",
|
||||
"Front Line",
|
||||
"Frontier Works",
|
||||
"Frontline",
|
||||
"Game 3D",
|
||||
"GeG Entertainment",
|
||||
"Gold Bear",
|
||||
"Goldenboy",
|
||||
"Green Bunny",
|
||||
"Himajin Planning",
|
||||
"Hoods Entertainment",
|
||||
"Horipro",
|
||||
"Hot Bear",
|
||||
"IMK",
|
||||
"Innocent Grey",
|
||||
"J.C.Staff",
|
||||
"Jam",
|
||||
"JapanAnime",
|
||||
"KSS",
|
||||
"Kadokawa Shoten",
|
||||
"King Bee",
|
||||
"Kitty Media",
|
||||
"Knack Productions",
|
||||
"Knack",
|
||||
"Kusama Art",
|
||||
"L.",
|
||||
"Leaf",
|
||||
"Lemon Heart",
|
||||
"Liberty Ship",
|
||||
"Lune Pictures",
|
||||
"MS Pictures",
|
||||
"Majin",
|
||||
"Marvelous Entertainment",
|
||||
"Mary Jane",
|
||||
"Media Blasters",
|
||||
"Media Station",
|
||||
"Metro Notes",
|
||||
"Milkshake",
|
||||
"Milky",
|
||||
"Mitsu",
|
||||
"Moonrock",
|
||||
"Moonstone Cherry",
|
||||
"Mousou Senka",
|
||||
"Museum Pictures",
|
||||
"Nihikime no Dozeu",
|
||||
"NuTech Digital",
|
||||
"Obtain Future",
|
||||
"Office Take Off",
|
||||
"Orbit",
|
||||
"Orc Soft",
|
||||
"Original Work",
|
||||
"Otodeli",
|
||||
"Oz Inc",
|
||||
"Oz",
|
||||
"Pashmina",
|
||||
"Peachpie",
|
||||
"Phoenix Entertainment",
|
||||
"Pix",
|
||||
"Pixy",
|
||||
"PoRO",
|
||||
"Poly Animation",
|
||||
"Poro",
|
||||
"Pìnk Pineapple",
|
||||
"Queen Bee",
|
||||
"SELFISH",
|
||||
"Sakura Purin Animation",
|
||||
"Schoolzone",
|
||||
"Seisei",
|
||||
"Selfish",
|
||||
"Seven",
|
||||
"Shaft",
|
||||
"Shelf",
|
||||
"Shinkuukan",
|
||||
"Shinyusha",
|
||||
"Shouten",
|
||||
"Showten",
|
||||
"Silky’s",
|
||||
"SoftCel Pictures",
|
||||
"SoftCell Pictures",
|
||||
"Sonsan Kikaku",
|
||||
"Speed",
|
||||
"Studio 9 Maiami",
|
||||
"Studio Eromatick",
|
||||
"Studio Fantasia",
|
||||
"Studio Jack",
|
||||
"Studio Sign",
|
||||
"Studio Tulip",
|
||||
"Studio Unicorn",
|
||||
"Sugar Boy",
|
||||
"Suzuki Mirano",
|
||||
"T-Rex",
|
||||
"TDK Core",
|
||||
"Toho Company",
|
||||
"Top Marschal",
|
||||
"Toranoana",
|
||||
"Triple X",
|
||||
"Tufos",
|
||||
"Union Cho",
|
||||
"Ursaite",
|
||||
"Valkyria",
|
||||
"White Bear",
|
||||
"Works",
|
||||
"YOUC",
|
||||
"ZIZ Entertainment",
|
||||
"ZIZ",
|
||||
"Zealot",
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.hentaistube
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Springboard that accepts https://www.hentaistube.com/<item> intents
|
||||
* and redirects them to the main Aniyomi process.
|
||||
*/
|
||||
class HentaisTubeUrlActivity : Activity() {
|
||||
|
||||
private val tag = javaClass.simpleName
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 0) {
|
||||
val item = pathSegments[0]
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.ANIMESEARCH"
|
||||
putExtra("query", "${HentaisTube.PREFIX_SEARCH}$item")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(tag, e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e(tag, "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package eu.kanade.tachiyomi.animeextension.pt.hentaistube.dto
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ItemsListDto(
|
||||
@SerialName("encontrado")
|
||||
val items: List<SearchItemDto>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SearchItemDto(
|
||||
@SerialName("titulo") val title: String,
|
||||
@SerialName("imagem") val thumbnail: String,
|
||||
@SerialName("estudio") val studios: String,
|
||||
val url: String,
|
||||
val tags: String,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue