diff --git a/src/all/ninenineninehentai/AndroidManifest.xml b/src/all/ninenineninehentai/AndroidManifest.xml
deleted file mode 100644
index cfd078c12..000000000
--- a/src/all/ninenineninehentai/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/all/ninenineninehentai/build.gradle b/src/all/ninenineninehentai/build.gradle
deleted file mode 100644
index 601328b4e..000000000
--- a/src/all/ninenineninehentai/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-ext {
- extName = 'AnimeH'
- extClass = '.AnimeHFactory'
- extVersionCode = 7
- isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 262ecd929..000000000
Binary files a/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 505ff6a4a..000000000
Binary files a/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 2c5e77183..000000000
Binary files a/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 4411c7a85..000000000
Binary files a/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index ac6f49e7b..000000000
Binary files a/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeH.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeH.kt
deleted file mode 100644
index f3bc3eb4e..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeH.kt
+++ /dev/null
@@ -1,334 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.content.SharedPreferences
-import androidx.preference.ListPreference
-import androidx.preference.PreferenceScreen
-import androidx.preference.SwitchPreferenceCompat
-import eu.kanade.tachiyomi.extension.all.ninenineninehentai.Url.Companion.toAbsUrl
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-import eu.kanade.tachiyomi.network.asObservableSuccess
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
-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 eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.decodeFromString
-import kotlinx.serialization.encodeToString
-import kotlinx.serialization.json.Json
-import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-import okhttp3.MediaType.Companion.toMediaTypeOrNull
-import okhttp3.Request
-import okhttp3.RequestBody
-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.text.SimpleDateFormat
-import java.util.Locale
-
-open class AnimeH(
- final override val lang: String,
- private val siteLang: String = lang,
-) : HttpSource(), ConfigurableSource {
-
- override val name = "AnimeH"
-
- override val baseUrl = "https://animeh.to"
-
- private val apiUrl = "https://api.animeh.to/api"
-
- override val supportsLatest = true
-
- private val json: Json by injectLazy()
-
- override val client = network.cloudflareClient.newBuilder()
- .addInterceptor { chain ->
- val request = chain.request()
- val url = request.url
-
- if (url.host != "127.0.0.1") {
- return@addInterceptor chain.proceed(request)
- }
-
- val newRequest = request.newBuilder()
- .url(
- url.newBuilder()
- .host(preference.cdnUrl)
- .build(),
- ).build()
-
- return@addInterceptor chain.proceed(newRequest)
- }
- .rateLimit(1)
- .build()
-
- private val preference by lazy {
- Injekt.get().getSharedPreferences("source_$id", 0x0000)
- }
-
- override fun headersBuilder() = super.headersBuilder()
- .set("Referer", "$baseUrl/")
-
- override fun popularMangaRequest(page: Int): Request {
- val payload = GraphQL(
- PopularVariables(size, page, 1, siteLang),
- POPULAR_QUERY,
- ).toJsonRequestBody()
-
- return POST(apiUrl, headers, payload)
- }
-
- override fun popularMangaParse(response: Response) = browseMangaParse(response)
-
- override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList())
- override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
-
- override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
- return if (query.startsWith(SEARCH_PREFIX)) {
- val mangaId = query.substringAfter(SEARCH_PREFIX)
- client.newCall(mangaFromIDRequest(mangaId))
- .asObservableSuccess()
- .map(::searchMangaFromIDParse)
- } else {
- super.fetchSearchManga(page, query, filters)
- }
- }
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- val payload = GraphQL(
- SearchVariables(
- size = size,
- page = page,
- search = SearchPayload(
- query = query.trim().takeUnless { it.isEmpty() },
- language = siteLang,
- sortBy = filters.firstInstanceOrNull()?.selected,
- format = filters.firstInstanceOrNull()?.selected,
- tags = filters.firstInstanceOrNull()?.tags,
- excludeTags = filters.firstInstanceOrNull()?.tags,
- pagesRangeStart = filters.firstInstanceOrNull()?.value,
- pagesRangeEnd = filters.firstInstanceOrNull()?.value,
- ),
- ),
- SEARCH_QUERY,
- ).toJsonRequestBody()
-
- return POST(apiUrl, headers, payload)
- }
-
- override fun searchMangaParse(response: Response) = browseMangaParse(response)
- override fun getFilterList() = getFilters()
-
- private fun mangaFromIDRequest(id: String): Request {
- val payload = GraphQL(
- IdVariables(id),
- DETAILS_QUERY,
- ).toJsonRequestBody()
-
- return POST(apiUrl, headers, payload)
- }
-
- private fun searchMangaFromIDParse(response: Response): MangasPage {
- val res = response.parseAs()
-
- val manga = res.data.details
- .takeIf { it.language == siteLang || lang == "all" }
- ?.let { manga ->
- preference.dateMap = preference.dateMap.also { dateMap ->
- manga.uploadDate?.let { dateMap[manga.id] = it }
- }
- manga.toSManga(preference.shortTitle)
- }
-
- return MangasPage(listOfNotNull(manga), false)
- }
-
- override fun mangaDetailsRequest(manga: SManga): Request {
- return mangaFromIDRequest(manga.url)
- }
-
- override fun mangaDetailsParse(response: Response): SManga {
- val res = response.parseAs()
- val manga = res.data.details
-
- preference.dateMap = preference.dateMap.also { dateMap ->
- manga.uploadDate?.let { dateMap[manga.id] = it }
- }
-
- return manga.toSManga(preference.shortTitle)
- }
-
- override fun getMangaUrl(manga: SManga) = "$baseUrl/hchapter/${manga.url}"
-
- override fun fetchChapterList(manga: SManga): Observable> {
- val group = manga.description
- ?.substringAfter("Group:", "")
- ?.substringBefore("\n")
- ?.trim()
- ?.takeUnless { it.isEmpty() }
-
- return Observable.just(
- listOf(
- SChapter.create().apply {
- name = "Chapter"
- url = manga.url
- date_upload = preference.dateMap[manga.url].parseDate()
- scanlator = group
- },
- ),
- )
- }
-
- override fun getChapterUrl(chapter: SChapter) = "$baseUrl/hchapter/${chapter.url}"
-
- override fun pageListRequest(chapter: SChapter): Request {
- val payload = GraphQL(
- IdVariables(chapter.url),
- PAGES_QUERY,
- ).toJsonRequestBody()
-
- return POST(apiUrl, headers, payload)
- }
-
- override fun pageListParse(response: Response): List {
- val res = response.parseAs()
-
- val pages = res.data.chapter.pages?.firstOrNull()
- ?: return emptyList()
-
- val cdnUrl = "https://${getUpdatedCdn(res.data.chapter.id)}/"
- val cdn = pages.urlPart.toAbsUrl(cdnUrl)
-
- val selectedImages = when (preference.getString(PREF_IMG_QUALITY_KEY, "original")) {
- "medium" -> pages.qualityMedium?.mapIndexed { i, it ->
- it ?: pages.qualityOriginal[i]
- }
- else -> pages.qualityOriginal
- } ?: pages.qualityOriginal
-
- return selectedImages.mapIndexed { index, image ->
- Page(index, "", "$cdn/${image.url}")
- }
- }
-
- private fun getUpdatedCdn(chapterId: String): String {
- val url = "$baseUrl/hchapter/$chapterId"
- val document = client.newCall(GET(url, headers))
- .execute().use { it.asJsoup() }
-
- val cdnHost = document.selectFirst("meta[property=og:image]")
- ?.attr("content")
- ?.toHttpUrlOrNull()
- ?.host
-
- return cdnHost?.also {
- preference.cdnUrl = it
- } ?: preference.cdnUrl
- }
-
- private inline fun String.parseAs(): T =
- json.decodeFromString(this)
-
- private inline fun Response.parseAs(): T =
- use { body.string() }.parseAs()
-
- private inline fun List<*>.firstInstanceOrNull(): T? =
- filterIsInstance().firstOrNull()
-
- private inline fun T.toJsonRequestBody(): RequestBody =
- json.encodeToString(this)
- .toRequestBody(JSON_MEDIA_TYPE)
-
- private fun String?.parseDate(): Long {
- return runCatching {
- dateFormat.parse(this!!.trim())!!.time
- }.getOrDefault(0L)
- }
-
- private inline fun browseMangaParse(response: Response): MangasPage {
- val res = response.parseAs>()
- val mangas = res.data.chapters.edges
- val dateMap = preference.dateMap
- val useShortTitle = preference.shortTitle
- val entries = mangas.map { manga ->
- manga.uploadDate?.let { dateMap[manga.id] = it }
- manga.toSManga(useShortTitle)
- }
- preference.dateMap = dateMap
- val hasNextPage = mangas.size == size
-
- return MangasPage(entries, hasNextPage)
- }
-
- override fun setupPreferenceScreen(screen: PreferenceScreen) {
- ListPreference(screen.context).apply {
- key = PREF_IMG_QUALITY_KEY
- title = "Default Image Quality"
- entries = arrayOf("Original", "Medium")
- entryValues = arrayOf("original", "medium")
- setDefaultValue("original")
- summary = "%s"
- }.also(screen::addPreference)
-
- SwitchPreferenceCompat(screen.context).apply {
- key = PREF_SHORT_TITLE
- title = "Display Short Titles"
- summaryOff = "Showing Long Titles"
- summaryOn = "Showing short Titles"
- setDefaultValue(false)
- }.also(screen::addPreference)
- }
-
- private var SharedPreferences.dateMap: MutableMap
- get() {
- val jsonMap = getString(PREF_DATE_MAP_KEY, "{}")!!
- val dateMap = runCatching { jsonMap.parseAs>() }
- return dateMap.getOrDefault(mutableMapOf())
- }
-
- @SuppressLint("ApplySharedPref")
- set(dateMap) {
- edit()
- .putString(PREF_DATE_MAP_KEY, json.encodeToString(dateMap))
- .commit()
- }
-
- private var SharedPreferences.cdnUrl: String
- get() = getString(PREF_CDN_URL, DEFAULT_CDN) ?: DEFAULT_CDN
-
- @SuppressLint("ApplySharedPref")
- set(cdnUrl) {
- edit().putString(PREF_CDN_URL, cdnUrl).commit()
- }
-
- private val SharedPreferences.shortTitle get() = getBoolean(PREF_SHORT_TITLE, false)
-
- override fun chapterListParse(response: Response) = throw UnsupportedOperationException()
- override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
-
- companion object {
- private const val size = 20
- const val SEARCH_PREFIX = "id:"
-
- private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull()
- private val dateFormat by lazy {
- SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH)
- }
-
- private const val PREF_DATE_MAP_KEY = "pref_date_map"
- private const val PREF_CDN_URL = "pref_cdn_url"
- private const val PREF_IMG_QUALITY_KEY = "pref_image_quality"
- private const val PREF_SHORT_TITLE = "pref_short_title"
-
- private const val DEFAULT_CDN = "edge.fast4speed.rsvp"
- }
-}
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHDto.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHDto.kt
deleted file mode 100644
index fff6a6421..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHDto.kt
+++ /dev/null
@@ -1,169 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import eu.kanade.tachiyomi.source.model.SManga
-import eu.kanade.tachiyomi.source.model.UpdateStrategy
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-import java.util.Locale
-
-typealias ApiDetailsResponse = Data
-
-typealias ApiPageListResponse = Data
-
-@Serializable
-data class Data(val data: T)
-
-@Serializable
-data class Edges(val edges: List)
-
-interface BrowseResponse {
- val chapters: Edges
-}
-
-@Serializable
-data class PopularResponse(
- @SerialName("queryPopularChapters") override val chapters: Edges,
-) : BrowseResponse
-
-@Serializable
-data class SearchResponse(
- @SerialName("queryChapters") override val chapters: Edges,
-) : BrowseResponse
-
-@Serializable
-data class DetailsResponse(
- @SerialName("queryChapter") val details: ChapterResponse,
-)
-
-@Serializable
-data class ChapterResponse(
- @SerialName("_id") val id: String,
- val name: String,
- val uploadDate: String? = null,
- val format: String? = null,
- val description: String? = null,
- val language: String? = null,
- val pages: Int? = null,
- @SerialName("firstPics") val cover: List? = emptyList(),
- val tags: List? = emptyList(),
-) {
- fun toSManga(shortTitle: Boolean) = SManga.create().apply {
- url = id
- title = if (shortTitle) name.replace(shortenTitleRegex, "").trim() else name
- thumbnail_url = cover?.firstOrNull()?.absUrl
- author = this@ChapterResponse.author
- artist = author
- genre = genres
- description = buildString {
- if (!this@ChapterResponse.description.isNullOrEmpty()) append(this@ChapterResponse.description.trim(), "\n\n")
- if (formatParsed != null) append("Format: ${formatParsed}\n")
- if (languageParsed != null) append("Language: $languageParsed\n")
- if (group != null) append("Group: $group\n")
- if (characters != null) append("Character(s): $characters\n")
- if (parody != null) append("Parody: $parody\n")
- if (magazine != null) append("Magazine: $magazine\n")
- if (pages != null) append("Pages: $pages\n")
- }
- status = SManga.COMPLETED
- update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
- initialized = true
- }
-
- private val formatParsed = when (format) {
- "artistcg" -> "ArtistCG"
- "gamecg" -> "GameCG"
- "imageset" -> "ImageSet"
- else -> format?.capitalize()
- }
-
- private val languageParsed = when (language) {
- "en" -> "English"
- "jp" -> "Japanese"
- "cn" -> "Chinese"
- "es" -> "Spanish"
- else -> language
- }
-
- private val author = tags?.firstOrNull { it.tagType == "artist" }?.tagName?.capitalize()
-
- private val group = tags?.filter { it.tagType == "group" }
- ?.joinToString { it.tagName.capitalize() }
- ?.takeUnless { it.isEmpty() }
-
- private val characters = tags?.filter { it.tagType == "character" }
- ?.joinToString { it.tagName.capitalize() }
- ?.takeUnless { it.isEmpty() }
-
- private val parody = tags?.filter { it.tagType == "parody" }
- ?.joinToString { it.tagName.capitalize() }
- ?.takeUnless { it.isEmpty() }
-
- private val magazine = tags?.filter { it.tagType == "magazine" }
- ?.joinToString { it.tagName.capitalize() }
- ?.takeUnless { it.isEmpty() }
-
- private val genres = tags?.filterNot { it.tagType in filterTags }
- ?.joinToString { it.tagName.capitalize() }
- ?.takeUnless { it.isEmpty() }
-
- companion object {
- private val filterTags = listOf("artist", "group", "character", "parody", "magazine")
- private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""")
-
- private fun String.capitalize(): String {
- return this.trim().split(" ").joinToString(" ") { word ->
- word.replaceFirstChar {
- if (it.isLowerCase()) {
- it.titlecase(
- Locale.getDefault(),
- )
- } else {
- it.toString()
- }
- }
- }
- }
- }
-}
-
-@Serializable
-data class Url(val url: String) {
- val absUrl get() = url.toAbsUrl()
-
- companion object {
- fun String.toAbsUrl(baseUrl: String = loUrl): String {
- return if (this.matches(urlRegex)) {
- this
- } else {
- baseUrl + this
- }
- }
-
- private const val loUrl = "https://127.0.0.1/"
- private val urlRegex = Regex("^https?://.*")
- }
-}
-
-@Serializable
-data class Tag(
- val tagName: String,
- val tagType: String? = "genre",
-)
-
-@Serializable
-data class PageList(
- @SerialName("queryChapter") val chapter: PageUrl,
-)
-
-@Serializable
-data class PageUrl(
- @SerialName("_id") val id: String,
- @SerialName("pictureUrls") val pages: List? = emptyList(),
-)
-
-@Serializable
-data class Pages(
- @SerialName("picCdn") val urlPart: String,
- @SerialName("pics") val qualityOriginal: List,
- @SerialName("picsM") val qualityMedium: List? = emptyList(),
-)
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFactory.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFactory.kt
deleted file mode 100644
index b3348969a..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFactory.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import eu.kanade.tachiyomi.source.SourceFactory
-
-class AnimeHFactory : SourceFactory {
- override fun createSources() = listOf(
- AnimeHAll(),
- AnimeHEn(),
- AnimeHJa(),
- AnimeHZh(),
- AnimeHEs(),
- )
-}
-
-class AnimeHAll : AnimeH("all") { override val id = 5098173700376022513 }
-class AnimeHEn : AnimeH("en") { override val id = 4370122548313941497 }
-class AnimeHJa : AnimeH("ja", "jp") { override val id = 8948948503520127713 }
-class AnimeHZh : AnimeH("zh", "cn") { override val id = 3874510362699054213 }
-class AnimeHEs : AnimeH("es") { override val id = 2790053117909987291 }
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFilters.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFilters.kt
deleted file mode 100644
index 61704e757..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHFilters.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import eu.kanade.tachiyomi.source.model.Filter
-import eu.kanade.tachiyomi.source.model.FilterList
-
-abstract class SelectFilter(
- displayName: String,
- private val options: Array>,
-) : Filter.Select(
- displayName,
- options.map { it.first }.toTypedArray(),
-) {
- val selected get() = options[state].second.takeUnless { it.isEmpty() }
-}
-
-abstract class TextFilter(name: String) : Filter.Text(name)
-
-abstract class TagFilter(name: String) : TextFilter(name) {
- val tags get() = state.split(",")
- .map { it.trim().lowercase() }
- .filter { it.isNotEmpty() }
- .takeUnless { it.isEmpty() }
-}
-
-abstract class PageFilter(name: String) : TextFilter(name) {
- val value get() = state.trim().toIntOrNull()
-}
-
-class SortFilter : SelectFilter(
- "Sort By",
- arrayOf(
- Pair("Update", ""),
- Pair("Popular", "Popular"),
- Pair("Name Ascending", "Name_ASC"),
- Pair("Name Descending", "Name_DESC"),
- ),
-)
-
-class FormatFilter : SelectFilter(
- "Format",
- arrayOf(
- Pair("", ""),
- Pair("Manga", "manga"),
- Pair("Doujinshi", "doujinshi"),
- Pair("ArtistCG", "artistcg"),
- Pair("GameCG", "gamecg"),
- Pair("ImageSet", "imageset"),
- ),
-)
-
-class MinPageFilter : PageFilter("Minimum Pages")
-
-class MaxPageFilter : PageFilter("Maximum Pages")
-
-class IncludedTagFilter : TagFilter("Include Tags")
-
-class ExcludedTagFilter : TagFilter("Exclude Tags")
-
-fun getFilters() = FilterList(
- SortFilter(),
- FormatFilter(),
- Filter.Separator(),
- MinPageFilter(),
- MaxPageFilter(),
- Filter.Separator(),
- IncludedTagFilter(),
- ExcludedTagFilter(),
- Filter.Header("comma (,) separated tag/parody/character/artist/group"),
-)
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHPayloadDto.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHPayloadDto.kt
deleted file mode 100644
index e7ebc3b3f..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHPayloadDto.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class GraphQL(
- val variables: T,
- val query: String,
-)
-
-@Serializable
-data class PopularVariables(
- val size: Int,
- val page: Int,
- val dateRange: Int,
- val language: String,
-)
-
-@Serializable
-data class SearchVariables(
- val size: Int,
- val page: Int,
- val search: SearchPayload,
-)
-
-@Serializable
-data class SearchPayload(
- val query: String?,
- val language: String,
- val sortBy: String?,
- val format: String?,
- val tags: List?,
- val excludeTags: List?,
- val pagesRangeStart: Int?,
- val pagesRangeEnd: Int?,
-)
-
-@Serializable
-data class IdVariables(val id: String)
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHQueries.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHQueries.kt
deleted file mode 100644
index 644eedb15..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHQueries.kt
+++ /dev/null
@@ -1,106 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-private fun buildQuery(queryAction: () -> String): String {
- return queryAction()
- .trimIndent()
- .replace("%", "$")
-}
-
-val POPULAR_QUERY: String = buildQuery {
- """
- query(
- %size: Int
- %language: String
- %dateRange: Int
- %page: Int
- ) {
- queryPopularChapters(
- size: %size
- language: %language
- dateRange: %dateRange
- page: %page
- ) {
- edges {
- _id
- name
- uploadDate
- format
- description
- language
- pages
- firstPics
- tags
- }
- }
- }
- """
-}
-
-val SEARCH_QUERY: String = buildQuery {
- """
- query(
- %search: SearchInput
- %size: Int
- %page: Int
- ) {
- queryChapters(
- limit: %size
- search: %search
- page: %page
- ) {
- edges {
- _id
- name
- uploadDate
- format
- description
- language
- pages
- firstPics
- tags
- }
- }
- }
- """
-}
-
-val DETAILS_QUERY: String = buildQuery {
- """
- query(
- %id: String
- ) {
- queryChapter(
- chapterId: %id
- ) {
- _id
- name
- uploadDate
- format
- description
- language
- pages
- firstPics
- tags
- }
- }
- """
-}
-
-val PAGES_QUERY: String = buildQuery {
- """
- query(
- %id: String
- ) {
- queryChapter(
- chapterId: %id
- ) {
- _id
- pictureUrls {
- picCdn
- pics
- picsM
- }
- }
- }
- """
-}
diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHUrlActivity.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHUrlActivity.kt
deleted file mode 100644
index 7d08b3904..000000000
--- a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/AnimeHUrlActivity.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.kanade.tachiyomi.extension.all.ninenineninehentai
-
-import android.app.Activity
-import android.content.ActivityNotFoundException
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import kotlin.system.exitProcess
-
-class AnimeHUrlActivity : Activity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val pathSegments = intent?.data?.pathSegments
- if (pathSegments != null && pathSegments.size > 1) {
- val id = pathSegments[1]
- val mainIntent = Intent().apply {
- action = "eu.kanade.tachiyomi.SEARCH"
- putExtra("query", "${AnimeH.SEARCH_PREFIX}$id")
- putExtra("filter", packageName)
- }
-
- try {
- startActivity(mainIntent)
- } catch (e: ActivityNotFoundException) {
- Log.e("AnimeHUrlActivity", e.toString())
- }
- } else {
- Log.e("AnimeHUrlActivity", "could not parse uri from intent $intent")
- }
-
- finish()
- exitProcess(0)
- }
-}
diff --git a/src/en/readallcomicscom/build.gradle b/src/en/readallcomicscom/build.gradle
deleted file mode 100644
index a4158f6c5..000000000
--- a/src/en/readallcomicscom/build.gradle
+++ /dev/null
@@ -1,7 +0,0 @@
-ext {
- extName = 'ReadAllComics'
- extClass = '.ReadAllComics'
- extVersionCode = 3
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/en/readallcomicscom/res/mipmap-hdpi/ic_launcher.png b/src/en/readallcomicscom/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index fd351bd91..000000000
Binary files a/src/en/readallcomicscom/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readallcomicscom/res/mipmap-mdpi/ic_launcher.png b/src/en/readallcomicscom/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 510e4c827..000000000
Binary files a/src/en/readallcomicscom/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readallcomicscom/res/mipmap-xhdpi/ic_launcher.png b/src/en/readallcomicscom/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 68107c649..000000000
Binary files a/src/en/readallcomicscom/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readallcomicscom/res/mipmap-xxhdpi/ic_launcher.png b/src/en/readallcomicscom/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index db82daebe..000000000
Binary files a/src/en/readallcomicscom/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readallcomicscom/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/readallcomicscom/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index aa394ccc6..000000000
Binary files a/src/en/readallcomicscom/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readallcomicscom/src/eu/kanade/tachiyomi/extension/en/readallcomicscom/ReadAllComics.kt b/src/en/readallcomicscom/src/eu/kanade/tachiyomi/extension/en/readallcomicscom/ReadAllComics.kt
deleted file mode 100644
index 3b7e65985..000000000
--- a/src/en/readallcomicscom/src/eu/kanade/tachiyomi/extension/en/readallcomicscom/ReadAllComics.kt
+++ /dev/null
@@ -1,193 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.readallcomicscom
-
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.asObservableSuccess
-import eu.kanade.tachiyomi.network.interceptor.rateLimit
-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.ParsedHttpSource
-import eu.kanade.tachiyomi.util.asJsoup
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-import okhttp3.Interceptor
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.Jsoup
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import org.jsoup.select.Elements
-import rx.Observable
-
-class ReadAllComics : ParsedHttpSource() {
-
- override val name = "ReadAllComics"
-
- override val baseUrl = "https://readallcomics.com"
-
- override val lang = "en"
-
- override val supportsLatest = false
-
- private lateinit var searchPageElements: Elements
-
- override val client = network.cloudflareClient.newBuilder()
- .addInterceptor(::archivedCategoryInterceptor)
- .rateLimit(2)
- .build()
-
- private fun archivedCategoryInterceptor(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val response = chain.proceed(request)
-
- val document = Jsoup.parse(
- response.peekBody(Long.MAX_VALUE).string(),
- request.url.toString(),
- )
-
- val newUrl = document.selectFirst(".description-archive > p > span > a")
- ?.attr("href")?.toHttpUrlOrNull()
- ?: return response
-
- if (newUrl.pathSegments.contains("category")) {
- response.close()
-
- return chain.proceed(
- request.newBuilder()
- .url(newUrl)
- .build(),
- )
- }
-
- return response
- }
-
- override fun popularMangaRequest(page: Int) =
- GET("$baseUrl${if (page > 1)"/page/$page/" else ""}", headers)
-
- override fun popularMangaFromElement(element: Element): SManga {
- val manga = SManga.create().apply {
- val category = element.classNames()
- .firstOrNull { it.startsWith("category-") }!!
- .substringAfter("category-")
-
- url = "/category/$category/"
- title = category.replace("-", " ").capitalizeEachWord()
- thumbnail_url = element.select("img").attr("abs:src")
- }
-
- return manga
- }
-
- override fun popularMangaSelector() = "#post-area > div"
- override fun popularMangaNextPageSelector() = "div.pagenavi > a.next"
-
- override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
- return if (page == 1) {
- client.newCall(searchMangaRequest(page, query, filters))
- .asObservableSuccess()
- .map { searchMangaParse(it) }
- } else {
- Observable.just(searchPageParse(page))
- }
- }
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- val url = baseUrl.toHttpUrl().newBuilder().apply {
- addQueryParameter("story", query)
- addQueryParameter("s", "")
- addQueryParameter("type", "comic")
- }.build()
-
- return GET(url, headers)
- }
-
- override fun searchMangaParse(response: Response): MangasPage {
- searchPageElements = response.asJsoup().select(searchMangaSelector())
-
- return searchPageParse(1)
- }
-
- private fun searchPageParse(page: Int): MangasPage {
- val mangas = mutableListOf()
- val endRange = ((page * 24) - 1).let { if (it <= searchPageElements.lastIndex) it else searchPageElements.lastIndex }
-
- for (i in (((page - 1) * 24)..endRange)) {
- mangas.add(
- searchMangaFromElement(searchPageElements[i]),
- )
- }
-
- return MangasPage(mangas, endRange < searchPageElements.lastIndex)
- }
-
- override fun searchMangaFromElement(element: Element) = SManga.create().apply {
- setUrlWithoutDomain(element.attr("href"))
- title = element.text()
- thumbnail_url = "https://fakeimg.pl/200x300/?text=No%20Cover%0AOn%20Search&font_size=62"
- }
-
- override fun searchMangaSelector() = ".categories a"
- override fun searchMangaNextPageSelector() = null
-
- override fun mangaDetailsParse(document: Document) = SManga.create().apply {
- title = document.selectFirst("h1")!!.text()
- genre = document.select("p strong").joinToString { it.text() }
- author = document.select("p > strong").last()?.text()
- description = buildString {
- document.select(".b > strong").forEach { element ->
- val vol = element.previousElementSibling()
- if (isNotBlank()) {
- append("\n\n")
- }
- if (vol?.tagName() == "span") {
- append(vol.text(), "\n")
- }
- append(element.text())
- }
- }
- thumbnail_url = document.select("p img").attr("abs:src")
- }
-
- override fun chapterListSelector() = ".list-story a"
-
- override fun chapterFromElement(element: Element) = SChapter.create().apply {
- setUrlWithoutDomain(element.attr("href"))
- name = element.attr("title")
- }
-
- override fun pageListParse(document: Document): List {
- return document.select("body img:not(body div[id=\"logo\"] img)").mapIndexed { idx, element ->
- Page(idx, "", element.attr("abs:src"))
- }
- }
-
- private fun String.capitalizeEachWord(): String {
- val result = StringBuilder(length)
- var capitalize = true
- for (char in this) {
- result.append(
- if (capitalize) {
- char.uppercase()
- } else {
- char.lowercase()
- },
- )
- capitalize = char.isWhitespace()
- }
- return result.toString()
- }
-
- override fun imageUrlParse(document: Document) =
- throw UnsupportedOperationException()
- override fun latestUpdatesRequest(page: Int) =
- throw UnsupportedOperationException()
- override fun latestUpdatesFromElement(element: Element) =
- throw UnsupportedOperationException()
- override fun latestUpdatesSelector() =
- throw UnsupportedOperationException()
- override fun latestUpdatesNextPageSelector() =
- throw UnsupportedOperationException()
-}
diff --git a/src/zh/qinqin/build.gradle b/src/zh/qinqin/build.gradle
deleted file mode 100644
index 1c7bbef3a..000000000
--- a/src/zh/qinqin/build.gradle
+++ /dev/null
@@ -1,9 +0,0 @@
-ext {
- extName = 'Qinqin Manhua'
- extClass = '.Qinqin'
- themePkg = 'sinmh'
- baseUrl = 'http://www.acgwd.com'
- overrideVersionCode = 2
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/zh/qinqin/res/mipmap-hdpi/ic_launcher.png b/src/zh/qinqin/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 9a6b358b8..000000000
Binary files a/src/zh/qinqin/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/zh/qinqin/res/mipmap-mdpi/ic_launcher.png b/src/zh/qinqin/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 5b665aae0..000000000
Binary files a/src/zh/qinqin/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/zh/qinqin/res/mipmap-xhdpi/ic_launcher.png b/src/zh/qinqin/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index c3183acf1..000000000
Binary files a/src/zh/qinqin/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/zh/qinqin/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/qinqin/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 9bd1f802b..000000000
Binary files a/src/zh/qinqin/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/zh/qinqin/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/qinqin/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index d7eae56f0..000000000
Binary files a/src/zh/qinqin/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/zh/qinqin/src/eu/kanade/tachiyomi/extension/zh/qinqin/Qinqin.kt b/src/zh/qinqin/src/eu/kanade/tachiyomi/extension/zh/qinqin/Qinqin.kt
deleted file mode 100644
index f4f8059cf..000000000
--- a/src/zh/qinqin/src/eu/kanade/tachiyomi/extension/zh/qinqin/Qinqin.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package eu.kanade.tachiyomi.extension.zh.qinqin
-
-import android.util.Base64
-import eu.kanade.tachiyomi.multisrc.sinmh.SinMH
-import eu.kanade.tachiyomi.network.GET
-import org.jsoup.nodes.Document
-import org.jsoup.select.Elements
-import javax.crypto.Cipher
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-
-class Qinqin : SinMH("亲亲漫画", "http://www.acgwd.com") {
-
- override fun popularMangaRequest(page: Int) = GET("$baseUrl/list/post/?page=$page", headers)
-
- override fun mangaDetailsParse(document: Document) = mangaDetailsParseDMZJStyle(document, hasBreadcrumb = true)
-
- override fun Elements.sectionsDescending() = this
-
- // https://www.acgud.com/js/jmzz20191018.js
- override fun parsePageImages(chapterImages: String): List {
- val key = SecretKeySpec("cxNB23W8xzKJV26O".toByteArray(), "AES")
- val iv = IvParameterSpec("opb4x7z21vg1f3gI".toByteArray())
- val result = Cipher.getInstance("AES/CBC/PKCS7Padding").run {
- init(Cipher.DECRYPT_MODE, key, iv)
- doFinal(Base64.decode(chapterImages, Base64.DEFAULT))
- }
- return super.parsePageImages(String(result))
- }
-}