update aniking, add de.movie4k, remove anime-shitai & xcine (#998)

This commit is contained in:
LuftVerbot
2022-11-04 17:58:56 +01:00
committed by GitHub
parent 3cc19933bf
commit d7188f1c6e
47 changed files with 515 additions and 985 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Aniking'
pkgNameSuffix = 'de.aniking'
extClass = '.Aniking'
extVersionCode = 1
extVersionCode = 2
libVersion = '13'
}

View File

@ -65,7 +65,16 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// episodes
override fun episodeListRequest(anime: SAnime): Request {
return GET("$baseUrl${anime.url}", headers = Headers.headersOf("if-modified-since", ""))
val interceptor = client.newBuilder().addInterceptor(CloudflareInterceptor()).build()
val headers = interceptor.newCall(
GET(
"$baseUrl${anime.url}",
headers =
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
)
)
.execute().request.headers
return GET("$baseUrl${anime.url}", headers = headers)
}
override fun episodeListSelector() = throw Exception("not used")
@ -268,6 +277,19 @@ class Aniking : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
// Details
override fun animeDetailsRequest(anime: SAnime): Request {
val interceptor = client.newBuilder().addInterceptor(CloudflareInterceptor()).build()
val headers = interceptor.newCall(
GET(
"$baseUrl${anime.url}",
headers =
Headers.headersOf("user-agent", "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36")
)
)
.execute().request.headers
return GET("$baseUrl${anime.url}", headers = headers)
}
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = document.select("div.tv-poster img").attr("src")

View File

@ -53,8 +53,7 @@ class CloudflareInterceptor() : Interceptor {
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = request.header("User-Agent")
?: "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63\""
userAgentString = "Mozilla/5.0 (Linux; Android 12; SM-T870 Build/SP2A.220305.013; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Safari/537.36"
}
webview.webViewClient = object : WebViewClient() {
@ -62,7 +61,7 @@ class CloudflareInterceptor() : Interceptor {
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
if (request.url.toString().contains("https://aniking.cc")) {
if (request.url.toString().contains("wp-content/themes/moviewp")) {
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
latch.countDown()
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,21 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.animeshitai
object ASConstants {
const val NAME_DOOD = "Doodstream"
const val NAME_STAPE = "Streamtape"
const val URL_DOOD = "https://dood"
const val URL_STAPE = "https://streamtape.com"
val HOSTER_NAMES = arrayOf(NAME_DOOD, NAME_STAPE)
val HOSTER_URLS = arrayOf(URL_DOOD, URL_STAPE)
const val LANG_SUB = "Sub"
const val LANG_DUB = "Dub"
val LANGS = arrayOf(LANG_SUB, LANG_DUB)
const val PREFERRED_HOSTER = "preferred_hoster"
const val PREFERRED_LANG = "preferred_sub"
const val HOSTER_SELECTION = "hoster_selection"
}

View File

@ -1,204 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.animeshitai
import eu.kanade.tachiyomi.animeextension.de.animeshitai.model.ASAnime
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import java.util.Calendar
object ASFilters {
open class CheckBoxFilterList(name: String, values: List<CheckBox>) : AnimeFilter.Group<AnimeFilter.CheckBox>(name, values)
private class CheckBoxVal(name: String, state: Boolean = false) : AnimeFilter.CheckBox(name, state)
open class TriStateFilterList(name: String, values: List<TriState>) : AnimeFilter.Group<AnimeFilter.TriState>(name, values)
private class TriStateVal(name: String) : AnimeFilter.TriState(name)
class SortFilter : AnimeFilter.Sort(
"Sortieren",
ASFilterData.sortables.map { it.first }.toTypedArray(),
Selection(0, true)
)
class FormatFilter : CheckBoxFilterList(
"Format",
ASFilterData.formats.map { CheckBoxVal(it.first, true) }
)
class LanguageFilter : CheckBoxFilterList(
"Sprache",
ASFilterData.languages.map { CheckBoxVal(it.first, true) }
)
class GenreFilter : TriStateFilterList(
"Genres",
ASFilterData.genres.map { TriStateVal(it) }
)
class YearsFilter : CheckBoxFilterList(
"Jahre",
ASFilterData.years.map { CheckBoxVal(it.toString()) }
)
class ABCFilter : CheckBoxFilterList(
"ABC",
ASFilterData.abc.map { CheckBoxVal(it.toString()) }
)
val filterList = AnimeFilterList(
SortFilter(),
FormatFilter(),
LanguageFilter(),
GenreFilter(),
YearsFilter(),
ABCFilter(),
)
data class FilterSearchParams(
var orderBy: String = "az",
var orderAscending: Boolean = true,
val includedFormats: ArrayList<String> = ArrayList(),
val includedLangs: ArrayList<String> = ArrayList(),
val includedGenres: ArrayList<String> = ArrayList(),
val blackListedGenres: ArrayList<String> = ArrayList(),
val includedYears: ArrayList<String> = ArrayList(),
val includedLetters: ArrayList<String> = ArrayList(),
)
internal fun getSearchParameters(filters: AnimeFilterList): FilterSearchParams {
val params = FilterSearchParams()
filters.forEach { filter ->
when (filter) {
is SortFilter -> {
if (filter.state != null) {
val query = ASFilterData.sortables[filter.state!!.index].second
params.orderAscending = filter.state!!.ascending
params.orderBy = query
}
}
is FormatFilter -> {
filter.state.forEach { format ->
if (format.state) {
val query = ASFilterData.formats.find { it.first == format.name }!!.second
params.includedFormats.add(query)
}
}
}
is LanguageFilter -> {
filter.state.forEach { lang ->
if (lang.state) {
val query = ASFilterData.languages.find { it.first == lang.name }!!.second
params.includedLangs.add(query)
}
}
}
is GenreFilter -> {
filter.state.forEach { genre ->
if (genre.isIncluded()) {
params.includedGenres.add(genre.name)
} else if (genre.isExcluded()) {
params.blackListedGenres.add(genre.name)
}
}
}
is YearsFilter -> {
filter.state.forEach { year ->
if (year.state)
params.includedYears.add(year.name)
}
}
is ABCFilter -> {
filter.state.forEach { letter ->
if (letter.state)
params.includedLetters.add(letter.name)
}
}
else -> {}
}
}
return params
}
fun MutableList<ASAnime>.applyFilterParams(params: FilterSearchParams) {
// Remove all entries with blacklisted genres
this.removeAll { anime -> params.blackListedGenres.any { it in anime.genre ?: "" } }
// Sort animes
when (params.orderBy) {
// Sort animes alphabetically
"az" -> {
if (!params.orderAscending)
this.reverse()
}
// Sort animes by year
"year" -> {
if (params.orderAscending)
this.sortBy { it.year }
else
this.sortByDescending { it.year }
}
}
}
}
private object ASFilterData {
// (1990..(Year.now().value)).reversed()
val years = (1990..(Calendar.getInstance().get(Calendar.YEAR))).reversed()
val abc = 'A'..'Z'
val sortables = arrayOf(
Pair("Alphabetisch", "az"),
Pair("Jahr", "year"),
)
val formats = arrayOf(
Pair("\uD83D\uDCFA Serien", "Serien"),
Pair("\uD83C\uDF10 OVAs", "OVAs"),
Pair("\uD83C\uDFAC Filme", "Filme")
)
val languages = arrayOf(
Pair("\uD83C\uDDE9\uD83C\uDDEA Ger Dub", "Ger Dub"),
Pair("\uD83C\uDDEF\uD83C\uDDF5 Ger Sub", "Ger Sub"),
Pair("\uD83C\uDDEC\uD83C\uDDE7 Eng Dub & Ger Sub", "Eng Dub")
)
val genres = listOf(
"Abenteuer",
"Action",
"Alltagsleben",
"Alternative Welt",
"Drama",
"Ecchi",
"Erotik",
"Fantasy",
"Fighting-Shounen",
"Ganbatte",
"Geistergeschichten",
"Harem",
"Historisch",
"Horror",
"Komödie",
"Krimi",
"Liebesdrama",
"Magical Girl",
"Magie",
"Mecha",
"Mystery",
"Nonsense-Komödie",
"Psychodrama",
"Romantische Komödie",
"Romanze",
"Schule",
"Sci-Fi",
"Sentimentales Drama",
"Shoujou",
"Shounen",
"Sport",
"Superpower",
"Thriller",
"Übermäßige Gewaltdarstellung",
"Unbestimmt",
"Yaoi",
"Yuri"
)
}

View File

@ -1,356 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.animeshitai
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.de.animeshitai.ASFilters.applyFilterParams
import eu.kanade.tachiyomi.animeextension.de.animeshitai.model.ASAnime
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.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.collections.ArrayList
class AnimeShitai : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "Anime Shitai"
override val baseUrl = "https://anime-shitai.com"
override val lang = "de"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
// ===== POPULAR ANIME =====
override fun popularAnimeSelector(): String = ".newanimes .newbox"
override fun popularAnimeNextPageSelector(): String? = null
override fun popularAnimeRequest(page: Int): Request = GET(baseUrl)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
val linkElement = element.selectFirst("a")
anime.url = linkElement.attr("href")
anime.thumbnail_url = linkElement.selectFirst("img").attr("src")
anime.title = element.selectFirst(".ntitel").text()
return anime
}
// ===== LATEST ANIME =====
override fun latestUpdatesSelector(): String = "a"
override fun latestUpdatesNextPageSelector(): String? = null
override fun latestUpdatesRequest(page: Int): Request = POST("$baseUrl/ichigo/indexep.php?option=1")
override fun latestUpdatesParse(response: Response): AnimesPage {
val document = Jsoup.parseBodyFragment(response.body?.string())
val animeList = mutableSetOf<String>()
val animes = document.select(latestUpdatesSelector()).mapNotNull { element ->
val animeName = element.selectFirst("span.t").text().substringBefore("...")
// Check if any anime is multiple times in the list
if (!animeList.contains(animeName)) {
animeList.add(animeName)
latestUpdatesFromElement(element)
} else
null
}
return AnimesPage(animes, false)
}
override fun latestUpdatesFromElement(element: Element): SAnime {
val anime = SAnime.create()
val animeId = element.attr("href").substringAfter("/anschauen/").substringBefore('/')
anime.url = "/anime/$animeId/"
anime.thumbnail_url = element.selectFirst("div.c").attr("style").substringAfter("background:url('").substringBefore("');")
anime.title = anime.thumbnail_url!!.substringAfter("/cover/").substringBefore(".jpg").replace("__", ": ").replace('_', ' ')
return anime
}
// ===== ANIME SEARCH =====
override fun fetchSearchAnime(page: Int, query: String, filters: AnimeFilterList): Observable<AnimesPage> {
val params = ASFilters.getSearchParameters(filters)
return client.newCall(searchAnimeRequest(page, query, params))
.asObservableSuccess()
.map { response ->
searchAnimeParse(response, params)
}
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request = throw UnsupportedOperationException("Not used.")
private fun searchAnimeRequest(page: Int, query: String, params: ASFilters.FilterSearchParams): Request {
fun listToFormStr(list: ArrayList<String>): String {
if (list.size > 0)
return list.joinToString(" - ", " - ")
return ""
}
val body = FormBody.Builder()
.add("p", page.toString())
.add("get_val", query)
.add("genre", listToFormStr(params.includedGenres))
.add("jahr", listToFormStr(params.includedYears))
.add("format", listToFormStr(params.includedFormats))
.add("omu", listToFormStr(params.includedLangs))
.add("abc", listToFormStr(params.includedLetters))
.build()
return POST("$baseUrl/ichigo/get_anilist.php", headers, body)
}
override fun searchAnimeSelector(): String = ".listinganime"
override fun searchAnimeNextPageSelector(): String? = ".nav"
override fun searchAnimeParse(response: Response): AnimesPage = throw UnsupportedOperationException("Not used.")
private fun searchAnimeParse(response: Response, params: ASFilters.FilterSearchParams): AnimesPage {
val document = Jsoup.parseBodyFragment(response.body?.string())
val animeTrackList = mutableSetOf<String>()
val animes = document.select(searchAnimeSelector()).mapNotNull { element ->
val anime = searchAnimeFromElement(element)
// Shows with sub and dub have two separate entries, exclude the second entry
if (!animeTrackList.contains(anime.thumbnail_url)) {
animeTrackList.add(anime.thumbnail_url!!)
anime
} else
null
}.toMutableList()
// Apply client-side filters
animes.applyFilterParams(params)
val hasNextPage = searchAnimeNextPageSelector()?.let { selector ->
document.select(selector).first()
} != null
return AnimesPage(animes, hasNextPage)
}
override fun searchAnimeFromElement(element: Element): ASAnime {
val anime = ASAnime.create()
anime.url = element.selectFirst("a").attr("href")
anime.thumbnail_url = element.selectFirst(".listinganicover").attr("style").substringAfter("url('").substringBefore("')")
anime.title = element.selectFirst(".listingtitle").text().replaceAfterLast(' ', "").trimStart()
// Get genres for client-side genre filtering
anime.genre = element.select(".listingdesc .alternativ").mapNotNull {
if (it.text().startsWith("Hauptgenre") || it.text().startsWith("Genres"))
it.text().substringAfter(':')
else
null
}.joinToString(", ")
anime.year = element.selectFirst(".listingtitle").text().substringAfterLast('(').substringBefore(')').toInt()
return anime
}
override fun getFilterList(): AnimeFilterList = ASFilters.filterList
// ===== ANIME DETAILS =====
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
val content = document.selectFirst("#ani .body")
anime.title = content.selectFirst("animename").ownText().trim()
anime.thumbnail_url = document.selectFirst("#ani .pic").attr("data-src")
anime.genre = content.select("div:eq(1) .hg, a").joinToString(", ") { it.text() }
anime.description = content.selectFirst(".br").ownText()
anime.status = SAnime.UNKNOWN
return anime
}
// ===== EPISODE =====
override fun episodeListSelector(): String = ".ep_table tbody tr"
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
return document.select(episodeListSelector()).reversed().map { episodeFromElement(it) }
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.setUrlWithoutDomain(baseUrl + element.attr("onclick").substringAfter("window.location.href='").substringBefore('#'))
val ep = element.child(0).text()
episode.episode_number = ep.toFloat()
episode.name = "Episode $ep"
return episode
}
// ===== VIDEO SOURCES =====
override fun videoListSelector(): String = "center a"
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return document.select(videoListSelector()).mapNotNull { videoFromElement(it, document) }
}
private fun getVidLink(document: Document): String {
val b64html = document.selectFirst(".c36 > script").data().substringAfter("atob('").substringBefore("');")
val decodedHtml = Base64.decode(b64html, Base64.DEFAULT).decodeToString()
return baseUrl + decodedHtml.substringAfter("src=\"").substringBefore('"')
}
override fun videoFromElement(element: Element): Video = throw UnsupportedOperationException("Not used.")
private fun videoFromElement(element: Element, document: Document): Video? {
val hosterLinkName = element.attr("href").substringAfter("/folge-").substringAfter('/')
val hosterName = when (hosterLinkName) {
"DODO" -> ASConstants.NAME_DOOD
"Streamtape" -> ASConstants.NAME_STAPE
else -> null
} ?: return null
val lang =
when (element.selectFirst(".languagehoster img").attr("src").substringAfter("/img/").substringBefore(".")) {
"de" -> ASConstants.LANG_DUB
"jap" -> ASConstants.LANG_SUB
"en" -> ASConstants.LANG_SUB
else -> "Unknown"
}
val link: String = when {
// If video link of loaded page is already the right one
element.selectFirst(".host").hasClass("hoston") -> getVidLink(document)
// If video link of loaded page is not the right one, get url for the right page and extract video link
else -> {
val response = client.newCall(GET("$baseUrl${element.attr("href")}")).execute()
getVidLink(response.asJsoup())
}
}
val quality = "$hosterName, $lang"
val hosterSelection = preferences.getStringSet(ASConstants.HOSTER_SELECTION, null)
when {
hosterName == ASConstants.NAME_DOOD && hosterSelection?.contains(ASConstants.NAME_DOOD) == true -> {
val video = try {
DoodExtractor(client).videoFromUrl(link, quality)
} catch (e: Exception) {
null
}
if (video != null) {
return video
}
}
hosterName == ASConstants.NAME_STAPE && hosterSelection?.contains(ASConstants.NAME_STAPE) == true -> {
val video = StreamTapeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
return video
}
}
}
return null
}
override fun List<Video>.sort(): List<Video> {
val hoster = preferences.getString(ASConstants.PREFERRED_HOSTER, null)
val subPreference = preferences.getString(ASConstants.PREFERRED_LANG, "Sub")!!
val hosterList = mutableListOf<Video>()
val otherList = mutableListOf<Video>()
if (hoster != null) {
for (video in this) {
if (video.url.contains(hoster)) {
hosterList.add(video)
} else {
otherList.add(video)
}
}
} else otherList += this
val newList = mutableListOf<Video>()
var preferred = 0
for (video in hosterList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else newList.add(video)
}
for (video in otherList) {
if (video.quality.contains(subPreference)) {
newList.add(preferred, video)
preferred++
} else newList.add(video)
}
return newList
}
override fun videoUrlParse(document: Document): String = throw UnsupportedOperationException("Not used.")
// ===== PREFERENCES ======
@Suppress("UNCHECKED_CAST")
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = ASConstants.PREFERRED_HOSTER
title = "Standard-Hoster"
entries = ASConstants.HOSTER_NAMES
entryValues = ASConstants.HOSTER_URLS
setDefaultValue(ASConstants.URL_STAPE)
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()
}
}
val subPref = ListPreference(screen.context).apply {
key = ASConstants.PREFERRED_LANG
title = "Standardmäßig Sub oder Dub?"
entries = ASConstants.LANGS
entryValues = ASConstants.LANGS
setDefaultValue(ASConstants.LANG_SUB)
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()
}
}
val subSelection = MultiSelectListPreference(screen.context).apply {
key = ASConstants.HOSTER_SELECTION
title = "Hoster auswählen"
entries = ASConstants.HOSTER_NAMES
entryValues = ASConstants.HOSTER_NAMES
setDefaultValue(ASConstants.HOSTER_NAMES.toSet())
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(subPref)
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -1,23 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.animeshitai.model
import eu.kanade.tachiyomi.animesource.model.SAnime
class ASAnime : SAnime {
override lateinit var url: String
override lateinit var title: String
override var artist: String? = null
override var author: String? = null
override var description: String? = null
override var genre: String? = null
override var status: Int = 0
override var thumbnail_url: String? = null
override var initialized: Boolean = false
var year: Int = 0
companion object {
fun create(): ASAnime {
return ASAnime()
}
}
}

View File

@ -3,16 +3,16 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Anime Shitai'
pkgNameSuffix = 'de.animeshitai'
extClass = '.AnimeShitai'
extVersionCode = 6
extName = 'Movie4k'
pkgNameSuffix = 'de.movie4k'
extClass = '.Movie4k'
extVersionCode = 1
libVersion = '13'
}
dependencies {
implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-dood-extractor'))
implementation(project(':lib-voe-extractor'))
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,452 @@
package eu.kanade.tachiyomi.animeextension.de.movie4k
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.de.movie4k.extractors.StreamZExtractor
import eu.kanade.tachiyomi.animeextension.de.movie4k.extractors.VidozaExtractor
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.AnimeHttpSource
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.float
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.Exception
class Movie4k : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "Movie4k"
override val baseUrl = "https://movie4k.stream"
private val apiUrl = "https://api.movie4k.stream"
override val lang = "de"
override val supportsLatest = false
override val client: OkHttpClient = network.client
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val json = Json {
isLenient = true
ignoreUnknownKeys = true
}
override fun popularAnimeRequest(page: Int): Request =
GET("$apiUrl/data/browse/?lang=2&keyword=&year=&rating=&votes=&genre=&country=&cast=&directors=&type=movies&order_by=trending&page=$page", headers = Headers.headersOf("if-none-match", ""))
override fun popularAnimeParse(response: Response): AnimesPage {
val responseString = response.body!!.string()
return parsePopularAnimeJson(responseString)
}
private fun parsePopularAnimeJson(jsonLine: String?): AnimesPage {
val jsonData = jsonLine ?: return AnimesPage(emptyList(), false)
val jObject = json.decodeFromString<JsonObject>(jsonData)
val pager = jObject["pager"]!!.jsonObject
val lastPage = pager.jsonObject["endPage"]!!.jsonPrimitive.int
val page = pager.jsonObject["currentPage"]!!.jsonPrimitive.int
val hasNextPage = page < lastPage
val array = jObject["movies"]!!.jsonArray
val animeList = mutableListOf<SAnime>()
for (item in array) {
val anime = SAnime.create()
anime.title = item.jsonObject["title"]!!.jsonPrimitive.content
val animeId = item.jsonObject["_id"]!!.jsonPrimitive.content
anime.setUrlWithoutDomain("$apiUrl/data/watch/?_id=$animeId")
anime.thumbnail_url = "https://image.tmdb.org/t/p/w300" + (item.jsonObject["poster_path"]?.jsonPrimitive?.content ?: "")
animeList.add(anime)
}
return AnimesPage(animeList, hasNextPage)
}
// episodes
override fun episodeListRequest(anime: SAnime): Request {
return GET(apiUrl + anime.url, headers = Headers.headersOf("if-none-match", ""))
}
override fun episodeListParse(response: Response): List<SEpisode> {
val responseString = response.body!!.string()
return parseEpisodePage(responseString)
}
private fun parseEpisodePage(jsonLine: String?): MutableList<SEpisode> {
val jsonData = jsonLine ?: return mutableListOf()
val jObject = json.decodeFromString<JsonObject>(jsonData)
val episodeList = mutableListOf<SEpisode>()
if (jObject["tv"]!!.jsonPrimitive.int == 1) {
val array = jObject["streams"]!!.jsonArray.distinctBy { it.jsonObject["e"] }.sortedBy { it.jsonObject["e"]!!.jsonPrimitive.int }
for (item in array) {
val episode = SEpisode.create()
val id = jObject["_id"]!!.jsonPrimitive.content
episode.episode_number = item.jsonObject["e"]!!.jsonPrimitive.float
val epNumString = if (episode.episode_number % 1F == 0F) episode.episode_number.toInt().toString() else episode.episode_number.toString()
val season = jObject["s"]!!.jsonPrimitive.content
episode.name = "Staffel $season Folge $epNumString"
episode.setUrlWithoutDomain("$apiUrl/data/watch/?_id=$id&e=$epNumString")
episodeList.add(episode)
}
} else {
val episode = SEpisode.create()
episode.episode_number = 1F
episode.name = jObject["title"]!!.jsonPrimitive.content
val id = jObject["_id"]!!.jsonPrimitive.content
episode.setUrlWithoutDomain("$apiUrl/data/watch/?_id=$id")
episodeList.add(episode)
}
return episodeList.asReversed()
}
// Video Extractor
override fun videoListRequest(episode: SEpisode): Request {
val url = episode.url.replace("&e=${episode.episode_number.toInt()}", "")
return GET(apiUrl + url, headers = Headers.headersOf("if-none-match", "", "e", episode.episode_number.toInt().toString()))
}
override fun videoListParse(response: Response): List<Video> {
return videosFromElement(response)
}
private fun videosFromElement(response: Response): List<Video> {
val jsonData = response.body!!.string()
val jObject = json.decodeFromString<JsonObject>(jsonData)
val array = jObject["streams"]!!.jsonArray
val videoList = mutableListOf<Video>()
val hosterSelection = preferences.getStringSet("hoster_selection", setOf("voe", "stape", "streamz", "vidoza"))
if (jObject["tv"]!!.jsonPrimitive.int == 1) {
for (item in array) {
if (item.jsonObject["e"]!!.jsonPrimitive.content == response.request.header("e").toString()) {
val link = item.jsonObject["stream"]!!.jsonPrimitive.content
when {
link.contains("//streamtape") && hosterSelection?.contains("stape") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
link.contains("//streamcrypt.net") || link.contains("//streamz") && hosterSelection?.contains("streamz") == true -> {
if (!link.contains("https:")) {
if (link.contains("//streamcrypt.net")) {
val url = "https:$link"
val zurl = client.newCall(GET(url)).execute().request.url.toString()
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(zurl, quality)
if (video != null) {
videoList.add(video)
}
} else {
val url = "https:$link"
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
}
} else {
if (link.contains("https://streamcrypt.net")) {
val url = client.newCall(GET(link)).execute().request.url.toString()
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
}
link.contains("vidoza") && hosterSelection?.contains("vidoza") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Vidoza"
val video = VidozaExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Vidoza"
val video = VidozaExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
link.contains("//voe.sx") || link.contains("//launchreliantcleaverriver") ||
link.contains("//fraudclatterflyingcar") ||
link.contains("//uptodatefinishconferenceroom") || link.contains("//realfinanceblogcenter") && hosterSelection?.contains("voe") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
} else {
for (item in array) {
val link = item.jsonObject["stream"]!!.jsonPrimitive.content
when {
link.contains("//streamtape") && hosterSelection?.contains("stape") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Streamtape"
val video = StreamTapeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
link.contains("//streamcrypt.net") || link.contains("https://streamz") && hosterSelection?.contains("streamz") == true -> {
if (!link.contains("https:")) {
if (link.contains("//streamcrypt.net")) {
val url = "https:$link"
val zurl = client.newCall(GET(url)).execute().request.url.toString()
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(zurl, quality)
if (video != null) {
videoList.add(video)
}
} else {
val url = "https:$link"
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
}
} else {
if (link.contains("https://streamcrypt.net")) {
val url = client.newCall(GET(link)).execute().request.url.toString()
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "StreamZ"
val video = StreamZExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
}
link.contains("vidoza") && hosterSelection?.contains("vidoza") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Vidoza"
val video = VidozaExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Vidoza"
val video = VidozaExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
link.contains("//voe.sx") || link.contains("//launchreliantcleaverriver") ||
link.contains("//fraudclatterflyingcar") ||
link.contains("//uptodatefinishconferenceroom") || link.contains("//realfinanceblogcenter") && hosterSelection?.contains("voe") == true -> {
if (!link.contains("https:")) {
val url = "https:$link"
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(url, quality)
if (video != null) {
videoList.add(video)
}
} else {
val quality = "Voe"
val video = VoeExtractor(client).videoFromUrl(link, quality)
if (video != null) {
videoList.add(video)
}
}
}
}
}
}
return videoList.reversed()
}
override fun List<Video>.sort(): List<Video> {
val hoster = preferences.getString("preferred_hoster", null)
if (hoster != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(hoster)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
// Search
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request =
GET("$apiUrl/data/browse/?lang=2&keyword=$query&year=&rating=&votes=&genre=&country=&cast=&directors=&type=&order_by=&page=$page", headers = Headers.headersOf("if-none-match", ""))
override fun searchAnimeParse(response: Response): AnimesPage {
val responseString = response.body!!.string()
return parseSearchAnimeJson(responseString)
}
private fun parseSearchAnimeJson(jsonLine: String?): AnimesPage {
val jsonData = jsonLine ?: return AnimesPage(emptyList(), false)
val jObject = json.decodeFromString<JsonObject>(jsonData)
val pager = jObject["pager"]!!.jsonObject
val lastPage = pager.jsonObject["endPage"]!!.jsonPrimitive.int
val page = pager.jsonObject["currentPage"]!!.jsonPrimitive.int
val hasNextPage = page < lastPage
val array = jObject["movies"]!!.jsonArray
val animeList = mutableListOf<SAnime>()
for (item in array) {
val anime = SAnime.create()
anime.title = item.jsonObject["title"]!!.jsonPrimitive.content
val animeId = item.jsonObject["_id"]!!.jsonPrimitive.content
anime.setUrlWithoutDomain("$apiUrl/data/watch/?_id=$animeId")
if (item.jsonObject["tv"]!!.jsonPrimitive.int == 1) {
anime.thumbnail_url = "https://image.tmdb.org/t/p/w300" + (
item.jsonObject["poster_path_season"]?.jsonPrimitive?.content
?: ""
)
} else {
anime.thumbnail_url = "https://image.tmdb.org/t/p/w300" + (
item.jsonObject["poster_path"]?.jsonPrimitive?.content
?: ""
)
}
animeList.add(anime)
}
return AnimesPage(animeList, hasNextPage)
}
// Details
override fun animeDetailsRequest(anime: SAnime): Request {
return GET(apiUrl + anime.url, headers = Headers.headersOf("if-modified-since", ""))
}
override fun animeDetailsParse(response: Response): SAnime {
val responseString = response.body!!.string()
return parseAnimeDetailsParseJson(responseString)
}
private fun parseAnimeDetailsParseJson(jsonLine: String?): SAnime {
val anime = SAnime.create()
val jsonData = jsonLine ?: return anime
val jObject = json.decodeFromString<JsonObject>(jsonData)
anime.title = jObject.jsonObject["title"]!!.jsonPrimitive.content
anime.description = jObject.jsonObject["storyline"]!!.jsonPrimitive.content
return anime
}
// Latest
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesParse(response: Response): AnimesPage = throw Exception("not Used")
// Preferences
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = "preferred_hoster"
title = "Standard-Hoster"
entries = arrayOf("Streamtape", "Voe", "StreamZ", "Vidoza")
entryValues = arrayOf("https://streamtape.com", "https://voe.sx", "https://streamz.ws", "https://vidoza.net")
setDefaultValue("https://streamtape.com")
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()
}
}
val subSelection = MultiSelectListPreference(screen.context).apply {
key = "hoster_selection"
title = "Hoster auswählen"
entries = arrayOf("Streamtape", "Voe", "StreamZ", "Vidoza")
entryValues = arrayOf("stape", "voe", "streamz", "vidoza")
setDefaultValue(setOf("stape", "voe", "streamz", "vidoza"))
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putStringSet(key, newValue as Set<String>).commit()
}
}
screen.addPreference(hosterPref)
screen.addPreference(subSelection)
}
}

View File

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.animeextension.de.movie4k.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import okhttp3.OkHttpClient
class StreamZExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val link = client.newCall(GET(url)).execute().request.url.toString()
val dllpart = link.substringAfter("/x")
val videoUrl = client.newCall(GET("https://get.streamz.tw/getlink-$dllpart.dll")).execute().request.url.toString()
return Video(url, quality, videoUrl)
}
}

View File

@ -0,0 +1,17 @@
package eu.kanade.tachiyomi.animeextension.de.movie4k.extractors
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
class VidozaExtractor(private val client: OkHttpClient) {
fun videoFromUrl(url: String, quality: String): Video? {
val document = client.newCall(GET(url)).execute().asJsoup()
val script = document.select("script:containsData(window.pData = {)")
.firstOrNull()?.data()?.substringAfter("sourcesCode: [{ src: \"") ?: return null
val videoUrl = script.substringAfter("sourcesCode: [{ src: \"").substringBefore("\", type:")
return Video(url, quality, videoUrl)
}
}

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.animeextension" />

View File

@ -1,13 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'xCine'
pkgNameSuffix = 'de.xcine'
extClass = '.xCine'
extVersionCode = 4
libVersion = '13'
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,88 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.xcine
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class CookieInterceptor : Interceptor {
private val context = Injekt.get<Application>()
private val handler by lazy { Handler(Looper.getMainLooper()) }
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("bruh")
return chain.proceed(newRequest)
}
@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request? {
// We need to lock this thread until the WebView finds the challenge solution url, because
// OkHttp doesn't support asynchronous interceptors.
val latch = CountDownLatch(1)
var webView: WebView? = null
val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
var newRequest: Request? = null
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = request.header("User-Agent")
?: "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63\""
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
if (request.url.toString().contains("favicon.ico")) {
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
// Wait a reasonable amount of time to retrieve the solution. The minimum should be
// around 4 seconds but it can take more due to slow networks or server issues.
latch.await(12, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return newRequest
}
}

View File

@ -1,52 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.xcine.extractors
import eu.kanade.tachiyomi.animeextension.de.xcine.CookieInterceptor
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers
import okhttp3.OkHttpClient
import org.jsoup.nodes.Document
class HDFilmExtractor(private val client: OkHttpClient) {
fun videosFromUrl(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
val id = document.select("#movie_report_error_form input[name=movie_id]").attr("value")
val link = if (document.select("#breadcrumbDiv a[title=\"Serien stream\"]").attr("href").contains("/serien1")) {
document.select("link[rel=canonical]").attr("href")
} else {
val movielink = document.select("a.play-film").attr("href")
// val cookies = cookieClient.newCall(GET(movielink)).execute().request.headers
val moviehtml = client.newCall(GET(movielink)).execute().asJsoup()
moviehtml.select("link[rel=canonical]").attr("href")
}
val loadid = if (document.select("#breadcrumbDiv a[title=\"Serien stream\"]").attr("href").contains("/serien1")) {
document.select("a[href=$link]").attr("data-episode-id")
} else {
val moviehtml = client.newCall(GET(link)).execute().asJsoup()
moviehtml.select("a#nextEpisodeToPlay").attr("data-episode-id")
}
val cookieClient = client.newBuilder().addInterceptor(CookieInterceptor()).build()
val cookieheaders = cookieClient.newCall(GET(link)).execute().headers
val headers = Headers.Builder()
.addAll(cookieheaders)
.add("origin", "https://xcine.me")
.add("referer", link)
.add("X-Requested-With", "XMLHttpRequest")
.add("Content-Type", "application/x-www-form-urlencoded")
.build()
val playlist = client.newCall(POST("https://xcine.me/movie/load-stream/$id/$loadid?server=1", headers = headers)).execute().asJsoup()
playlist.toString().substringAfter("var vip_source = [{\"file\":\"").substringBefore("];").split("\"file\":\"").forEach {
val quality = it.substringAfter("\"label\":\"").substringBefore("\"}")
val videoUrl = it.substringAfter("\"file\":\"").substringBefore("\",\"type").replace("\\", "")
if (client.newCall(GET(videoUrl)).execute().code == 204) {
throw Exception("Einmal WebView öffnen und wieder schließen")
} else {
videoList.addAll((listOf(Video(videoUrl, quality, videoUrl))))
}
}
return videoList
}
}

View File

@ -1,216 +0,0 @@
package eu.kanade.tachiyomi.animeextension.de.xcine
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.de.xcine.extractors.HDFilmExtractor
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 okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
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 kotlin.Exception
class xCine : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = "xCine"
override val baseUrl = "https://xcine.me"
override val lang = "de"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("cookie", "PHPSESSID=2d88418b469e522a75dd4c8327c3a936; SERVERID=s2; _ga=GA1.2.826139063.1658618592; _gid=GA1.2.162365688.1658618592; _gat_gtag_UA_144665518_1=1; __gads=ID=4fe53672dd9aeab8-22d4864165d40078:T=1658618593:RT=1658618593:S=ALNI_MYrmBvhl86smHeaNwW8I5UMS2ZOlA; _pop=1; dom3ic8zudi28v8lr6fgphwffqoz0j6c=3f6f53a9-7930-4d7a-87b1-c67125d128e0%3A2%3A1; m5a4xojbcp2nx3gptmm633qal3gzmadn=hopefullyapricot.com; _TqHT6=_0xc53")
override fun popularAnimeSelector(): String = "div.group-film-small a"
override fun popularAnimeRequest(page: Int): Request = POST(
"$baseUrl/filme1?page=$page&sort=view_total&sort_type=desc", body = "load=full-page".toRequestBody("application/x-www-form-urlencoded".toMediaType()),
headers = headersBuilder().build()
)
override fun popularAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
val urlpart = element.select("div.poster-film-small").toString()
.substringAfter("data-src=\"https://cdn.xcine.me/img/").substringBefore("\"")
anime.thumbnail_url = "https://cdn.xcine.me/img/$urlpart"
anime.title = element.attr("title").replace(" stream", "")
return anime
}
override fun popularAnimeNextPageSelector(): String = ".pag-next a[title=Next]"
// episodes
override fun episodeListSelector() = throw Exception("not used")
override fun episodeListParse(response: Response): List<SEpisode> {
val document = response.asJsoup()
val episodeList = mutableListOf<SEpisode>()
if (document.select("#breadcrumbDiv a[title=\"Serien stream\"]").attr("href").contains("/serien1")) {
val seriesLink = document.select("link[rel=canonical]").attr("abs:href")
val seasonHtml = client.newCall(GET("$seriesLink/folge-1", headers = Headers.headersOf("Referer", document.location())))
.execute().asJsoup()
val episodeElement = seasonHtml.select("ul.list-inline.list-film li")
episodeElement.forEach {
val episode = parseEpisodesFromSeries(it)
episodeList.addAll(episode)
}
} else {
val episode = SEpisode.create()
episode.name = document.select("h1.title-film-detail-1").text()
episode.episode_number = 1F
episode.setUrlWithoutDomain(document.select("link[rel=canonical]").attr("href"))
episodeList.add(episode)
}
return episodeList.reversed()
}
private fun parseEpisodesFromSeries(element: Element): List<SEpisode> {
val episodeElements = element.select("a")
return episodeElements.map { episodeFromElement(it) }
}
override fun episodeFromElement(element: Element): SEpisode {
val episode = SEpisode.create()
episode.episode_number = element.text().substringAfter("-").toFloat()
episode.name = element.attr("title").replace("stream", "")
episode.setUrlWithoutDomain(element.attr("href"))
return episode
}
// Video Extractor
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return videosFromElement(document)
}
private fun videosFromElement(document: Document): List<Video> {
val videoList = mutableListOf<Video>()
val videos = HDFilmExtractor(client).videosFromUrl(document)
videoList.addAll(videos)
return videoList.reversed()
}
override fun List<Video>.sort(): List<Video> {
val hoster = preferences.getString("preferred_hoster", null)
if (hoster != null) {
val newList = mutableListOf<Video>()
var preferred = 0
for (video in this) {
if (video.quality.contains(hoster)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
return newList
}
return this
}
override fun videoListSelector() = throw Exception("not used")
override fun videoFromElement(element: Element) = throw Exception("not used")
override fun videoUrlParse(document: Document) = throw Exception("not used")
// Search
override fun searchAnimeFromElement(element: Element): SAnime {
val anime = SAnime.create()
anime.setUrlWithoutDomain(element.attr("href"))
val urlpart = element.select("div.poster-film-small").toString()
.substringAfter("data-src=\"https://cdn.xcine.me/img/").substringBefore("\"")
anime.thumbnail_url = "https://cdn.xcine.me/img/$urlpart"
anime.title = element.attr("title").replace(" stream", "")
return anime
}
override fun searchAnimeNextPageSelector(): String = ".pag-next a[title=Next]"
override fun searchAnimeSelector(): String = "div.group-film-small a"
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
return POST(
"$baseUrl/search?page=$page&key=$query", body = "load=full-page".toRequestBody("application/x-www-form-urlencoded; charset=UTF-8".toMediaType()),
headers = headersBuilder().build()
)
}
// Details
override fun animeDetailsParse(document: Document): SAnime {
val anime = SAnime.create()
anime.thumbnail_url = anime.thumbnail_url
anime.title = document.select("h1.title-film-detail-1").text()
anime.genre = document.select("ul[class=infomation-film]").joinToString(", ") {
it.toString()
.substringAfter("Genre:").replace("<span>", "").substringBefore("</span>")
}
anime.description = document.select("p[class=content-film]").text()
anime.author = document.select("ul[class=infomation-film]").joinToString(", ") {
it.toString()
.substringAfter("Regisseur:").replace("<span>", "").substringBefore("</span>")
}
anime.status = SAnime.COMPLETED
return anime
}
// Latest
override fun latestUpdatesNextPageSelector(): String = throw Exception("Not used")
override fun latestUpdatesFromElement(element: Element): SAnime = throw Exception("Not used")
override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used")
override fun latestUpdatesSelector(): String = throw Exception("Not used")
// Preferences
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val hosterPref = ListPreference(screen.context).apply {
key = "preferred_hoster"
title = "Standard-Hoster"
entries = arrayOf("2k", "1080p", "720p", "480p", "360p")
entryValues = arrayOf("2", "1080", "720", "480", "360")
setDefaultValue("2")
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()
}
}
screen.addPreference(hosterPref)
}
}