diff --git a/src/de/animebase/AndroidManifest.xml b/src/de/animebase/AndroidManifest.xml
new file mode 100644
index 000000000..acb4de356
--- /dev/null
+++ b/src/de/animebase/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/src/de/animebase/build.gradle b/src/de/animebase/build.gradle
new file mode 100644
index 000000000..1271d80c9
--- /dev/null
+++ b/src/de/animebase/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+ extName = 'Anime-Base'
+ pkgNameSuffix = 'de.animebase'
+ extClass = '.Anime-Base'
+ extVersionCode = 1
+ libVersion = '13'
+}
+
+dependencies {
+ implementation(project(':lib-streamsb-extractor'))
+}
+
+apply from: "$rootDir/common.gradle"
diff --git a/src/de/animebase/res/mipmap-hdpi/ic_launcher.png b/src/de/animebase/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..dfa1af584
Binary files /dev/null and b/src/de/animebase/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/de/animebase/res/mipmap-mdpi/ic_launcher.png b/src/de/animebase/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..6fdc5b037
Binary files /dev/null and b/src/de/animebase/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/de/animebase/res/mipmap-xhdpi/ic_launcher.png b/src/de/animebase/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..d857aaf2c
Binary files /dev/null and b/src/de/animebase/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/de/animebase/res/mipmap-xxhdpi/ic_launcher.png b/src/de/animebase/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..80e8afc70
Binary files /dev/null and b/src/de/animebase/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/de/animebase/res/mipmap-xxxhdpi/ic_launcher.png b/src/de/animebase/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..83329614e
Binary files /dev/null and b/src/de/animebase/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/Anime-Base.kt b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/Anime-Base.kt
new file mode 100644
index 000000000..3d40fbe9a
--- /dev/null
+++ b/src/de/animebase/src/eu/kanade/tachiyomi/animeextension/de/animebase/Anime-Base.kt
@@ -0,0 +1,325 @@
+package eu.kanade.tachiyomi.animeextension.de.animebase
+
+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.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.streamsbextractor.StreamSBExtractor
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.util.asJsoup
+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 `Anime-Base` : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
+
+ override val name = "Anime-Base"
+
+ override val baseUrl = "https://anime-base.net"
+
+ override val lang = "de"
+
+ override val supportsLatest = false
+
+ override val client: OkHttpClient = network.client
+
+ private val preferences: SharedPreferences by lazy {
+ Injekt.get().getSharedPreferences("source_$id", 0x0000)
+ }
+
+ override fun popularAnimeSelector(): String = "div.table-responsive a"
+
+ override fun popularAnimeRequest(page: Int): Request {
+ val cookieInterceptor = client.newBuilder().addInterceptor(CookieInterceptor(baseUrl)).build()
+ val headers = cookieInterceptor.newCall(GET(baseUrl)).execute().request.headers
+ return GET("$baseUrl/favorites", headers = headers)
+ }
+
+ override fun popularAnimeFromElement(element: Element): SAnime {
+ val anime = SAnime.create()
+ anime.setUrlWithoutDomain(element.attr("href"))
+ anime.thumbnail_url = element.select("div.thumbnail img").attr("src")
+ anime.title = element.select("div.thumbnail div.caption h3").text()
+ return anime
+ }
+
+ override fun popularAnimeNextPageSelector(): String? = null
+
+ // episodes
+
+ override fun episodeListSelector() = throw Exception("not used")
+
+ override fun episodeListParse(response: Response): List {
+ val document = response.asJsoup()
+ val episodeList = mutableListOf()
+ val episodeElement = document.select(
+ "div.tab-content table#angebotTabelle tbody tr.episodetoggleclass-gersub, div.tab-content table#angebotTabelle tbody tr.episodetoggleclass-Filme button[${
+ if (document.select("div.tab-content table#angebotTabelle tbody tr.episodetoggleclass-Filme button[data-dubbed=\"0\"]").isNullOrEmpty()){
+ "data-dubbed=\"1\""
+ } else {
+ "data-dubbed=\"0\""
+ }
+ }][data-hoster=\"1\"], div.tab-content table#angebotTabelle tbody tr.episodetoggleclass-Specials button[data-dubbed=\"0\"][data-hoster=\"1\"]"
+ )
+ episodeElement.forEach {
+ val episode = episodeFromElement(it)
+ episodeList.add(episode)
+ }
+ return episodeList.reversed()
+ }
+
+ override fun episodeFromElement(element: Element): SEpisode {
+ val episode = SEpisode.create()
+ val id = element.select("button[data-hoster=\"1\"]").attr("data-serieid")
+ val epnum = element.select("button[data-hoster=\"1\"]").attr("data-folge")
+ val host = element.select("button[data-hoster=\"1\"]").attr("data-hoster")
+ if (element.attr("data-dubbed").contains("1")) {
+ if (element.attr("data-special").contains("2")) {
+ episode.episode_number = 1F
+ episode.name = "Film $epnum"
+ episode.setUrlWithoutDomain("/episode/$id/$epnum/1/$host/2")
+ }
+ } else {
+ if (element.select("button[data-hoster=\"1\"]").attr("data-special").contains("2")) {
+ episode.episode_number = 1F
+ episode.name = "Film ${epnum.toInt() - 1}"
+ episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/2")
+ } else {
+ val season = element.attr("class")
+ .substringAfter("-").substringBefore(" ger")
+ episode.name = "Staffel $season Folge $epnum : " + element.select("td.openEpisodeEmbed").toString()
+ .substringAfter("\">").substringBefore("Filler!", "").replace(" ", "")
+ episode.episode_number = element.select("button[data-hoster=\"1\"]").attr("data-folge").toFloat()
+ episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/0")
+ }
+ if (element.select("button[data-hoster=\"1\"]").attr("data-special").contains("1")) {
+ episode.episode_number = 1F
+ episode.name = "Special ${epnum.toInt() - 1}"
+ episode.setUrlWithoutDomain("/episode/$id/$epnum/0/$host/1")
+ }
+ }
+ return episode
+ }
+
+ // Video Extractor
+
+ override fun videoListParse(response: Response): List