Remove dead sources
Closes #16546 Closes #16547 Closes #16548 Closes #16577 Closes #16578 Closes #16583
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 226 KiB |
|
@ -1,68 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.vi.phemanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS
|
||||
|
||||
class PheManga : MyMangaCMS("Phê Manga", "https://phemanga.net", "vi") {
|
||||
override fun dateUpdatedParser(date: String): Long =
|
||||
runCatching { super.dateUpdatedParser(date.split(" - ")[1]) }.getOrNull() ?: 0L
|
||||
|
||||
override fun getGenreList() = listOf(
|
||||
Genre("16+", 1),
|
||||
Genre("18+", 2),
|
||||
Genre("Action", 3),
|
||||
Genre("Adult", 4),
|
||||
Genre("Adventure", 5),
|
||||
Genre("Anime", 6),
|
||||
Genre("Comedy", 7),
|
||||
Genre("Comic", 8),
|
||||
Genre("Doujinshi", 9),
|
||||
Genre("Drama", 10),
|
||||
Genre("Ecchi", 11),
|
||||
Genre("Fantasy", 13),
|
||||
Genre("Full màu", 14),
|
||||
Genre("Game", 15),
|
||||
Genre("Gender Bender", 16),
|
||||
Genre("Harem", 17),
|
||||
Genre("Historical", 18),
|
||||
Genre("Horror", 19),
|
||||
Genre("Isekai/Dị giới/Trọng sinh", 20),
|
||||
Genre("Josei", 21),
|
||||
Genre("Live action", 22),
|
||||
Genre("Magic", 23),
|
||||
Genre("Manga", 24),
|
||||
Genre("Manhua", 25),
|
||||
Genre("Manhwa", 26),
|
||||
Genre("Martial Arts", 27),
|
||||
Genre("Mature", 28),
|
||||
Genre("Mecha", 29),
|
||||
Genre("Mystery", 30),
|
||||
Genre("Nấu Ăn", 31),
|
||||
Genre("Ngôn Tình", 32),
|
||||
Genre("NTR", 33),
|
||||
Genre("One shot", 34),
|
||||
Genre("Psychological", 35),
|
||||
Genre("Romance", 36),
|
||||
Genre("School Life", 37),
|
||||
Genre("Sci-fi", 38),
|
||||
Genre("Seinen", 39),
|
||||
Genre("Shoujo", 40),
|
||||
Genre("Shoujo Ai", 41),
|
||||
Genre("Shounen", 42),
|
||||
Genre("Shounen Ai", 43),
|
||||
Genre("Slice of life", 44),
|
||||
Genre("Smut", 45),
|
||||
Genre("Soft Yaoi", 46),
|
||||
Genre("Soft Yuri", 47),
|
||||
Genre("Sports", 48),
|
||||
Genre("Supernatural", 49),
|
||||
Genre("Tạp chí truyện tranh", 50),
|
||||
Genre("Tragedy", 51),
|
||||
Genre("Trap (Crossdressing)", 52),
|
||||
Genre("Trinh Thám", 53),
|
||||
Genre("Truyện scan", 54),
|
||||
Genre("Tu chân - tu tiên", 55),
|
||||
Genre("VnComic", 56),
|
||||
Genre("Webtoon", 57),
|
||||
Genre("Yuri", 58),
|
||||
)
|
||||
}
|
|
@ -25,7 +25,6 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("247Manga", "https://247manga.com", "en", className = "Manga247", overrideVersionCode = 1),
|
||||
SingleLang("365Manga", "https://365manga.com", "en", className = "ThreeSixtyFiveManga", overrideVersionCode = 1),
|
||||
SingleLang("Adonis Fansub", "https://manga.adonisfansub.com", "tr", overrideVersionCode = 1),
|
||||
SingleLang("Adult Painful Nightz", "https://adults.painfulnightz.com", "en", isNsfw = true),
|
||||
SingleLang("Adult Webtoon", "https://adultwebtoon.com", "en", isNsfw = true, overrideVersionCode = 1),
|
||||
SingleLang("AiYuManga", "https://aiyumangascanlation.com", "es", overrideVersionCode = 1),
|
||||
SingleLang("Akuma no Tenshi", "https://akumanotenshi.com", "pt-BR", className = "AkumaNoTenshi"),
|
||||
|
@ -211,7 +210,6 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("Manga-Scantrad", "https://manga-scantrad.io", "fr", className = "MangaScantrad", overrideVersionCode = 3),
|
||||
SingleLang("Manga-TX", "https://manga-tx.com", "en", className = "Mangatxunoriginal"),
|
||||
SingleLang("Manga18fx", "https://manga18fx.com", "en", isNsfw = true, overrideVersionCode = 5),
|
||||
SingleLang("Manga1st", "https://manga1st.com", "en", overrideVersionCode = 2),
|
||||
SingleLang("Manga1st.online", "https://manga1st.online", "en", className = "MangaFirstOnline", overrideVersionCode = 1),
|
||||
SingleLang("Manga347", "https://manga347.com", "en", overrideVersionCode = 3),
|
||||
SingleLang("Manga3S", "https://manga3s.com", "en", overrideVersionCode = 4),
|
||||
|
@ -265,7 +263,6 @@ class MadaraGenerator : ThemeSourceGenerator {
|
|||
SingleLang("MangaXP", "https://mangaxp.com", "en", overrideVersionCode = 1),
|
||||
SingleLang("MangaYami", "https://www.mangayami.club", "en", overrideVersionCode = 2),
|
||||
SingleLang("Manhatic", "https://manhatic.com", "ar", isNsfw = true),
|
||||
SingleLang("Manhua Dragon", "https://manhuadragon.com", "en"),
|
||||
SingleLang("Manhua ES", "https://manhuaes.com", "en", overrideVersionCode = 6),
|
||||
SingleLang("Manhua Kiss", "https://manhuakiss.com", "en", isNsfw = true, overrideVersionCode = 1),
|
||||
SingleLang("Manhua Plus", "https://manhuaplus.com", "en", overrideVersionCode = 6),
|
||||
|
|
|
@ -20,7 +20,6 @@ class MMRCMSGenerator : ThemeSourceGenerator {
|
|||
SingleLang("Scan VF", "https://www.scan-vf.net", "fr", overrideVersionCode = 1),
|
||||
SingleLang("Scan OP", "https://scan-op.cc", "fr"),
|
||||
SingleLang("Komikid", "https://www.komikid.com", "id"),
|
||||
SingleLang("Fallen Angels Scans", "https://truyen.fascans.com", "vi"),
|
||||
SingleLang("Mangadoor", "https://mangadoor.com", "es", overrideVersionCode = 1),
|
||||
SingleLang("Utsukushii", "https://manga.utsukushii-bg.com", "bg", overrideVersionCode = 1),
|
||||
SingleLang("Phoenix-Scans", "https://phoenix-scans.pl", "pl", className = "PhoenixScans", overrideVersionCode = 1),
|
||||
|
|
|
@ -14,7 +14,6 @@ object SourceData {
|
|||
"https://scan-op.cc" -> """{"name":"Scan OP","base_url":"https://scan-op.cc","supports_latest":true,"item_url":"https://scan-op.cc/manga/","categories":[{"id":"1","name":"Comedy"},{"id":"2","name":"Doujinshi"},{"id":"3","name":"Drama"},{"id":"4","name":"Ecchi"},{"id":"5","name":"Fantasy"},{"id":"6","name":"Gender Bender"},{"id":"7","name":"Josei"},{"id":"8","name":"Mature"},{"id":"9","name":"Mecha"},{"id":"10","name":"Mystery"},{"id":"11","name":"One Shot"},{"id":"12","name":"Psychological"},{"id":"13","name":"Romance"},{"id":"14","name":"School Life"},{"id":"15","name":"Sci-fi"},{"id":"16","name":"Seinen"},{"id":"17","name":"Shoujo"},{"id":"18","name":"Shoujo Ai"},{"id":"19","name":"Shounen"},{"id":"20","name":"Shounen Ai"},{"id":"21","name":"Slice of Life"},{"id":"22","name":"Sports"},{"id":"23","name":"Supernatural"},{"id":"24","name":"Tragedy"},{"id":"25","name":"Yaoi"},{"id":"26","name":"Yuri"},{"id":"27","name":"Comics"},{"id":"28","name":"Autre"}],"tags":[{"id":"nouveau","name":"nouveau"}]}"""
|
||||
"https://www.komikid.com" -> """{"name":"Komikid","base_url":"https://www.komikid.com","supports_latest":true,"item_url":"https://www.komikid.com/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Fantasy"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Historical"},{"id":"9","name":"Horror"},{"id":"10","name":"Josei"},{"id":"11","name":"Martial Arts"},{"id":"12","name":"Mature"},{"id":"13","name":"Mecha"},{"id":"14","name":"Mystery"},{"id":"15","name":"One Shot"},{"id":"16","name":"Psychological"},{"id":"17","name":"Romance"},{"id":"18","name":"School Life"},{"id":"19","name":"Sci-fi"},{"id":"20","name":"Seinen"},{"id":"21","name":"Shoujo"},{"id":"22","name":"Shoujo Ai"},{"id":"23","name":"Shounen"},{"id":"24","name":"Shounen Ai"},{"id":"25","name":"Slice of Life"},{"id":"26","name":"Sports"},{"id":"27","name":"Supernatural"},{"id":"28","name":"Tragedy"},{"id":"29","name":"Yaoi"},{"id":"30","name":"Yuri"}]}"""
|
||||
"http://azbivo.webd.pro" -> """{"name":"Nikushima","base_url":"http://azbivo.webd.pro","supports_latest":false,"item_url":"\u003chtml\u003e \n \u003chead\u003e \n \u003cmeta http-equiv\u003d\"Content-Language\" content\u003d\"pl\"\u003e \n \u003cmeta http-equiv name\u003d\"pragma\" content\u003d\"no-cache\"\u003e \n \u003clink href\u003d\"style/style.css\" rel\u003d\"stylesheet\" type\u003d\"text/css\"\u003e \n \u003cmeta http-equiv\u003d\"Refresh\" content\u003d\"0; url\u003dhttps://www.webd.pl/_errnda.php?utm_source\u003dwn07\u0026amp;utm_medium\u003dwww\u0026amp;utm_campaign\u003dblock\"\u003e \n \u003cmeta name\u003d\"Robots\" content\u003d\"index, follow\"\u003e \n \u003cmeta name\u003d\"revisit-after\" content\u003d\"2 days\"\u003e \n \u003cmeta name\u003d\"rating\" content\u003d\"general\"\u003e \n \u003cmeta name\u003d\"keywords\" content\u003d\"STRONA ZAWIESZONA, WEBD, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, DOMENY, DOMENA, NET, .COM, .ORG, TANIE, PHP+MySQL, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL\"\u003e \n \u003cmeta name\u003d\"description\" content\u003d\"STRONA ZAWIESZONA - Oferujemy profesjonalny hosting z PHP + MySQL, rejestrujemy domeny. Sprawdz nasz hosting i przetestuj nasze serwery. Kupuj tanio domeny i serwery!\"\u003e \n \u003ctitle\u003eSTRONA ZAWIESZONA - WEBD.PL - Tw<54>j profesjonalny hosting za jedyne 4.99PLN! Serwery z PHP+MySQL, tanie domeny, serwer + domena .pl - taniej sie nie da!\u003c/title\u003e \n \u003cscript type\u003d\"text/javascript\"\u003e\nfunction init() {\n if (!document.getElementById) return\n var imgOriginSrc;\n var imgTemp \u003d new Array();\n var imgarr \u003d document.getElementsByTagName(\u0027img\u0027);\n for (var i \u003d 0; i \u003c imgarr.length; i++) {\n if (imgarr[i].getAttribute(\u0027hsrc\u0027)) {\n imgTemp[i] \u003d new Image();\n imgTemp[i].src \u003d imgarr[i].getAttribute(\u0027hsrc\u0027);\n imgarr[i].onmouseover \u003d function() {\n imgOriginSrc \u003d this.getAttribute(\u0027src\u0027);\n this.setAttribute(\u0027src\u0027,this.getAttribute(\u0027hsrc\u0027))\n }\n imgarr[i].onmouseout \u003d function() {\n this.setAttribute(\u0027src\u0027,imgOriginSrc)\n }\n }\n }\n}\nonload\u003dinit;\n\u003c/script\u003e \n \u003c/head\u003e \n \u003cbody\u003e\n Trwa przekierowanie .... \u0026gt;\u0026gt;\u0026gt;\u0026gt; \u003c!--\n--\u003e \n \u003c/body\u003e\n\u003c/html\u003e/","categories":[]}"""
|
||||
"https://truyen.fascans.com" -> """{"name":"Fallen Angels Scans","base_url":"https://truyen.fascans.com","supports_latest":true,"item_url":"https://truyen.fascans.com/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}"""
|
||||
"https://mangadoor.com" -> """{"name":"Mangadoor","base_url":"https://mangadoor.com","supports_latest":true,"item_url":"https://mangadoor.com/manga/","categories":[{"id":"1","name":"Acción"},{"id":"2","name":"Aventura"},{"id":"3","name":"Comedia"},{"id":"4","name":"Drama"},{"id":"5","name":"Ecchi"},{"id":"6","name":"Fantasía"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Harem"},{"id":"9","name":"Histórico"},{"id":"10","name":"Horror"},{"id":"11","name":"Josei"},{"id":"12","name":"Artes Marciales"},{"id":"13","name":"Maduro"},{"id":"14","name":"Mecha"},{"id":"15","name":"Misterio"},{"id":"16","name":"One Shot"},{"id":"17","name":"Psicológico"},{"id":"18","name":"Romance"},{"id":"19","name":"Escolar"},{"id":"20","name":"Ciencia Ficción"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shoujo"},{"id":"23","name":"Shoujo Ai"},{"id":"24","name":"Shounen"},{"id":"25","name":"Shounen Ai"},{"id":"26","name":"Recuentos de la vida"},{"id":"27","name":"Deportes"},{"id":"28","name":"Supernatural"},{"id":"29","name":"Tragedia"},{"id":"30","name":"Yaoi"},{"id":"31","name":"Yuri"},{"id":"32","name":"Demonios"},{"id":"33","name":"Juegos"},{"id":"34","name":"Policial"},{"id":"35","name":"Militar"},{"id":"36","name":"Thriller"},{"id":"37","name":"Autos"},{"id":"38","name":"Música"},{"id":"39","name":"Vampiros"},{"id":"40","name":"Magia"},{"id":"41","name":"Samurai"},{"id":"42","name":"Boys love"},{"id":"43","name":"Hentai"}]}"""
|
||||
"https://mangas.in" -> """{"name":"Mangas.pw","base_url":"https://mangas.in","supports_latest":true,"item_url":"https://mangas.in/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Hentai"},{"id":"34","name":"Smut"}]}"""
|
||||
"https://manga.utsukushii-bg.com" -> """{"name":"Utsukushii","base_url":"https://manga.utsukushii-bg.com","supports_latest":true,"item_url":"https://manga.utsukushii-bg.com/manga/","categories":[{"id":"1","name":"Екшън"},{"id":"2","name":"Приключенски"},{"id":"3","name":"Комедия"},{"id":"4","name":"Драма"},{"id":"5","name":"Фентъзи"},{"id":"6","name":"Исторически"},{"id":"7","name":"Ужаси"},{"id":"8","name":"Джосей"},{"id":"9","name":"Бойни изкуства"},{"id":"10","name":"Меха"},{"id":"11","name":"Мистерия"},{"id":"12","name":"Самостоятелна/Пилотна глава"},{"id":"13","name":"Психологически"},{"id":"14","name":"Романтика"},{"id":"15","name":"Училищни"},{"id":"16","name":"Научна фантастика"},{"id":"17","name":"Сейнен"},{"id":"18","name":"Шоджо"},{"id":"19","name":"Реализъм"},{"id":"20","name":"Спорт"},{"id":"21","name":"Свръхестествено"},{"id":"22","name":"Трагедия"},{"id":"23","name":"Йокаи"},{"id":"24","name":"Паралелна вселена"},{"id":"25","name":"Супер сили"},{"id":"26","name":"Пародия"},{"id":"27","name":"Шонен"}]}"""
|
||||
|
|
|
@ -18,14 +18,6 @@ class MyMangaCMSGenerator : ThemeSourceGenerator {
|
|||
"vi",
|
||||
overrideVersionCode = 9,
|
||||
),
|
||||
SingleLang(
|
||||
"Phê Manga",
|
||||
"https://phemanga.net",
|
||||
"vi",
|
||||
true,
|
||||
"PheManga",
|
||||
"phemanga",
|
||||
),
|
||||
SingleLang("LKDTT", "https://lkdttee.com", "vi", true, overrideVersionCode = 4),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,47 +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=".vi.blogtruyen.BlogTruyenUrlActivity"
|
||||
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="blogtruyen.vn" />
|
||||
<data android:host="m.blogtruyen.vn" />
|
||||
<data android:scheme="https" />
|
||||
|
||||
<data android:pathPattern="/tac-gia/..*" />
|
||||
<data android:pathPattern="/nhom-dich/..*" />
|
||||
|
||||
<!--
|
||||
Try to ensure that the passed URL is a chapter `c{id}` or a manga `{id}`, with `id`
|
||||
being a number.
|
||||
-->
|
||||
<data android:pathPattern="/c1.*/..*" />
|
||||
<data android:pathPattern="/c2.*/..*" />
|
||||
<data android:pathPattern="/c3.*/..*" />
|
||||
<data android:pathPattern="/c4.*/..*" />
|
||||
<data android:pathPattern="/c5.*/..*" />
|
||||
<data android:pathPattern="/c6.*/..*" />
|
||||
<data android:pathPattern="/c7.*/..*" />
|
||||
<data android:pathPattern="/c8.*/..*" />
|
||||
<data android:pathPattern="/c9.*/..*" />
|
||||
|
||||
<data android:pathPattern="/1.*/..*" />
|
||||
<data android:pathPattern="/2.*/..*" />
|
||||
<data android:pathPattern="/3.*/..*" />
|
||||
<data android:pathPattern="/4.*/..*" />
|
||||
<data android:pathPattern="/5.*/..*" />
|
||||
<data android:pathPattern="/6.*/..*" />
|
||||
<data android:pathPattern="/7.*/..*" />
|
||||
<data android:pathPattern="/8.*/..*" />
|
||||
<data android:pathPattern="/9.*/..*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
|
@ -1,12 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
extName = 'BlogTruyen'
|
||||
pkgNameSuffix = 'vi.blogtruyen'
|
||||
extClass = '.BlogTruyen'
|
||||
extVersionCode = 14
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 87 KiB |
|
@ -1,432 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.vi.blogtruyen
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
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 kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
class BlogTruyen : ParsedHttpSource() {
|
||||
|
||||
override val name = "BlogTruyen"
|
||||
|
||||
override val baseUrl = "https://blogtruyen.vn"
|
||||
|
||||
override val lang = "vi"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
|
||||
.rateLimit(1)
|
||||
.build()
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val dateFormat: SimpleDateFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.ENGLISH)
|
||||
|
||||
companion object {
|
||||
const val PREFIX_ID_SEARCH = "id:"
|
||||
const val PREFIX_AUTHOR_SEARCH = "author:"
|
||||
const val PREFIX_TEAM_SEARCH = "team:"
|
||||
}
|
||||
|
||||
override fun headersBuilder(): Headers.Builder =
|
||||
super.headersBuilder().add("Referer", "$baseUrl/")
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$baseUrl/ajax/Search/AjaxLoadListManga?key=tatca&orderBy=3&p=$page", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val manga = document.select(popularMangaSelector()).map {
|
||||
val tiptip = it.attr("data-tiptip")
|
||||
popularMangaFromElement(it, document.getElementById(tiptip)!!)
|
||||
}
|
||||
|
||||
val hasNextPage = document.selectFirst(popularMangaNextPageSelector()) != null
|
||||
|
||||
return MangasPage(manga, hasNextPage)
|
||||
}
|
||||
|
||||
override fun popularMangaSelector() = ".list .tiptip"
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
private fun popularMangaFromElement(element: Element, tiptip: Element) = SManga.create().apply {
|
||||
val anchor = element.selectFirst("a")!!
|
||||
setUrlWithoutDomain(anchor.attr("href"))
|
||||
title = anchor.attr("title").replace("truyện tranh ", "").trim()
|
||||
|
||||
thumbnail_url = tiptip.selectFirst("img")!!.attr("abs:src")
|
||||
description = tiptip.selectFirst(".al-j")!!.text()
|
||||
}
|
||||
|
||||
override fun popularMangaNextPageSelector() = ".paging:last-child:not(.current_page)"
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET(baseUrl + if (page != 1) "/page-$page" else "", headers)
|
||||
|
||||
override fun latestUpdatesSelector() = ".storyitem .fl-l"
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply {
|
||||
setUrlWithoutDomain(element.select("a").attr("href"))
|
||||
title = element.select("a").attr("title")
|
||||
thumbnail_url = element.select("img").attr("abs:src")
|
||||
}
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = "select.slcPaging option:last-child:not([selected])"
|
||||
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): Observable<MangasPage> {
|
||||
return when {
|
||||
query.startsWith(PREFIX_ID_SEARCH) -> {
|
||||
var id = query.removePrefix(PREFIX_ID_SEARCH).trim()
|
||||
|
||||
// it's a chapter, resolve to manga ID
|
||||
if (id.startsWith("c")) {
|
||||
val document = client.newCall(GET("$baseUrl/$id", headers)).execute().asJsoup()
|
||||
id = document.selectFirst(".breadcrumbs a:last-child")!!.attr("href").removePrefix("/")
|
||||
}
|
||||
|
||||
fetchMangaDetails(
|
||||
SManga.create().apply {
|
||||
url = "/$id"
|
||||
},
|
||||
)
|
||||
.map { MangasPage(listOf(it.apply { url = "/$id" }), false) }
|
||||
}
|
||||
else -> super.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractIdFromQuery(prefix: String, query: String): String {
|
||||
val q = query.substringAfter(prefix).trim()
|
||||
return if (q.contains("-")) {
|
||||
q.substringAfterLast("-")
|
||||
} else {
|
||||
q
|
||||
}
|
||||
}
|
||||
|
||||
private val ajaxSearchUrls: Map<String, String> = mapOf(
|
||||
PREFIX_AUTHOR_SEARCH to "Author/AjaxLoadMangaByAuthor?orderBy=3",
|
||||
PREFIX_TEAM_SEARCH to "TranslateTeam/AjaxLoadMangaByTranslateTeam",
|
||||
)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
ajaxSearchUrls.keys.forEach {
|
||||
if (!query.startsWith(it)) {
|
||||
return@forEach
|
||||
}
|
||||
val id = extractIdFromQuery(it, query)
|
||||
val url = "$baseUrl/ajax/${ajaxSearchUrls[it]}".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("id", id)
|
||||
.addQueryParameter("p", page.toString())
|
||||
.build()
|
||||
.toString()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
val url = "$baseUrl/timkiem/nangcao/1".toHttpUrl().newBuilder().apply {
|
||||
addQueryParameter("txt", query)
|
||||
addQueryParameter("p", page.toString())
|
||||
|
||||
val genres = mutableListOf<Int>()
|
||||
val genresEx = mutableListOf<Int>()
|
||||
var status = 0
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach {
|
||||
when (it.state) {
|
||||
Filter.TriState.STATE_INCLUDE -> genres.add(it.id)
|
||||
Filter.TriState.STATE_EXCLUDE -> genresEx.add(it.id)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
is Author -> {
|
||||
addQueryParameter("aut", filter.state)
|
||||
}
|
||||
is Scanlator -> {
|
||||
addQueryParameter("gr", filter.state)
|
||||
}
|
||||
is Status -> {
|
||||
status = filter.state
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
addPathSegment(status.toString())
|
||||
addPathSegment(genres.joinToString(","))
|
||||
addPathSegment(genresEx.joinToString(","))
|
||||
}.build().toString()
|
||||
return GET(url, headers)
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val manga = document.select(searchMangaSelector()).map {
|
||||
val tiptip = it.attr("data-tiptip")
|
||||
searchMangaFromElement(it, document.getElementById(tiptip)!!)
|
||||
}
|
||||
|
||||
val hasNextPage = document.selectFirst(searchMangaNextPageSelector()) != null
|
||||
|
||||
return MangasPage(manga, hasNextPage)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga =
|
||||
throw UnsupportedOperationException("Not used")
|
||||
|
||||
private fun searchMangaFromElement(element: Element, tiptip: Element) =
|
||||
popularMangaFromElement(element, tiptip)
|
||||
|
||||
override fun searchMangaNextPageSelector() = ".pagination .glyphicon-step-forward"
|
||||
|
||||
private fun getMangaTitle(document: Document) = document.selectFirst(".entry-title a")!!
|
||||
.attr("title")
|
||||
.replaceFirst("truyện tranh", "", false)
|
||||
.trim()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val anchor = document.selectFirst(".entry-title a")!!
|
||||
setUrlWithoutDomain(anchor.attr("href"))
|
||||
title = getMangaTitle(document)
|
||||
|
||||
thumbnail_url = document.select(".thumbnail img").attr("abs:src")
|
||||
author = document.select("a[href*=tac-gia]").joinToString { it.text() }
|
||||
genre = document.select("span.category a").joinToString { it.text() }
|
||||
status = parseStatus(
|
||||
document.select("span.color-red:not(.bold)").text(),
|
||||
)
|
||||
|
||||
description = StringBuilder().apply {
|
||||
// the actual synopsis
|
||||
val synopsisBlock = document.selectFirst(".manga-detail .detail .content")!!
|
||||
|
||||
// replace the facebook blockquote in synopsis with the link (if there is one)
|
||||
val fbElement = synopsisBlock.selectFirst(".fb-page, .fb-group")
|
||||
if (fbElement != null) {
|
||||
val fbLink = fbElement.attr("data-href")
|
||||
|
||||
val node = document.createElement("p")
|
||||
node.appendText(fbLink)
|
||||
|
||||
fbElement.replaceWith(node)
|
||||
}
|
||||
appendLine(synopsisBlock.textWithNewlines().trim())
|
||||
appendLine()
|
||||
|
||||
// other metadata
|
||||
document.select(".description p").forEach {
|
||||
val text = it.text()
|
||||
if (text.contains("Thể loại") ||
|
||||
text.contains("Tác giả") ||
|
||||
text.isBlank()
|
||||
) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
if (text.contains("Trạng thái")) {
|
||||
appendLine(text.substringBefore("Trạng thái").trim())
|
||||
return@forEach
|
||||
}
|
||||
|
||||
if (text.contains("Nguồn") ||
|
||||
text.contains("Tham gia update") ||
|
||||
text.contains("Nhóm dịch")
|
||||
) {
|
||||
val key = text.substringBefore(":")
|
||||
val value = it.select("a").joinToString { el -> el.text() }
|
||||
appendLine("$key: $value")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
it.select("a, span").append("\\n")
|
||||
appendLine(it.text().replace("\\n", "\n").replace("\n ", "\n").trim())
|
||||
}
|
||||
}.toString().trim()
|
||||
}
|
||||
|
||||
private fun Element.textWithNewlines() = run {
|
||||
select("p").prepend("\\n")
|
||||
select("br").prepend("\\n")
|
||||
text().replace("\\n", "\n").replace("\n ", "\n")
|
||||
}
|
||||
|
||||
private fun parseStatus(status: String) = when {
|
||||
status.contains("Đang tiến hành") -> SManga.ONGOING
|
||||
status.contains("Đã hoàn thành") -> SManga.COMPLETED
|
||||
status.contains("Tạm ngưng") -> SManga.ON_HIATUS
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
val title = getMangaTitle(document)
|
||||
return document.select(chapterListSelector()).map { chapterFromElement(it, title) }
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "div.list-wrap > p"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter = throw UnsupportedOperationException("Not used")
|
||||
|
||||
private fun chapterFromElement(element: Element, title: String): SChapter = SChapter.create().apply {
|
||||
val anchor = element.select("span > a").first()!!
|
||||
|
||||
setUrlWithoutDomain(anchor.attr("href"))
|
||||
name = anchor.attr("title").replace(title, "", true).trim()
|
||||
date_upload = runCatching {
|
||||
dateFormat.parse(
|
||||
element.selectFirst("span.publishedDate")!!.text(),
|
||||
)?.time
|
||||
}.getOrNull() ?: 0L
|
||||
}
|
||||
|
||||
private fun countViewRequest(mangaId: String, chapterId: String): Request = POST(
|
||||
"$baseUrl/Chapter/UpdateView",
|
||||
headers,
|
||||
FormBody.Builder()
|
||||
.add("mangaId", mangaId)
|
||||
.add("chapterId", chapterId)
|
||||
.build(),
|
||||
)
|
||||
|
||||
private fun countView(document: Document) {
|
||||
val mangaId = document.getElementById("MangaId")!!.attr("value")
|
||||
val chapterId = document.getElementById("ChapterId")!!.attr("value")
|
||||
runCatching {
|
||||
client.newCall(countViewRequest(mangaId, chapterId)).execute().close()
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
document.select("#content > img").forEachIndexed { i, e ->
|
||||
pages.add(Page(i, imageUrl = e.attr("abs:src")))
|
||||
}
|
||||
|
||||
// Some chapters use js script to render images
|
||||
document.select("#content > script:containsData(listImageCaption)").lastOrNull()
|
||||
?.let { script ->
|
||||
val imagesStr = script.data().substringBefore(";").substringAfterLast("=").trim()
|
||||
val imageArr = json.parseToJsonElement(imagesStr).jsonArray
|
||||
imageArr.forEach {
|
||||
val imageUrl = it.jsonObject["url"]!!.jsonPrimitive.content
|
||||
pages.add(Page(pages.size, imageUrl = imageUrl))
|
||||
}
|
||||
}
|
||||
|
||||
countView(document)
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
|
||||
|
||||
private class Status : Filter.Select<String>(
|
||||
"Status",
|
||||
arrayOf("Sao cũng được", "Đang tiến hành", "Đã hoàn thành", "Tạm ngưng"),
|
||||
)
|
||||
|
||||
private class Author : Filter.Text("Tác giả")
|
||||
private class Scanlator : Filter.Text("Nhóm dịch")
|
||||
private class Genre(name: String, val id: Int) : Filter.TriState(name)
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Thể loại", genres)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Author(),
|
||||
Scanlator(),
|
||||
Status(),
|
||||
GenreList(getGenreList()),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("16+", 54),
|
||||
Genre("18+", 45),
|
||||
Genre("Action", 1),
|
||||
Genre("Adult", 2),
|
||||
Genre("Adventure", 3),
|
||||
Genre("Anime", 4),
|
||||
Genre("Comedy", 5),
|
||||
Genre("Comic", 6),
|
||||
Genre("Doujinshi", 7),
|
||||
Genre("Drama", 49),
|
||||
Genre("Ecchi", 48),
|
||||
Genre("Even BT", 60),
|
||||
Genre("Fantasy", 50),
|
||||
Genre("Game", 61),
|
||||
Genre("Gender Bender", 51),
|
||||
Genre("Harem", 12),
|
||||
Genre("Historical", 13),
|
||||
Genre("Horror", 14),
|
||||
Genre("Isekai/Dị Giới", 63),
|
||||
Genre("Josei", 15),
|
||||
Genre("Live Action", 16),
|
||||
Genre("Magic", 46),
|
||||
Genre("Manga", 55),
|
||||
Genre("Manhua", 17),
|
||||
Genre("Manhwa", 18),
|
||||
Genre("Martial Arts", 19),
|
||||
Genre("Mature", 20),
|
||||
Genre("Mecha", 21),
|
||||
Genre("Mystery", 22),
|
||||
Genre("Nấu ăn", 56),
|
||||
Genre("NTR", 62),
|
||||
Genre("One shot", 23),
|
||||
Genre("Psychological", 24),
|
||||
Genre("Romance", 25),
|
||||
Genre("School Life", 26),
|
||||
Genre("Sci-fi", 27),
|
||||
Genre("Seinen", 28),
|
||||
Genre("Shoujo", 29),
|
||||
Genre("Shoujo Ai", 30),
|
||||
Genre("Shounen", 31),
|
||||
Genre("Shounen Ai", 32),
|
||||
Genre("Slice of Life", 33),
|
||||
Genre("Smut", 34),
|
||||
Genre("Soft Yaoi", 35),
|
||||
Genre("Soft Yuri", 36),
|
||||
Genre("Sports", 37),
|
||||
Genre("Supernatural", 38),
|
||||
Genre("Tạp chí truyện tranh", 39),
|
||||
Genre("Tragedy", 40),
|
||||
Genre("Trap", 58),
|
||||
Genre("Trinh thám", 57),
|
||||
Genre("Truyện scan", 41),
|
||||
Genre("Video clip", 53),
|
||||
Genre("VnComic", 42),
|
||||
Genre("Webtoon", 52),
|
||||
Genre("Yuri", 59),
|
||||
)
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package eu.kanade.tachiyomi.extension.vi.blogtruyen
|
||||
|
||||
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 BlogTruyenUrlActivity : Activity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 1) {
|
||||
try {
|
||||
startActivity(
|
||||
Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
with(pathSegments[0]) {
|
||||
when {
|
||||
equals("tac-gia") -> putExtra("query", "${BlogTruyen.PREFIX_AUTHOR_SEARCH}${pathSegments[1]}")
|
||||
equals("nhom-dich") -> putExtra("query", "${BlogTruyen.PREFIX_TEAM_SEARCH}${pathSegments[1]}")
|
||||
else -> putExtra("query", "${BlogTruyen.PREFIX_ID_SEARCH}${pathSegments[0]}/${pathSegments[1]}")
|
||||
}
|
||||
}
|
||||
putExtra("filter", packageName)
|
||||
},
|
||||
)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("BlogTruyenUrlActivity", e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e("BlogTruyenUrlActivity", "Could not parse URI from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|