diff --git a/src/es/metroseries/AndroidManifest.xml b/src/es/metroseries/AndroidManifest.xml new file mode 100644 index 000000000..568741e54 --- /dev/null +++ b/src/es/metroseries/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/es/metroseries/build.gradle b/src/es/metroseries/build.gradle new file mode 100644 index 000000000..888991033 --- /dev/null +++ b/src/es/metroseries/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'MetroSeries' + pkgNameSuffix = 'es.metroseries' + extClass = '.MetroSeries' + extVersionCode = 1 + libVersion = '13' +} + +dependencies { + implementation(project(':lib-burstcloud-extractor')) + implementation(project(':lib-mp4upload-extractor')) + implementation(project(':lib-streamwish-extractor')) + implementation(project(':lib-voe-extractor')) + implementation(project(':lib-yourupload-extractor')) + implementation(project(':lib-fastream-extractor')) + implementation(project(':lib-upstream-extractor')) + implementation(project(':lib-filemoon-extractor')) +} + +apply from: "$rootDir/common.gradle" diff --git a/src/es/metroseries/res/mipmap-hdpi/ic_launcher.png b/src/es/metroseries/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..9fe6ce837 Binary files /dev/null and b/src/es/metroseries/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/es/metroseries/res/mipmap-mdpi/ic_launcher.png b/src/es/metroseries/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..0e6e21e74 Binary files /dev/null and b/src/es/metroseries/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/es/metroseries/res/mipmap-xhdpi/ic_launcher.png b/src/es/metroseries/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..1015fa3ea Binary files /dev/null and b/src/es/metroseries/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/es/metroseries/res/mipmap-xxhdpi/ic_launcher.png b/src/es/metroseries/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..5b0d38a80 Binary files /dev/null and b/src/es/metroseries/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/es/metroseries/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/metroseries/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..ce6069700 Binary files /dev/null and b/src/es/metroseries/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/es/metroseries/src/eu/kanade/tachiyomi/animeextension/es/metroseries/MetroSeries.kt b/src/es/metroseries/src/eu/kanade/tachiyomi/animeextension/es/metroseries/MetroSeries.kt new file mode 100644 index 000000000..e3db778dc --- /dev/null +++ b/src/es/metroseries/src/eu/kanade/tachiyomi/animeextension/es/metroseries/MetroSeries.kt @@ -0,0 +1,238 @@ +package eu.kanade.tachiyomi.animeextension.es.metroseries + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +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.burstcloudextractor.BurstCloudExtractor +import eu.kanade.tachiyomi.lib.fastreamextractor.FastreamExtractor +import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor +import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor +import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor +import eu.kanade.tachiyomi.lib.upstreamextractor.UpstreamExtractor +import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor +import eu.kanade.tachiyomi.lib.youruploadextractor.YourUploadExtractor +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import okhttp3.FormBody +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.Locale + +class MetroSeries : ConfigurableAnimeSource, AnimeHttpSource() { + + override val name = "MetroSeries" + + override val baseUrl = "https://metroseries.net" + + override val lang = "es" + + private val json: Json by injectLazy() + + override val supportsLatest = false + + override val client: OkHttpClient = network.cloudflareClient + + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + + override fun popularAnimeRequest(page: Int) = GET("$baseUrl/series/page/$page", headers) + + override fun popularAnimeParse(response: Response): AnimesPage { + val document = response.asJsoup() + val elements = document.select(".post-list, .results-post > .post") + val nextPage = document.select(".nav-links .current ~ a").any() + val animeList = elements.map { element -> + SAnime.create().apply { + setUrlWithoutDomain(element.selectFirst(".lnk-blk")?.attr("abs:href") ?: "") + title = element.selectFirst(".entry-header .entry-title")?.text() ?: "" + thumbnail_url = element.selectFirst(".post-thumbnail figure img")?.attr("abs:src") ?: "" + } + } + return AnimesPage(animeList, nextPage) + } + + override fun latestUpdatesRequest(page: Int) = popularAnimeRequest(page) + + override fun latestUpdatesParse(response: Response) = popularAnimeParse(response) + + override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList) = GET("$baseUrl/?s=$query", headers) + + override fun searchAnimeParse(response: Response) = popularAnimeParse(response) + + override fun animeDetailsParse(response: Response): SAnime { + val document = response.asJsoup() + return SAnime.create().apply { + title = document.selectFirst("main .entry-header .entry-title")?.text() ?: "" + description = document.select("main .entry-content p").joinToString { it.text() } + thumbnail_url = document.selectFirst("main .post-thumbnail img")?.attr("abs:src") + genre = document.select("main .entry-content .tagcloud a").joinToString { it.text() } + status = SAnime.UNKNOWN + } + } + + override fun episodeListParse(response: Response): List { + val episodes = mutableListOf() + val document = response.asJsoup() + document.select(".season-list li a") + .sortedByDescending { it.attr("data-season") }.map { + val post = it.attr("data-post") + val season = it.attr("data-season") + val objectNumber = document.select("#aa-season").attr("data-object") + + val formBody = FormBody.Builder() + .add("action", "action_select_season") + .add("season", season) + .add("post", post) + .add("object", objectNumber) + .build() + + val request = Request.Builder() + .url("https://metroseries.net/wp-admin/admin-ajax.php") + .post(formBody) + .header("Origin", baseUrl) + .header("Referer", response.request.url.toString()) + .header("Content-Type", "application/x-www-form-urlencoded") + .build() + val docEpisodes = client.newCall(request).execute().asJsoup() + + docEpisodes.select(".episodes-list li a").reversed().map { + val epNumber = it.ownText().substringAfter("x").substringBefore("–").trim() + val episode = SEpisode.create().apply { + setUrlWithoutDomain(it.attr("abs:href")) + name = "T$season - E$epNumber - ${it.ownText().substringAfter("–").trim()}" + date_upload = try { + SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH).parse(it.select("span").text()).time + } catch (_: Exception) { System.currentTimeMillis() } + } + episodes.add(episode) + } + } + return episodes + } + + override fun videoListParse(response: Response): List