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)) - } -}