[RU]Multi Lib (MangaLib and HentaiLib) + fixes/updates (#12104)
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ru.libhentai.LibHentaiActivity"
|
android:name="eu.kanade.tachiyomi.multisrc.libgroup.LibUrlActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<!-- LibHentaiActivity sites can be added here. -->
|
<!-- LibUrlActivity sites can be added here. -->
|
||||||
<data
|
<data
|
||||||
android:host="hentailib.me"
|
android:host="hentailib.me"
|
||||||
android:pathPattern="/..*/v..*"
|
android:pathPattern="/..*/v..*"
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,254 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.ru.hentailib
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
import eu.kanade.tachiyomi.multisrc.libgroup.LibGroup
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class HentaiLib : LibGroup("HentaiLib", "https://hentailib.me", "ru") {
|
||||||
|
|
||||||
|
override val id: Long = 6425650164840473547
|
||||||
|
|
||||||
|
override val client: OkHttpClient = super.client.newBuilder()
|
||||||
|
.addInterceptor(::imageContentTypeIntercept)
|
||||||
|
.addInterceptor { chain ->
|
||||||
|
val originalRequest = chain.request()
|
||||||
|
if (originalRequest.url.toString().contains(baseUrl))
|
||||||
|
if (!network.cloudflareClient.newCall(GET(baseUrl, headers))
|
||||||
|
.execute().body!!.string().contains("m-menu__user-info")
|
||||||
|
)
|
||||||
|
throw IOException("Для просмотра 18+ контента необходима авторизация через WebView")
|
||||||
|
return@addInterceptor chain.proceed(originalRequest)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private var csrfToken: String = ""
|
||||||
|
|
||||||
|
private fun catalogHeaders() = Headers.Builder()
|
||||||
|
.apply {
|
||||||
|
add("Accept", "application/json, text/plain, */*")
|
||||||
|
add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
add("x-csrf-token", csrfToken)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
if (csrfToken.isEmpty()) {
|
||||||
|
val tokenResponse = client.newCall(popularMangaRequest(page)).execute()
|
||||||
|
val resBody = tokenResponse.body!!.string()
|
||||||
|
csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
|
||||||
|
}
|
||||||
|
val url = super.searchMangaRequest(page, query, filters).url.newBuilder()
|
||||||
|
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is TagList -> filter.state.forEach { tag ->
|
||||||
|
if (tag.state != Filter.TriState.STATE_IGNORE) {
|
||||||
|
url.addQueryParameter(
|
||||||
|
if (tag.isIncluded()) "tags[include][]" else "tags[exclude][]",
|
||||||
|
tag.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return POST(url.toString(), catalogHeaders())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
private class SearchFilter(name: String, val id: String) : Filter.TriState(name)
|
||||||
|
|
||||||
|
private class TagList(tags: List<SearchFilter>) : Filter.Group<SearchFilter>("Теги", tags)
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList {
|
||||||
|
val filters = super.getFilterList().toMutableList()
|
||||||
|
filters.add(4, TagList(getTagList()))
|
||||||
|
return FilterList(filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTagList() = listOf(
|
||||||
|
SearchFilter("3D", "1"),
|
||||||
|
SearchFilter("Defloration", "287"),
|
||||||
|
SearchFilter("FPP(Вид от первого лица)", "289"),
|
||||||
|
SearchFilter("Footfuck", "5"),
|
||||||
|
SearchFilter("Handjob", "6"),
|
||||||
|
SearchFilter("Lactation", "7"),
|
||||||
|
SearchFilter("Living clothes", "284"),
|
||||||
|
SearchFilter("Mind break", "9"),
|
||||||
|
SearchFilter("Scat", "13"),
|
||||||
|
SearchFilter("Selfcest", "286"),
|
||||||
|
SearchFilter("Shemale", "220"),
|
||||||
|
SearchFilter("Tomboy", "14"),
|
||||||
|
SearchFilter("Unbirth", "283"),
|
||||||
|
SearchFilter("X-Ray", "15"),
|
||||||
|
SearchFilter("Алкоголь", "16"),
|
||||||
|
SearchFilter("Анал", "17"),
|
||||||
|
SearchFilter("Андроид", "18"),
|
||||||
|
SearchFilter("Анилингус", "19"),
|
||||||
|
SearchFilter("Анимация (GIF)", "350"),
|
||||||
|
SearchFilter("Арт", "20"),
|
||||||
|
SearchFilter("Ахэгао", "2"),
|
||||||
|
SearchFilter("БДСМ", "22"),
|
||||||
|
SearchFilter("Бакуню", "21"),
|
||||||
|
SearchFilter("Бара", "293"),
|
||||||
|
SearchFilter("Без проникновения", "336"),
|
||||||
|
SearchFilter("Без текста", "23"),
|
||||||
|
SearchFilter("Без трусиков", "24"),
|
||||||
|
SearchFilter("Без цензуры", "25"),
|
||||||
|
SearchFilter("Беременность", "26"),
|
||||||
|
SearchFilter("Бикини", "27"),
|
||||||
|
SearchFilter("Близнецы", "28"),
|
||||||
|
SearchFilter("Боди-арт", "29"),
|
||||||
|
SearchFilter("Больница", "30"),
|
||||||
|
SearchFilter("Большая грудь", "31"),
|
||||||
|
SearchFilter("Большая попка", "32"),
|
||||||
|
SearchFilter("Борьба", "33"),
|
||||||
|
SearchFilter("Буккакэ", "34"),
|
||||||
|
SearchFilter("В бассейне", "35"),
|
||||||
|
SearchFilter("В ванной", "36"),
|
||||||
|
SearchFilter("В государственном учреждении", "37"),
|
||||||
|
SearchFilter("В общественном месте", "38"),
|
||||||
|
SearchFilter("В очках", "8"),
|
||||||
|
SearchFilter("В первый раз", "39"),
|
||||||
|
SearchFilter("В транспорте", "40"),
|
||||||
|
SearchFilter("Вампиры", "41"),
|
||||||
|
SearchFilter("Вибратор", "42"),
|
||||||
|
SearchFilter("Втроём", "43"),
|
||||||
|
SearchFilter("Гипноз", "44"),
|
||||||
|
SearchFilter("Глубокий минет", "45"),
|
||||||
|
SearchFilter("Горячий источник", "46"),
|
||||||
|
SearchFilter("Групповой секс", "47"),
|
||||||
|
SearchFilter("Гуро", "307"),
|
||||||
|
SearchFilter("Гяру и Гангуро", "48"),
|
||||||
|
SearchFilter("Двойное проникновение", "49"),
|
||||||
|
SearchFilter("Девочки-волшебницы", "50"),
|
||||||
|
SearchFilter("Девушка-туалет", "51"),
|
||||||
|
SearchFilter("Демон", "52"),
|
||||||
|
SearchFilter("Дилдо", "53"),
|
||||||
|
SearchFilter("Домохозяйка", "54"),
|
||||||
|
SearchFilter("Дыра в стене", "55"),
|
||||||
|
SearchFilter("Жестокость", "56"),
|
||||||
|
SearchFilter("Золотой дождь", "57"),
|
||||||
|
SearchFilter("Зомби", "58"),
|
||||||
|
SearchFilter("Зоофилия", "351"),
|
||||||
|
SearchFilter("Зрелые женщины", "59"),
|
||||||
|
SearchFilter("Избиение", "223"),
|
||||||
|
SearchFilter("Измена", "60"),
|
||||||
|
SearchFilter("Изнасилование", "61"),
|
||||||
|
SearchFilter("Инопланетяне", "62"),
|
||||||
|
SearchFilter("Инцест", "63"),
|
||||||
|
SearchFilter("Исполнение желаний", "64"),
|
||||||
|
SearchFilter("Историческое", "65"),
|
||||||
|
SearchFilter("Камера", "66"),
|
||||||
|
SearchFilter("Кляп", "288"),
|
||||||
|
SearchFilter("Колготки", "67"),
|
||||||
|
SearchFilter("Косплей", "68"),
|
||||||
|
SearchFilter("Кримпай", "3"),
|
||||||
|
SearchFilter("Куннилингус", "69"),
|
||||||
|
SearchFilter("Купальники", "70"),
|
||||||
|
SearchFilter("ЛГБТ", "343"),
|
||||||
|
SearchFilter("Латекс и кожа", "71"),
|
||||||
|
SearchFilter("Магия", "72"),
|
||||||
|
SearchFilter("Маленькая грудь", "73"),
|
||||||
|
SearchFilter("Мастурбация", "74"),
|
||||||
|
SearchFilter("Медсестра", "221"),
|
||||||
|
SearchFilter("Мейдочка", "75"),
|
||||||
|
SearchFilter("Мерзкий дядька", "76"),
|
||||||
|
SearchFilter("Милф", "77"),
|
||||||
|
SearchFilter("Много девушек", "78"),
|
||||||
|
SearchFilter("Много спермы", "79"),
|
||||||
|
SearchFilter("Молоко", "80"),
|
||||||
|
SearchFilter("Монашка", "353"),
|
||||||
|
SearchFilter("Монстродевушки", "81"),
|
||||||
|
SearchFilter("Монстры", "82"),
|
||||||
|
SearchFilter("Мочеиспускание", "83"),
|
||||||
|
SearchFilter("На природе", "84"),
|
||||||
|
SearchFilter("Наблюдение", "85"),
|
||||||
|
SearchFilter("Насекомые", "285"),
|
||||||
|
SearchFilter("Небритая киска", "86"),
|
||||||
|
SearchFilter("Небритые подмышки", "87"),
|
||||||
|
SearchFilter("Нетораре", "88"),
|
||||||
|
SearchFilter("Нэтори", "11"),
|
||||||
|
SearchFilter("Обмен телами", "89"),
|
||||||
|
SearchFilter("Обычный секс", "90"),
|
||||||
|
SearchFilter("Огромная грудь", "91"),
|
||||||
|
SearchFilter("Огромный член", "92"),
|
||||||
|
SearchFilter("Омораси", "93"),
|
||||||
|
SearchFilter("Оральный секс", "94"),
|
||||||
|
SearchFilter("Орки", "95"),
|
||||||
|
SearchFilter("Остановка времени", "296"),
|
||||||
|
SearchFilter("Пайзури", "96"),
|
||||||
|
SearchFilter("Парень пассив", "97"),
|
||||||
|
SearchFilter("Переодевание", "98"),
|
||||||
|
SearchFilter("Пирсинг", "308"),
|
||||||
|
SearchFilter("Пляж", "99"),
|
||||||
|
SearchFilter("Повседневность", "100"),
|
||||||
|
SearchFilter("Подвязки", "282"),
|
||||||
|
SearchFilter("Подглядывание", "101"),
|
||||||
|
SearchFilter("Подчинение", "102"),
|
||||||
|
SearchFilter("Похищение", "103"),
|
||||||
|
SearchFilter("Превозмогание", "104"),
|
||||||
|
SearchFilter("Принуждение", "105"),
|
||||||
|
SearchFilter("Прозрачная одежда", "106"),
|
||||||
|
SearchFilter("Проституция", "107"),
|
||||||
|
SearchFilter("Психические отклонения", "108"),
|
||||||
|
SearchFilter("Публично", "109"),
|
||||||
|
SearchFilter("Пытки", "224"),
|
||||||
|
SearchFilter("Пьяные", "110"),
|
||||||
|
SearchFilter("Рабы", "356"),
|
||||||
|
SearchFilter("Рабыни", "111"),
|
||||||
|
SearchFilter("С Сюжетом", "337"),
|
||||||
|
SearchFilter("Сuminside", "4"),
|
||||||
|
SearchFilter("Секс-игрушки", "112"),
|
||||||
|
SearchFilter("Сексуально возбуждённая", "113"),
|
||||||
|
SearchFilter("Сибари", "114"),
|
||||||
|
SearchFilter("Спортивная форма", "117"),
|
||||||
|
SearchFilter("Спортивное тело", "335"),
|
||||||
|
SearchFilter("Спящие", "118"),
|
||||||
|
SearchFilter("Страпон", "119"),
|
||||||
|
SearchFilter("Суккуб", "120"),
|
||||||
|
SearchFilter("Темнокожие", "121"),
|
||||||
|
SearchFilter("Тентакли", "122"),
|
||||||
|
SearchFilter("Толстушки", "123"),
|
||||||
|
SearchFilter("Трагедия", "124"),
|
||||||
|
SearchFilter("Трап", "125"),
|
||||||
|
SearchFilter("Ужасы", "126"),
|
||||||
|
SearchFilter("Униформа", "127"),
|
||||||
|
SearchFilter("Учитель и ученик", "352"),
|
||||||
|
SearchFilter("Ушастые", "128"),
|
||||||
|
SearchFilter("Фантазии", "129"),
|
||||||
|
SearchFilter("Фемдом", "130"),
|
||||||
|
SearchFilter("Фестиваль", "131"),
|
||||||
|
SearchFilter("Фетиш", "132"),
|
||||||
|
SearchFilter("Фистинг", "133"),
|
||||||
|
SearchFilter("Фурри", "134"),
|
||||||
|
SearchFilter("Футанари", "136"),
|
||||||
|
SearchFilter("Футанари имеет парня", "137"),
|
||||||
|
SearchFilter("Цельный купальник", "138"),
|
||||||
|
SearchFilter("Цундэрэ", "139"),
|
||||||
|
SearchFilter("Чикан", "140"),
|
||||||
|
SearchFilter("Чулки", "141"),
|
||||||
|
SearchFilter("Шлюха", "142"),
|
||||||
|
SearchFilter("Эксгибиционизм", "143"),
|
||||||
|
SearchFilter("Эльф", "144"),
|
||||||
|
SearchFilter("Юные", "145"),
|
||||||
|
SearchFilter("Яндэрэ", "146")
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<application>
|
<application>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ru.libmanga.LibMangaActivity"
|
android:name="eu.kanade.tachiyomi.multisrc.libgroup.LibUrlActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<!-- LibMangaActivity sites can be added here. -->
|
<!-- LibUrlActivity sites can be added here. -->
|
||||||
<data
|
<data
|
||||||
android:host="mangalib.me"
|
android:host="mangalib.me"
|
||||||
android:pathPattern="/..*/v..*"
|
android:pathPattern="/..*/v..*"
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 186 KiB |
|
@ -0,0 +1,222 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.ru.mangalib
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
|
import eu.kanade.tachiyomi.multisrc.libgroup.LibGroup
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import okhttp3.Headers
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class MangaLib : LibGroup("MangaLib", "https://mangalib.me", "ru") {
|
||||||
|
|
||||||
|
override val id: Long = 6111047689498497237
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val client: OkHttpClient = super.client.newBuilder()
|
||||||
|
.addInterceptor(::imageContentTypeIntercept)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private var csrfToken: String = ""
|
||||||
|
|
||||||
|
private fun catalogHeaders() = Headers.Builder()
|
||||||
|
.apply {
|
||||||
|
add("Accept", "application/json, text/plain, */*")
|
||||||
|
add("X-Requested-With", "XMLHttpRequest")
|
||||||
|
add("x-csrf-token", csrfToken)
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val baseOrig: String = "https://mangalib.me"
|
||||||
|
private val baseMirr: String = "https://mangalib.org"
|
||||||
|
private var domain: String? = preferences.getString(DOMAIN_PREF, baseOrig)
|
||||||
|
override val baseUrl: String = domain.toString()
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
if (csrfToken.isEmpty()) {
|
||||||
|
val tokenResponse = client.newCall(popularMangaRequest(page)).execute()
|
||||||
|
val resBody = tokenResponse.body!!.string()
|
||||||
|
csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
|
||||||
|
}
|
||||||
|
val url = super.searchMangaRequest(page, query, filters).url.newBuilder()
|
||||||
|
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is AgeList -> filter.state.forEach { age ->
|
||||||
|
if (age.state) {
|
||||||
|
url.addQueryParameter("caution[]", age.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TagList -> filter.state.forEach { tag ->
|
||||||
|
if (tag.state != Filter.TriState.STATE_IGNORE) {
|
||||||
|
url.addQueryParameter(
|
||||||
|
if (tag.isIncluded()) "tags[include][]" else "tags[exclude][]",
|
||||||
|
tag.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return POST(url.toString(), catalogHeaders())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
private class SearchFilter(name: String, val id: String) : Filter.TriState(name)
|
||||||
|
private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name)
|
||||||
|
|
||||||
|
private class TagList(tags: List<SearchFilter>) : Filter.Group<SearchFilter>("Теги", tags)
|
||||||
|
private class AgeList(ages: List<CheckFilter>) : Filter.Group<CheckFilter>("Возрастное ограничение", ages)
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList {
|
||||||
|
val filters = super.getFilterList().toMutableList()
|
||||||
|
filters.add(4, TagList(getTagList()))
|
||||||
|
filters.add(7, AgeList(getAgeList()))
|
||||||
|
return FilterList(filters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTagList() = listOf(
|
||||||
|
SearchFilter("Азартные игры", "304"),
|
||||||
|
SearchFilter("Алхимия", "225"),
|
||||||
|
SearchFilter("Ангелы", "226"),
|
||||||
|
SearchFilter("Антигерой", "175"),
|
||||||
|
SearchFilter("Антиутопия", "227"),
|
||||||
|
SearchFilter("Апокалипсис", "228"),
|
||||||
|
SearchFilter("Армия", "229"),
|
||||||
|
SearchFilter("Артефакты", "230"),
|
||||||
|
SearchFilter("Боги", "215"),
|
||||||
|
SearchFilter("Бои на мечах", "231"),
|
||||||
|
SearchFilter("Борьба за власть", "231"),
|
||||||
|
SearchFilter("Брат и сестра", "233"),
|
||||||
|
SearchFilter("Будущее", "234"),
|
||||||
|
SearchFilter("Ведьма", "338"),
|
||||||
|
SearchFilter("Вестерн", "235"),
|
||||||
|
SearchFilter("Видеоигры", "185"),
|
||||||
|
SearchFilter("Виртуальная реальность", "195"),
|
||||||
|
SearchFilter("Владыка демонов", "236"),
|
||||||
|
SearchFilter("Военные", "179"),
|
||||||
|
SearchFilter("Война", "237"),
|
||||||
|
SearchFilter("Волшебники / маги", "281"),
|
||||||
|
SearchFilter("Волшебные существа", "239"),
|
||||||
|
SearchFilter("Воспоминания из другого мира", "240"),
|
||||||
|
SearchFilter("Выживание", "193"),
|
||||||
|
SearchFilter("ГГ женщина", "243"),
|
||||||
|
SearchFilter("ГГ имба", "291"),
|
||||||
|
SearchFilter("ГГ мужчина", "244"),
|
||||||
|
SearchFilter("Геймеры", "241"),
|
||||||
|
SearchFilter("Гильдии", "242"),
|
||||||
|
SearchFilter("Глупый ГГ", "297"),
|
||||||
|
SearchFilter("Гоблины", "245"),
|
||||||
|
SearchFilter("Горничные", "169"),
|
||||||
|
SearchFilter("Гяру", "178"),
|
||||||
|
SearchFilter("Демоны", "151"),
|
||||||
|
SearchFilter("Драконы", "246"),
|
||||||
|
SearchFilter("Дружба", "247"),
|
||||||
|
SearchFilter("Жестокий мир", "249"),
|
||||||
|
SearchFilter("Животные компаньоны", "250"),
|
||||||
|
SearchFilter("Завоевание мира", "251"),
|
||||||
|
SearchFilter("Зверолюди", "162"),
|
||||||
|
SearchFilter("Злые духи", "252"),
|
||||||
|
SearchFilter("Зомби", "149"),
|
||||||
|
SearchFilter("Игровые элементы", "253"),
|
||||||
|
SearchFilter("Империи", "254"),
|
||||||
|
SearchFilter("Квесты", "255"),
|
||||||
|
SearchFilter("Космос", "256"),
|
||||||
|
SearchFilter("Кулинария", "152"),
|
||||||
|
SearchFilter("Культивация", "160"),
|
||||||
|
SearchFilter("Легендарное оружие", "257"),
|
||||||
|
SearchFilter("Лоли", "187"),
|
||||||
|
SearchFilter("Магическая академия", "258"),
|
||||||
|
SearchFilter("Магия", "168"),
|
||||||
|
SearchFilter("Мафия", "172"),
|
||||||
|
SearchFilter("Медицина", "153"),
|
||||||
|
SearchFilter("Месть", "259"),
|
||||||
|
SearchFilter("Монстр Девушки", "188"),
|
||||||
|
SearchFilter("Монстры", "189"),
|
||||||
|
SearchFilter("Музыка", "190"),
|
||||||
|
SearchFilter("Навыки / способности", "260"),
|
||||||
|
SearchFilter("Насилие / жестокость", "262"),
|
||||||
|
SearchFilter("Наёмники", "261"),
|
||||||
|
SearchFilter("Нежить", "263"),
|
||||||
|
SearchFilter("Ниндая", "180"),
|
||||||
|
SearchFilter("Обратный Гарем", "191"),
|
||||||
|
SearchFilter("Огнестрельное оружие", "264"),
|
||||||
|
SearchFilter("Офисные Работники", "181"),
|
||||||
|
SearchFilter("Пародия", "265"),
|
||||||
|
SearchFilter("Пираты", "340"),
|
||||||
|
SearchFilter("Подземелья", "266"),
|
||||||
|
SearchFilter("Политика", "267"),
|
||||||
|
SearchFilter("Полиция", "182"),
|
||||||
|
SearchFilter("Преступники / Криминал", "186"),
|
||||||
|
SearchFilter("Призраки / Духи", "177"),
|
||||||
|
SearchFilter("Путешествие во времени", "194"),
|
||||||
|
SearchFilter("Разумные расы", "268"),
|
||||||
|
SearchFilter("Ранги силы", "248"),
|
||||||
|
SearchFilter("Реинкарнация", "148"),
|
||||||
|
SearchFilter("Роботы", "269"),
|
||||||
|
SearchFilter("Рыцари", "270"),
|
||||||
|
SearchFilter("Самураи", "183"),
|
||||||
|
SearchFilter("Система", "271"),
|
||||||
|
SearchFilter("Скрытие личности", "273"),
|
||||||
|
SearchFilter("Спасение мира", "274"),
|
||||||
|
SearchFilter("Спортивное тело", "334"),
|
||||||
|
SearchFilter("Средневековье", "173"),
|
||||||
|
SearchFilter("Стимпанк", "272"),
|
||||||
|
SearchFilter("Супергерои", "275"),
|
||||||
|
SearchFilter("Традиционные игры", "184"),
|
||||||
|
SearchFilter("Умный ГГ", "302"),
|
||||||
|
SearchFilter("Учитель / ученик", "276"),
|
||||||
|
SearchFilter("Философия", "277"),
|
||||||
|
SearchFilter("Хикикомори", "166"),
|
||||||
|
SearchFilter("Холодное оружие", "278"),
|
||||||
|
SearchFilter("Шантаж", "279"),
|
||||||
|
SearchFilter("Эльфы", "216"),
|
||||||
|
SearchFilter("Якудза", "164"),
|
||||||
|
SearchFilter("Япония", "280")
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getAgeList() = listOf(
|
||||||
|
CheckFilter("Отсутствует", "0"),
|
||||||
|
CheckFilter("16+", "1"),
|
||||||
|
CheckFilter("18+", "2")
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||||
|
private const val DOMAIN_PREF = "MangaLibDomain"
|
||||||
|
private const val DOMAIN_PREF_Title = "Выбор домена"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
super.setupPreferenceScreen(screen)
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = DOMAIN_PREF
|
||||||
|
title = DOMAIN_PREF_Title
|
||||||
|
entries = arrayOf("Основной (mangalib.me)", "Зеркало (mangalib.org)")
|
||||||
|
entryValues = arrayOf(baseOrig, baseMirr)
|
||||||
|
summary = "%s"
|
||||||
|
setDefaultValue(baseOrig)
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
try {
|
||||||
|
val res = preferences.edit().putString(DOMAIN_PREF, newValue as String).commit()
|
||||||
|
val warning = "Для смены домена необходимо перезапустить приложение с полной остановкой."
|
||||||
|
Toast.makeText(screen.context, warning, Toast.LENGTH_LONG).show()
|
||||||
|
res
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.let(screen::addPreference)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package eu.kanade.tachiyomi.multisrc.libgroup
|
||||||
|
|
||||||
|
import generator.ThemeSourceData.SingleLang
|
||||||
|
import generator.ThemeSourceGenerator
|
||||||
|
|
||||||
|
class LibGenerator: ThemeSourceGenerator {
|
||||||
|
|
||||||
|
override val themePkg = "libgroup"
|
||||||
|
|
||||||
|
override val themeClass = "LibGroup"
|
||||||
|
|
||||||
|
override val baseVersionCode: Int = 1
|
||||||
|
|
||||||
|
override val sources = listOf(
|
||||||
|
SingleLang("MangaLib", "https://mangalib.me", "ru", overrideVersionCode = 74),
|
||||||
|
SingleLang("HentaiLib", "https://hentailib.me", "ru",isNsfw = true, overrideVersionCode = 19)
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
LibGenerator().createAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.extension.ru.libmanga
|
package eu.kanade.tachiyomi.multisrc.libgroup
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
@ -13,6 +13,8 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
@ -32,8 +34,6 @@ import kotlinx.serialization.json.jsonObject
|
||||||
import kotlinx.serialization.json.jsonPrimitive
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
import okhttp3.Interceptor
|
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
@ -46,7 +46,11 @@ import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class LibManga : ConfigurableSource, HttpSource() {
|
abstract class LibGroup(
|
||||||
|
override val name: String,
|
||||||
|
override val baseUrl: String,
|
||||||
|
final override val lang: String
|
||||||
|
) : ConfigurableSource, HttpSource() {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
@ -54,12 +58,9 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_${id}_2", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val name: String = "Mangalib"
|
|
||||||
|
|
||||||
override val lang = "ru"
|
|
||||||
|
|
||||||
override val supportsLatest = true
|
override val supportsLatest = true
|
||||||
private fun imageContentTypeIntercept(chain: Interceptor.Chain): Response {
|
|
||||||
|
protected fun imageContentTypeIntercept(chain: Interceptor.Chain): Response {
|
||||||
val originalRequest = chain.request()
|
val originalRequest = chain.request()
|
||||||
val response = chain.proceed(originalRequest)
|
val response = chain.proceed(originalRequest)
|
||||||
val urlRequest = originalRequest.url.toString()
|
val urlRequest = originalRequest.url.toString()
|
||||||
|
@ -75,14 +76,8 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
.connectTimeout(10, TimeUnit.SECONDS)
|
.connectTimeout(10, TimeUnit.SECONDS)
|
||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
.rateLimit(3)
|
.rateLimit(3)
|
||||||
.addInterceptor { imageContentTypeIntercept(it) }
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val baseOrig: String = "https://mangalib.me"
|
|
||||||
private val baseMirr: String = "https://mangalib.org"
|
|
||||||
private var domain: String? = preferences.getString(DOMAIN_PREF, baseOrig)
|
|
||||||
override val baseUrl: String = domain.toString()
|
|
||||||
|
|
||||||
override fun headersBuilder() = Headers.Builder().apply {
|
override fun headersBuilder() = Headers.Builder().apply {
|
||||||
// User-Agent required for authorization through third-party accounts (mobile version for correct display in WebView)
|
// User-Agent required for authorization through third-party accounts (mobile version for correct display in WebView)
|
||||||
add("User-Agent", "Mozilla/5.0 (Linux; Android 10; SM-G980F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36")
|
add("User-Agent", "Mozilla/5.0 (Linux; Android 10; SM-G980F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36")
|
||||||
|
@ -248,12 +243,14 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
SManga.LICENSED
|
SManga.LICENSED
|
||||||
} else
|
} else
|
||||||
when (
|
when (
|
||||||
body.select("div.media-info-list__title:contains(Статус перевода) + div")
|
body.select("div.media-info-list__title:contains(Статус тайтла) + div")
|
||||||
.text()
|
.text()
|
||||||
.lowercase(Locale.ROOT)
|
.lowercase(Locale.ROOT)
|
||||||
) {
|
) {
|
||||||
"продолжается" -> SManga.ONGOING
|
"онгоинг" -> SManga.ONGOING
|
||||||
"завершен" -> SManga.COMPLETED
|
"завершён" -> SManga.COMPLETED
|
||||||
|
"приостановлен" -> SManga.ON_HIATUS
|
||||||
|
"выпуск прекращён" -> SManga.CANCELLED
|
||||||
else -> SManga.UNKNOWN
|
else -> SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
manga.genre = category + ", " + rawAgeStop + ", " + genres.joinToString { it.trim() }
|
manga.genre = category + ", " + rawAgeStop + ", " + genres.joinToString { it.trim() }
|
||||||
|
@ -303,59 +300,45 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortChaptersByTranslator
|
private fun sortChaptersByTranslator
|
||||||
(sortingList: String?, chaptersList: JsonArray?, slug: String, branches: List<JsonElement>): List<SChapter>? {
|
(sortingList: String?, chaptersList: JsonArray?, slug: String, branches: List<JsonElement>): List<SChapter>? {
|
||||||
var chapters: List<SChapter>? = null
|
var chapters: List<SChapter>? = null
|
||||||
val volume = "(?<=/v)[0-9]+(?=/c[0-9]+)".toRegex()
|
val volume = "(?<=/v)[0-9]+(?=/c[0-9]+)".toRegex()
|
||||||
when (sortingList) {
|
val tempChaptersList = mutableListOf<SChapter>()
|
||||||
"ms_mixing" -> {
|
for (currentBranch in branches.withIndex()) {
|
||||||
val tempChaptersList = mutableListOf<SChapter>()
|
val branch = branches[currentBranch.index]
|
||||||
for (currentBranch in branches.withIndex()) {
|
val teamId = branch.jsonObject["id"]!!.jsonPrimitive.int
|
||||||
val branch = branches[currentBranch.index]
|
val teams = branch.jsonObject["teams"]!!.jsonArray
|
||||||
val teamId = branch.jsonObject["id"]!!.jsonPrimitive.int
|
val isActive = teams.filter { it.jsonObject["is_active"]?.jsonPrimitive?.intOrNull == 1 }
|
||||||
val teams = branch.jsonObject["teams"]!!.jsonArray.filter { it.jsonObject["is_active"]?.jsonPrimitive?.intOrNull == 1 }
|
val teamsBranch = if (isActive.size == 1)
|
||||||
val teamsBranch = if (teams.size == 1)
|
isActive[0].jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||||
teams[0].jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
else if (teams.isNotEmpty() && isActive.isEmpty())
|
||||||
else if (teams.isEmpty())
|
teams[0].jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
||||||
branch.jsonObject["teams"]!!.jsonArray[0].jsonObject["name"]!!.jsonPrimitive.content
|
else "Неизвестный"
|
||||||
else null
|
chapters = chaptersList
|
||||||
chapters = chaptersList
|
?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
|
||||||
?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
|
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
|
||||||
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
|
when (sortingList) {
|
||||||
|
"ms_mixing" -> {
|
||||||
chapters?.let {
|
chapters?.let {
|
||||||
if ((tempChaptersList.size < it.size) && !groupTranslates.contains(teamsBranch.toString()))
|
if ((tempChaptersList.size < it.size) && !groupTranslates.contains(teamsBranch.toString()))
|
||||||
tempChaptersList.addAll(0, it)
|
tempChaptersList.addAll(0, it)
|
||||||
else
|
else
|
||||||
tempChaptersList.addAll(it)
|
tempChaptersList.addAll(it)
|
||||||
}
|
}
|
||||||
|
chapters = tempChaptersList.distinctBy { volume.find(it.url)?.value + "--" + it.chapter_number }.sortedWith(compareBy({ -it.chapter_number }, { volume.find(it.url)?.value }))
|
||||||
}
|
}
|
||||||
chapters = tempChaptersList.distinctBy { volume.find(it.url)?.value + "--" + it.chapter_number }.sortedWith(compareBy({ -it.chapter_number }, { volume.find(it.url)?.value }))
|
"ms_combining" -> {
|
||||||
}
|
|
||||||
"ms_combining" -> {
|
|
||||||
val tempChaptersList = mutableListOf<SChapter>()
|
|
||||||
for (currentBranch in branches.withIndex()) {
|
|
||||||
val branch = branches[currentBranch.index]
|
|
||||||
val teamId = branch.jsonObject["id"]!!.jsonPrimitive.int
|
|
||||||
val teams = branch.jsonObject["teams"]!!.jsonArray.filter { it.jsonObject["is_active"]?.jsonPrimitive?.intOrNull == 1 }
|
|
||||||
val teamsBranch = if (teams.size == 1)
|
|
||||||
teams[0].jsonObject["name"]?.jsonPrimitive?.contentOrNull
|
|
||||||
else if (teams.isEmpty())
|
|
||||||
branch.jsonObject["teams"]!!.jsonArray[0].jsonObject["name"]!!.jsonPrimitive.content
|
|
||||||
else null
|
|
||||||
chapters = chaptersList
|
|
||||||
?.filter { it.jsonObject["branch_id"]?.jsonPrimitive?.intOrNull == teamId && it.jsonObject["status"]?.jsonPrimitive?.intOrNull != 2 }
|
|
||||||
?.map { chapterFromElement(it, sortingList, slug, teamId, branches) }
|
|
||||||
if (!groupTranslates.contains(teamsBranch.toString()))
|
if (!groupTranslates.contains(teamsBranch.toString()))
|
||||||
chapters?.let { tempChaptersList.addAll(it) }
|
chapters?.let { tempChaptersList.addAll(it) }
|
||||||
|
chapters = tempChaptersList
|
||||||
}
|
}
|
||||||
chapters = tempChaptersList
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chapters
|
return chapters
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun chapterFromElement
|
private fun chapterFromElement
|
||||||
(chapterItem: JsonElement, sortingList: String?, slug: String, teamIdParam: Int? = null, branches: List<JsonElement>? = null, teams: List<JsonElement>? = null, chaptersList: JsonArray? = null): SChapter {
|
(chapterItem: JsonElement, sortingList: String?, slug: String, teamIdParam: Int? = null, branches: List<JsonElement>? = null, teams: List<JsonElement>? = null, chaptersList: JsonArray? = null): SChapter {
|
||||||
val chapter = SChapter.create()
|
val chapter = SChapter.create()
|
||||||
|
|
||||||
val volume = chapterItem.jsonObject["chapter_volume"]!!.jsonPrimitive.int
|
val volume = chapterItem.jsonObject["chapter_volume"]!!.jsonPrimitive.int
|
||||||
|
@ -384,7 +367,7 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
for (currentBranch in branches.withIndex()) {
|
for (currentBranch in branches.withIndex()) {
|
||||||
val branch = branches[currentBranch.index].jsonObject
|
val branch = branches[currentBranch.index].jsonObject
|
||||||
val teams = branch["teams"]!!.jsonArray
|
val teams = branch["teams"]!!.jsonArray
|
||||||
if (chapterItem.jsonObject["branch_id"]!!.jsonPrimitive.int == branch["id"]!!.jsonPrimitive.int) {
|
if (chapterItem.jsonObject["branch_id"]!!.jsonPrimitive.int == branch["id"]!!.jsonPrimitive.int && teams.isNotEmpty()) {
|
||||||
for (currentTeam in teams.withIndex()) {
|
for (currentTeam in teams.withIndex()) {
|
||||||
val team = teams[currentTeam.index].jsonObject
|
val team = teams[currentTeam.index].jsonObject
|
||||||
val scanlatorId = chapterItem.jsonObject["chapter_scanlator_id"]!!.jsonPrimitive.int
|
val scanlatorId = chapterItem.jsonObject["chapter_scanlator_id"]!!.jsonPrimitive.int
|
||||||
|
@ -539,16 +522,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
url.addQueryParameter("dir", if (filter.state!!.ascending) "asc" else "desc")
|
url.addQueryParameter("dir", if (filter.state!!.ascending) "asc" else "desc")
|
||||||
url.addQueryParameter("sort", arrayOf("rate", "name", "views", "created_at", "last_chapter_at", "chap_count")[filter.state!!.index])
|
url.addQueryParameter("sort", arrayOf("rate", "name", "views", "created_at", "last_chapter_at", "chap_count")[filter.state!!.index])
|
||||||
}
|
}
|
||||||
is AgeList -> filter.state.forEach { age ->
|
|
||||||
if (age.state) {
|
|
||||||
url.addQueryParameter("caution[]", age.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TagList -> filter.state.forEach { tag ->
|
|
||||||
if (tag.state != Filter.TriState.STATE_IGNORE) {
|
|
||||||
url.addQueryParameter(if (tag.isIncluded()) "tags[include][]" else "tags[exclude][]", tag.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is MyList -> filter.state.forEach { favorite ->
|
is MyList -> filter.state.forEach { favorite ->
|
||||||
if (favorite.state != Filter.TriState.STATE_IGNORE) {
|
if (favorite.state != Filter.TriState.STATE_IGNORE) {
|
||||||
url.addQueryParameter(if (favorite.isIncluded()) "bookmarks[include][]" else "bookmarks[exclude][]", favorite.id)
|
url.addQueryParameter(if (favorite.isIncluded()) "bookmarks[include][]" else "bookmarks[exclude][]", favorite.id)
|
||||||
|
@ -603,8 +576,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
private class StatusList(statuses: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус перевода", statuses)
|
private class StatusList(statuses: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус перевода", statuses)
|
||||||
private class StatusTitleList(titles: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус тайтла", titles)
|
private class StatusTitleList(titles: List<CheckFilter>) : Filter.Group<CheckFilter>("Статус тайтла", titles)
|
||||||
private class GenreList(genres: List<SearchFilter>) : Filter.Group<SearchFilter>("Жанры", genres)
|
private class GenreList(genres: List<SearchFilter>) : Filter.Group<SearchFilter>("Жанры", genres)
|
||||||
private class TagList(tags: List<SearchFilter>) : Filter.Group<SearchFilter>("Теги", tags)
|
|
||||||
private class AgeList(ages: List<CheckFilter>) : Filter.Group<CheckFilter>("Возрастное ограничение", ages)
|
|
||||||
private class MyList(favorites: List<SearchFilter>) : Filter.Group<SearchFilter>("Мои списки", favorites)
|
private class MyList(favorites: List<SearchFilter>) : Filter.Group<SearchFilter>("Мои списки", favorites)
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
override fun getFilterList() = FilterList(
|
||||||
|
@ -612,10 +583,8 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
CategoryList(getCategoryList()),
|
CategoryList(getCategoryList()),
|
||||||
FormatList(getFormatList()),
|
FormatList(getFormatList()),
|
||||||
GenreList(getGenreList()),
|
GenreList(getGenreList()),
|
||||||
TagList(getTagList()),
|
|
||||||
StatusList(getStatusList()),
|
StatusList(getStatusList()),
|
||||||
StatusTitleList(getStatusTitleList()),
|
StatusTitleList(getStatusTitleList()),
|
||||||
AgeList(getAgeList()),
|
|
||||||
MyList(getMyList())
|
MyList(getMyList())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -625,11 +594,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
Selection(2, false)
|
Selection(2, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
* Use console
|
|
||||||
* Object.entries(__FILTER_ITEMS__.types).map(([k, v]) => `SearchFilter("${v.label}", "${v.id}")`).join(',\n')
|
|
||||||
* on /manga-list
|
|
||||||
*/
|
|
||||||
private fun getCategoryList() = listOf(
|
private fun getCategoryList() = listOf(
|
||||||
CheckFilter("Манга", "1"),
|
CheckFilter("Манга", "1"),
|
||||||
CheckFilter("OEL-манга", "4"),
|
CheckFilter("OEL-манга", "4"),
|
||||||
|
@ -648,11 +612,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
SearchFilter("Веб", "6")
|
SearchFilter("Веб", "6")
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
* Use console
|
|
||||||
* Object.entries(__FILTER_ITEMS__.status).map(([k, v]) => `SearchFilter("${v.label}", "${v.id}")`).join(',\n')
|
|
||||||
* on /manga-list
|
|
||||||
*/
|
|
||||||
private fun getStatusList() = listOf(
|
private fun getStatusList() = listOf(
|
||||||
CheckFilter("Продолжается", "1"),
|
CheckFilter("Продолжается", "1"),
|
||||||
CheckFilter("Завершен", "2"),
|
CheckFilter("Завершен", "2"),
|
||||||
|
@ -668,11 +627,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
CheckFilter("Выпуск прекращён", "5"),
|
CheckFilter("Выпуск прекращён", "5"),
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
* Use console
|
|
||||||
* __FILTER_ITEMS__.genres.map(it => `SearchFilter("${it.name}", "${it.id}")`).join(',\n')
|
|
||||||
* on /manga-list
|
|
||||||
*/
|
|
||||||
private fun getGenreList() = listOf(
|
private fun getGenreList() = listOf(
|
||||||
SearchFilter("арт", "32"),
|
SearchFilter("арт", "32"),
|
||||||
SearchFilter("боевик", "34"),
|
SearchFilter("боевик", "34"),
|
||||||
|
@ -720,113 +674,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
SearchFilter("яой", "74")
|
SearchFilter("яой", "74")
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getTagList() = listOf(
|
|
||||||
SearchFilter("Азартные игры", "304"),
|
|
||||||
SearchFilter("Алхимия", "225"),
|
|
||||||
SearchFilter("Ангелы", "226"),
|
|
||||||
SearchFilter("Антигерой", "175"),
|
|
||||||
SearchFilter("Антиутопия", "227"),
|
|
||||||
SearchFilter("Апокалипсис", "228"),
|
|
||||||
SearchFilter("Армия", "229"),
|
|
||||||
SearchFilter("Артефакты", "230"),
|
|
||||||
SearchFilter("Боги", "215"),
|
|
||||||
SearchFilter("Бои на мечах", "231"),
|
|
||||||
SearchFilter("Борьба за власть", "231"),
|
|
||||||
SearchFilter("Брат и сестра", "233"),
|
|
||||||
SearchFilter("Будущее", "234"),
|
|
||||||
SearchFilter("Ведьма", "338"),
|
|
||||||
SearchFilter("Вестерн", "235"),
|
|
||||||
SearchFilter("Видеоигры", "185"),
|
|
||||||
SearchFilter("Виртуальная реальность", "195"),
|
|
||||||
SearchFilter("Владыка демонов", "236"),
|
|
||||||
SearchFilter("Военные", "179"),
|
|
||||||
SearchFilter("Война", "237"),
|
|
||||||
SearchFilter("Волшебники / маги", "281"),
|
|
||||||
SearchFilter("Волшебные существа", "239"),
|
|
||||||
SearchFilter("Воспоминания из другого мира", "240"),
|
|
||||||
SearchFilter("Выживание", "193"),
|
|
||||||
SearchFilter("ГГ женщина", "243"),
|
|
||||||
SearchFilter("ГГ имба", "291"),
|
|
||||||
SearchFilter("ГГ мужчина", "244"),
|
|
||||||
SearchFilter("Геймеры", "241"),
|
|
||||||
SearchFilter("Гильдии", "242"),
|
|
||||||
SearchFilter("Глупый ГГ", "297"),
|
|
||||||
SearchFilter("Гоблины", "245"),
|
|
||||||
SearchFilter("Горничные", "169"),
|
|
||||||
SearchFilter("Гяру", "178"),
|
|
||||||
SearchFilter("Демоны", "151"),
|
|
||||||
SearchFilter("Драконы", "246"),
|
|
||||||
SearchFilter("Дружба", "247"),
|
|
||||||
SearchFilter("Жестокий мир", "249"),
|
|
||||||
SearchFilter("Животные компаньоны", "250"),
|
|
||||||
SearchFilter("Завоевание мира", "251"),
|
|
||||||
SearchFilter("Зверолюди", "162"),
|
|
||||||
SearchFilter("Злые духи", "252"),
|
|
||||||
SearchFilter("Зомби", "149"),
|
|
||||||
SearchFilter("Игровые элементы", "253"),
|
|
||||||
SearchFilter("Империи", "254"),
|
|
||||||
SearchFilter("Квесты", "255"),
|
|
||||||
SearchFilter("Космос", "256"),
|
|
||||||
SearchFilter("Кулинария", "152"),
|
|
||||||
SearchFilter("Культивация", "160"),
|
|
||||||
SearchFilter("Легендарное оружие", "257"),
|
|
||||||
SearchFilter("Лоли", "187"),
|
|
||||||
SearchFilter("Магическая академия", "258"),
|
|
||||||
SearchFilter("Магия", "168"),
|
|
||||||
SearchFilter("Мафия", "172"),
|
|
||||||
SearchFilter("Медицина", "153"),
|
|
||||||
SearchFilter("Месть", "259"),
|
|
||||||
SearchFilter("Монстр Девушки", "188"),
|
|
||||||
SearchFilter("Монстры", "189"),
|
|
||||||
SearchFilter("Музыка", "190"),
|
|
||||||
SearchFilter("Навыки / способности", "260"),
|
|
||||||
SearchFilter("Насилие / жестокость", "262"),
|
|
||||||
SearchFilter("Наёмники", "261"),
|
|
||||||
SearchFilter("Нежить", "263"),
|
|
||||||
SearchFilter("Ниндая", "180"),
|
|
||||||
SearchFilter("Обратный Гарем", "191"),
|
|
||||||
SearchFilter("Огнестрельное оружие", "264"),
|
|
||||||
SearchFilter("Офисные Работники", "181"),
|
|
||||||
SearchFilter("Пародия", "265"),
|
|
||||||
SearchFilter("Пираты", "340"),
|
|
||||||
SearchFilter("Подземелья", "266"),
|
|
||||||
SearchFilter("Политика", "267"),
|
|
||||||
SearchFilter("Полиция", "182"),
|
|
||||||
SearchFilter("Преступники / Криминал", "186"),
|
|
||||||
SearchFilter("Призраки / Духи", "177"),
|
|
||||||
SearchFilter("Путешествие во времени", "194"),
|
|
||||||
SearchFilter("Разумные расы", "268"),
|
|
||||||
SearchFilter("Ранги силы", "248"),
|
|
||||||
SearchFilter("Реинкарнация", "148"),
|
|
||||||
SearchFilter("Роботы", "269"),
|
|
||||||
SearchFilter("Рыцари", "270"),
|
|
||||||
SearchFilter("Самураи", "183"),
|
|
||||||
SearchFilter("Система", "271"),
|
|
||||||
SearchFilter("Скрытие личности", "273"),
|
|
||||||
SearchFilter("Спасение мира", "274"),
|
|
||||||
SearchFilter("Спортивное тело", "334"),
|
|
||||||
SearchFilter("Средневековье", "173"),
|
|
||||||
SearchFilter("Стимпанк", "272"),
|
|
||||||
SearchFilter("Супергерои", "275"),
|
|
||||||
SearchFilter("Традиционные игры", "184"),
|
|
||||||
SearchFilter("Умный ГГ", "302"),
|
|
||||||
SearchFilter("Учитель / ученик", "276"),
|
|
||||||
SearchFilter("Философия", "277"),
|
|
||||||
SearchFilter("Хикикомори", "166"),
|
|
||||||
SearchFilter("Холодное оружие", "278"),
|
|
||||||
SearchFilter("Шантаж", "279"),
|
|
||||||
SearchFilter("Эльфы", "216"),
|
|
||||||
SearchFilter("Якудза", "164"),
|
|
||||||
SearchFilter("Япония", "280")
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getAgeList() = listOf(
|
|
||||||
CheckFilter("Отсутствует", "0"),
|
|
||||||
CheckFilter("16+", "1"),
|
|
||||||
CheckFilter("18+", "2")
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun getMyList() = listOf(
|
private fun getMyList() = listOf(
|
||||||
SearchFilter("Читаю", "1"),
|
SearchFilter("Читаю", "1"),
|
||||||
SearchFilter("В планах", "2"),
|
SearchFilter("В планах", "2"),
|
||||||
|
@ -848,13 +695,8 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
private const val TRANSLATORS_TITLE = "Чёрный список переводчиков\n(для красоты через «/» или с новой строки)"
|
private const val TRANSLATORS_TITLE = "Чёрный список переводчиков\n(для красоты через «/» или с новой строки)"
|
||||||
private const val TRANSLATORS_DEFAULT = ""
|
private const val TRANSLATORS_DEFAULT = ""
|
||||||
|
|
||||||
private const val DOMAIN_PREF = "MangaLibDomain"
|
|
||||||
private const val DOMAIN_PREF_Title = "Выбор домена"
|
|
||||||
|
|
||||||
private const val LANGUAGE_PREF = "MangaLibTitleLanguage"
|
private const val LANGUAGE_PREF = "MangaLibTitleLanguage"
|
||||||
private const val LANGUAGE_PREF_Title = "Выбор языка на обложке"
|
private const val LANGUAGE_PREF_Title = "Выбор языка на обложке"
|
||||||
|
|
||||||
private const val COVER_URL = "https://staticlib.me"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var server: String? = preferences.getString(SERVER_PREF, null)
|
private var server: String? = preferences.getString(SERVER_PREF, null)
|
||||||
|
@ -899,25 +741,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
preferences.edit().putBoolean(key, checkValue).commit()
|
preferences.edit().putBoolean(key, checkValue).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val domainPref = ListPreference(screen.context).apply {
|
|
||||||
key = DOMAIN_PREF
|
|
||||||
title = DOMAIN_PREF_Title
|
|
||||||
entries = arrayOf("Основной (mangalib.me)", "Зеркало (mangalib.org)")
|
|
||||||
entryValues = arrayOf(baseOrig, baseMirr)
|
|
||||||
summary = "%s"
|
|
||||||
setDefaultValue(baseOrig)
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
try {
|
|
||||||
val res = preferences.edit().putString(DOMAIN_PREF, newValue as String).commit()
|
|
||||||
val warning = "Для смены домена необходимо перезапустить приложение с полной остановкой."
|
|
||||||
Toast.makeText(screen.context, warning, Toast.LENGTH_LONG).show()
|
|
||||||
res
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val titleLanguagePref = ListPreference(screen.context).apply {
|
val titleLanguagePref = ListPreference(screen.context).apply {
|
||||||
key = LANGUAGE_PREF
|
key = LANGUAGE_PREF
|
||||||
title = LANGUAGE_PREF_Title
|
title = LANGUAGE_PREF_Title
|
||||||
|
@ -932,8 +755,6 @@ class LibManga : ConfigurableSource, HttpSource() {
|
||||||
titleLanguage
|
titleLanguage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
screen.addPreference(domainPref)
|
|
||||||
screen.addPreference(serverPref)
|
screen.addPreference(serverPref)
|
||||||
screen.addPreference(sortingPref)
|
screen.addPreference(sortingPref)
|
||||||
screen.addPreference(screen.editTextPreference(TRANSLATORS_TITLE, TRANSLATORS_DEFAULT, groupTranslates))
|
screen.addPreference(screen.editTextPreference(TRANSLATORS_TITLE, TRANSLATORS_DEFAULT, groupTranslates))
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.extension.ru.libmanga
|
package eu.kanade.tachiyomi.multisrc.libgroup
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
|
@ -8,12 +8,12 @@ import android.util.Log
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Springboard that accepts https://mangalib.me/xxx intents and redirects them to
|
* Springboard that accepts https://xxxxlib.me/xxx intents and redirects them to
|
||||||
* the main tachiyomi process. The idea is to not install the intent filter unless
|
* 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
|
* you have this extension installed, but still let the main tachiyomi app control
|
||||||
* things.
|
* things.
|
||||||
*/
|
*/
|
||||||
class LibMangaActivity : Activity() {
|
class LibUrlActivity : Activity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -22,17 +22,17 @@ class LibMangaActivity : Activity() {
|
||||||
val titleid = pathSegments[0]
|
val titleid = pathSegments[0]
|
||||||
val mainIntent = Intent().apply {
|
val mainIntent = Intent().apply {
|
||||||
action = "eu.kanade.tachiyomi.SEARCH"
|
action = "eu.kanade.tachiyomi.SEARCH"
|
||||||
putExtra("query", "${LibManga.PREFIX_SLUG_SEARCH}$titleid")
|
putExtra("query", "${LibGroup.PREFIX_SLUG_SEARCH}$titleid")
|
||||||
putExtra("filter", packageName)
|
putExtra("filter", packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
startActivity(mainIntent)
|
startActivity(mainIntent)
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Log.e("LibMangaActivity", e.toString())
|
Log.e("LibUrlActivity", e.toString())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.e("LibMangaActivity", "could not parse uri from intent $intent")
|
Log.e("LibUrlActivity", "could not parse uri from intent $intent")
|
||||||
}
|
}
|
||||||
|
|
||||||
finish()
|
finish()
|
|
@ -1,13 +0,0 @@
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply plugin: 'kotlinx-serialization'
|
|
||||||
|
|
||||||
ext {
|
|
||||||
extName = 'HentaiLib'
|
|
||||||
pkgNameSuffix = 'ru.libhentai'
|
|
||||||
extClass = '.LibHentai'
|
|
||||||
extVersionCode = 19
|
|
||||||
isNsfw = true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
|
@ -1,41 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.ru.libhentai
|
|
||||||
|
|
||||||
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://hentailib.me/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 LibHentaiActivity : 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", "${LibHentai.PREFIX_SLUG_SEARCH}$titleid")
|
|
||||||
putExtra("filter", packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
startActivity(mainIntent)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Log.e("LibHentaiActivity", e.toString())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.e("LibHentaiActivity", "could not parse uri from intent $intent")
|
|
||||||
}
|
|
||||||
|
|
||||||
finish()
|
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
apply plugin: 'com.android.application'
|
|
||||||
apply plugin: 'kotlin-android'
|
|
||||||
apply plugin: 'kotlinx-serialization'
|
|
||||||
|
|
||||||
ext {
|
|
||||||
extName = 'MangaLib'
|
|
||||||
pkgNameSuffix = 'ru.libmanga'
|
|
||||||
extClass = '.LibManga'
|
|
||||||
extVersionCode = 74
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|