diff --git a/src/en/toptoonplus/AndroidManifest.xml b/src/en/toptoonplus/AndroidManifest.xml
new file mode 100644
index 000000000..b4571bfa8
--- /dev/null
+++ b/src/en/toptoonplus/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest package="eu.kanade.tachiyomi.extension" />
\ No newline at end of file
diff --git a/src/en/toptoonplus/build.gradle b/src/en/toptoonplus/build.gradle
new file mode 100644
index 000000000..53ecfe3ea
--- /dev/null
+++ b/src/en/toptoonplus/build.gradle
@@ -0,0 +1,19 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlinx-serialization'
+
+ext {
+    extName = 'TOPTOON+'
+    pkgNameSuffix = 'en.toptoonplus'
+    extClass = '.TopToonPlus'
+    extVersionCode = 1
+    libVersion = '1.2'
+    containsNsfw = true
+}
+
+dependencies {
+    implementation project(':lib-ratelimit')
+}
+
+apply from: "$rootDir/common.gradle"
+
diff --git a/src/en/toptoonplus/res/mipmap-hdpi/ic_launcher.png b/src/en/toptoonplus/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..ca1d9bdcd
Binary files /dev/null and b/src/en/toptoonplus/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/src/en/toptoonplus/res/mipmap-mdpi/ic_launcher.png b/src/en/toptoonplus/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..88a6c07fd
Binary files /dev/null and b/src/en/toptoonplus/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/src/en/toptoonplus/res/mipmap-xhdpi/ic_launcher.png b/src/en/toptoonplus/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..bcb95ea8b
Binary files /dev/null and b/src/en/toptoonplus/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/src/en/toptoonplus/res/mipmap-xxhdpi/ic_launcher.png b/src/en/toptoonplus/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..d82a08ce5
Binary files /dev/null and b/src/en/toptoonplus/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/src/en/toptoonplus/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/toptoonplus/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..f17a049b8
Binary files /dev/null and b/src/en/toptoonplus/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/src/en/toptoonplus/res/web_hi_res_512.png b/src/en/toptoonplus/res/web_hi_res_512.png
new file mode 100644
index 000000000..6eb7a2f09
Binary files /dev/null and b/src/en/toptoonplus/res/web_hi_res_512.png differ
diff --git a/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlus.kt b/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlus.kt
new file mode 100644
index 000000000..30b3fab3e
--- /dev/null
+++ b/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlus.kt
@@ -0,0 +1,487 @@
+package eu.kanade.tachiyomi.extension.en.toptoonplus
+
+import android.app.Application
+import android.content.SharedPreferences
+import android.text.InputType
+import android.util.Base64
+import androidx.preference.CheckBoxPreference
+import androidx.preference.EditTextPreference
+import androidx.preference.PreferenceScreen
+import eu.kanade.tachiyomi.annotations.Nsfw
+import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.network.POST
+import eu.kanade.tachiyomi.network.asObservableSuccess
+import eu.kanade.tachiyomi.source.ConfigurableSource
+import eu.kanade.tachiyomi.source.model.FilterList
+import eu.kanade.tachiyomi.source.model.MangasPage
+import eu.kanade.tachiyomi.source.model.Page
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import eu.kanade.tachiyomi.source.online.HttpSource
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.buildJsonObject
+import kotlinx.serialization.json.put
+import okhttp3.CacheControl
+import okhttp3.Headers
+import okhttp3.HttpUrl.Companion.toHttpUrl
+import okhttp3.Interceptor
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import okhttp3.Response
+import rx.Observable
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import uy.kohesive.injekt.injectLazy
+import java.io.IOException
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Locale
+import java.util.UUID
+import java.util.concurrent.TimeUnit
+
+@Nsfw
+class TopToonPlus : HttpSource(), ConfigurableSource {
+
+    override val name = "TOPTOON+"
+
+    override val baseUrl = "https://toptoonplus.com"
+
+    override val lang = "en"
+
+    override val supportsLatest = true
+
+    override val client: OkHttpClient = network.cloudflareClient.newBuilder()
+        .addInterceptor(::authIntercept)
+        .addInterceptor(RateLimitInterceptor(2, 1, TimeUnit.SECONDS))
+        .build()
+
+    private val json: Json by injectLazy()
+
+    private val day: String
+        get() = Calendar.getInstance()
+            .getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.US)!!
+            .toUpperCase(Locale.US)
+
+    private val preferences: SharedPreferences by lazy {
+        Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
+    }
+
+    private val email: String
+        get() = preferences.getString(EMAIL_PREF_KEY, "")!!
+
+    private val password: String
+        get() = preferences.getString(PASSWORD_PREF_KEY, "")!!
+
+    private val showMatureTitles: Boolean
+        get() = preferences.getBoolean(MATURE_PREF_KEY, false)
+
+    private val deviceId: String by lazy { UUID.randomUUID().toString() }
+
+    private var token: String? = null
+    private var userMature: Boolean = false
+
+    override fun headersBuilder(): Headers.Builder = Headers.Builder()
+        .add("Origin", baseUrl)
+        .add("Referer", "$baseUrl/")
+
+    override fun popularMangaRequest(page: Int): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        return GET("$API_URL/api/v1/page/ranking", newHeaders, CacheControl.FORCE_NETWORK)
+    }
+
+    override fun popularMangaParse(response: Response): MangasPage {
+        val result = json.decodeFromString<TopToonResult<TopToonRanking>>(response.body!!.string())
+
+        if (result.data == null) {
+            return MangasPage(emptyList(), hasNextPage = false)
+        }
+
+        val comicList = result.data.ranking.map(::popularMangaFromObject)
+
+        return MangasPage(comicList, hasNextPage = false)
+    }
+
+    private fun popularMangaFromObject(comic: TopToonComic) = SManga.create().apply {
+        title = comic.information?.title.orEmpty()
+        thumbnail_url = comic.thumbnailImage?.jpeg?.firstOrNull()?.path.orEmpty()
+        url = "/comic/${comic.comicId}"
+    }
+
+    override fun latestUpdatesRequest(page: Int): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        return GET("$API_URL/api/v1/page/daily/$day", newHeaders, CacheControl.FORCE_NETWORK)
+    }
+
+    override fun latestUpdatesParse(response: Response): MangasPage {
+        val result = json.decodeFromString<TopToonResult<TopToonDaily>>(response.body!!.string())
+
+        if (result.data == null) {
+            return MangasPage(emptyList(), hasNextPage = false)
+        }
+
+        val comicList = result.data.daily.map(::latestMangaFromObject)
+
+        return MangasPage(comicList, hasNextPage = false)
+    }
+
+    private fun latestMangaFromObject(comic: TopToonComic) = popularMangaFromObject(comic)
+
+    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
+        return super.fetchSearchManga(page, query, filters)
+            .map { result ->
+                val filteredList = result.mangas.filter { it.title.contains(query, true) }
+                MangasPage(filteredList, result.hasNextPage)
+            }
+    }
+
+    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("Language", lang)
+            .add("Mature", if (showMatureTitles) "1" else "0")
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        return GET("$API_URL/api/v1/search/totalsearch", newHeaders, CacheControl.FORCE_NETWORK)
+    }
+
+    override fun searchMangaParse(response: Response): MangasPage {
+        if (response.request.url.toString().contains("ranking")) {
+            return popularMangaParse(response)
+        }
+
+        val result = json.decodeFromString<TopToonResult<List<TopToonComic>>>(response.body!!.string())
+
+        if (result.data == null) {
+            return MangasPage(emptyList(), hasNextPage = false)
+        }
+
+        val comicList = result.data.map(::searchMangaFromObject)
+
+        return MangasPage(comicList, hasNextPage = false)
+    }
+
+    private fun searchMangaFromObject(comic: TopToonComic) = popularMangaFromObject(comic)
+
+    // Workaround to allow "Open in browser" use the real URL.
+    override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
+        return client.newCall(mangaDetailsApiRequest(manga.url))
+            .asObservableSuccess()
+            .map { response ->
+                mangaDetailsParse(response).apply { initialized = true }
+            }
+    }
+
+    private fun mangaDetailsApiRequest(mangaUrl: String): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        val comicId = mangaUrl.substringAfterLast("/")
+
+        return GET("$API_URL/api/v1/page/episode?comicId=$comicId", newHeaders)
+    }
+
+    override fun mangaDetailsRequest(manga: SManga): Request {
+        val newHeaders = headersBuilder()
+            .removeAll("Accept")
+            .build()
+
+        return GET(baseUrl + manga.url, newHeaders)
+    }
+
+    override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
+        val result = json.decodeFromString<TopToonResult<TopToonDetails>>(response.body!!.string())
+
+        if (result.data == null) {
+            throw Exception(COULD_NOT_PARSE_RESPONSE)
+        }
+
+        val comic = result.data.comic!!
+
+        title = comic.information?.title.orEmpty()
+        thumbnail_url = comic.thumbnailImage?.jpeg?.firstOrNull()?.path.orEmpty()
+        description = comic.information?.description
+        status = SManga.ONGOING
+        author = comic.author.joinToString(", ") { it.trim() }
+    }
+
+    override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga.url)
+
+    override fun chapterListParse(response: Response): List<SChapter> {
+        val result = json.decodeFromString<TopToonResult<TopToonDetails>>(response.body!!.string())
+
+        if (result.data == null) {
+            throw Exception(COULD_NOT_PARSE_RESPONSE)
+        }
+
+        return result.data.episode
+            .filter { episode -> episode.information?.payType == 0 }
+            .map(::chapterFromObject)
+            .reversed()
+    }
+
+    private fun chapterFromObject(chapter: TopToonEpisode): SChapter = SChapter.create().apply {
+        name = chapter.information?.title.orEmpty() +
+            (if (chapter.information?.subTitle.isNullOrEmpty().not()) " - " + chapter.information?.subTitle else "")
+        chapter_number = chapter.order.toFloat()
+        scanlator = this@TopToonPlus.name
+        date_upload = chapter.information?.publishedAt?.date.orEmpty().toDate()
+        url = "/comic/${chapter.comicId}/${chapter.episodeId}"
+    }
+
+    override fun pageListRequest(chapter: SChapter): Request {
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("Language", "en")
+            .add("Token", token.orEmpty().ifEmpty { "null" })
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        val comicId = chapter.url
+            .substringAfter("/comic/")
+            .substringBefore("/")
+        val episodeId = chapter.url.substringAfterLast("/")
+
+        val apiUrl = "$API_URL/check/isUsableEpisode".toHttpUrl().newBuilder()
+            .addQueryParameter("comicId", comicId)
+            .addQueryParameter("episodeId", episodeId)
+            .addQueryParameter("location", "episode")
+            .addQueryParameter("action", "episode_click")
+            .toString()
+
+        return GET(apiUrl, newHeaders, CacheControl.FORCE_NETWORK)
+    }
+
+    override fun pageListParse(response: Response): List<Page> {
+        val result = json.decodeFromString<TopToonResult<TopToonUsableEpisode>>(response.body!!.string())
+
+        if (result.data == null) {
+            throw Exception(COULD_NOT_PARSE_RESPONSE)
+        }
+
+        val usableEpisode = result.data
+
+        if (usableEpisode.isFree.not() ||
+            usableEpisode.episodePrice?.payType != 0 ||
+            usableEpisode.purchaseMethod.firstOrNull() != "FREE_EPISODE"
+        ) {
+            throw Exception(CHAPTER_NOT_FREE)
+        }
+
+        return usableEpisode.episode!!.contentImage?.jpeg.orEmpty()
+            .mapIndexed { i, page ->
+                Page(i, baseUrl, page.path)
+            }
+    }
+
+    override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
+
+    override fun imageUrlParse(response: Response): String = ""
+
+    override fun imageRequest(page: Page): Request {
+        val newHeaders = headersBuilder()
+            .set("Accept", ACCEPT_IMAGE)
+            .set("Referer", page.url)
+            .build()
+
+        return GET(page.imageUrl!!, newHeaders)
+    }
+
+    override fun setupPreferenceScreen(screen: PreferenceScreen) {
+        val emailPref = EditTextPreference(screen.context).apply {
+            key = EMAIL_PREF_KEY
+            title = EMAIL_PREF_TITLE
+            setDefaultValue("")
+            summary = EMAIL_PREF_SUMMARY
+            dialogTitle = EMAIL_PREF_TITLE
+
+            setOnPreferenceChangeListener { _, newValue ->
+                token = null
+
+                preferences.edit()
+                    .putString(EMAIL_PREF_KEY, newValue as String)
+                    .commit()
+            }
+        }
+
+        val passwordPref = EditTextPreference(screen.context).apply {
+            key = PASSWORD_PREF_KEY
+            title = PASSWORD_PREF_TITLE
+            setDefaultValue("")
+            summary = PASSWORD_PREF_SUMMARY
+            dialogTitle = PASSWORD_PREF_TITLE
+
+            setOnBindEditTextListener {
+                it.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
+            }
+
+            setOnPreferenceChangeListener { _, newValue ->
+                token = null
+
+                preferences.edit()
+                    .putString(PASSWORD_PREF_KEY, newValue as String)
+                    .commit()
+            }
+        }
+
+        val maturePref = CheckBoxPreference(screen.context).apply {
+            key = MATURE_PREF_KEY
+            title = MATURE_PREF_TITLE
+            setDefaultValue(MATURE_PREF_DEFAULT)
+            summary = MATURE_PREF_SUMMARY
+
+            setOnPreferenceChangeListener { _, newValue ->
+                preferences.edit()
+                    .putBoolean(MATURE_PREF_KEY, newValue as Boolean)
+                    .commit()
+            }
+        }
+
+        screen.addPreference(emailPref)
+        screen.addPreference(passwordPref)
+        screen.addPreference(maturePref)
+    }
+
+    private fun authIntercept(chain: Interceptor.Chain): Response {
+        val isApiCall = chain.request().url.toString().contains(API_URL)
+
+        if (isApiCall && email.isNotBlank() && password.isNotBlank()) {
+            if (token == null) {
+                val loginRequest = loginRequest(email, password)
+                val loginResponse = chain.proceed(loginRequest)
+                token = loginParse(loginResponse)
+
+                loginResponse.close()
+            }
+
+            if (userMature != showMatureTitles && token != null) {
+                // Preference takes precedence over website.
+                val matureRequest = matureRequest(token!!, showMatureTitles)
+                val matureResponse = chain.proceed(matureRequest)
+                userMature = showMatureTitles
+
+                matureResponse.close()
+            }
+
+            val newRequest = chain.request().newBuilder()
+                .removeHeader("Token")
+                .addHeader("Token", token.orEmpty().ifEmpty { "null" })
+                .build()
+
+            return chain.proceed(newRequest)
+        }
+
+        return chain.proceed(chain.request())
+    }
+
+    private fun loginRequest(email: String, password: String): Request {
+        val requestPayload = buildJsonObject {
+            put("auth", 0)
+            put("deviceId", deviceId)
+            put("is17", false)
+            put("password", password)
+            put("userId", email)
+        }
+
+        val requestBody = requestPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
+
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("Content-Length", requestBody.contentLength().toString())
+            .add("Content-Type", requestBody.contentType().toString())
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        return POST("$API_URL/auth/generateToken", newHeaders, requestBody, CacheControl.FORCE_NETWORK)
+    }
+
+    private fun loginParse(response: Response): String {
+        if (response.code != 200) {
+            throw IOException(COULD_NOT_LOGIN)
+        }
+
+        val result = json.decodeFromString<TopToonResult<TopToonAuth>>(response.body!!.string())
+
+        if (result.data == null) {
+            throw IOException(COULD_NOT_LOGIN)
+        }
+
+        userMature = result.data.mature == 1
+
+        return result.data.token
+    }
+
+    private fun matureRequest(token: String, mature: Boolean): Request {
+        val requestPayload = buildJsonObject {
+            put("mature", if (mature) 1 else 0)
+        }
+
+        val requestBody = requestPayload.toString().toRequestBody(JSON_MEDIA_TYPE)
+
+        val newHeaders = headersBuilder()
+            .add("Accept", ACCEPT_JSON)
+            .add("Content-Length", requestBody.contentLength().toString())
+            .add("Content-Type", requestBody.contentType().toString())
+            .add("Token", token)
+            .add("Uuid", deviceId)
+            .add("X-Api-Key", API_KEY)
+            .build()
+
+        return POST("$API_URL/users/setUser", newHeaders, requestBody, CacheControl.FORCE_NETWORK)
+    }
+
+    private fun String.toDate(): Long {
+        return try {
+            DATE_FORMATTER.parse(this)?.time ?: 0L
+        } catch (e: ParseException) {
+            0L
+        }
+    }
+
+    companion object {
+        private const val API_URL = "https://api.toptoonplus.com"
+        private val API_KEY by lazy {
+            Base64.decode("U1VQRVJDT09MQVBJS0VZMjAyMSNAIyg=", Base64.DEFAULT)
+                .toString(charset("UTF-8"))
+        }
+
+        private const val ACCEPT_JSON = "application/json, text/plain, */*"
+        private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/*,*/*;q=0.8"
+
+        private val JSON_MEDIA_TYPE = "application/json; charset=UTF-8".toMediaType()
+
+        private const val COULD_NOT_PARSE_RESPONSE = "Could not parse the API response."
+        private const val COULD_NOT_LOGIN = "The e-mail or password provided are incorrect."
+        private const val CHAPTER_NOT_FREE = "This chapter is not free to read."
+
+        private const val EMAIL_PREF_KEY = "email"
+        private const val EMAIL_PREF_TITLE = "E-mail"
+        private const val EMAIL_PREF_SUMMARY = "Define here the e-mail of your existing account."
+
+        private const val PASSWORD_PREF_KEY = "password"
+        private const val PASSWORD_PREF_TITLE = "Password"
+        private const val PASSWORD_PREF_SUMMARY = "Define here your account password."
+
+        private const val MATURE_PREF_KEY = "mature"
+        private const val MATURE_PREF_TITLE = "Show mature titles"
+        private const val MATURE_PREF_SUMMARY = "This setting only takes effect if you are signed in."
+        private const val MATURE_PREF_DEFAULT = false
+
+        private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) }
+    }
+}
diff --git a/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlusDto.kt b/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlusDto.kt
new file mode 100644
index 000000000..e2d6a00ba
--- /dev/null
+++ b/src/en/toptoonplus/src/eu/kanade/tachiyomi/extension/en/toptoonplus/TopToonPlusDto.kt
@@ -0,0 +1,95 @@
+package eu.kanade.tachiyomi.extension.en.toptoonplus
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class TopToonResult<T>(
+    val uuid: String? = "",
+    val data: T? = null
+)
+
+@Serializable
+data class TopToonRanking(
+    val ranking: List<TopToonComic> = emptyList()
+)
+
+@Serializable
+data class TopToonDaily(
+    val daily: List<TopToonComic> = emptyList()
+)
+
+@Serializable
+data class TopToonDetails(
+    val comic: TopToonComic? = null,
+    val episode: List<TopToonEpisode> = emptyList()
+)
+
+@Serializable
+data class TopToonUsableEpisode(
+    val episode: TopToonEpisode? = null,
+    val episodePrice: TopToonEpisodePrice? = null,
+    val isFree: Boolean = false,
+    val needLogin: Boolean = false,
+    val purchaseMethod: List<String> = emptyList()
+)
+
+@Serializable
+data class TopToonEpisodePrice(
+    val payType: Int = -1
+)
+
+@Serializable
+data class TopToonComic(
+    val author: List<String> = emptyList(),
+    val comicId: Int = -1,
+    val information: TopToonComicInfo? = null,
+    val thumbnailImage: TopToonComicPoster? = null,
+)
+
+@Serializable
+data class TopToonComicInfo(
+    val description: String = "",
+    val mature: Int = 0,
+    val title: String = ""
+)
+
+@Serializable
+data class TopToonComicPoster(
+    val jpeg: List<TopToonImage> = emptyList()
+)
+
+@Serializable
+data class TopToonImage(
+    val path: String = ""
+)
+
+@Serializable
+data class TopToonEpisode(
+    val comicId: Int = -1,
+    val contentImage: TopToonComicPoster? = null,
+    val episodeId: Int = -1,
+    val information: TopToonEpisodeInfo? = null,
+    val order: Int = -1
+)
+
+@Serializable
+data class TopToonEpisodeInfo(
+    val needLogin: Int = 0,
+    val payType: Int = 0,
+    val publishedAt: TopToonEpisodeDate? = null,
+    val subTitle: String = "",
+    val title: String = ""
+)
+
+@Serializable
+data class TopToonEpisodeDate(
+    val date: String = ""
+)
+
+@Serializable
+data class TopToonAuth(
+    val auth: Int = 0,
+    val mature: Int = 0,
+    val sign: Int = 0,
+    val token: String = ""
+)