Remove Cứu Truyện (#16654)

* Remove Cứu Truyện

* Add to REMOVED_SOURCES.md

* extra regex
This commit is contained in:
beerpsi 2023-06-06 16:19:42 +07:00 committed by GitHub
parent 6aa5ac0f17
commit 7ddf4ac5c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 2 additions and 515 deletions

View File

@ -43,7 +43,7 @@ jobs:
}, },
{ {
"type": "both", "type": "both",
"regex": ".*(hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|colamanhua|mangadig|hitomi\\.la|copymanga|neox|1manga\\.co|mangafox\\.fun|mangahere\\.onl|mangakakalot\\.fun|manganel(?!o)|mangaonline\\.fun|mangatoday|manga\\.town|onemanga\\.info|koushoku|ksk\\.moe|comikey|leercapitulo).*", "regex": ".*(hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|colamanhua|mangadig|hitomi\\.la|copymanga|neox|1manga\\.co|mangafox\\.fun|mangahere\\.onl|mangakakalot\\.fun|manganel(?!o)|mangaonline\\.fun|mangatoday|manga\\.town|onemanga\\.info|koushoku|ksk\\.moe|comikey|leercapitulo|c[uứ]u\\s*truy[eệ]n).*",
"ignoreCase": true, "ignoreCase": true,
"labels": ["invalid"], "labels": ["invalid"],
"message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information." "message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information."

View File

@ -5,6 +5,7 @@
- ColaManhua (COLA漫画) https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11445 - ColaManhua (COLA漫画) https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11445
- Comikey https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11971 - Comikey https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11971
- CopyManga (拷贝漫画) https://github.com/tachiyomiorg/tachiyomi-extensions/pull/12376 - CopyManga (拷贝漫画) https://github.com/tachiyomiorg/tachiyomi-extensions/pull/12376
- Cứu Truyện https://github.com/tachiyomiorg/tachiyomi-extensions/pull/16654
- Hentai Kai https://github.com/tachiyomiorg/tachiyomi-extensions/issues/9999 - Hentai Kai https://github.com/tachiyomiorg/tachiyomi-extensions/issues/9999
- Hitomi.la https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11613 - Hitomi.la https://github.com/tachiyomiorg/tachiyomi-extensions/pull/11613
- HQ Dragon https://github.com/tachiyomiorg/tachiyomi-extensions/pull/7065 - HQ Dragon https://github.com/tachiyomiorg/tachiyomi-extensions/pull/7065

View File

@ -1,24 +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="eu.kanade.tachiyomi.extension.vi.cuutruyen.CuuTruyenUrlActivity"
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="cuutruyen.net"
android:pathPattern="/mangas/..*"
android:scheme="https" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,13 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Cứu Truyện'
pkgNameSuffix = 'vi.cuutruyen'
extClass = '.CuuTruyen'
extVersionCode = 5
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,195 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.cuutruyen
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.vi.cuutruyen.dto.ChapterDto
import eu.kanade.tachiyomi.extension.vi.cuutruyen.dto.MangaDto
import eu.kanade.tachiyomi.extension.vi.cuutruyen.dto.ResponseDto
import eu.kanade.tachiyomi.network.GET
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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.CacheControl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class CuuTruyen : HttpSource(), ConfigurableSource {
override val name = "Cứu Truyện"
override val lang = "vi"
override val baseUrl = "https://cuutruyen.net"
private val apiUrl = "https://kakarot.cuutruyen.net/api/v2"
override val supportsLatest = true
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
private val json: Json by injectLazy()
override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/")
override val client = network.client.newBuilder()
.rateLimit(3)
.addInterceptor(CuuTruyenImageInterceptor())
.build()
override fun popularMangaRequest(page: Int): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("mangas/top")
addQueryParameter("duration", "all")
addQueryParameter("page", page.toString())
addQueryParameter("per_page", "24")
}.build().toString()
return GET(url, headers = headers, cache = CacheControl.FORCE_NETWORK)
}
override fun popularMangaParse(response: Response): MangasPage {
if (response.code == 500) {
return MangasPage(emptyList(), false)
}
val responseDto = response.parseAs<ResponseDto<List<MangaDto>>>()
val hasMoreResults = responseDto.metadata!!.currentPage < responseDto.metadata.totalPages
val coverKey = preferences.coverQuality
return MangasPage(
responseDto.data.map { it.toSManga(coverKey) },
hasMoreResults,
)
}
override fun latestUpdatesRequest(page: Int): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("mangas/recently_updated")
addQueryParameter("page", page.toString())
addQueryParameter("per_page", "24")
}.build().toString()
return GET(url, headers = headers, cache = CacheControl.FORCE_NETWORK)
}
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return when {
query.startsWith(PREFIX_ID_SEARCH) -> {
val id = query.removePrefix(PREFIX_ID_SEARCH).trim()
if (id.toIntOrNull() == null) {
throw Exception("ID tìm kiếm không hợp lệ (phải là một số).")
}
val url = "/mangas/$id"
fetchMangaDetails(
SManga.create().apply {
this.url = url
},
)
.map {
it.url = url
MangasPage(listOf(it), false)
}
}
else -> super.fetchSearchManga(page, query, filters)
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegments("mangas/search")
addQueryParameter("q", query)
addQueryParameter("page", page.toString())
addQueryParameter("per_page", "24")
}.build().toString()
return GET(url, headers = headers, cache = CacheControl.FORCE_NETWORK)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
client.newCall(GET("$apiUrl${manga.url}", headers = headers, cache = CacheControl.FORCE_NETWORK))
.asObservableSuccess()
.map { mangaDetailsParse(it) }
override fun mangaDetailsRequest(manga: SManga): Request = GET("$baseUrl${manga.url}")
override fun mangaDetailsParse(response: Response): SManga {
val responseDto = response.parseAs<ResponseDto<MangaDto>>()
return responseDto.data.toSManga(preferences.coverQuality)
}
override fun chapterListRequest(manga: SManga): Request =
GET("$apiUrl${manga.url}/chapters", headers = headers, cache = CacheControl.FORCE_NETWORK)
override fun chapterListParse(response: Response): List<SChapter> {
val segments = response.request.url.pathSegments
val lastIndex = segments.lastIndex
val mangaUrl = "/${segments[lastIndex - 2]}/${segments[lastIndex - 1]}"
return response.parseAs<ResponseDto<List<ChapterDto>>>().data.map { it.toSChapter(mangaUrl) }
}
override fun pageListRequest(chapter: SChapter): Request {
val url = apiUrl.toHttpUrl().newBuilder().apply {
val chapterId = chapter.url.split("/").last()
addPathSegment("chapters")
addPathSegment(chapterId)
}.build().toString()
return GET(url, headers = headers, cache = CacheControl.FORCE_NETWORK)
}
override fun pageListParse(response: Response): List<Page> {
val chapterDto = response.parseAs<ResponseDto<ChapterDto>>()
return chapterDto.data.pages!!.map { it.toPage() }
}
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used")
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromString(body.string())
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
val coverQualityPref = ListPreference(screen.context).apply {
key = "coverQuality"
title = "Chất lượng ảnh bìa"
entries = arrayOf("Chất lượng cao", "Di động")
entryValues = arrayOf("cover_url", "cover_mobile_url")
setDefaultValue("cover_url")
setOnPreferenceChangeListener { _, newValue ->
val selected = newValue as String
val index = findIndexOfValue(selected)
val entry = entryValues[index] as String
preferences.edit()
.putString("coverQuality", entry)
.commit()
}
}
screen.addPreference(coverQualityPref)
}
private val SharedPreferences.coverQuality
get() = getString("coverQuality", "")
companion object {
const val PREFIX_ID_SEARCH = "id:"
}
}

View File

@ -1,37 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.cuutruyen
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://cuutruyen.net/mangas/xxxx intents
*/
class CuuTruyenUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val id = pathSegments[1]
try {
startActivity(
Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
putExtra("query", "${CuuTruyen.PREFIX_ID_SEARCH}$id")
putExtra("filter", packageName)
},
)
} catch (e: ActivityNotFoundException) {
Log.e("CuuTruyenUrlActivity", e.toString())
}
} else {
Log.e("CuuTruyenUrlActivity", "Could not parse URI from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -1,61 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.cuutruyen.dto
import eu.kanade.tachiyomi.extension.vi.cuutruyen.CuuTruyenImageInterceptor
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh")
}
@Serializable
data class ChapterDto(
val id: Int,
val order: Int,
val number: String,
@SerialName("updated_at") val updatedAt: String,
val name: String? = null,
val pages: List<PageDto>? = null,
) {
fun toSChapter(mangaUrl: String) = SChapter.create().apply {
val dto = this@ChapterDto
url = "$mangaUrl/chapters/$id"
name = "Chapter ${dto.number}"
if (dto.name != null && dto.name.isNotBlank()) {
name += ": ${dto.name}"
}
date_upload = runCatching {
DATE_FORMATTER.parse(dto.updatedAt.replace("+07:00", "Z"))?.time
}.getOrNull() ?: 0L
chapter_number = dto.number.toFloatOrNull() ?: -1f
}
}
@Serializable
data class PageDto(
val id: Int,
val order: Int,
val width: Int?,
val height: Int?,
val status: String,
@SerialName("image_url") val imageUrl: String,
@SerialName("image_url_size") val imageUrlSize: Int,
@SerialName("drm_data") val drmData: String,
) {
fun toPage(): Page {
val dto = this@PageDto
val url = imageUrl.toHttpUrl().newBuilder()
.fragment("${CuuTruyenImageInterceptor.KEY}=$drmData")
.build()
.toString()
return Page(dto.order, imageUrl = url)
}
}

View File

@ -1,51 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.cuutruyen.dto
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class AuthorDto(
val name: String,
)
@Serializable
data class TeamDto(
val id: Int,
val name: String,
val description: String,
)
@Serializable
data class MangaDto(
val id: Int,
val name: String,
@SerialName("cover_url") val coverUrl: String? = null,
@SerialName("cover_mobile_url") val coverMobileUrl: String? = null,
val author: AuthorDto? = null,
@SerialName("author_name") val authorName: String? = null,
val description: String? = null,
val team: TeamDto? = null,
) {
fun toSManga(coverQuality: String? = null): SManga = SManga.create().apply {
val dto = this@MangaDto
url = "/mangas/${dto.id}"
title = dto.name
author = dto.author?.name ?: dto.authorName
description = ""
if (dto.team != null) {
description += "Nhóm dịch: ${dto.team.name}\n\n"
}
description += dto.description ?: ""
thumbnail_url = dto.coverUrl
if (coverQuality == "cover_url") {
thumbnail_url = dto.coverUrl
} else if (coverQuality == "cover_mobile_url") {
thumbnail_url = dto.coverMobileUrl
}
}
}

View File

@ -1,18 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.cuutruyen.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ResponseDto<T>(
val data: T,
@SerialName("_metadata") val metadata: PaginationMetadataDto? = null,
)
@Serializable
data class PaginationMetadataDto(
@SerialName("total_count") val totalCount: Int,
@SerialName("total_pages") val totalPages: Int,
@SerialName("current_page") val currentPage: Int,
@SerialName("per_page") val perPage: Int,
)