[RU]GroupLe (ReadManga, MintManga, SelfManga, AllHentai + RuMIX) (#12112)
* [RU]GroupLe (ReadManga, MintManga, SelfManga, AllHentai) * fix empty katalog * new source RuMIX * icon style
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 8.4 KiB |
After Width: | Height: | Size: 13 KiB |
BIN
multisrc/overrides/grouple/allhentai/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 49 KiB |
259
multisrc/overrides/grouple/allhentai/src/AllHentai.kt
Normal file
@ -0,0 +1,259 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.allhentai
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
|
||||
class AllHentai : GroupLe("AllHentai", "http://23.allhen.online", "ru"){
|
||||
|
||||
override val id: Long = 1809051393403180443
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
is Tags -> {
|
||||
if (filter.state > 0) {
|
||||
val tagName = getTagsList()[filter.state].url
|
||||
val tagUrl = "$baseUrl/list/tag/$tagName?offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(tagUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return if (url.toString().contains("?"))
|
||||
GET(url.toString().replace("=%3D", "="), headers)
|
||||
else popularMangaRequest(page)
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
private class Tags(tags: Array<String>) : Filter.Select<String>("Тэг (только)", tags)
|
||||
|
||||
private data class Tag(val name: String, val url: String)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Tags(tagsName),
|
||||
GenreList(getGenreList()),
|
||||
Category(getCategoryList()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("ahegao", "el_855"),
|
||||
Genre("анал", "el_828"),
|
||||
Genre("бдсм", "el_78"),
|
||||
Genre("без цензуры", "el_888"),
|
||||
Genre("большая грудь", "el_837"),
|
||||
Genre("большая попка", "el_3156"),
|
||||
Genre("большой член", "el_884"),
|
||||
Genre("бондаж", "el_5754"),
|
||||
Genre("в первый раз", "el_811"),
|
||||
Genre("в цвете", "el_290"),
|
||||
Genre("гарем", "el_87"),
|
||||
Genre("гендарная интрига", "el_89"),
|
||||
Genre("групповой секс", "el_88"),
|
||||
Genre("драма", "el_95"),
|
||||
Genre("зрелые женщины", "el_5679"),
|
||||
Genre("измена", "el_291"),
|
||||
Genre("изнасилование", "el_124"),
|
||||
Genre("инцест", "el_85"),
|
||||
Genre("исторический", "el_93"),
|
||||
Genre("комедия", "el_73"),
|
||||
Genre("маленькая грудь", "el_870"),
|
||||
Genre("научная фантастика", "el_76"),
|
||||
Genre("нетораре", "el_303"),
|
||||
Genre("оральный секс", "el_853"),
|
||||
Genre("романтика", "el_74"),
|
||||
Genre("тентакли", "el_69"),
|
||||
Genre("трагедия", "el_1321"),
|
||||
Genre("ужасы", "el_75"),
|
||||
Genre("футанари", "el_77"),
|
||||
Genre("фэнтези", "el_70"),
|
||||
Genre("чикан", "el_1059"),
|
||||
Genre("этти", "el_798"),
|
||||
Genre("юри", "el_84"),
|
||||
Genre("яой", "el_83")
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("3D", "el_626"),
|
||||
Genre("Анимация", "el_5777"),
|
||||
Genre("Без текста", "el_3157"),
|
||||
Genre("Порно комикс", "el_1003"),
|
||||
Genre("Порно манхва", "el_1104")
|
||||
)
|
||||
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
Genre("Продается", "s_sale")
|
||||
)
|
||||
|
||||
private fun getTagsList() = listOf(
|
||||
Tag("Без тега", "not"),
|
||||
Tag("handjob", "handjob"),
|
||||
Tag("inseki", "inseki"),
|
||||
Tag("алкоголь", "alcohol"),
|
||||
Tag("андроид", "android"),
|
||||
Tag("анилингус", "anilingus"),
|
||||
Tag("бассейн", "pool"),
|
||||
Tag("без трусиков", "without_panties"),
|
||||
Tag("беременность", "pregnancy"),
|
||||
Tag("бикини", "bikini"),
|
||||
Tag("близнецы", "twins"),
|
||||
Tag("боди-арт", "body_art"),
|
||||
Tag("больница", "hospital"),
|
||||
Tag("буккакэ", "bukkake"),
|
||||
Tag("в ванной", "in_bathroom"),
|
||||
Tag("в общественном месте", "in_public_place"),
|
||||
Tag("в транспорте", "in_vehicle"),
|
||||
Tag("вампиры", "vampires"),
|
||||
Tag("вибратор", "vibrator"),
|
||||
Tag("втянутые соски", "inverted_nipples"),
|
||||
Tag("гипноз", "hypnosis"),
|
||||
Tag("глубокий минет", "deepthroat"),
|
||||
Tag("горничные", "maids"),
|
||||
Tag("горячий источник", "hot_spring"),
|
||||
Tag("гэнгбэнг", "gangbang"),
|
||||
Tag("гяру", "gyaru"),
|
||||
Tag("двойное проникновение", "double_penetration"),
|
||||
Tag("Девочки волшебницы", "magical_girl"),
|
||||
Tag("демоны", "demons"),
|
||||
Tag("дефекация", "scat"),
|
||||
Tag("дилдо", "dildo"),
|
||||
Tag("додзинси", "doujinshi"),
|
||||
Tag("домохозяйки", "housewives"),
|
||||
Tag("дыра в стене", "hole_in_the_wall"),
|
||||
Tag("жестокость", "cruelty"),
|
||||
Tag("загар", "tan_lines"),
|
||||
Tag("зомби", "zombie"),
|
||||
Tag("инопланетяне", "aliens"),
|
||||
Tag("исполнение желаний", "granting_wish"),
|
||||
Tag("камера", "camera"),
|
||||
Tag("косплей", "cosplay"),
|
||||
Tag("кремпай", "creampie"),
|
||||
Tag("куннилингус", "cunnilingus"),
|
||||
Tag("купальник", "swimsuit"),
|
||||
Tag("лактация", "lactation"),
|
||||
Tag("латекс и кожа", "latex"),
|
||||
Tag("Ломка Психики", "mind_break"),
|
||||
Tag("магия", "magic"),
|
||||
Tag("мастурбация", "masturbation"),
|
||||
Tag("медсестра", "nurse"),
|
||||
Tag("мерзкий дядька", "terrible_oyaji"),
|
||||
Tag("много девушек", "many_girls"),
|
||||
Tag("много спермы", "a_lot_of_sperm"),
|
||||
Tag("монстрдевушки", "monstergirl"),
|
||||
Tag("монстры", "monsters"),
|
||||
Tag("мужчина крепкого телосложения", "muscle_man"),
|
||||
Tag("на природе", "outside"),
|
||||
Tag("не бритая киска", "hairy_pussy"),
|
||||
Tag("не бритые подмышки", "hairy_armpits"),
|
||||
Tag("нетори", "netori"),
|
||||
Tag("нижнее бельё", "lingerie"),
|
||||
Tag("обмен партнерами", "swinging"),
|
||||
Tag("обмен телами", "body_swap"),
|
||||
Tag("обычный секс", "normal_sex"),
|
||||
Tag("огромная грудь", "super_big_boobs"),
|
||||
Tag("орки", "orcs"),
|
||||
Tag("очки", "megane"),
|
||||
Tag("пайзури", "titsfuck"),
|
||||
Tag("парень пассив", "passive_guy"),
|
||||
Tag("пацанка", "tomboy"),
|
||||
Tag("пеггинг", "pegging"),
|
||||
Tag("переодевание", "disguise"),
|
||||
Tag("пирсинг", "piercing"),
|
||||
Tag("писают", "peeing"),
|
||||
Tag("пляж", "beach"),
|
||||
Tag("повседневность", "slice_of_life"),
|
||||
Tag("повязка на глаза", "blindfold"),
|
||||
Tag("подглядывание", "peeping"),
|
||||
Tag("подчинение", "submission"),
|
||||
Tag("похищение", "kidnapping"),
|
||||
Tag("принуждение", "forced"),
|
||||
Tag("прозрачная одежда", "transparent_clothes"),
|
||||
Tag("проституция", "prostitution"),
|
||||
Tag("психические отклонения", "mental_illness"),
|
||||
Tag("публичный секс", "public_sex"),
|
||||
Tag("пьяные", "drunk"),
|
||||
Tag("рабы", "slaves"),
|
||||
Tag("рентген зрение", "x_ray"),
|
||||
Tag("сверхъестественное", "supernatural"),
|
||||
Tag("секс втроем", "threesome"),
|
||||
Tag("секс игрушки", "sex_toys"),
|
||||
Tag("сексуально возбужденная", "horny"),
|
||||
Tag("спортивная форма", "sports_uniform"),
|
||||
Tag("спящие", "sleeping"),
|
||||
Tag("страпон", "strapon"),
|
||||
Tag("Суккуб", "succubus"),
|
||||
Tag("темнокожие", "dark_skin"),
|
||||
Tag("толстушки", "fatties"),
|
||||
Tag("трап", "trap"),
|
||||
Tag("униформа", "uniform"),
|
||||
Tag("ушастые", "eared"),
|
||||
Tag("фантазии", "dreams"),
|
||||
Tag("фемдом", "femdom"),
|
||||
Tag("фестиваль", "festival"),
|
||||
Tag("фетиш", "fetish"),
|
||||
Tag("фистинг", "fisting"),
|
||||
Tag("фурри", "furry"),
|
||||
Tag("футанари имеет парня", "futanari_on_boy"),
|
||||
Tag("футджаб", "footfuck"),
|
||||
Tag("цельный купальник", "full_swimsuit"),
|
||||
Tag("цундэрэ", "tsundere"),
|
||||
Tag("чулки", "hose"),
|
||||
Tag("шалава", "slut"),
|
||||
Tag("шантаж", "blackmail"),
|
||||
Tag("эксгибиционизм", "exhibitionism"),
|
||||
Tag("эльфы", "elves"),
|
||||
Tag("яндере", "yandere")
|
||||
)
|
||||
|
||||
private val tagsName = getTagsList().map {
|
||||
it.name
|
||||
}.toTypedArray()
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name=".ru.mintmanga.MintmangaActivity"
|
||||
android:name="eu.kanade.tachiyomi.multisrc.grouple.GroupLeUrlActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:theme="@android:style/Theme.NoDisplay">
|
||||
@ -14,11 +14,11 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<!-- ReadmangaActivity sites can be added here. -->
|
||||
<!-- GroupLeUrlActivity sites can be added here. -->
|
||||
<data
|
||||
android:host="mintmanga.live"
|
||||
android:host="${SOURCEHOST}"
|
||||
android:pathPattern="/..*/vol..*"
|
||||
android:scheme="https" />
|
||||
android:scheme="${SOURCESCHEME}" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 31 KiB |
BIN
multisrc/overrides/grouple/mintmanga/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 190 KiB |
157
multisrc/overrides/grouple/mintmanga/src/MintManga.kt
Normal file
@ -0,0 +1,157 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.mintmanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
|
||||
class MintManga : GroupLe("MintManga", "https://mintmanga.live", "ru"){
|
||||
|
||||
override val id: Long = 6
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is AgeList -> filter.state.forEach { age ->
|
||||
if (age.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(age.id, arrayOf("=", "=in", "=ex")[age.state])
|
||||
}
|
||||
}
|
||||
is More -> filter.state.forEach { more ->
|
||||
if (more.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(more.id, arrayOf("=", "=in", "=ex")[more.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return if (url.toString().contains("?"))
|
||||
GET(url.toString().replace("=%3D", "="), headers)
|
||||
else popularMangaRequest(page)
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class AgeList(ages: List<Genre>) : Filter.Group<Genre>("Возрастная рекомендация", ages)
|
||||
private class More(moren: List<Genre>) : Filter.Group<Genre>("Прочее", moren)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList()),
|
||||
AgeList(getAgeList()),
|
||||
More(getMore()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
)
|
||||
private fun getMore() = listOf(
|
||||
Genre("В цвете", "el_4614"),
|
||||
Genre("Веб", "el_1355"),
|
||||
Genre("Выпуск приостановлен", "el_5232"),
|
||||
Genre("Не Яой", "el_1874"),
|
||||
Genre("Сборник", "el_1348")
|
||||
)
|
||||
|
||||
private fun getAgeList() = listOf(
|
||||
Genre("R(16+)", "el_3968"),
|
||||
Genre("NC-17(18+)", "el_3969"),
|
||||
Genre("R18+(18+)", "el_3990")
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Ёнкома", "el_2741"),
|
||||
Genre("Комикс западный", "el_1903"),
|
||||
Genre("Комикс русский", "el_2173"),
|
||||
Genre("Манхва", "el_1873"),
|
||||
Genre("Маньхуа", "el_1875"),
|
||||
Genre("Ранобэ", "el_5688"),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("арт", "el_2220"),
|
||||
Genre("бара", "el_1353"),
|
||||
Genre("боевик", "el_1346"),
|
||||
Genre("боевые искусства", "el_1334"),
|
||||
Genre("вампиры", "el_1339"),
|
||||
Genre("гарем", "el_1333"),
|
||||
Genre("гендерная интрига", "el_1347"),
|
||||
Genre("героическое фэнтези", "el_1337"),
|
||||
Genre("детектив", "el_1343"),
|
||||
Genre("дзёсэй", "el_1349"),
|
||||
Genre("додзинси", "el_1332"),
|
||||
Genre("драма", "el_1310"),
|
||||
Genre("игра", "el_5229"),
|
||||
Genre("история", "el_1311"),
|
||||
Genre("киберпанк", "el_1351"),
|
||||
Genre("комедия", "el_1328"),
|
||||
Genre("меха", "el_1318"),
|
||||
Genre("научная фантастика", "el_1325"),
|
||||
Genre("омегаверс", "el_5676"),
|
||||
Genre("повседневность", "el_1327"),
|
||||
Genre("постапокалиптика", "el_1342"),
|
||||
Genre("приключения", "el_1322"),
|
||||
Genre("психология", "el_1335"),
|
||||
Genre("романтика", "el_1313"),
|
||||
Genre("самурайский боевик", "el_1316"),
|
||||
Genre("сверхъестественное", "el_1350"),
|
||||
Genre("сёдзё", "el_1314"),
|
||||
Genre("сёдзё-ай", "el_1320"),
|
||||
Genre("сёнэн", "el_1326"),
|
||||
Genre("сёнэн-ай", "el_1330"),
|
||||
Genre("спорт", "el_1321"),
|
||||
Genre("сэйнэн", "el_1329"),
|
||||
Genre("трагедия", "el_1344"),
|
||||
Genre("триллер", "el_1341"),
|
||||
Genre("ужасы", "el_1317"),
|
||||
Genre("фэнтези", "el_1323"),
|
||||
Genre("школа", "el_1319"),
|
||||
Genre("эротика", "el_1340"),
|
||||
Genre("этти", "el_1354"),
|
||||
Genre("юри", "el_1315"),
|
||||
Genre("яой", "el_1336")
|
||||
)
|
||||
}
|
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 31 KiB |
BIN
multisrc/overrides/grouple/readmanga/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 186 KiB |
156
multisrc/overrides/grouple/readmanga/src/ReadManga.kt
Normal file
@ -0,0 +1,156 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.readmanga
|
||||
|
||||
import android.widget.Toast
|
||||
import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
|
||||
class ReadManga : GroupLe("ReadManga", "https://readmanga.io", "ru"){
|
||||
|
||||
override val id: Long = 5
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is AgeList -> filter.state.forEach { age ->
|
||||
if (age.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(age.id, arrayOf("=", "=in", "=ex")[age.state])
|
||||
}
|
||||
}
|
||||
is More -> filter.state.forEach { more ->
|
||||
if (more.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(more.id, arrayOf("=", "=in", "=ex")[more.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return if (url.toString().contains("?"))
|
||||
GET(url.toString().replace("=%3D", "="), headers)
|
||||
else popularMangaRequest(page)
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class AgeList(ages: List<Genre>) : Filter.Group<Genre>("Возрастная рекомендация", ages)
|
||||
private class More(moren: List<Genre>) : Filter.Group<Genre>("Прочее", moren)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList()),
|
||||
AgeList(getAgeList()),
|
||||
More(getMore()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
Genre("Продается", "s_sale")
|
||||
)
|
||||
private fun getMore() = listOf(
|
||||
Genre("В цвете", "el_7290"),
|
||||
Genre("Веб", "el_2160"),
|
||||
Genre("Выпуск приостановлен", "el_8033"),
|
||||
Genre("Сборник", "el_2157")
|
||||
)
|
||||
|
||||
private fun getAgeList() = listOf(
|
||||
Genre("G(0+)", "el_6180"),
|
||||
Genre("PG-13(12+)", "el_6181"),
|
||||
Genre("PG(16+)", "el_6179")
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Ёнкома", "el_2161"),
|
||||
Genre("Комикс западный", "el_3515"),
|
||||
Genre("Манхва", "el_3001"),
|
||||
Genre("Маньхуа", "el_3002"),
|
||||
Genre("Ранобэ", "el_8575"),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("арт", "el_5685"),
|
||||
Genre("боевик", "el_2155"),
|
||||
Genre("боевые искусства", "el_2143"),
|
||||
Genre("вампиры", "el_2148"),
|
||||
Genre("гарем", "el_2142"),
|
||||
Genre("гендерная интрига", "el_2156"),
|
||||
Genre("героическое фэнтези", "el_2146"),
|
||||
Genre("детектив", "el_2152"),
|
||||
Genre("дзёсэй", "el_2158"),
|
||||
Genre("додзинси", "el_2141"),
|
||||
Genre("драма", "el_2118"),
|
||||
Genre("игра", "el_2154"),
|
||||
Genre("история", "el_2119"),
|
||||
Genre("киберпанк", "el_8032"),
|
||||
Genre("кодомо", "el_2137"),
|
||||
Genre("комедия", "el_2136"),
|
||||
Genre("махо-сёдзё", "el_2147"),
|
||||
Genre("меха", "el_2126"),
|
||||
Genre("научная фантастика", "el_2133"),
|
||||
Genre("повседневность", "el_2135"),
|
||||
Genre("постапокалиптика", "el_2151"),
|
||||
Genre("приключения", "el_2130"),
|
||||
Genre("психология", "el_2144"),
|
||||
Genre("романтика", "el_2121"),
|
||||
Genre("самурайский боевик", "el_2124"),
|
||||
Genre("сверхъестественное", "el_2159"),
|
||||
Genre("сёдзё", "el_2122"),
|
||||
Genre("сёдзё-ай", "el_2128"),
|
||||
Genre("сёнэн", "el_2134"),
|
||||
Genre("сёнэн-ай", "el_2139"),
|
||||
Genre("спорт", "el_2129"),
|
||||
Genre("сэйнэн", "el_2138"),
|
||||
Genre("трагедия", "el_2153"),
|
||||
Genre("триллер", "el_2150"),
|
||||
Genre("ужасы", "el_2125"),
|
||||
Genre("фэнтези", "el_2131"),
|
||||
Genre("школа", "el_2127"),
|
||||
Genre("этти", "el_2149"),
|
||||
Genre("юри", "el_2123")
|
||||
)
|
||||
}
|
BIN
multisrc/overrides/grouple/rumix/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
multisrc/overrides/grouple/rumix/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 19 KiB |
BIN
multisrc/overrides/grouple/rumix/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 89 KiB |
23
multisrc/overrides/grouple/rumix/src/RuMIX.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.rumix
|
||||
|
||||
import android.widget.Toast
|
||||
import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
|
||||
class RuMIX : GroupLe("RuMIX", "https://rumix.me", "ru"){
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return if (url.toString().contains("?"))
|
||||
GET(url.toString().replace("=%3D", "="), headers)
|
||||
else popularMangaRequest(page)
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 8.5 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 27 KiB |
BIN
multisrc/overrides/grouple/selfmanga/res/web_hi_res_512.png
Normal file
After Width: | Height: | Size: 155 KiB |
93
multisrc/overrides/grouple/selfmanga/src/SelfManga.kt
Normal file
@ -0,0 +1,93 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.selfmanga
|
||||
|
||||
import eu.kanade.tachiyomi.multisrc.grouple.GroupLe
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Request
|
||||
|
||||
class SelfManga : GroupLe("SelfManga", "https://selfmanga.live", "ru") {
|
||||
|
||||
override val id: Long = 5227602742162454547
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return if (url.toString().contains("?"))
|
||||
GET(url.toString().replace("=%3D", "="), headers)
|
||||
else popularMangaRequest(page)
|
||||
}
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Category", categories)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList())
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Артбук", "el_5894"),
|
||||
Genre("Веб", "el_2160"),
|
||||
Genre("Журнал", "el_4983"),
|
||||
Genre("Ранобэ", "el_5215"),
|
||||
Genre("Сборник", "el_2157")
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("боевик", "el_2155"),
|
||||
Genre("боевые искусства", "el_2143"),
|
||||
Genre("вампиры", "el_2148"),
|
||||
Genre("гарем", "el_2142"),
|
||||
Genre("гендерная интрига", "el_2156"),
|
||||
Genre("героическое фэнтези", "el_2146"),
|
||||
Genre("детектив", "el_2152"),
|
||||
Genre("дзёсэй", "el_2158"),
|
||||
Genre("додзинси", "el_2141"),
|
||||
Genre("драма", "el_2118"),
|
||||
Genre("ёнкома", "el_2161"),
|
||||
Genre("история", "el_2119"),
|
||||
Genre("комедия", "el_2136"),
|
||||
Genre("махо-сёдзё", "el_2147"),
|
||||
Genre("мистика", "el_2132"),
|
||||
Genre("научная фантастика", "el_2133"),
|
||||
Genre("повседневность", "el_2135"),
|
||||
Genre("постапокалиптика", "el_2151"),
|
||||
Genre("приключения", "el_2130"),
|
||||
Genre("психология", "el_2144"),
|
||||
Genre("романтика", "el_2121"),
|
||||
Genre("сверхъестественное", "el_2159"),
|
||||
Genre("сёдзё", "el_2122"),
|
||||
Genre("сёдзё-ай", "el_2128"),
|
||||
Genre("сёнэн", "el_2134"),
|
||||
Genre("сёнэн-ай", "el_2139"),
|
||||
Genre("спорт", "el_2129"),
|
||||
Genre("сэйнэн", "el_5838"),
|
||||
Genre("трагедия", "el_2153"),
|
||||
Genre("триллер", "el_2150"),
|
||||
Genre("ужасы", "el_2125"),
|
||||
Genre("фантастика", "el_2140"),
|
||||
Genre("фэнтези", "el_2131"),
|
||||
Genre("школа", "el_2127"),
|
||||
Genre("этти", "el_4982")
|
||||
)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.readmanga
|
||||
package eu.kanade.tachiyomi.multisrc.grouple
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
@ -32,17 +32,15 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class Readmanga : ConfigurableSource, ParsedHttpSource() {
|
||||
abstract class GroupLe(
|
||||
override val name: String,
|
||||
override val baseUrl: String,
|
||||
final override val lang: String
|
||||
) : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val id: Long = 5
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
override val name = "Readmanga"
|
||||
|
||||
override val baseUrl = "https://readmanga.io"
|
||||
|
||||
override val lang = "ru"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
@ -90,50 +88,6 @@ class Readmanga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is AgeList -> filter.state.forEach { age ->
|
||||
if (age.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(age.id, arrayOf("=", "=in", "=ex")[age.state])
|
||||
}
|
||||
}
|
||||
is More -> filter.state.forEach { more ->
|
||||
if (more.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(more.id, arrayOf("=", "=in", "=ex")[more.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return GET(url.toString().replace("=%3D", "="), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
@ -344,101 +298,6 @@ class Readmanga : ConfigurableSource, ParsedHttpSource() {
|
||||
}
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class AgeList(ages: List<Genre>) : Filter.Group<Genre>("Возрастная рекомендация", ages)
|
||||
private class More(moren: List<Genre>) : Filter.Group<Genre>("Прочее", moren)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList()),
|
||||
AgeList(getAgeList()),
|
||||
More(getMore()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
Genre("Продается", "s_sale")
|
||||
)
|
||||
private fun getMore() = listOf(
|
||||
Genre("В цвете", "el_7290"),
|
||||
Genre("Веб", "el_2160"),
|
||||
Genre("Выпуск приостановлен", "el_8033"),
|
||||
Genre("Сборник", "el_2157")
|
||||
)
|
||||
|
||||
private fun getAgeList() = listOf(
|
||||
Genre("G(0+)", "el_6180"),
|
||||
Genre("PG-13(12+)", "el_6181"),
|
||||
Genre("PG(16+)", "el_6179")
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Ёнкома", "el_2161"),
|
||||
Genre("Комикс западный", "el_3515"),
|
||||
Genre("Манхва", "el_3001"),
|
||||
Genre("Маньхуа", "el_3002"),
|
||||
Genre("Ранобэ", "el_8575"),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("арт", "el_5685"),
|
||||
Genre("боевик", "el_2155"),
|
||||
Genre("боевые искусства", "el_2143"),
|
||||
Genre("вампиры", "el_2148"),
|
||||
Genre("гарем", "el_2142"),
|
||||
Genre("гендерная интрига", "el_2156"),
|
||||
Genre("героическое фэнтези", "el_2146"),
|
||||
Genre("детектив", "el_2152"),
|
||||
Genre("дзёсэй", "el_2158"),
|
||||
Genre("додзинси", "el_2141"),
|
||||
Genre("драма", "el_2118"),
|
||||
Genre("игра", "el_2154"),
|
||||
Genre("история", "el_2119"),
|
||||
Genre("киберпанк", "el_8032"),
|
||||
Genre("кодомо", "el_2137"),
|
||||
Genre("комедия", "el_2136"),
|
||||
Genre("махо-сёдзё", "el_2147"),
|
||||
Genre("меха", "el_2126"),
|
||||
Genre("научная фантастика", "el_2133"),
|
||||
Genre("повседневность", "el_2135"),
|
||||
Genre("постапокалиптика", "el_2151"),
|
||||
Genre("приключения", "el_2130"),
|
||||
Genre("психология", "el_2144"),
|
||||
Genre("романтика", "el_2121"),
|
||||
Genre("самурайский боевик", "el_2124"),
|
||||
Genre("сверхъестественное", "el_2159"),
|
||||
Genre("сёдзё", "el_2122"),
|
||||
Genre("сёдзё-ай", "el_2128"),
|
||||
Genre("сёнэн", "el_2134"),
|
||||
Genre("сёнэн-ай", "el_2139"),
|
||||
Genre("спорт", "el_2129"),
|
||||
Genre("сэйнэн", "el_2138"),
|
||||
Genre("трагедия", "el_2153"),
|
||||
Genre("триллер", "el_2150"),
|
||||
Genre("ужасы", "el_2125"),
|
||||
Genre("фэнтези", "el_2131"),
|
||||
Genre("школа", "el_2127"),
|
||||
Genre("этти", "el_2149"),
|
||||
Genre("юри", "el_2123")
|
||||
)
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
screen.addPreference(screen.editTextPreference(UAGENT_TITLE, UAGENT_DEFAULT, uagent))
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package eu.kanade.tachiyomi.multisrc.grouple
|
||||
|
||||
import generator.ThemeSourceData.SingleLang
|
||||
import generator.ThemeSourceGenerator
|
||||
|
||||
class GroupLeGenerator: ThemeSourceGenerator {
|
||||
|
||||
override val themePkg = "grouple"
|
||||
|
||||
override val themeClass = "GroupLe"
|
||||
|
||||
override val baseVersionCode: Int = 1
|
||||
|
||||
override val sources = listOf(
|
||||
SingleLang("ReadManga", "https://readmanga.io", "ru", overrideVersionCode = 45),
|
||||
SingleLang("MintManga", "https://mintmanga.live", "ru", overrideVersionCode = 46),
|
||||
SingleLang("AllHentai", "http://23.allhen.online", "ru",isNsfw = true, overrideVersionCode = 22),
|
||||
SingleLang("SelfManga", "https://selfmanga.live", "ru", overrideVersionCode = 22),
|
||||
SingleLang("RuMIX", "https://rumix.me", "ru", overrideVersionCode = 1)
|
||||
)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
GroupLeGenerator().createAll()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.readmanga
|
||||
package eu.kanade.tachiyomi.multisrc.grouple
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ActivityNotFoundException
|
||||
@ -12,7 +12,7 @@ import kotlin.system.exitProcess
|
||||
* you have this extension installed, but still let the main tachiyomi app control
|
||||
* things.
|
||||
*/
|
||||
class ReadmangaActivity : Activity() {
|
||||
class GroupLeUrlActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -21,17 +21,17 @@ class ReadmangaActivity : Activity() {
|
||||
val titleid = pathSegments[0]
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "${Readmanga.PREFIX_SLUG_SEARCH}$titleid")
|
||||
putExtra("query", "${GroupLe.PREFIX_SLUG_SEARCH}$titleid")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("ReadmangaActivity", e.toString())
|
||||
Log.e("GroupLeUrlActivity", e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e("ReadmangaActivity", "could not parse uri from intent $intent")
|
||||
Log.e("GroupLeUrlActivity", "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
@ -1,12 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
extName = 'AllHentai'
|
||||
pkgNameSuffix = 'ru.allhentai'
|
||||
extClass = '.AllHentai'
|
||||
extVersionCode = 22
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 818 B |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 11 KiB |
@ -1,565 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.allhentai
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
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.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
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.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
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.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
import java.text.DecimalFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class AllHentai : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
|
||||
override val name = "AllHentai"
|
||||
|
||||
private var domain: String = preferences.getString(DOMAIN_TITLE, DOMAIN_DEFAULT)!!
|
||||
override val baseUrl: String = domain
|
||||
|
||||
override val lang = "ru"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.rateLimit(2)
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalRequest = chain.request()
|
||||
val response = chain.proceed(originalRequest)
|
||||
if (originalRequest.url.toString().contains(baseUrl) and (originalRequest.url.toString().contains("internal/redirect") or (response.code == 301)))
|
||||
throw IOException("Манга переехала на другой адрес/ссылку!")
|
||||
response
|
||||
}
|
||||
.build()
|
||||
|
||||
override fun popularMangaSelector() = "div.tile"
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")?.replace("_p.", ".")
|
||||
element.select("h3 > a").first().let {
|
||||
manga.setUrlWithoutDomain(it.attr("href"))
|
||||
manga.title = it.attr("title")
|
||||
}
|
||||
return manga
|
||||
}
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga =
|
||||
popularMangaFromElement(element)
|
||||
|
||||
override fun popularMangaNextPageSelector() = "a.nextLink"
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
is Tags -> {
|
||||
if (filter.state > 0) {
|
||||
val tagName = getTagsList()[filter.state].url
|
||||
val tagUrl = "$baseUrl/list/tag/$tagName?offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(tagUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return GET(url.toString().replace("=%3D", "="), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
// max 200 results (exception OrderBy,Tags)
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val infoElement = document.select(".expandable").first()
|
||||
val rawCategory = infoElement.select("span.elem_category").text()
|
||||
val category = if (rawCategory.isNotEmpty()) {
|
||||
rawCategory.lowercase()
|
||||
} else {
|
||||
"манга"
|
||||
}
|
||||
|
||||
val manga = SManga.create()
|
||||
var authorElement = infoElement.select("span.elem_author").first()?.text()
|
||||
if (authorElement == null) {
|
||||
authorElement = infoElement.select("span.elem_screenwriter").first()?.text()
|
||||
}
|
||||
manga.title = document.select("h1.names .name").text()
|
||||
manga.author = authorElement
|
||||
manga.artist = infoElement.select("span.elem_illustrator").first()?.text()
|
||||
manga.genre = category + ", " + infoElement.select("span.elem_genre").text().split(",").joinToString { it.trim() }
|
||||
manga.description = document.select("div#tab-description .manga-description").text()
|
||||
manga.status = parseStatus(infoElement.html())
|
||||
manga.thumbnail_url = infoElement.select("img").attr("data-full")
|
||||
return manga
|
||||
}
|
||||
|
||||
private fun parseStatus(element: String): Int = when {
|
||||
element.contains("Запрещена публикация произведения по копирайту") || element.contains("ЗАПРЕЩЕНА К ПУБЛИКАЦИИ НА ТЕРРИТОРИИ РФ!") -> SManga.LICENSED
|
||||
element.contains("<b>Перевод:</b> продолжается") -> SManga.ONGOING
|
||||
element.contains("<b>Сингл</b>") || element.contains("<b>Перевод:</b> завер") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
return if (manga.status != SManga.LICENSED) {
|
||||
client.newCall(chapterListRequest(manga))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
chapterListParse(response, manga)
|
||||
}
|
||||
} else {
|
||||
Observable.error(java.lang.Exception("Licensed - No chapters to show"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
return document.select(chapterListSelector()).map { chapterFromElement(it, manga) }
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "div.chapters-link > table > tbody > tr:has(td > a):has(td.date:not(.text-info))"
|
||||
|
||||
private fun chapterFromElement(element: Element, manga: SManga): SChapter {
|
||||
val urlElement = element.select("a").first()
|
||||
val chapterInf = element.select("td.item-title").first()
|
||||
val urlText = urlElement.text()
|
||||
|
||||
val chapter = SChapter.create()
|
||||
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=true") // mtr is 18+ skip
|
||||
|
||||
var translators = ""
|
||||
val translatorElement = urlElement.attr("title")
|
||||
if (!translatorElement.isNullOrBlank()) {
|
||||
translators = translatorElement
|
||||
.replace("(Переводчик),", "&")
|
||||
.removeSuffix(" (Переводчик)")
|
||||
}
|
||||
chapter.scanlator = translators
|
||||
|
||||
chapter.name = urlText.removeSuffix(" новое").trim()
|
||||
if (manga.title.length > 25) {
|
||||
for (word in manga.title.split(' ')) {
|
||||
chapter.name = chapter.name.removePrefix(word).trim()
|
||||
}
|
||||
}
|
||||
val dots = chapter.name.indexOf("…")
|
||||
val numbers = chapter.name.findAnyOf(IntRange(0, 9).map { it.toString() })?.first ?: 0
|
||||
|
||||
if (dots in 0 until numbers) {
|
||||
chapter.name = chapter.name.substringAfter("…").trim()
|
||||
}
|
||||
|
||||
chapter.chapter_number = chapterInf.attr("data-num").toFloat() / 10
|
||||
|
||||
chapter.date_upload = element.select("td.d-none").last()?.text()?.let {
|
||||
try {
|
||||
SimpleDateFormat("dd.MM.yy", Locale.US).parse(it)?.time ?: 0L
|
||||
} catch (e: ParseException) {
|
||||
SimpleDateFormat("dd/MM/yy", Locale.US).parse(it)?.time ?: 0L
|
||||
}
|
||||
} ?: 0
|
||||
return chapter
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
throw Exception("Not used")
|
||||
}
|
||||
|
||||
override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
|
||||
val extra = Regex("""\s*([0-9]+\sЭкстра)\s*""")
|
||||
val single = Regex("""\s*Сингл\s*""")
|
||||
when {
|
||||
extra.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Экстра").trim().isEmpty())
|
||||
chapter.name = chapter.name.replaceFirst(" ", " - " + DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " ")
|
||||
}
|
||||
|
||||
single.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Сингл").trim().isEmpty())
|
||||
chapter.name = DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " " + chapter.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val html = response.body!!.string()
|
||||
val trimmedHtml = html.substringAfter("rm_h.initReader(").substringBefore(");")
|
||||
|
||||
val p = Pattern.compile("'.*?','.*?',\".*?\"")
|
||||
val m = p.matcher(trimmedHtml)
|
||||
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
var i = 0
|
||||
while (m.find()) {
|
||||
val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',')
|
||||
val url = when {
|
||||
(urlParts[1].isEmpty() && urlParts[2].startsWith("/static/")) -> {
|
||||
baseUrl + urlParts[2]
|
||||
}
|
||||
urlParts[1].endsWith("/manga/") -> {
|
||||
urlParts[0] + urlParts[2]
|
||||
}
|
||||
urlParts[1].isEmpty() -> {
|
||||
val imageUrl = urlParts[2].split('?')
|
||||
"https:" + urlParts[0] + imageUrl[0]
|
||||
}
|
||||
else -> {
|
||||
urlParts[1] + urlParts[0] + urlParts[2]
|
||||
}
|
||||
}
|
||||
|
||||
pages.add(Page(i++, "", url))
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
throw Exception("Not used")
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = ""
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val imgHeader = Headers.Builder().apply {
|
||||
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
|
||||
add("Referer", baseUrl)
|
||||
}.build()
|
||||
return GET(page.imageUrl!!, imgHeader)
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
private class Tags(tags: Array<String>) : Filter.Select<String>("Тэг (только)", tags)
|
||||
|
||||
private data class Tag(val name: String, val url: String)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Tags(tagsName),
|
||||
GenreList(getGenreList()),
|
||||
Category(getCategoryList()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
|
||||
/*
|
||||
* [...document.querySelectorAll('.search-form > .form-group')[1].querySelectorAll('span.js-link')]
|
||||
* .map((el) =>
|
||||
* `Genre("${el.textContent.trim()}", "${el
|
||||
* .getAttribute('onclick')
|
||||
* .match(/el_\d+/)}")`
|
||||
* )
|
||||
* .join(',\n');
|
||||
* on allhen.live/search/advanced
|
||||
*/
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("ahegao", "el_855"),
|
||||
Genre("анал", "el_828"),
|
||||
Genre("бдсм", "el_78"),
|
||||
Genre("без цензуры", "el_888"),
|
||||
Genre("большая грудь", "el_837"),
|
||||
Genre("большая попка", "el_3156"),
|
||||
Genre("большой член", "el_884"),
|
||||
Genre("бондаж", "el_5754"),
|
||||
Genre("в первый раз", "el_811"),
|
||||
Genre("в цвете", "el_290"),
|
||||
Genre("гарем", "el_87"),
|
||||
Genre("гендарная интрига", "el_89"),
|
||||
Genre("групповой секс", "el_88"),
|
||||
Genre("драма", "el_95"),
|
||||
Genre("зрелые женщины", "el_5679"),
|
||||
Genre("измена", "el_291"),
|
||||
Genre("изнасилование", "el_124"),
|
||||
Genre("инцест", "el_85"),
|
||||
Genre("исторический", "el_93"),
|
||||
Genre("комедия", "el_73"),
|
||||
Genre("маленькая грудь", "el_870"),
|
||||
Genre("научная фантастика", "el_76"),
|
||||
Genre("нетораре", "el_303"),
|
||||
Genre("оральный секс", "el_853"),
|
||||
Genre("романтика", "el_74"),
|
||||
Genre("тентакли", "el_69"),
|
||||
Genre("трагедия", "el_1321"),
|
||||
Genre("ужасы", "el_75"),
|
||||
Genre("футанари", "el_77"),
|
||||
Genre("фэнтези", "el_70"),
|
||||
Genre("чикан", "el_1059"),
|
||||
Genre("этти", "el_798"),
|
||||
Genre("юри", "el_84"),
|
||||
Genre("яой", "el_83")
|
||||
)
|
||||
|
||||
/*
|
||||
* [...document.querySelectorAll('.search-form > .form-group')[2].querySelectorAll('span.js-link')]
|
||||
* .map((el) =>
|
||||
* `Genre("${el.textContent.trim()}", "${el
|
||||
* .getAttribute('onclick')
|
||||
* .match(/el_\d+/)}")`
|
||||
* )
|
||||
* .join(',\n');
|
||||
* on allhen.live/search/advanced
|
||||
*/
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("3D", "el_626"),
|
||||
Genre("Анимация", "el_5777"),
|
||||
Genre("Без текста", "el_3157"),
|
||||
Genre("Порно комикс", "el_1003"),
|
||||
Genre("Порно манхва", "el_1104")
|
||||
)
|
||||
|
||||
/*
|
||||
* [...document.querySelectorAll('.search-form > .form-group')[1].querySelectorAll('span.js-link')]
|
||||
* .map((el) =>
|
||||
* `Genre("${el.textContent.trim()}", "${el
|
||||
* .getAttribute('onclick')
|
||||
* .match(/s_\w+/)}")`
|
||||
* )
|
||||
* .join(',\n');
|
||||
* on allhen.live/search/advanced
|
||||
*/
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
Genre("Продается", "s_sale")
|
||||
)
|
||||
|
||||
/**
|
||||
* [...document.querySelectorAll('tbody .element-link')]
|
||||
* .map((it) =>
|
||||
* `Tag("${it.textContent.trim()}", "${it
|
||||
* .getAttribute('href')
|
||||
* .split('tag/')
|
||||
* .pop()}")`
|
||||
* )
|
||||
* .join(',\n');
|
||||
* on allhen.live/list/tags/sort_NAME
|
||||
*/
|
||||
private fun getTagsList() = listOf(
|
||||
Tag("Без тега", "not"),
|
||||
Tag("handjob", "handjob"),
|
||||
Tag("inseki", "inseki"),
|
||||
Tag("алкоголь", "alcohol"),
|
||||
Tag("андроид", "android"),
|
||||
Tag("анилингус", "anilingus"),
|
||||
Tag("бассейн", "pool"),
|
||||
Tag("без трусиков", "without_panties"),
|
||||
Tag("беременность", "pregnancy"),
|
||||
Tag("бикини", "bikini"),
|
||||
Tag("близнецы", "twins"),
|
||||
Tag("боди-арт", "body_art"),
|
||||
Tag("больница", "hospital"),
|
||||
Tag("буккакэ", "bukkake"),
|
||||
Tag("в ванной", "in_bathroom"),
|
||||
Tag("в общественном месте", "in_public_place"),
|
||||
Tag("в транспорте", "in_vehicle"),
|
||||
Tag("вампиры", "vampires"),
|
||||
Tag("вибратор", "vibrator"),
|
||||
Tag("втянутые соски", "inverted_nipples"),
|
||||
Tag("гипноз", "hypnosis"),
|
||||
Tag("глубокий минет", "deepthroat"),
|
||||
Tag("горничные", "maids"),
|
||||
Tag("горячий источник", "hot_spring"),
|
||||
Tag("гэнгбэнг", "gangbang"),
|
||||
Tag("гяру", "gyaru"),
|
||||
Tag("двойное проникновение", "double_penetration"),
|
||||
Tag("Девочки волшебницы", "magical_girl"),
|
||||
Tag("демоны", "demons"),
|
||||
Tag("дефекация", "scat"),
|
||||
Tag("дилдо", "dildo"),
|
||||
Tag("додзинси", "doujinshi"),
|
||||
Tag("домохозяйки", "housewives"),
|
||||
Tag("дыра в стене", "hole_in_the_wall"),
|
||||
Tag("жестокость", "cruelty"),
|
||||
Tag("загар", "tan_lines"),
|
||||
Tag("зомби", "zombie"),
|
||||
Tag("инопланетяне", "aliens"),
|
||||
Tag("исполнение желаний", "granting_wish"),
|
||||
Tag("камера", "camera"),
|
||||
Tag("косплей", "cosplay"),
|
||||
Tag("кремпай", "creampie"),
|
||||
Tag("куннилингус", "cunnilingus"),
|
||||
Tag("купальник", "swimsuit"),
|
||||
Tag("лактация", "lactation"),
|
||||
Tag("латекс и кожа", "latex"),
|
||||
Tag("Ломка Психики", "mind_break"),
|
||||
Tag("магия", "magic"),
|
||||
Tag("мастурбация", "masturbation"),
|
||||
Tag("медсестра", "nurse"),
|
||||
Tag("мерзкий дядька", "terrible_oyaji"),
|
||||
Tag("много девушек", "many_girls"),
|
||||
Tag("много спермы", "a_lot_of_sperm"),
|
||||
Tag("монстрдевушки", "monstergirl"),
|
||||
Tag("монстры", "monsters"),
|
||||
Tag("мужчина крепкого телосложения", "muscle_man"),
|
||||
Tag("на природе", "outside"),
|
||||
Tag("не бритая киска", "hairy_pussy"),
|
||||
Tag("не бритые подмышки", "hairy_armpits"),
|
||||
Tag("нетори", "netori"),
|
||||
Tag("нижнее бельё", "lingerie"),
|
||||
Tag("обмен партнерами", "swinging"),
|
||||
Tag("обмен телами", "body_swap"),
|
||||
Tag("обычный секс", "normal_sex"),
|
||||
Tag("огромная грудь", "super_big_boobs"),
|
||||
Tag("орки", "orcs"),
|
||||
Tag("очки", "megane"),
|
||||
Tag("пайзури", "titsfuck"),
|
||||
Tag("парень пассив", "passive_guy"),
|
||||
Tag("пацанка", "tomboy"),
|
||||
Tag("пеггинг", "pegging"),
|
||||
Tag("переодевание", "disguise"),
|
||||
Tag("пирсинг", "piercing"),
|
||||
Tag("писают", "peeing"),
|
||||
Tag("пляж", "beach"),
|
||||
Tag("повседневность", "slice_of_life"),
|
||||
Tag("повязка на глаза", "blindfold"),
|
||||
Tag("подглядывание", "peeping"),
|
||||
Tag("подчинение", "submission"),
|
||||
Tag("похищение", "kidnapping"),
|
||||
Tag("принуждение", "forced"),
|
||||
Tag("прозрачная одежда", "transparent_clothes"),
|
||||
Tag("проституция", "prostitution"),
|
||||
Tag("психические отклонения", "mental_illness"),
|
||||
Tag("публичный секс", "public_sex"),
|
||||
Tag("пьяные", "drunk"),
|
||||
Tag("рабы", "slaves"),
|
||||
Tag("рентген зрение", "x_ray"),
|
||||
Tag("сверхъестественное", "supernatural"),
|
||||
Tag("секс втроем", "threesome"),
|
||||
Tag("секс игрушки", "sex_toys"),
|
||||
Tag("сексуально возбужденная", "horny"),
|
||||
Tag("спортивная форма", "sports_uniform"),
|
||||
Tag("спящие", "sleeping"),
|
||||
Tag("страпон", "strapon"),
|
||||
Tag("Суккуб", "succubus"),
|
||||
Tag("темнокожие", "dark_skin"),
|
||||
Tag("толстушки", "fatties"),
|
||||
Tag("трап", "trap"),
|
||||
Tag("униформа", "uniform"),
|
||||
Tag("ушастые", "eared"),
|
||||
Tag("фантазии", "dreams"),
|
||||
Tag("фемдом", "femdom"),
|
||||
Tag("фестиваль", "festival"),
|
||||
Tag("фетиш", "fetish"),
|
||||
Tag("фистинг", "fisting"),
|
||||
Tag("фурри", "furry"),
|
||||
Tag("футанари имеет парня", "futanari_on_boy"),
|
||||
Tag("футджаб", "footfuck"),
|
||||
Tag("цельный купальник", "full_swimsuit"),
|
||||
Tag("цундэрэ", "tsundere"),
|
||||
Tag("чулки", "hose"),
|
||||
Tag("шалава", "slut"),
|
||||
Tag("шантаж", "blackmail"),
|
||||
Tag("эксгибиционизм", "exhibitionism"),
|
||||
Tag("эльфы", "elves"),
|
||||
Tag("яндере", "yandere")
|
||||
)
|
||||
|
||||
private val tagsName = getTagsList().map {
|
||||
it.name
|
||||
}.toTypedArray()
|
||||
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
screen.addPreference(screen.editTextPreference(DOMAIN_TITLE, DOMAIN_DEFAULT, domain))
|
||||
}
|
||||
|
||||
private fun androidx.preference.PreferenceScreen.editTextPreference(title: String, default: String, value: String): androidx.preference.EditTextPreference {
|
||||
return androidx.preference.EditTextPreference(context).apply {
|
||||
key = title
|
||||
this.title = title
|
||||
summary = value
|
||||
this.setDefaultValue(default)
|
||||
dialogTitle = title
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val res = preferences.edit().putString(title, newValue as String).commit()
|
||||
Toast.makeText(context, "Для смены домена необходимо перезапустить приложение с полной остановкой.", Toast.LENGTH_LONG).show()
|
||||
res
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
private const val DOMAIN_TITLE = "Домен"
|
||||
private const val DOMAIN_DEFAULT = "http://23.allhen.online"
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
extName = 'Mintmanga'
|
||||
pkgNameSuffix = 'ru.mintmanga'
|
||||
extClass = '.Mintmanga'
|
||||
extVersionCode = 46
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 40 KiB |
@ -1,469 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.mintmanga
|
||||
|
||||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
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.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 okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
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.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.IOException
|
||||
import java.text.DecimalFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class Mintmanga : ConfigurableSource, ParsedHttpSource() {
|
||||
|
||||
override val id: Long = 6
|
||||
private val preferences: SharedPreferences by lazy {
|
||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
}
|
||||
override val name = "Mintmanga"
|
||||
|
||||
override val baseUrl = "https://mintmanga.live"
|
||||
|
||||
override val lang = "ru"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.rateLimit(2)
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalRequest = chain.request()
|
||||
val response = chain.proceed(originalRequest)
|
||||
if (originalRequest.url.toString().contains(baseUrl) and (originalRequest.url.toString().contains("internal/redirect") or (response.code == 301)))
|
||||
throw IOException("Манга переехала на другой адрес/ссылку!")
|
||||
response
|
||||
}
|
||||
.build()
|
||||
|
||||
private var uagent: String = preferences.getString(UAGENT_TITLE, UAGENT_DEFAULT)!!
|
||||
override fun headersBuilder() = Headers.Builder().apply {
|
||||
add("User-Agent", uagent)
|
||||
add("Referer", baseUrl)
|
||||
}
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun popularMangaSelector() = "div.tile"
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")?.replace("_p.", ".")
|
||||
element.select("h3 > a").first().let {
|
||||
manga.setUrlWithoutDomain(it.attr("href"))
|
||||
manga.title = it.attr("title")
|
||||
}
|
||||
return manga
|
||||
}
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga =
|
||||
popularMangaFromElement(element)
|
||||
|
||||
override fun popularMangaNextPageSelector() = "a.nextLink"
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
is AgeList -> filter.state.forEach { age ->
|
||||
if (age.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(age.id, arrayOf("=", "=in", "=ex")[age.state])
|
||||
}
|
||||
}
|
||||
is More -> filter.state.forEach { more ->
|
||||
if (more.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(more.id, arrayOf("=", "=in", "=ex")[more.state])
|
||||
}
|
||||
}
|
||||
is FilList -> filter.state.forEach { fils ->
|
||||
if (fils.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(fils.id, arrayOf("=", "=in", "=ex")[fils.state])
|
||||
}
|
||||
}
|
||||
is OrderBy -> {
|
||||
if (filter.state > 0) {
|
||||
val ord = arrayOf("not", "year", "rate", "popularity", "votes", "created", "updated")[filter.state]
|
||||
val ordUrl = "$baseUrl/list?sortType=$ord&offset=${70 * (page - 1)}".toHttpUrlOrNull()!!.newBuilder()
|
||||
return GET(ordUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return GET(url.toString().replace("=%3D", "="), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
// max 200 results (exception OrderBy)
|
||||
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val infoElement = document.select(".expandable").first()
|
||||
val rawCategory = infoElement.select("span.elem_category").text()
|
||||
val category = if (rawCategory.isNotEmpty()) {
|
||||
rawCategory.lowercase()
|
||||
} else {
|
||||
"манга"
|
||||
}
|
||||
val ratingValue = infoElement.select(".col-sm-7 .rating-block").attr("data-score").toFloat() * 2
|
||||
val ratingValueOver = infoElement.select(".info-icon").attr("data-content").substringBeforeLast("/5</b><br/>").substringAfterLast(": <b>").replace(",", ".").toFloat() * 2
|
||||
val ratingVotes = infoElement.select(".col-sm-7 .user-rating meta[itemprop=\"ratingCount\"]").attr("content")
|
||||
val ratingStar = when {
|
||||
ratingValue > 9.5 -> "★★★★★"
|
||||
ratingValue > 8.5 -> "★★★★✬"
|
||||
ratingValue > 7.5 -> "★★★★☆"
|
||||
ratingValue > 6.5 -> "★★★✬☆"
|
||||
ratingValue > 5.5 -> "★★★☆☆"
|
||||
ratingValue > 4.5 -> "★★✬☆☆"
|
||||
ratingValue > 3.5 -> "★★☆☆☆"
|
||||
ratingValue > 2.5 -> "★✬☆☆☆"
|
||||
ratingValue > 1.5 -> "★☆☆☆☆"
|
||||
ratingValue > 0.5 -> "✬☆☆☆☆"
|
||||
else -> "☆☆☆☆☆"
|
||||
}
|
||||
val rawAgeValue = infoElement.select(".elem_limitation .element-link").first()?.text()
|
||||
val rawAgeStop = when (rawAgeValue) {
|
||||
"NC-17" -> "18+"
|
||||
"R18+" -> "18+"
|
||||
else -> "16+"
|
||||
}
|
||||
val manga = SManga.create()
|
||||
var authorElement = infoElement.select("span.elem_author").first()?.text()
|
||||
if (authorElement == null) {
|
||||
authorElement = infoElement.select("span.elem_screenwriter").first()?.text()
|
||||
}
|
||||
manga.title = document.select("h1.names .name").text()
|
||||
manga.author = authorElement
|
||||
manga.artist = infoElement.select("span.elem_illustrator").first()?.text()
|
||||
manga.genre = category + ", " + rawAgeStop + ", " + infoElement.select("span.elem_genre").text().split(",").joinToString { it.trim() }
|
||||
var altName = ""
|
||||
if (infoElement.select(".another-names").isNotEmpty()) {
|
||||
altName = "Альтернативные названия:\n" + infoElement.select(".another-names").text() + "\n\n"
|
||||
}
|
||||
manga.description = ratingStar + " " + ratingValue + "[ⓘ" + ratingValueOver + "]" + " (голосов: " + ratingVotes + ")\n" + altName + document.select("div#tab-description .manga-description").text()
|
||||
manga.status = parseStatus(infoElement.html())
|
||||
manga.thumbnail_url = infoElement.select("img").attr("data-full")
|
||||
return manga
|
||||
}
|
||||
|
||||
private fun parseStatus(element: String): Int = when {
|
||||
element.contains("Запрещена публикация произведения по копирайту") || element.contains("ЗАПРЕЩЕНА К ПУБЛИКАЦИИ НА ТЕРРИТОРИИ РФ!") -> SManga.LICENSED
|
||||
element.contains("<b>Перевод:</b> продолжается") -> SManga.ONGOING
|
||||
element.contains("<b>Сингл</b>") || element.contains("<b>Перевод:</b> завер") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
return if (manga.status != SManga.LICENSED) {
|
||||
client.newCall(chapterListRequest(manga))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
chapterListParse(response, manga)
|
||||
}
|
||||
} else {
|
||||
Observable.error(java.lang.Exception("Licensed - No chapters to show"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
return document.select(chapterListSelector()).map { chapterFromElement(it, manga) }
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "div.chapters-link > table > tbody > tr:has(td > a):has(td.date:not(.text-info))"
|
||||
|
||||
private fun chapterFromElement(element: Element, manga: SManga): SChapter {
|
||||
val urlElement = element.select("a").first()
|
||||
val chapterInf = element.select("td.item-title").first()
|
||||
val urlText = urlElement.text()
|
||||
|
||||
val chapter = SChapter.create()
|
||||
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=true") // mtr is 18+ skip
|
||||
|
||||
var translators = ""
|
||||
val translatorElement = urlElement.attr("title")
|
||||
if (!translatorElement.isNullOrBlank()) {
|
||||
translators = translatorElement
|
||||
.replace("(Переводчик),", "&")
|
||||
.removeSuffix(" (Переводчик)")
|
||||
}
|
||||
chapter.scanlator = translators
|
||||
|
||||
chapter.name = urlText.removeSuffix(" новое").trim()
|
||||
if (manga.title.length > 25) {
|
||||
for (word in manga.title.split(' ')) {
|
||||
chapter.name = chapter.name.removePrefix(word).trim()
|
||||
}
|
||||
}
|
||||
val dots = chapter.name.indexOf("…")
|
||||
val numbers = chapter.name.findAnyOf(IntRange(0, 9).map { it.toString() })?.first ?: 0
|
||||
|
||||
if (dots in 0 until numbers) {
|
||||
chapter.name = chapter.name.substringAfter("…").trim()
|
||||
}
|
||||
|
||||
chapter.chapter_number = chapterInf.attr("data-num").toFloat() / 10
|
||||
|
||||
chapter.date_upload = element.select("td.d-none").last()?.text()?.let {
|
||||
try {
|
||||
SimpleDateFormat("dd.MM.yy", Locale.US).parse(it)?.time ?: 0L
|
||||
} catch (e: ParseException) {
|
||||
SimpleDateFormat("dd/MM/yy", Locale.US).parse(it)?.time ?: 0L
|
||||
}
|
||||
} ?: 0
|
||||
return chapter
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
throw Exception("Not used")
|
||||
}
|
||||
|
||||
override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
|
||||
val extra = Regex("""\s*([0-9]+\sЭкстра)\s*""")
|
||||
val single = Regex("""\s*Сингл\s*""")
|
||||
when {
|
||||
extra.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Экстра").trim().isEmpty())
|
||||
chapter.name = chapter.name.replaceFirst(" ", " - " + DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " ")
|
||||
}
|
||||
|
||||
single.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Сингл").trim().isEmpty())
|
||||
chapter.name = DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " " + chapter.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val html = response.body!!.string()
|
||||
val beginIndex = html.indexOf("rm_h.initReader( [")
|
||||
val endIndex = html.indexOf(");", beginIndex)
|
||||
val trimmedHtml = html.substring(beginIndex, endIndex)
|
||||
|
||||
val p = Pattern.compile("'.*?','.*?',\".*?\"")
|
||||
val m = p.matcher(trimmedHtml)
|
||||
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
var i = 0
|
||||
while (m.find()) {
|
||||
val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',')
|
||||
val url = if (urlParts[1].isEmpty() && urlParts[2].startsWith("/static/")) {
|
||||
baseUrl + urlParts[2]
|
||||
} else {
|
||||
if (urlParts[1].endsWith("/manga/")) {
|
||||
urlParts[0] + urlParts[2]
|
||||
} else {
|
||||
urlParts[1] + urlParts[0] + urlParts[2]
|
||||
}
|
||||
}
|
||||
pages.add(Page(i++, "", url))
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
throw Exception("Not used")
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = ""
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val imgHeader = Headers.Builder().apply {
|
||||
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
|
||||
add("Referer", baseUrl)
|
||||
}.build()
|
||||
return GET(page.imageUrl!!, imgHeader)
|
||||
}
|
||||
private fun searchMangaByIdRequest(id: String): Request {
|
||||
return GET("$baseUrl/$id", headers)
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
||||
return if (query.startsWith(PREFIX_SLUG_SEARCH)) {
|
||||
val realQuery = query.removePrefix(PREFIX_SLUG_SEARCH)
|
||||
client.newCall(searchMangaByIdRequest(realQuery))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
val details = mangaDetailsParse(response)
|
||||
details.url = "/$realQuery"
|
||||
MangasPage(listOf(details), false)
|
||||
}
|
||||
} else {
|
||||
client.newCall(searchMangaRequest(page, query, filters))
|
||||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
searchMangaParse(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class OrderBy : Filter.Select<String>(
|
||||
"Сортировка (только)",
|
||||
arrayOf("Без сортировки", "По году", "По популярности", "Популярно сейчас", "По рейтингу", "Новинки", "По дате обновления")
|
||||
)
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Жанры", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Категории", categories)
|
||||
private class AgeList(ages: List<Genre>) : Filter.Group<Genre>("Возрастная рекомендация", ages)
|
||||
private class More(moren: List<Genre>) : Filter.Group<Genre>("Прочее", moren)
|
||||
private class FilList(fils: List<Genre>) : Filter.Group<Genre>("Фильтры", fils)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
OrderBy(),
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList()),
|
||||
AgeList(getAgeList()),
|
||||
More(getMore()),
|
||||
FilList(getFilList())
|
||||
)
|
||||
private fun getFilList() = listOf(
|
||||
Genre("Высокий рейтинг", "s_high_rate"),
|
||||
Genre("Сингл", "s_single"),
|
||||
Genre("Для взрослых", "s_mature"),
|
||||
Genre("Завершенная", "s_completed"),
|
||||
Genre("Переведено", "s_translated"),
|
||||
Genre("Длинная", "s_many_chapters"),
|
||||
Genre("Ожидает загрузки", "s_wait_upload"),
|
||||
)
|
||||
private fun getMore() = listOf(
|
||||
Genre("В цвете", "el_4614"),
|
||||
Genre("Веб", "el_1355"),
|
||||
Genre("Выпуск приостановлен", "el_5232"),
|
||||
Genre("Не Яой", "el_1874"),
|
||||
Genre("Сборник", "el_1348")
|
||||
)
|
||||
|
||||
private fun getAgeList() = listOf(
|
||||
Genre("R(16+)", "el_3968"),
|
||||
Genre("NC-17(18+)", "el_3969"),
|
||||
Genre("R18+(18+)", "el_3990")
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Ёнкома", "el_2741"),
|
||||
Genre("Комикс западный", "el_1903"),
|
||||
Genre("Комикс русский", "el_2173"),
|
||||
Genre("Манхва", "el_1873"),
|
||||
Genre("Маньхуа", "el_1875"),
|
||||
Genre("Ранобэ", "el_5688"),
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("арт", "el_2220"),
|
||||
Genre("бара", "el_1353"),
|
||||
Genre("боевик", "el_1346"),
|
||||
Genre("боевые искусства", "el_1334"),
|
||||
Genre("вампиры", "el_1339"),
|
||||
Genre("гарем", "el_1333"),
|
||||
Genre("гендерная интрига", "el_1347"),
|
||||
Genre("героическое фэнтези", "el_1337"),
|
||||
Genre("детектив", "el_1343"),
|
||||
Genre("дзёсэй", "el_1349"),
|
||||
Genre("додзинси", "el_1332"),
|
||||
Genre("драма", "el_1310"),
|
||||
Genre("игра", "el_5229"),
|
||||
Genre("история", "el_1311"),
|
||||
Genre("киберпанк", "el_1351"),
|
||||
Genre("комедия", "el_1328"),
|
||||
Genre("меха", "el_1318"),
|
||||
Genre("научная фантастика", "el_1325"),
|
||||
Genre("омегаверс", "el_5676"),
|
||||
Genre("повседневность", "el_1327"),
|
||||
Genre("постапокалиптика", "el_1342"),
|
||||
Genre("приключения", "el_1322"),
|
||||
Genre("психология", "el_1335"),
|
||||
Genre("романтика", "el_1313"),
|
||||
Genre("самурайский боевик", "el_1316"),
|
||||
Genre("сверхъестественное", "el_1350"),
|
||||
Genre("сёдзё", "el_1314"),
|
||||
Genre("сёдзё-ай", "el_1320"),
|
||||
Genre("сёнэн", "el_1326"),
|
||||
Genre("сёнэн-ай", "el_1330"),
|
||||
Genre("спорт", "el_1321"),
|
||||
Genre("сэйнэн", "el_1329"),
|
||||
Genre("трагедия", "el_1344"),
|
||||
Genre("триллер", "el_1341"),
|
||||
Genre("ужасы", "el_1317"),
|
||||
Genre("фэнтези", "el_1323"),
|
||||
Genre("школа", "el_1319"),
|
||||
Genre("эротика", "el_1340"),
|
||||
Genre("этти", "el_1354"),
|
||||
Genre("юри", "el_1315"),
|
||||
Genre("яой", "el_1336")
|
||||
)
|
||||
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
|
||||
screen.addPreference(screen.editTextPreference(UAGENT_TITLE, UAGENT_DEFAULT, uagent))
|
||||
}
|
||||
|
||||
private fun androidx.preference.PreferenceScreen.editTextPreference(title: String, default: String, value: String): androidx.preference.EditTextPreference {
|
||||
return androidx.preference.EditTextPreference(context).apply {
|
||||
key = title
|
||||
this.title = title
|
||||
summary = value
|
||||
this.setDefaultValue(default)
|
||||
dialogTitle = title
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
val res = preferences.edit().putString(title, newValue as String).commit()
|
||||
Toast.makeText(context, "Для смены User-Agent необходимо перезапустить приложение с полной остановкой.", Toast.LENGTH_LONG).show()
|
||||
res
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
private const val UAGENT_TITLE = "User-Agent(для некоторых стран)"
|
||||
private const val UAGENT_DEFAULT = "arora"
|
||||
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.mintmanga
|
||||
|
||||
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://mintmanga.live/xxx intents and redirects them to
|
||||
* the main tachiyomi process. The idea is to not install the intent filter unless
|
||||
* you have this extension installed, but still let the main tachiyomi app control
|
||||
* things.
|
||||
*/
|
||||
class MintmangaActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val pathSegments = intent?.data?.pathSegments
|
||||
if (pathSegments != null && pathSegments.size > 0) {
|
||||
val titleid = pathSegments[0]
|
||||
val mainIntent = Intent().apply {
|
||||
action = "eu.kanade.tachiyomi.SEARCH"
|
||||
putExtra("query", "${Mintmanga.PREFIX_SLUG_SEARCH}$titleid")
|
||||
putExtra("filter", packageName)
|
||||
}
|
||||
|
||||
try {
|
||||
startActivity(mainIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e("MintmangaActivity", e.toString())
|
||||
}
|
||||
} else {
|
||||
Log.e("MintmangaaActivity", "could not parse uri from intent $intent")
|
||||
}
|
||||
|
||||
finish()
|
||||
exitProcess(0)
|
||||
}
|
||||
}
|
@ -1,25 +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=".ru.readmanga.ReadmangaActivity"
|
||||
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" />
|
||||
|
||||
<!-- ReadmangaActivity sites can be added here. -->
|
||||
<data
|
||||
android:host="readmanga.io"
|
||||
android:pathPattern="/..*/vol..*"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
@ -1,11 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
extName = 'Readmanga'
|
||||
pkgNameSuffix = 'ru.readmanga'
|
||||
extClass = '.Readmanga'
|
||||
extVersionCode = 45
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 24 KiB |
@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
@ -1,11 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
extName = 'Selfmanga'
|
||||
pkgNameSuffix = 'ru.selfmanga'
|
||||
extClass = '.Selfmanga'
|
||||
extVersionCode = 22
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 131 KiB |
@ -1,266 +0,0 @@
|
||||
package eu.kanade.tachiyomi.extension.ru.selfmanga
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
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.Page
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.io.IOException
|
||||
import java.text.DecimalFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class Selfmanga : ParsedHttpSource() {
|
||||
|
||||
override val name = "Selfmanga"
|
||||
|
||||
override val baseUrl = "https://selfmanga.live"
|
||||
|
||||
override val lang = "ru"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.client.newBuilder()
|
||||
.rateLimit(2)
|
||||
.addNetworkInterceptor { chain ->
|
||||
val originalRequest = chain.request()
|
||||
val response = chain.proceed(originalRequest)
|
||||
if (originalRequest.url.toString().contains(baseUrl) and (originalRequest.url.toString().contains("internal/redirect") or (response.code == 301)))
|
||||
throw IOException("Манга переехала на другой адрес/ссылку!")
|
||||
response
|
||||
}
|
||||
.build()
|
||||
|
||||
override fun popularMangaSelector() = "div.tile"
|
||||
|
||||
override fun latestUpdatesSelector() = popularMangaSelector()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}", headers)
|
||||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.thumbnail_url = element.select("img.lazy").first()?.attr("data-original")?.replace("_p.", ".")
|
||||
element.select("h3 > a").first().let {
|
||||
manga.setUrlWithoutDomain(it.attr("href"))
|
||||
manga.title = it.attr("title")
|
||||
}
|
||||
return manga
|
||||
}
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga =
|
||||
popularMangaFromElement(element)
|
||||
|
||||
override fun popularMangaNextPageSelector() = "a.nextLink"
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/search/advanced".toHttpUrlOrNull()!!.newBuilder()
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is GenreList -> filter.state.forEach { genre ->
|
||||
if (genre.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(genre.id, arrayOf("=", "=in", "=ex")[genre.state])
|
||||
}
|
||||
}
|
||||
is Category -> filter.state.forEach { category ->
|
||||
if (category.state != Filter.TriState.STATE_IGNORE) {
|
||||
url.addQueryParameter(category.id, arrayOf("=", "=in", "=ex")[category.state])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
url.addQueryParameter("q", query)
|
||||
}
|
||||
return GET(url.toString().replace("=%3D", "="), headers)
|
||||
}
|
||||
|
||||
override fun searchMangaSelector() = popularMangaSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
|
||||
// max 200 results
|
||||
override fun searchMangaNextPageSelector(): Nothing? = null
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga {
|
||||
val infoElement = document.select(".expandable").first()
|
||||
|
||||
val manga = SManga.create()
|
||||
manga.title = document.select("h1.names .name").text()
|
||||
manga.author = infoElement.select("span.elem_author").first()?.text()
|
||||
manga.genre = infoElement.select("span.elem_genre").text().split(",").joinToString { it.trim() }
|
||||
manga.description = document.select("div#tab-description .manga-description").text()
|
||||
manga.status = parseStatus(infoElement.html())
|
||||
manga.thumbnail_url = infoElement.select("img").attr("data-full")
|
||||
return manga
|
||||
}
|
||||
|
||||
private fun parseStatus(element: String): Int = when {
|
||||
element.contains("Запрещена публикация произведения по копирайту") || element.contains("ЗАПРЕЩЕНА К ПУБЛИКАЦИИ НА ТЕРРИТОРИИ РФ!") -> SManga.LICENSED
|
||||
element.contains("<b>Перевод:</b> продолжается") -> SManga.ONGOING
|
||||
element.contains("<b>Сингл</b>") || element.contains(", завер") -> SManga.COMPLETED
|
||||
else -> SManga.UNKNOWN
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "div.chapters-link > table > tbody > tr:has(td > a):has(td.date:not(.text-info))"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
val urlElement = element.select("a").first()
|
||||
val chapterInf = element.select("td.item-title").first()
|
||||
val urlText = urlElement.text()
|
||||
|
||||
val chapter = SChapter.create()
|
||||
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=true") // mtr is 18+ skip
|
||||
if (urlText.endsWith(" новое")) {
|
||||
chapter.name = urlText.dropLast(6)
|
||||
} else {
|
||||
chapter.name = urlText
|
||||
}
|
||||
|
||||
chapter.chapter_number = chapterInf.attr("data-num").toFloat() / 10
|
||||
|
||||
chapter.date_upload = element.select("td.d-none").last()?.text()?.let {
|
||||
try {
|
||||
SimpleDateFormat("dd/MM/yy", Locale.US).parse(it)?.time ?: 0L
|
||||
} catch (e: ParseException) {
|
||||
SimpleDateFormat("dd.MM.yy", Locale.US).parse(it)?.time ?: 0L
|
||||
}
|
||||
} ?: 0
|
||||
return chapter
|
||||
}
|
||||
|
||||
override fun prepareNewChapter(chapter: SChapter, manga: SManga) {
|
||||
val extra = Regex("""\s*([0-9]+\sЭкстра)\s*""")
|
||||
val single = Regex("""\s*Сингл\s*""")
|
||||
when {
|
||||
extra.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Экстра").trim().isEmpty())
|
||||
chapter.name = chapter.name.replaceFirst(" ", " - " + DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " ")
|
||||
}
|
||||
|
||||
single.containsMatchIn(chapter.name) -> {
|
||||
if (chapter.name.substringAfter("Сингл").trim().isEmpty())
|
||||
chapter.name = DecimalFormat("#,###.##").format(chapter.chapter_number).replace(",", ".") + " " + chapter.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val html = response.body!!.string()
|
||||
val beginIndex = html.indexOf("rm_h.initReader( [")
|
||||
val endIndex = html.indexOf(");", beginIndex)
|
||||
val trimmedHtml = html.substring(beginIndex, endIndex)
|
||||
|
||||
val p = Pattern.compile("'.*?','.*?',\".*?\"")
|
||||
val m = p.matcher(trimmedHtml)
|
||||
|
||||
val pages = mutableListOf<Page>()
|
||||
|
||||
var i = 0
|
||||
while (m.find()) {
|
||||
val urlParts = m.group().replace("[\"\']+".toRegex(), "").split(',')
|
||||
val url = if (urlParts[1].isEmpty() && urlParts[2].startsWith("/static/")) {
|
||||
baseUrl + urlParts[2]
|
||||
} else {
|
||||
if (urlParts[1].endsWith("/manga/")) {
|
||||
urlParts[0] + urlParts[2]
|
||||
} else {
|
||||
urlParts[1] + urlParts[0] + urlParts[2]
|
||||
}
|
||||
}
|
||||
pages.add(Page(i++, "", url))
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
throw Exception("Not used")
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document) = ""
|
||||
|
||||
override fun imageRequest(page: Page): Request {
|
||||
val imgHeader = Headers.Builder().apply {
|
||||
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
|
||||
add("Referer", baseUrl)
|
||||
}.build()
|
||||
return GET(page.imageUrl!!, imgHeader)
|
||||
}
|
||||
|
||||
private class Genre(name: String, val id: String) : Filter.TriState(name)
|
||||
private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genres", genres)
|
||||
private class Category(categories: List<Genre>) : Filter.Group<Genre>("Category", categories)
|
||||
|
||||
/* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")]
|
||||
* .map(el => `Genre("${el.textContent.trim()}", $"{el.getAttribute('onclick')
|
||||
* .substr(31,el.getAttribute('onclick').length-33)"})`).join(',\n')
|
||||
* on https://selfmanga.ru/search/advanced
|
||||
*/
|
||||
override fun getFilterList() = FilterList(
|
||||
Category(getCategoryList()),
|
||||
GenreList(getGenreList())
|
||||
)
|
||||
|
||||
private fun getCategoryList() = listOf(
|
||||
Genre("Артбук", "el_5894"),
|
||||
Genre("Веб", "el_2160"),
|
||||
Genre("Журнал", "el_4983"),
|
||||
Genre("Ранобэ", "el_5215"),
|
||||
Genre("Сборник", "el_2157")
|
||||
)
|
||||
|
||||
private fun getGenreList() = listOf(
|
||||
Genre("боевик", "el_2155"),
|
||||
Genre("боевые искусства", "el_2143"),
|
||||
Genre("вампиры", "el_2148"),
|
||||
Genre("гарем", "el_2142"),
|
||||
Genre("гендерная интрига", "el_2156"),
|
||||
Genre("героическое фэнтези", "el_2146"),
|
||||
Genre("детектив", "el_2152"),
|
||||
Genre("дзёсэй", "el_2158"),
|
||||
Genre("додзинси", "el_2141"),
|
||||
Genre("драма", "el_2118"),
|
||||
Genre("ёнкома", "el_2161"),
|
||||
Genre("история", "el_2119"),
|
||||
Genre("комедия", "el_2136"),
|
||||
Genre("махо-сёдзё", "el_2147"),
|
||||
Genre("мистика", "el_2132"),
|
||||
Genre("научная фантастика", "el_2133"),
|
||||
Genre("повседневность", "el_2135"),
|
||||
Genre("постапокалиптика", "el_2151"),
|
||||
Genre("приключения", "el_2130"),
|
||||
Genre("психология", "el_2144"),
|
||||
Genre("романтика", "el_2121"),
|
||||
Genre("сверхъестественное", "el_2159"),
|
||||
Genre("сёдзё", "el_2122"),
|
||||
Genre("сёдзё-ай", "el_2128"),
|
||||
Genre("сёнэн", "el_2134"),
|
||||
Genre("сёнэн-ай", "el_2139"),
|
||||
Genre("спорт", "el_2129"),
|
||||
Genre("сэйнэн", "el_5838"),
|
||||
Genre("трагедия", "el_2153"),
|
||||
Genre("триллер", "el_2150"),
|
||||
Genre("ужасы", "el_2125"),
|
||||
Genre("фантастика", "el_2140"),
|
||||
Genre("фэнтези", "el_2131"),
|
||||
Genre("школа", "el_2127"),
|
||||
Genre("этти", "el_4982")
|
||||
)
|
||||
}
|