Initial commit

This commit is contained in:
almightyhak 2024-06-20 11:54:12 +07:00
commit 98ed7e8839
2263 changed files with 108711 additions and 0 deletions

View 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=".tr.anizm.AnizmUrlActivity"
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="anizm.net"
android:pathPattern="/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

22
src/tr/anizm/build.gradle Normal file
View file

@ -0,0 +1,22 @@
ext {
extName = 'Anizm'
extClass = '.Anizm'
extVersionCode = 20
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:playlist-utils"))
implementation(project(":lib:dood-extractor"))
implementation(project(":lib:filemoon-extractor"))
implementation(project(":lib:gdriveplayer-extractor"))
implementation(project(":lib:mp4upload-extractor"))
implementation(project(":lib:okru-extractor"))
implementation(project(":lib:sendvid-extractor"))
implementation(project(":lib:sibnet-extractor"))
implementation(project(":lib:streamtape-extractor"))
implementation(project(':lib:uqload-extractor'))
implementation(project(":lib:voe-extractor"))
implementation(project(":lib:yourupload-extractor"))
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,454 @@
package eu.kanade.tachiyomi.animeextension.tr.anizm
import android.app.Application
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.tr.anizm.AnizmFilters.applyFilterParams
import eu.kanade.tachiyomi.animeextension.tr.anizm.extractors.AincradExtractor
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.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.gdriveplayerextractor.GdrivePlayerExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.sendvidextractor.SendvidExtractor
import eu.kanade.tachiyomi.lib.sibnetextractor.SibnetExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.uqloadextractor.UqloadExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor
import eu.kanade.tachiyomi.network.GET
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 kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Elements
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class Anizm : ParsedAnimeHttpSource(), ConfigurableAnimeSource {
override val name = "Anizm"
override val baseUrl = "https://anizm.net"
override val lang = "tr"
override val supportsLatest = true
override fun headersBuilder() = super.headersBuilder()
.add("Origin", baseUrl)
.add("Referer", "$baseUrl/")
private val json: Json by injectLazy()
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ============================== Popular ===============================
override fun popularAnimeRequest(page: Int) = GET(baseUrl, headers)
override fun popularAnimeSelector() = "div.popularAnimeCarousel a.slideAnimeLink"
override fun popularAnimeFromElement(element: Element) = SAnime.create().apply {
title = element.selectFirst(".title")!!.text()
thumbnail_url = element.selectFirst("img")!!.attr("src")
element.attr("href")
.substringBefore("-bolum-izle")
.substringBeforeLast("-")
.also { setUrlWithoutDomain(it) }
}
override fun popularAnimeNextPageSelector() = null
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/anime-izle?sayfa=$page", headers)
override fun latestUpdatesSelector() = "div#episodesMiddle div.posterBlock > a"
override fun latestUpdatesFromElement(element: Element) = popularAnimeFromElement(element)
override fun latestUpdatesNextPageSelector() = "div.nextBeforeButtons > div.ui > a.right:not(.disabled)"
// =============================== Search ===============================
private val animeList by lazy {
client.newCall(GET("$baseUrl/getAnimeListForSearch", headers)).execute()
.parseAs<List<SearchItemDto>>()
.asSequence()
}
override fun getFilterList(): AnimeFilterList = AnizmFilters.FILTER_LIST
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 = AnizmFilters.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
url = "/" + it.slug
thumbnail_url = baseUrl + "/storage/pcovers/" + it.thumbnail
}
}
}
AnimesPage(currentPage, hasNextPage)
}
}
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())
title = document.selectFirst("h2.anizm_pageTitle")!!.text()
thumbnail_url = document.selectFirst("div.infoPosterImg > img")!!.attr("abs:src")
val infosDiv = document.selectFirst("div.anizm_boxContent")!!
genre = infosDiv.select("span.dataValue > span.tag > span.label").eachText().joinToString()
artist = infosDiv.selectFirst("span.dataTitle:contains(Stüdyo) + span")?.text()
description = buildString {
infosDiv.selectFirst("div.infoDesc")?.text()?.also(::append)
infosDiv.select("li.dataRow:not(:has(span.ui.tag)):not(:has(div.star)) > span")
.forEach {
when {
it.hasClass("dataTitle") -> append("\n${it.text()}: ")
else -> append(it.text())
}
}
}
}
// ============================== Episodes ==============================
override fun episodeListParse(response: Response) = super.episodeListParse(response).reversed()
override fun episodeListSelector() = "div.episodeListTabContent div > a"
override fun episodeFromElement(element: Element) = SEpisode.create().apply {
setUrlWithoutDomain(element.attr("href"))
episode_number = element.text().filter(Char::isDigit).toFloatOrNull() ?: 1F
name = element.text()
}
// ============================ Video Links =============================
@Serializable
data class ResponseDto(val data: String)
override fun videoListParse(response: Response): List<Video> {
val doc = response.asJsoup()
val fansubUrls = doc.select("div#fansec > a")
.filterSubs()
.map { it.text().fixedFansubName() to it.attr("translator") }
.ifEmpty {
throw Exception("No fansubs available! Have you filtered them out?")
}
val chosenHosts = preferences.getStringSet(PREF_HOSTS_SELECTION_KEY, PREF_HOSTS_SELECTION_DEFAULT)!!
val playerUrls = fansubUrls.flatMap { pair ->
val (fansub, url) = pair
runCatching {
client.newCall(GET(url, headers)).execute()
.parseAs<ResponseDto>()
.data
.let(Jsoup::parse)
.select("a.videoPlayerButtons")
.toList()
.filter { host ->
val hostName = host.text().trim()
chosenHosts.any { hostName.contains(it, true) }
}
.map { fansub to it.attr("video").replace("/video/", "/player/") }
}.getOrElse { emptyList() }
}
return playerUrls.parallelCatchingFlatMapBlocking { pair ->
val (fansub, url) = pair
getVideosFromUrl(url).map {
Video(
it.url,
"[$fansub] ${it.quality}",
it.videoUrl,
it.headers,
it.subtitleTracks,
it.audioTracks,
)
}
}
}
private val noRedirectClient by lazy {
client.newBuilder().followRedirects(false).build()
}
private val aincradExtractor by lazy { AincradExtractor(client, headers, json) }
private val doodExtractor by lazy { DoodExtractor(client) }
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val gdrivePlayerExtractor by lazy { GdrivePlayerExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private val okruExtractor by lazy { OkruExtractor(client) }
private val sendvidExtractor by lazy { SendvidExtractor(client, headers) }
private val sibnetExtractor by lazy { SibnetExtractor(client) }
private val streamtapeExtractor by lazy { StreamTapeExtractor(client) }
private val uqloadExtractor by lazy { UqloadExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val yourUploadExtractor by lazy { YourUploadExtractor(client) }
private fun getVideosFromUrl(firstUrl: String): List<Video> {
val url = noRedirectClient.newCall(GET(firstUrl, headers)).execute()
.use { it.headers["location"] }
?: return emptyList()
return when {
"filemoon.sx" in url -> filemoonExtractor.videosFromUrl(url, headers = headers)
"sendvid.com" in url -> sendvidExtractor.videosFromUrl(url)
"video.sibnet" in url -> sibnetExtractor.videosFromUrl(url)
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, headers)
"ok.ru" in url || "odnoklassniki.ru" in url -> okruExtractor.videosFromUrl(url)
"yourupload" in url -> yourUploadExtractor.videoFromUrl(url, headers)
"streamtape" in url -> streamtapeExtractor.videoFromUrl(url)?.let(::listOf)
"dood" in url -> doodExtractor.videoFromUrl(url)?.let(::listOf)
"drive.google" in url -> {
val newUrl = "https://gdriveplayer.to/embed2.php?link=$url"
gdrivePlayerExtractor.videosFromUrl(newUrl, "GdrivePlayer", headers)
}
"uqload" in url -> uqloadExtractor.videosFromUrl(url)
"voe.sx" in url -> voeExtractor.videosFromUrl(url)
"anizmplayer.com" in url -> aincradExtractor.videosFromUrl(url)
else -> null
} ?: emptyList()
}
override fun videoListSelector(): String {
throw UnsupportedOperationException()
}
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_VALUES
setDefaultValue(PREF_QUALITY_DEFAULT)
summary = "%s"
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit().putString(key, entry).commit()
}
}.also(screen::addPreference)
MultiSelectListPreference(screen.context).apply {
key = PREF_FANSUB_SELECTION_KEY
title = PREF_FANSUB_SELECTION_TITLE
PREF_FANSUB_SELECTION_ENTRIES.let {
entries = it
entryValues = it
setDefaultValue(it.toSet())
}
setOnPreferenceChangeListener { _, newValue ->
@Suppress("UNCHECKED_CAST")
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}.also(screen::addPreference)
EditTextPreference(screen.context).apply {
key = PREF_ADDITIONAL_FANSUBS_KEY
title = PREF_ADDITIONAL_FANSUBS_TITLE
dialogTitle = PREF_ADDITIONAL_FANSUBS_DIALOG_TITLE
dialogMessage = PREF_ADDITIONAL_FANSUBS_DIALOG_MESSAGE
setDefaultValue(PREF_ADDITIONAL_FANSUBS_DEFAULT)
summary = PREF_ADDITIONAL_FANSUBS_SUMMARY
setOnPreferenceChangeListener { _, newValue ->
runCatching {
val value = newValue as String
Toast.makeText(screen.context, PREF_ADDITIONAL_FANSUBS_TOAST, Toast.LENGTH_LONG).show()
preferences.edit().putString(key, value).commit()
}.getOrDefault(false)
}
}.also(screen::addPreference)
MultiSelectListPreference(screen.context).apply {
key = PREF_HOSTS_SELECTION_KEY
title = PREF_HOSTS_SELECTION_TITLE
entries = PREF_HOSTS_SELECTION_ENTRIES
entryValues = PREF_HOSTS_SELECTION_ENTRIES
setDefaultValue(PREF_HOSTS_SELECTION_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
@Suppress("UNCHECKED_CAST")
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}.also(screen::addPreference)
}
// ============================= Utilities ==============================
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString(PREF_QUALITY_KEY, PREF_QUALITY_DEFAULT)!!
return sortedWith(
compareBy(
{ it.quality.contains(quality) }, // preferred quality first
{ it.quality.substringBefore("]") }, // then group by fansub
// then group by quality
{ Regex("""(\d+)p""").find(it.quality)?.groupValues?.get(1)?.toIntOrNull() ?: 0 },
),
).reversed()
}
private fun String.fixedFansubName(): String =
substringBefore("- BD")
.substringBefore("Fansub")
.substringBefore("Bağımsız")
.trim()
private fun Elements.filterSubs(): List<Element> {
val allFansubs = PREF_FANSUB_SELECTION_ENTRIES
val chosenFansubs = preferences.getStringSet(PREF_FANSUB_SELECTION_KEY, allFansubs.toSet())!!
return toList().filter {
val text = it.text().fixedFansubName()
text in chosenFansubs || text !in allFansubs
}
}
private val PREF_FANSUB_SELECTION_ENTRIES: Array<String> get() {
val additional = preferences.getString(PREF_ADDITIONAL_FANSUBS_KEY, "")!!
.split(",")
.map { it.fixedFansubName() }
.filter(String::isNotBlank)
.toSet()
return (DEFAULT_FANSUBS + additional).sorted().toTypedArray()
}
companion object {
const val PREFIX_SEARCH = "id:"
private const val PREF_QUALITY_KEY = "pref_quality_key"
private const val PREF_QUALITY_TITLE = "Preferred quality"
private const val PREF_QUALITY_DEFAULT = "720p"
private val PREF_QUALITY_ENTRIES = arrayOf("1080p", "720p", "480p", "360p")
private val PREF_QUALITY_VALUES = PREF_QUALITY_ENTRIES
private const val PREF_FANSUB_SELECTION_KEY = "pref_fansub_selection"
private const val PREF_FANSUB_SELECTION_TITLE = "Enable/Disable Fansubs"
private val DEFAULT_FANSUBS by lazy {
setOf(
"Adonis",
"Akatsuki",
"AnimeSeverler",
"AniSekai",
"Aoi",
"ARE-YOU-SURE",
"ÇeviriBükücüler",
"DeiraSubs",
"Güncellenecek",
"hitokirireaper",
"Holy",
"Lawsonia",
"LoliSubs",
"LowSubs",
"Magnum357",
"NaoSubs",
"Origami",
"PijamalıKoi",
"Tempest",
"UragiriSubs",
"whosgoodbadass",
"Yuki",
"YuushaSubs",
)
}
private const val PREF_ADDITIONAL_FANSUBS_KEY = "pref_additional_fansubs_key"
private const val PREF_ADDITIONAL_FANSUBS_TITLE = "Add custom fansubs to the selection preference"
private const val PREF_ADDITIONAL_FANSUBS_DEFAULT = ""
private const val PREF_ADDITIONAL_FANSUBS_DIALOG_TITLE = "Enter a list of additional fansubs, separated by a comma."
private const val PREF_ADDITIONAL_FANSUBS_DIALOG_MESSAGE = "Example: AntichristHaters Fansub, 2cm erect subs"
private const val PREF_ADDITIONAL_FANSUBS_SUMMARY = "You can add more fansubs to the previous preference from here."
private const val PREF_ADDITIONAL_FANSUBS_TOAST = "Reopen the extension's preferences for it to take effect."
private const val PREF_HOSTS_SELECTION_KEY = "pref_hosts_selection"
private const val PREF_HOSTS_SELECTION_TITLE = "Disable/enable video hosts"
private val PREF_HOSTS_SELECTION_ENTRIES = arrayOf(
"Aincrad",
"DoodStream",
"FileMoon",
"GDrive",
"MP4Upload",
"Odnoklassniki",
"SendVid",
"Sibnet",
"StreamTape",
"UQload",
"Voe",
"YourUpload",
)
private val PREF_HOSTS_SELECTION_DEFAULT by lazy { PREF_HOSTS_SELECTION_ENTRIES.toSet() }
}
}

View file

@ -0,0 +1,551 @@
package eu.kanade.tachiyomi.animeextension.tr.anizm
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilter.TriState
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
object AnizmFilters {
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 {
val included = it.get(TriState.STATE_INCLUDE)?.map { it.second } ?: emptyList<String>()
val excluded = it.get(TriState.STATE_EXCLUDE)?.map { it.second } ?: emptyList<String>()
listOf(included, excluded)
}
}
class InitialLetterFilter : QueryPartFilter("İlk harf", AnizmFiltersData.INITIAL_LETTER)
class SortFilter : AnimeFilter.Sort(
"Sıra",
AnizmFiltersData.ORDERS.map { it.first }.toTypedArray(),
Selection(0, true),
)
class StudiosFilter : TriStateFilterList("Stüdyos", AnizmFiltersData.STUDIOS)
val FILTER_LIST get() = AnimeFilterList(
InitialLetterFilter(),
SortFilter(),
AnimeFilter.Separator(),
StudiosFilter(),
)
data class FilterSearchParams(
val initialLetter: String = "",
val sortBy: String = "A-Z",
val orderAscending: Boolean = true,
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 (order, isAscending) = filters.getFirst<SortFilter>().state?.let {
Pair(AnizmFiltersData.ORDERS[it.index].second, it.ascending)
} ?: Pair("A-Z", true)
val (includedStudios, excludedStudios) = filters.parseTriFilter<StudiosFilter>()
return FilterSearchParams(
initialLetter = filters.asQueryPart<InitialLetterFilter>(),
sortBy = order,
orderAscending = isAscending,
blackListedStudios = excludedStudios,
includedStudios = includedStudios,
)
}
private fun mustRemove(anime: SearchItemDto, params: FilterSearchParams): Boolean {
return when {
params.animeName != "" && !anime.names.any { it.contains(params.animeName, true) } -> true
params.initialLetter != "" && !anime.title.lowercase().startsWith(params.initialLetter) -> true
params.blackListedStudios.size > 0 && params.blackListedStudios.any {
anime.studios?.contains(it, true) == true
} -> true
params.includedStudios.size > 0 && params.includedStudios.any {
anime.studios?.contains(it, true)?.not() == 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) }.let { results ->
when (params.sortBy) {
"A-Z" -> results.sortedByIf(params.orderAscending) { it.title.lowercase() }
"year" -> results.sortedByIf(params.orderAscending) { it.year?.toIntOrNull() ?: 0 }
"mal" -> results.sortedByIf(params.orderAscending) { it.malpoint ?: 0.0 }
else -> results
}
}
}
private object AnizmFiltersData {
val INITIAL_LETTER = arrayOf(Pair("Select", "")) + ('A'..'Z').map {
Pair(it.toString(), it.toString().lowercase())
}.toTypedArray()
val ORDERS = arrayOf(
Pair("Alfabetik sıra", "A-Z"),
Pair("Yapım Yılı", "year"),
Pair("MAL Score", "mal"),
)
val STUDIOS = arrayOf(
"2:10 AM Animation",
"3xCube",
"5 Inc.",
"8bit",
"A-1 Pictures",
"A-Real",
"A.C.G.T.",
"AHA Entertainment",
"AIC",
"APPP",
"AQUA ARIS",
"ARECT",
"ASK Animation Studio",
"AXsiZ",
"Acca effe",
"Actas",
"Adonero",
"Agent 21",
"Ajia-Do",
"Akatsuki",
"Albacrow",
"Alfred Imageworks",
"Anima",
"Anima&Co.",
"Animate Film",
"Animation Do",
"Anime Beans",
"Anpro",
"Ark",
"Arms",
"Artland",
"Artmic",
"Arvo Animation",
"Asahi Production",
"Ascension",
"Ashi Production",
"Asread",
"Asread.",
"AtelierPontdarc",
"B.CMAY PICTURES",
"BUG FILMS",
"Bakken Record",
"Bandai Namco Pictures",
"Barnum Studio",
"BeSTACK",
"Bee Media",
"Bee Train",
"Bibury Animation CG",
"Bibury Animation Studios",
"BigFireBird Animation",
"Blade",
"Bones",
"Brain's Base",
"Bridge",
"C-Station",
"C2C",
"CANDY BOX",
"CG Year",
"CGCG Studio",
"CLAP",
"Chaos Project",
"Charaction",
"Children's Playground Entertainment",
"CloverWorks",
"CoMix Wave Films",
"Code",
"Colored Pencil Animation",
"Colored Pencil Animation Japan",
"Connect",
"Craftar Studios",
"Creators in Pack",
"Cyclone Graphics",
"CygamesPictures",
"DLE",
"DMM.futureworks",
"DR Movie",
"DRAWIZ",
"Da Huoniao Donghua",
"Dai-Ichi Douga",
"DandeLion Animation Studio",
"Daume",
"David Production",
"Digital Frontier",
"Digital Network Animation",
"Diomedea",
"Diomedéa",
"Disney Plus",
"Doga Kobo",
"Domerica",
"Dongwoo A&E",
"Drive",
"Drop",
"Dwango",
"Dynamo Pictures",
"E&G Films",
"EKACHI EPILKA",
"EMT Squared",
"ENGI",
"East Fish Studio",
"Egg Firm",
"Emon",
"Encourage Films",
"EzÏ<EFBFBD>la",
"FILMONY",
"Fanworks",
"Feel.",
"Felix Film",
"Fenz",
"Fifth Avenue",
"Filmlink International",
"Flat Studio",
"Front Line",
"Fuji TV",
"Fukushima Gaina",
"G&G Entertainment",
"G-angle",
"GANSIS",
"GEEK TOYS",
"GEMBA",
"GIFTanimation",
"GRIZZLY",
"Gaina",
"Gainax",
"Gallop",
"Gathering",
"Geek Toys",
"Gekkou",
"Geno Studio",
"Giga Production",
"Ginga Ya",
"GoHands",
"Gonzo",
"Gosay Studio",
"Graphinica",
"Gravity Well",
"Group TAC",
"Grouper Productions",
"HORNETS",
"Hal Film Maker",
"Haoliners Animation League",
"Helo.inc",
"Hoods Drifters Studio",
"Hoods Entertainment",
"Hotline",
"I.Gzwei",
"IDRAGONS Creative Studio",
"ILCA",
"IMAGICA Lab.",
"Imagin",
"Imagineer",
"Indivision",
"Irawias",
"Ishikawa Pro",
"Issen",
"Ixtl",
"J.C.Staff",
"JCF",
"Japan Vistec",
"Jinnis Animation Studios",
"Jumondo",
"KOO-KI",
"Kachidoki Studio",
"Kamikaze Douga",
"Kanaban Graphics",
"Kaname Productions",
"Kazami Gakuen Koushiki Douga-bu",
"KeyEast",
"Khara",
"Kinema Citrus",
"Kitty Film Mitaka Studio",
"Kitty Films",
"Kyoto Animation",
"Kyotoma",
"L-a-unchã<68>»BOX",
"LAN Studio",
"LEVELS",
"LICO",
"LIDENFILMS",
"LIDENFILMS Kyoto Studio",
"LIDENFILMS Osaka Studio",
"LMD",
"LandQ studios",
"Lapin Track",
"Larx Entertainment",
"Lay-duce",
"Lerche",
"Lesprit",
"Liber",
"Life Work",
"Light Chaser Animation Studios",
"Lilix",
"L²Studio",
"M.S.C",
"MAPPA",
"MASTER LIGHTS",
"MMT Technology",
"Madhouse",
"Magia Doraglier",
"Magic Bus",
"Maho Film",
"Manglobe",
"Marine Entertainment",
"Marvy Jack",
"Marza Animation Planet",
"Milky Cartoon",
"Millepensee",
"Mimoid",
"Minami Machi Bugyousho",
"Monofilmo",
"MooGoo",
"Mook Animation",
"Mook DLE",
"Motion Magic",
"Mushi Production",
"NAZ",
"NHK",
"Namu Animation",
"Netflix",
"Next Media Animation",
"Nexus",
"Nice Boat Animation",
"Nihon Ad Systems",
"Nippon Animation",
"Nomad",
"Nut",
"OLM",
"OLM Digital",
"OLM Team Yoshioka",
"OZ",
"Office DCI",
"Office No. 8",
"Oh! Production",
"Okuruto Noboru",
"Opera House",
"Orange",
"Ordet",
"Oxybot",
"P.A. Works",
"P.I.C.S.",
"PRA",
"Pancake",
"Passione",
"Pastel",
"Pb Animation Co. Ltd.",
"Pencil Lead Animate",
"Phoenix Entertainment",
"Picture Magic",
"Pierrot",
"Pierrot Plus",
"Pine Jam",
"Planet",
"Platinum Vision",
"Plum",
"Polygon Pictures",
"Primastea",
"PrimeTime",
"Production +h.",
"Production GoodBook",
"Production I.G",
"Production IMS",
"Production Reed",
"Production doA",
"Project No.9",
"Purple Cow Studio Japan",
"Quad",
"Qualia Animation",
"Qubic Pictures",
"REALTHING",
"Radix",
"Red Dog Culture House",
"Remic",
"Revoroot",
"Rikuentai",
"Rising Force",
"Robot Communications",
"Rockwell Eyes",
"Ruo Hong Culture",
"SANZIGEN",
"SILVER LINK.",
"Saetta",
"Saigo no Shudan",
"Sakura Create",
"Samsara Animation Studio",
"Sanctuary",
"Sanrio",
"Satelight",
"Science SARU",
"Scooter Films",
"Seven",
"Seven Arcs",
"Seven Stone Entertainment",
"Shaft",
"Shanghai Animation Film Studio",
"Shanghai Foch Film",
"Shenying Animation",
"Shimogumi",
"Shin-Ei Animation",
"Shirogumi",
"Shuka",
"Signal.MD",
"Silver",
"Silver Link.",
"Sola Digital Arts",
"Soyep",
"Space Neko Company",
"Sparkly Key Animation Studio",
"Square Enix Visual Works",
"Staple Entertainment",
"Steve N' Steven",
"Stingray",
"Studio 3Hz",
"Studio 4°C",
"Studio A-CAT",
"Studio Animal",
"Studio Bind",
"Studio Blanc",
"Studio Blanc.",
"Studio Chizu",
"Studio Colorido",
"Studio Comet",
"Studio Dadashow",
"Studio Daisy",
"Studio Deen",
"Studio Fantasia",
"Studio Flad",
"Studio Flag",
"Studio GOONEYS",
"Studio Ghibli",
"Studio Gokumi",
"Studio Hibari",
"Studio Hokiboshi",
"Studio Jemi",
"Studio Junio",
"Studio Kafka",
"Studio Kai",
"Studio Kikan",
"Studio LAN",
"Studio Lings",
"Studio Live",
"Studio M2",
"Studio MOTHER",
"Studio March",
"Studio Matrix",
"Studio Moriken",
"Studio Palette",
"Studio Pierrot",
"Studio Ponoc",
"Studio PuYUKAI",
"Studio Rikka",
"Studio Signal",
"Studio Signpost",
"Studio VOLN",
"Studio Z5",
"Studio elle",
"Studio! Cucuri",
"Sublimation",
"Success Corp.",
"Sunrise",
"Sunrise Beyond",
"Super Normal Studio",
"SynergySP",
"TMS Entertainment",
"TNK",
"TROYCA",
"TYO Animations",
"Tama Production",
"Tamura Shigeru Studio",
"Tatsunoko Production",
"Team Yamahitsuji",
"Team YokkyuFuman",
"TeamKG",
"Tear Studio",
"Telecom Animation Film",
"Tencent Penguin Pictures",
"Tengu Kobo",
"Tezuka Productions",
"The Answer Studio",
"Thundray",
"Toei Animation",
"Toho Interactive Animation",
"Tokyo Kids",
"Tokyo Movie Shinsha",
"Tomason",
"Tomovies",
"Topcraft",
"Trans Arts",
"Tri-Slash",
"TriF Studio",
"Triangle Staff",
"Trigger",
"Trinet Entertainment",
"Tsuchida Productions",
"Twilight Studio",
"Typhoon Graphics",
"UWAN Pictures",
"Ufotable",
"Vega Entertainment",
"View Works",
"W-Toon Studio",
"WAO World",
"Wawayu Animation",
"White Fox",
"Wit Studio",
"Wolf Smoke Studio",
"Wolfsbane",
"XFLAG",
"Xebec",
"YHKT Entertainment",
"Yaoyorozu",
"Yokohama Animation Lab",
"Yostar Pictures",
"Yumeta Company",
"Zero-G",
"Zexcs",
)
}
}

View file

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.animeextension.tr.anizm
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://anizm.net/<item> intents
* and redirects them to the main Aniyomi process.
*/
class AnizmUrlActivity : 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.first()
val mainIntent = Intent().apply {
action = "eu.kanade.tachiyomi.ANIMESEARCH"
putExtra("query", "${Anizm.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)
}
}

View file

@ -0,0 +1,18 @@
package eu.kanade.tachiyomi.animeextension.tr.anizm
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class SearchItemDto(
@SerialName("info_title") val title: String,
@SerialName("info_othernames") val othernames: String?,
@SerialName("info_japanese") val japanese: String?,
@SerialName("info_slug") val slug: String,
@SerialName("info_studios") val studios: String?,
@SerialName("info_poster") val thumbnail: String,
@SerialName("info_year") val year: String?,
@SerialName("info_malpoint") val malpoint: Double?,
) {
val names by lazy { listOfNotNull(othernames, japanese, title) }
}

View file

@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.animeextension.tr.anizm.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.POST
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
class AincradExtractor(
private val client: OkHttpClient,
private val headers: Headers,
private val json: Json,
) {
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
fun videosFromUrl(url: String): List<Video> {
val hash = url.substringAfterLast("video/").substringBefore("/")
val body = FormBody.Builder()
.add("hash", hash)
.add("r", "https://anizm.net/")
.build()
val headers = headers.newBuilder()
.set("Origin", DOMAIN)
.set("Referer", url)
.set("X-Requested-With", "XMLHttpRequest")
.build()
val req = POST("$DOMAIN/player/index.php?data=$hash&do=getVideo", headers, body)
val res = client.newCall(req).execute().body.string()
return runCatching {
val data = json.decodeFromString<ResponseDto>(res)
playlistUtils.extractFromHls(
data.securedLink!!,
referer = url,
videoNameGen = { "Aincrad - $it" },
)
}.getOrElse { emptyList() }
}
@Serializable
data class ResponseDto(val securedLink: String?)
companion object {
private const val DOMAIN = "https://anizmplayer.com"
}
}