Remove CatManga (#9800)
This commit is contained in:
		
							parent
							
								
									4b45240f36
								
							
						
					
					
						commit
						ad0c8e2fec
					
				@ -1,23 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    package="eu.kanade.tachiyomi.extension">
 | 
			
		||||
    <application>
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".en.catmanga.CatMangaUrlActivity"
 | 
			
		||||
            android:excludeFromRecents="true"
 | 
			
		||||
            android:exported="true"
 | 
			
		||||
            android:theme="@android:style/Theme.NoDisplay">
 | 
			
		||||
            <intent-filter>
 | 
			
		||||
                <action android:name="android.intent.action.VIEW" />
 | 
			
		||||
 | 
			
		||||
                <category android:name="android.intent.category.DEFAULT" />
 | 
			
		||||
                <category android:name="android.intent.category.BROWSABLE" />
 | 
			
		||||
 | 
			
		||||
                <data
 | 
			
		||||
                    android:host="catmanga.org"
 | 
			
		||||
                    android:pathPattern="/series/..*"
 | 
			
		||||
                    android:scheme="https" />
 | 
			
		||||
            </intent-filter>
 | 
			
		||||
        </activity>
 | 
			
		||||
    </application>
 | 
			
		||||
</manifest>
 | 
			
		||||
@ -1,12 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
apply plugin: 'kotlinx-serialization'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    extName = 'CatManga'
 | 
			
		||||
    pkgNameSuffix = "en.catmanga"
 | 
			
		||||
    extClass = '.CatManga'
 | 
			
		||||
    extVersionCode = 7
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1.7 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 1.2 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 2.4 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 3.7 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 5.2 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 18 KiB  | 
@ -1,57 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.en.catmanga
 | 
			
		||||
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class CatSeries(
 | 
			
		||||
    val alt_titles: List<String>,
 | 
			
		||||
    val authors: List<String>,
 | 
			
		||||
    val genres: List<String>,
 | 
			
		||||
    val chapters: List<CatSeriesChapter>? = null,
 | 
			
		||||
    val title: String,
 | 
			
		||||
    val series_id: String,
 | 
			
		||||
    val description: String,
 | 
			
		||||
    val status: String,
 | 
			
		||||
    val cover_art: CatSeriesCover,
 | 
			
		||||
    val all_covers: List<CatSeriesCover>? = null
 | 
			
		||||
) {
 | 
			
		||||
    fun toSManga() = this.let { series ->
 | 
			
		||||
        SManga.create().apply {
 | 
			
		||||
            url = "/series/${series.series_id}"
 | 
			
		||||
            title = series.title
 | 
			
		||||
            thumbnail_url = series.cover_art.source
 | 
			
		||||
            author = series.authors.joinToString(", ")
 | 
			
		||||
            description = series.description
 | 
			
		||||
            genre = series.genres.joinToString(", ")
 | 
			
		||||
            status = when (series.status) {
 | 
			
		||||
                "ongoing" -> SManga.ONGOING
 | 
			
		||||
                "completed" -> SManga.COMPLETED
 | 
			
		||||
                else -> SManga.UNKNOWN
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (alt_titles.isNotEmpty()) {
 | 
			
		||||
                description += "\n\nAlternative titles:\n"
 | 
			
		||||
                alt_titles.forEach {
 | 
			
		||||
                    description += "• $it\n"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class CatSeriesChapter(
 | 
			
		||||
    val title: String? = null,
 | 
			
		||||
    val groups: List<String>,
 | 
			
		||||
    val number: Float,
 | 
			
		||||
    val display_number: String? = null,
 | 
			
		||||
    val volume: Int? = null
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class CatSeriesCover(
 | 
			
		||||
    val source: String,
 | 
			
		||||
    val width: Int,
 | 
			
		||||
    val height: Int
 | 
			
		||||
)
 | 
			
		||||
@ -1,179 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.en.catmanga
 | 
			
		||||
 | 
			
		||||
import android.app.Application
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.asObservableSuccess
 | 
			
		||||
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.ExperimentalSerializationApi
 | 
			
		||||
import kotlinx.serialization.decodeFromString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import kotlinx.serialization.json.boolean
 | 
			
		||||
import kotlinx.serialization.json.decodeFromJsonElement
 | 
			
		||||
import kotlinx.serialization.json.jsonObject
 | 
			
		||||
import kotlinx.serialization.json.jsonPrimitive
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
@ExperimentalSerializationApi
 | 
			
		||||
class CatManga : HttpSource() {
 | 
			
		||||
 | 
			
		||||
    private val application: Application by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override val name = "CatManga"
 | 
			
		||||
    override val baseUrl = "https://catmanga.org"
 | 
			
		||||
    override val supportsLatest = false
 | 
			
		||||
    override val lang = "en"
 | 
			
		||||
    private val json: Json by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val allSeriesRequest = GET("$baseUrl/api/series/allSeries")
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = allSeriesRequest
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = allSeriesRequest
 | 
			
		||||
 | 
			
		||||
    override fun chapterListRequest(manga: SManga): Request {
 | 
			
		||||
        val seriesId = manga.url.substringAfter("/series/")
 | 
			
		||||
        return GET("$baseUrl/api/series/$seriesId")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
 | 
			
		||||
        return client.newCall(searchMangaRequest(page, query, filters))
 | 
			
		||||
            .asObservableSuccess()
 | 
			
		||||
            .map { response ->
 | 
			
		||||
                val manga = json.decodeFromString<List<CatSeries>>(response.body!!.string())
 | 
			
		||||
                    .filter {
 | 
			
		||||
                        if (query.startsWith(SERIES_ID_SEARCH_PREFIX)) {
 | 
			
		||||
                            return@filter it.series_id.contains(query.removePrefix(SERIES_ID_SEARCH_PREFIX), true)
 | 
			
		||||
                        }
 | 
			
		||||
                        sequence { yieldAll(it.alt_titles); yield(it.title) }
 | 
			
		||||
                            .any { title -> title.contains(query, true) }
 | 
			
		||||
                    }
 | 
			
		||||
                    .map { it.toSManga() }
 | 
			
		||||
                    .toList()
 | 
			
		||||
                MangasPage(manga, false)
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
 | 
			
		||||
        val seriesId = manga.url.substringAfter("/series/")
 | 
			
		||||
        return client.newCall(allSeriesRequest)
 | 
			
		||||
            .asObservableSuccess()
 | 
			
		||||
            .map { response ->
 | 
			
		||||
                json.decodeFromString<List<CatSeries>>(response.body!!.string())
 | 
			
		||||
                    .find { it.series_id == seriesId }
 | 
			
		||||
                    ?.toSManga() ?: manga
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListParse(response: Response): List<SChapter> {
 | 
			
		||||
        val series = json.decodeFromString<CatSeries>(response.body!!.string())
 | 
			
		||||
        val seriesPrefs = application.getSharedPreferences("source_${id}_time_found:${series.series_id}", 0)
 | 
			
		||||
        val seriesPrefsEditor = seriesPrefs.edit()
 | 
			
		||||
        val chapters = series.chapters!!
 | 
			
		||||
            .asReversed()
 | 
			
		||||
            .map { chapter ->
 | 
			
		||||
                val title = chapter.title ?: ""
 | 
			
		||||
                val groups = chapter.groups.joinToString(", ")
 | 
			
		||||
                val numberUrl = chapter.number.chapterNumberToUrlPath()
 | 
			
		||||
                val displayNumber = chapter.display_number ?: numberUrl
 | 
			
		||||
                SChapter.create().apply {
 | 
			
		||||
                    url = "/series/${series.series_id}/$numberUrl"
 | 
			
		||||
                    chapter_number = chapter.number
 | 
			
		||||
                    scanlator = groups
 | 
			
		||||
 | 
			
		||||
                    name = if (chapter.volume != null) {
 | 
			
		||||
                        "Vol.${chapter.volume} "
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ""
 | 
			
		||||
                    }
 | 
			
		||||
                    name += "Ch.$displayNumber"
 | 
			
		||||
                    if (title.isNotBlank()) {
 | 
			
		||||
                        name += " - $title"
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Save current time when a chapter is found for the first time, and reuse it on future
 | 
			
		||||
                    // checks to prevent manga entry without any new chapter bumped to the top of
 | 
			
		||||
                    // "Latest chapter" list when the library is updated.
 | 
			
		||||
                    val currentTimeMillis = System.currentTimeMillis()
 | 
			
		||||
                    if (!seriesPrefs.contains(numberUrl)) {
 | 
			
		||||
                        seriesPrefsEditor.putLong(numberUrl, currentTimeMillis)
 | 
			
		||||
                    }
 | 
			
		||||
                    date_upload = seriesPrefs.getLong(numberUrl, currentTimeMillis)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        seriesPrefsEditor.apply()
 | 
			
		||||
        return chapters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaParse(response: Response): MangasPage {
 | 
			
		||||
        val mangas = json.decodeFromString<List<CatSeries>>(response.body!!.string()).map { it.toSManga() }
 | 
			
		||||
        return MangasPage(mangas, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
 | 
			
		||||
        return client.newCall(pageListRequest(chapter))
 | 
			
		||||
            .asObservableSuccess()
 | 
			
		||||
            .map {
 | 
			
		||||
                val doc = it.asJsoup().getDataJsonObject()
 | 
			
		||||
                val pages = if (doc["isFallback"]!!.jsonPrimitive.boolean) {
 | 
			
		||||
                    val buildId = doc["buildId"]!!.jsonPrimitive.content
 | 
			
		||||
                    val directRequest = GET("$baseUrl/_next/data/$buildId/${chapter.url}.json")
 | 
			
		||||
                    val directResponse = client.newCall(directRequest).execute()
 | 
			
		||||
                    json.parseToJsonElement(directResponse.body!!.string())
 | 
			
		||||
                } else {
 | 
			
		||||
                    doc["props"]!!
 | 
			
		||||
                }.jsonObject["pageProps"]!!.jsonObject["pages"]!!
 | 
			
		||||
                json.decodeFromJsonElement<List<String>>(pages)
 | 
			
		||||
                    .mapIndexed { index, s -> Page(index, "", s) }
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns json object of site data
 | 
			
		||||
     */
 | 
			
		||||
    private fun Document.getDataJsonObject() = json.parseToJsonElement(getElementById("__NEXT_DATA__").html()).jsonObject
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns string without decimal when it is not relevant
 | 
			
		||||
     */
 | 
			
		||||
    private fun Float.chapterNumberToUrlPath(): String {
 | 
			
		||||
        return if (toInt().toFloat() == this) toInt().toString() else toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(response: Response): List<Page> {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int): Request {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesParse(response: Response): MangasPage {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(response: Response): SManga {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaParse(response: Response): MangasPage {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun imageUrlParse(response: Response): String {
 | 
			
		||||
        throw UnsupportedOperationException("Not used.")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val SERIES_ID_SEARCH_PREFIX = "series_id:"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,38 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.en.catmanga
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.ActivityNotFoundException
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.util.Log
 | 
			
		||||
import kotlin.system.exitProcess
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Springboard that accepts https://catmanga.org/series/xxxxxx intents and redirects them to
 | 
			
		||||
 * the main Tachiyomi process.
 | 
			
		||||
 */
 | 
			
		||||
class CatMangaUrlActivity : 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", "${CatManga.SERIES_ID_SEARCH_PREFIX}$id")
 | 
			
		||||
                putExtra("filter", packageName)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                startActivity(mainIntent)
 | 
			
		||||
            } catch (e: ActivityNotFoundException) {
 | 
			
		||||
                Log.e("CatMangaUrlActivity", e.toString())
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            Log.e("CatMangaUrlActivity", "could not parse uri from intent $intent")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        finish()
 | 
			
		||||
        exitProcess(0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user