diff --git a/src/de/serienstream/build.gradle b/src/de/serienstream/build.gradle index dc4557e5..4267a35e 100644 --- a/src/de/serienstream/build.gradle +++ b/src/de/serienstream/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Serienstream' extClass = '.Serienstream' - extVersionCode = 19 + extVersionCode = 20 } apply from: "$rootDir/common.gradle" @@ -10,4 +10,4 @@ dependencies { implementation(project(':lib:voe-extractor')) implementation(project(':lib:streamtape-extractor')) implementation(project(':lib:dood-extractor')) -} \ No newline at end of file +} diff --git a/src/de/serienstream/src/eu/kanade/tachiyomi/animeextension/de/serienstream/Serienstream.kt b/src/de/serienstream/src/eu/kanade/tachiyomi/animeextension/de/serienstream/Serienstream.kt index 5a74c6e3..fcc6e65d 100644 --- a/src/de/serienstream/src/eu/kanade/tachiyomi/animeextension/de/serienstream/Serienstream.kt +++ b/src/de/serienstream/src/eu/kanade/tachiyomi/animeextension/de/serienstream/Serienstream.kt @@ -37,7 +37,7 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override val name = "Serienstream" - override val baseUrl = "http://186.2.175.5" + override val baseUrl = "https://s.to" override val lang = "de" @@ -91,7 +91,7 @@ class Serienstream : ConfigurableAnimeSource, ParsedAnimeHttpSource() { override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { val headers = Headers.Builder() - .add("Referer", "http://186.2.175.5/search") + .add("Referer", "https://s.to/search") .add("origin", baseUrl) .add("connection", "keep-alive") .add("user-agent", "Mozilla/5.0 (Linux; Android 12; Pixel 5 Build/SP2A.220405.004; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/100.0.4896.127 Safari/537.36") diff --git a/src/en/rule34video/build.gradle b/src/en/rule34video/build.gradle index fa562806..1ab31bdb 100644 --- a/src/en/rule34video/build.gradle +++ b/src/en/rule34video/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Rule34Video' extClass = '.Rule34Video' - extVersionCode = 9 + extVersionCode = 8 isNsfw = true } diff --git a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt index cbf1632b..52a84082 100644 --- a/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt +++ b/src/en/rule34video/src/eu/kanade/tachiyomi/animeextension/en/rule34video/Rule34Video.kt @@ -1,10 +1,8 @@ package eu.kanade.tachiyomi.animeextension.en.rule34video + import android.app.Application -import android.util.Log -import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilterList @@ -43,21 +41,7 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { } // ============================== Popular =============================== - override fun popularAnimeRequest(page: Int): Request { - return if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false)) { - val uploaderId = preferences.getString(PREF_UPLOADER_ID_KEY, "") ?: "" - if (uploaderId.isNotBlank()) { - val url = "$baseUrl/members/$uploaderId/videos/?mode=async&function=get_block&block_id=list_videos_uploaded_videos&sort_by=&from_videos=$page" - Log.e("Rule34Video", "Loading popular videos from uploader ID: $uploaderId, page: $page, URL: $url") - GET(url) - } else { - Log.e("Rule34Video", "Uploader filter enabled but ID is blank, loading latest updates.") - GET("$baseUrl/latest-updates/$page/") - } - } else { - GET("$baseUrl/latest-updates/$page/") - } - } + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/latest-updates/$page/") override fun popularAnimeSelector() = "div.item.thumb" @@ -118,60 +102,21 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // =========================== Anime Details ============================ override fun animeDetailsParse(document: Document) = SAnime.create().apply { - title = document.selectFirst("h1.title_video")?.text().toString() - - val infoRow = document.selectFirst("div.info.row") - val detailRows = document.select("div.row") - - val artistElement = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name").firstOrNull() - author = artistElement?.text().orEmpty() - + title = document.selectFirst("h1.title_video")!!.text() + val info = document.selectFirst("#tab_video_info")!! + author = info.select("div.label:contains(Artist:) + a").eachText().joinToString() description = buildString { - detailRows.select("div.row:has(div.label > em) > div.label > em").html() - .replace("
", "\n") // Ensure single
tags are followed by a newline - .let { text -> - append(text) - } - append("\n\n") // Add extra spacing - - infoRow?.selectFirst("div.item_info:nth-child(1) > span")?.text()?.let { - append("Uploaded: $it\n") - } - - val artist = detailRows.select("div.col:has(div.label:contains(Artist)) a.item span.name") - .eachText() - .joinToString() - if (artist.isNotEmpty()) { - append("Artists: $artist\n") - } - - val categories = detailRows.select("div.col:has(div.label:contains(Categories)) a.item span") - .eachText() - .joinToString() - if (categories.isNotEmpty()) { - append("Categories: $categories\n") - } - - val uploader = detailRows.select("div.col:has(div.label:contains(Uploaded by)) a.item").text() - if (uploader.isNotEmpty()) { - append("Uploader: $uploader\n") - } - - infoRow?.select("div.item_info:nth-child(2) > span")?.text()?.let { - val views = it.substringBefore(" ").replace(",", "") - append("Views: $views\n") - } - infoRow?.select("div.item_info:nth-child(3) > span")?.text()?.let { append("Duration: $it\n") } - document.select("div.row:has(div.label:contains(Download)) a.tag_item") + info.selectFirst("div.label:contains(Description:) > em")?.text()?.also { append("$it\n") } + info.selectFirst("i.icon-eye + span")?.text()?.also { append("\nViews : ${it.replace(" ", ",")}") } + info.selectFirst("i.icon-clock + span")?.text()?.also { append("\nDuration : $it") } + document.select("div.label:contains(Download) ~ a.tag_item") .eachText() .joinToString { it.substringAfter(" ") } - .also { append("Quality: $it") } + .also { append("\nQuality : $it") } } - - genre = document.select("div.row_spacer:has(div.label:contains(Tags)) a.tag_item:not(:contains(Suggest))") + genre = document.select("div.label:contains(Tags) ~ a.tag_item:not(:contains(Suggest))") .eachText() .joinToString() - status = SAnime.COMPLETED } @@ -241,24 +186,6 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { // ============================== Settings ============================== override fun setupPreferenceScreen(screen: PreferenceScreen) { - SwitchPreferenceCompat(screen.context).apply { - key = PREF_UPLOADER_FILTER_ENABLED_KEY - title = "Filter by Uploader" - summary = "Load videos only from the specified uploader ID." - setDefaultValue(false) - }.also(screen::addPreference) - - EditTextPreference(screen.context).apply { - key = PREF_UPLOADER_ID_KEY - title = "Uploader ID" - summary = "Enter the ID of the uploader (e.g., 98965). Requires \"Filter by Uploader\" to be enabled." - dialogTitle = "Enter Uploader ID" - var dependency = PREF_UPLOADER_FILTER_ENABLED_KEY - setOnPreferenceChangeListener { _, newValue -> - newValue?.toString().isNullOrBlank().not() - } - }.also(screen::addPreference) - ListPreference(screen.context).apply { key = PREF_QUALITY_KEY title = PREF_QUALITY_TITLE @@ -291,20 +218,14 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { return tagList.toTypedArray() } - override fun getFilterList(): AnimeFilterList = if (preferences.getBoolean(PREF_UPLOADER_FILTER_ENABLED_KEY, false) && - preferences.getString(PREF_UPLOADER_ID_KEY, "")?.isNotBlank() == true - ) { - AnimeFilterList() // If uploader filter is enabled and ID is set, show no other filters - } else { - AnimeFilterList( - OrderFilter(), - CategoryBy(), - AnimeFilter.Separator(), - AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."), - TagFilter(), - TagSearch(tagsResults(tagDocument)), - ) - } + override fun getFilterList(): AnimeFilterList = AnimeFilterList( + OrderFilter(), + CategoryBy(), + AnimeFilter.Separator(), + AnimeFilter.Header("Entered a \"tag\", click on \"filter\" then Click \"reset\" to load tags."), + TagFilter(), + TagSearch(tagsResults(tagDocument)), + ) private class TagFilter : AnimeFilter.Text("Click \"reset\" without any text to load all A-Z tags.", "") @@ -346,8 +267,5 @@ class Rule34Video : ConfigurableAnimeSource, ParsedAnimeHttpSource() { private const val PREF_QUALITY_TITLE = "Preferred quality" private const val PREF_QUALITY_DEFAULT = "1080p" private val PREF_QUALITY_ENTRIES = arrayOf("2160p", "1080p", "720p", "480p", "360p") - - private const val PREF_UPLOADER_FILTER_ENABLED_KEY = "uploader_filter_enabled" - private const val PREF_UPLOADER_ID_KEY = "uploader_id" } } diff --git a/src/pt/tomato/build.gradle b/src/pt/tomato/build.gradle deleted file mode 100644 index 6b964445..00000000 --- a/src/pt/tomato/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -ext { - extName = 'Tomato' - extClass = '.Tomato' - extVersionCode = 1 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/pt/tomato/res/mipmap-hdpi/ic_launcher.png b/src/pt/tomato/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a9e39ecc..00000000 Binary files a/src/pt/tomato/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/pt/tomato/res/mipmap-mdpi/ic_launcher.png b/src/pt/tomato/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 2adff7e0..00000000 Binary files a/src/pt/tomato/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/pt/tomato/res/mipmap-xhdpi/ic_launcher.png b/src/pt/tomato/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 31fa1a88..00000000 Binary files a/src/pt/tomato/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/pt/tomato/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/tomato/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index cc673719..00000000 Binary files a/src/pt/tomato/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/pt/tomato/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/tomato/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index d2f201dc..00000000 Binary files a/src/pt/tomato/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/pt/tomato/src/eu/kanade/tachiyomi/animeextension/pt/tomato/Tomato.kt b/src/pt/tomato/src/eu/kanade/tachiyomi/animeextension/pt/tomato/Tomato.kt deleted file mode 100644 index a8b31601..00000000 --- a/src/pt/tomato/src/eu/kanade/tachiyomi/animeextension/pt/tomato/Tomato.kt +++ /dev/null @@ -1,336 +0,0 @@ -package eu.kanade.tachiyomi.animeextension.pt.tomato - -import android.app.Application -import android.util.Log -import androidx.preference.ListPreference -import androidx.preference.PreferenceScreen -import eu.kanade.tachiyomi.animeextension.pt.tomato.dto.AnimeResultDto -import eu.kanade.tachiyomi.animeextension.pt.tomato.dto.EpisodeInfoDto -import eu.kanade.tachiyomi.animeextension.pt.tomato.dto.EpisodesResultDto -import eu.kanade.tachiyomi.animeextension.pt.tomato.dto.SearchAnimeItemDto -import eu.kanade.tachiyomi.animeextension.pt.tomato.dto.SearchResultDto -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.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.interceptor.rateLimitHost -import eu.kanade.tachiyomi.util.parallelMapBlocking -import eu.kanade.tachiyomi.util.parseAs -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.int -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonArray -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import kotlin.time.Duration.Companion.seconds - -class Tomato : ConfigurableAnimeSource, AnimeHttpSource() { - - override val name = "Tomato" - - override val baseUrl = "https://beta-api.tomatoanimes.com" - - override val lang = "pt-BR" - - override val supportsLatest = true - - private val preferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - - private val json: Json by injectLazy() - - override fun headersBuilder() = super.headersBuilder().add( - "Authorization", - "Bearer $TOKEN", - ) - - private val episodesClient by lazy { - client.newBuilder().rateLimitHost(baseUrl.toHttpUrl(), 1, 0.5.seconds).build() - } - - // ============================== Popular =============================== - override fun popularAnimeRequest(page: Int) = - GET("$baseUrl/v2/animes/feed", headers = headers) - - override fun popularAnimeParse(response: Response): AnimesPage { - val responseJson = response.parseAs() - - val emAlta = responseJson["data"]?.jsonArray?.find { - it.jsonObject["title"]?.jsonPrimitive?.content?.contains("curtidos") == true - } - - val animes = emAlta?.jsonObject?.get("data")?.jsonArray?.parallelMapBlocking { - animeFromId(it.jsonObject["anime_id"]!!.jsonPrimitive.int) - } - ?: emptyList() - - return AnimesPage(animes, false) - } - - // =============================== Latest =============================== - override fun latestUpdatesRequest(page: Int) = - GET("$baseUrl/v2/animes/feed", headers = headers) - - override fun latestUpdatesParse(response: Response): AnimesPage { - val responseJson = response.parseAs() - - val emAlta = responseJson["data"]?.jsonArray?.find { - it.jsonObject["type"]!!.jsonPrimitive.int == 7 - } - - val animes = emAlta?.jsonObject?.get("data")?.jsonArray?.parallelMapBlocking { - animeFromId(it.jsonObject["ep_anime_id"]!!.jsonPrimitive.int) - } - ?: emptyList() - - return AnimesPage(animes, false) - } - - // =============================== Search =============================== - override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request { - val params = TomatoFilters.getSearchParameters(filters) - - val data = buildJsonObject { - put("token", TOKEN) - put("search", query) - put("content_type", "anime") - put("page", page - 1) - - if (params.genres.isNotEmpty()) { - putJsonArray("tags") { - params.genres.forEach { add(it) } - } - } - } - - val body = json.encodeToString(JsonObject.serializer(), data) - .toRequestBody("application/json".toMediaType()) - - return POST("$baseUrl/v2/content/search", headers = headers, body = body) - } - - override fun searchAnimeParse(response: Response): AnimesPage { - val searchResult = response.parseAs().result - val results = searchResult.map { it.toSAnime() } - return AnimesPage(results, false) - } - - private fun SearchAnimeItemDto.toSAnime(): SAnime { - return SAnime.create().apply { - setUrlWithoutDomain("$baseUrl/v2/anime/$id") - title = name - thumbnail_url = image - } - } - - override fun getFilterList() = TomatoFilters.FILTER_LIST - - // =========================== Anime Details ============================ - private fun animeFromId(id: Int): SAnime { - val response = client.newCall( - GET("$baseUrl/v2/anime/$id", headers = headers), - ).execute() - - return animeDetailsParse(response) - } - - override fun animeDetailsParse(response: Response): SAnime { - val anime = response.parseAs() - return SAnime.create().apply { - setUrlWithoutDomain("$baseUrl/v2/anime/${anime.animeDetails.animeId}") - title = anime.animeDetails.animeName - description = anime.animeDetails.animeDescription - genre = anime.animeDetails.animeGenre - thumbnail_url = anime.animeDetails.animeCoverUrl - } - } - - // ============================== Episodes ============================== - override fun episodeListParse(response: Response): List { - val anime = response.parseAs() - - val seasons = anime.animeSeasons - - val episodeList = mutableListOf() - - seasons.forEach { season -> - var nextPage = 0 - do { - val data = buildJsonObject { - put("token", TOKEN) - put("page", nextPage) - put("order", "ASC") - } - - val body = json.encodeToString(JsonObject.serializer(), data) - .toRequestBody("application/json".toMediaType()) - - val request = POST( - "$baseUrl/season/${season.seasonId}/episodes", - headers = headers, - body = body, - ) - val episodes = - episodesClient.newCall(request).execute().parseAs().data - - episodes.forEach { episode -> - val partName = "Temporada ${season.seasonNumber} x ${episode.epNumber}" - val fullName = "$partName - ${episode.epName}" - - val prev = episodeList.find { it.name.contains(partName) } - - val newUrl = "&episode[${season.seasonDubbed}]=${episode.epId}" - if (prev != null) { - prev.url += newUrl - } else { - episodeList.add( - SEpisode.create().apply { - episode_number = episode.epNumber - name = fullName - url = "http://localhost?season=${season.seasonNumber}$newUrl" - }, - ) - } - } - - if (episodes.size == 25) nextPage += 1 else nextPage = -1 - } while (nextPage != -1) - } - - return episodeList.reversed() - } - - // ============================ Video Links ============================= - override suspend fun getVideoList(episode: SEpisode): List