Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b7779ba14f | |||
| 9a291e6da4 | |||
| e7423e3715 | |||
| 0ed26dbc49 | |||
| a08e4e616d | |||
| 655126eaa2 | |||
| af070a3f0a | |||
| 2b9d564841 | |||
| d09de07a3f | |||
| 048587468d | |||
| 5dcdd3454b | |||
| 5d5678861d | |||
| 85bd12e731 | |||
| f322a7e660 | |||
| 5f7b7c652c | |||
| 214cbed3f0 | |||
| 71db4eebea | |||
| 9a577e1c69 | |||
| 9a5ea9b507 | |||
| 474eea1c84 | |||
| 43010e92ac | |||
| 38b7240728 | |||
| d52511d5ce | |||
| 06f0817bec | |||
| 2ee6d2d902 | |||
| 8df8622dfa | |||
| 58ef239959 | |||
| a126180ca3 | |||
| ae7a4744bd | |||
| 63cd8f8c07 | |||
| 2ecd2bce51 | |||
| c7ecb58c61 | |||
| 422721bb64 | |||
| 92bc9d8801 |
+3
-1
@@ -2,4 +2,6 @@
|
||||
indent_size=4
|
||||
insert_final_newline=true
|
||||
ij_kotlin_allow_trailing_comma=true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site=true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site=true
|
||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||
@@ -3,7 +3,7 @@
|
||||
I acknowledge that:
|
||||
|
||||
- I have updated:
|
||||
- To the latest version of the app (stable is v1.8.4)
|
||||
- To the latest version of the app (stable is v1.8.5)
|
||||
- All extensions
|
||||
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/
|
||||
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions
|
||||
|
||||
@@ -53,7 +53,7 @@ body:
|
||||
label: Tachiyomi version
|
||||
description: You can find your Tachiyomi version in **More → About**.
|
||||
placeholder: |
|
||||
Example: "1.8.4"
|
||||
Example: "1.8.5"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -98,7 +98,7 @@ body:
|
||||
required: true
|
||||
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
|
||||
required: true
|
||||
- label: I have updated the app to version **[1.8.4](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||
- label: I have updated the app to version **[1.8.5](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||
required: true
|
||||
- label: I have updated all installed extensions.
|
||||
required: true
|
||||
|
||||
@@ -33,7 +33,7 @@ body:
|
||||
required: true
|
||||
- label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose).
|
||||
required: true
|
||||
- label: I have updated the app to version **[1.8.4](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||
- label: I have updated the app to version **[1.8.5](https://github.com/jobobby04/tachiyomisy/releases/latest)**.
|
||||
required: true
|
||||
- label: I will fill out all of the requested information in this form.
|
||||
required: true
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
org.gradle.daemon=false
|
||||
org.gradle.jvmargs=-Xmx5120m
|
||||
org.gradle.workers.max=2
|
||||
|
||||
kotlin.incremental=false
|
||||
@@ -37,11 +37,6 @@ jobs:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Write google-services.json
|
||||
uses: DamianReeves/write-file-action@v1.0
|
||||
with:
|
||||
|
||||
@@ -33,11 +33,6 @@ jobs:
|
||||
java-version: 11
|
||||
distribution: adopt
|
||||
|
||||
- name: Copy CI gradle.properties
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
cp .github/runner-files/ci-gradle.properties ~/.gradle/gradle.properties
|
||||
|
||||
- name: Build app
|
||||
uses: gradle/gradle-command-action@v2
|
||||
with:
|
||||
|
||||
+22
-16
@@ -1,3 +1,4 @@
|
||||
import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
@@ -18,6 +19,7 @@ if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
|
||||
shortcutHelper.setFilePath("./shortcuts.xml")
|
||||
|
||||
android {
|
||||
namespace = "eu.kanade.tachiyomi"
|
||||
compileSdk = AndroidConfig.compileSdk
|
||||
ndkVersion = AndroidConfig.ndk
|
||||
|
||||
@@ -25,8 +27,8 @@ android {
|
||||
applicationId = "eu.kanade.tachiyomi.sy"
|
||||
minSdk = AndroidConfig.minSdk
|
||||
targetSdk = AndroidConfig.targetSdk
|
||||
versionCode = 35
|
||||
versionName = "1.8.4"
|
||||
versionCode = 36
|
||||
versionName = "1.8.5"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||
@@ -225,13 +227,10 @@ dependencies {
|
||||
|
||||
// Tests
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.assertj.core)
|
||||
testImplementation(libs.mockito.core)
|
||||
|
||||
testImplementation(libs.bundles.robolectric)
|
||||
|
||||
// For detecting memory leaks; see https://square.github.io/leakcanary/
|
||||
// debugImplementation(libs.leakcanary.android)
|
||||
implementation(libs.leakcanary.plumber)
|
||||
|
||||
// SY -->
|
||||
// Changelog
|
||||
@@ -258,23 +257,30 @@ dependencies {
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<Test> {
|
||||
useJUnitPlatform()
|
||||
testLogging {
|
||||
events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
|
||||
withType<KotlinCompile> {
|
||||
kotlinOptions.freeCompilerArgs += listOf(
|
||||
"-Xopt-in=kotlin.Experimental",
|
||||
"-Xopt-in=kotlin.RequiresOptIn",
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-Xopt-in=kotlinx.coroutines.FlowPreview",
|
||||
"-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||
"-Xopt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
"-Xopt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-Xopt-in=kotlin.time.ExperimentalTime",
|
||||
"-opt-in=kotlin.Experimental",
|
||||
"-opt-in=kotlin.RequiresOptIn",
|
||||
"-opt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-opt-in=kotlinx.coroutines.FlowPreview",
|
||||
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
|
||||
"-opt-in=kotlinx.coroutines.InternalCoroutinesApi",
|
||||
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
|
||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-opt-in=kotlin.time.ExperimentalTime",
|
||||
)
|
||||
}
|
||||
|
||||
// Duplicating Hebrew string assets due to some locale code issues on different devices
|
||||
val copyHebrewStrings = task("copyHebrewStrings", type = Copy::class) {
|
||||
val copyHebrewStrings by registering(Copy::class) {
|
||||
from("./src/main/res/values-he")
|
||||
into("./src/main/res/values-iw")
|
||||
include("**/*")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="eu.kanade.tachiyomi">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Internet -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
@@ -72,6 +72,7 @@ import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
import java.security.Security
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
@@ -182,6 +183,7 @@ open class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
preferences.lastAppClosed().set(Date().time)
|
||||
if (!AuthenticatorUtil.isAuthenticating && preferences.lockAppAfter().get() >= 0) {
|
||||
SecureActivityDelegate.locked = true
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateJob
|
||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
@@ -106,10 +105,9 @@ object Migrations {
|
||||
// Reset sorting preference if using removed sort by source
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
if (oldSortingMode == LibrarySort.SOURCE) {
|
||||
if (oldSortingMode == 5 /* SOURCE */) {
|
||||
prefs.edit {
|
||||
putInt(PreferenceKeys.librarySortingMode, LibrarySort.ALPHA)
|
||||
putInt(PreferenceKeys.librarySortingMode, 0 /* ALPHABETICAL */)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,16 +200,15 @@ object Migrations {
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
|
||||
val oldSortingDirection = prefs.getBoolean(PreferenceKeys.librarySortingDirection, true)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val newSortingMode = when (oldSortingMode) {
|
||||
LibrarySort.ALPHA -> SortModeSetting.ALPHABETICAL
|
||||
LibrarySort.LAST_READ -> SortModeSetting.LAST_READ
|
||||
LibrarySort.LAST_CHECKED -> SortModeSetting.LAST_CHECKED
|
||||
LibrarySort.UNREAD -> SortModeSetting.UNREAD
|
||||
LibrarySort.TOTAL -> SortModeSetting.TOTAL_CHAPTERS
|
||||
LibrarySort.LATEST_CHAPTER -> SortModeSetting.LATEST_CHAPTER
|
||||
LibrarySort.CHAPTER_FETCH_DATE -> SortModeSetting.DATE_FETCHED
|
||||
LibrarySort.DATE_ADDED -> SortModeSetting.DATE_ADDED
|
||||
0 -> SortModeSetting.ALPHABETICAL
|
||||
1 -> SortModeSetting.LAST_READ
|
||||
2 -> SortModeSetting.LAST_CHECKED
|
||||
3 -> SortModeSetting.UNREAD
|
||||
4 -> SortModeSetting.TOTAL_CHAPTERS
|
||||
6 -> SortModeSetting.LATEST_CHAPTER
|
||||
8 -> SortModeSetting.DATE_FETCHED
|
||||
7 -> SortModeSetting.DATE_ADDED
|
||||
else -> SortModeSetting.ALPHABETICAL
|
||||
}
|
||||
|
||||
|
||||
@@ -115,5 +115,14 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
db.setForeignKeyConstraintsEnabled(true)
|
||||
setPragma(db, "foreign_keys = ON")
|
||||
setPragma(db, "journal_mode = WAL")
|
||||
setPragma(db, "synchronous = NORMAL")
|
||||
}
|
||||
|
||||
private fun setPragma(db: SupportSQLiteDatabase, pragma: String) {
|
||||
val cursor = db.query("PRAGMA $pragma")
|
||||
cursor.moveToFirst()
|
||||
cursor.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,6 @@ interface Manga : SManga {
|
||||
return chapter_flags and CHAPTER_SORT_MASK == CHAPTER_SORT_DESC
|
||||
}
|
||||
|
||||
fun getGenres(): List<String>? {
|
||||
if (genre.isNullOrBlank()) return null
|
||||
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
|
||||
}
|
||||
|
||||
// SY -->
|
||||
fun getOriginalGenres(): List<String>? {
|
||||
return originalGenre?.split(", ")?.map { it.trim() }
|
||||
|
||||
@@ -275,7 +275,7 @@ class Downloader(
|
||||
|
||||
// Start downloader if needed
|
||||
if (autoStart && wasEmpty) {
|
||||
val queuedDownloads = queue.filter { it.source !is UnmeteredSource }.count()
|
||||
val queuedDownloads = queue.count { it.source !is UnmeteredSource }
|
||||
val maxDownloadsFromSource = queue
|
||||
.groupBy { it.source }
|
||||
.filterKeys { it !is UnmeteredSource }
|
||||
@@ -347,8 +347,8 @@ class Downloader(
|
||||
// Get all the URLs to the source images, fetch pages if necessary
|
||||
.flatMap { download.source.fetchAllImageUrlsFromPageList(it) }
|
||||
// Start downloading images, consider we can have downloaded images already
|
||||
// Concurrently do 5 pages at a time
|
||||
.flatMap({ page -> getOrDownloadImage(page, download, tmpDir, dataSaver) }, 5)
|
||||
// Concurrently do 2 pages at a time
|
||||
.flatMap({ page -> getOrDownloadImage(page, download, tmpDir, dataSaver).subscribeOn(Schedulers.io()) }, 2)
|
||||
.onBackpressureLatest()
|
||||
// Do when page is downloaded.
|
||||
.doOnNext { notifier.onProgressChange(download) }
|
||||
@@ -358,6 +358,7 @@ class Downloader(
|
||||
.doOnNext { ensureSuccessfulDownload(download, mangaDir, tmpDir, chapterDirname) }
|
||||
// If the page list threw, it will resume here
|
||||
.onErrorReturn { error ->
|
||||
logcat(LogPriority.ERROR, error)
|
||||
download.status = Download.State.ERROR
|
||||
notifier.onError(error.message, download.chapter.name, download.manga.title)
|
||||
download
|
||||
@@ -385,7 +386,7 @@ class Downloader(
|
||||
tmpFile?.delete()
|
||||
|
||||
// Try to find the image file.
|
||||
val imageFile = tmpDir.listFiles()!!.find { it.name!!.startsWith("$filename.") }
|
||||
val imageFile = tmpDir.listFiles()!!.find { it.name!!.startsWith("$filename.") || it.name!!.contains("${filename}__001") }
|
||||
|
||||
// If the image is already downloaded, do nothing. Otherwise download from network
|
||||
val pageObservable = when {
|
||||
@@ -395,8 +396,12 @@ class Downloader(
|
||||
}
|
||||
|
||||
return pageObservable
|
||||
// When the image is ready, set image path, progress (just in case) and status
|
||||
// When the page is ready, set page path, progress (just in case) and status
|
||||
.doOnNext { file ->
|
||||
val success = splitTallImageIfNeeded(page, tmpDir)
|
||||
if (success.not()) {
|
||||
notifier.onError(context.getString(R.string.download_notifier_split_failed), download.chapter.name, download.manga.title)
|
||||
}
|
||||
page.uri = file.uri
|
||||
page.progress = 100
|
||||
download.downloadedImages++
|
||||
@@ -407,6 +412,7 @@ class Downloader(
|
||||
.onErrorReturn {
|
||||
page.progress = 0
|
||||
page.status = Page.ERROR
|
||||
notifier.onError(it.message, download.chapter.name, download.manga.title)
|
||||
page
|
||||
}
|
||||
}
|
||||
@@ -471,7 +477,7 @@ class Downloader(
|
||||
*/
|
||||
private fun getImageExtension(response: Response, file: UniFile): String {
|
||||
// Read content type if available.
|
||||
val mime = response.body?.contentType()?.let { ct -> "${ct.type}/${ct.subtype}" }
|
||||
val mime = response.body?.contentType()?.run { if (type == "image") "image/$subtype" else null }
|
||||
// Else guess from the uri.
|
||||
?: context.contentResolver.getType(file.uri)
|
||||
// Else read magic numbers.
|
||||
@@ -480,6 +486,26 @@ class Downloader(
|
||||
return ImageUtil.getExtensionFromMimeType(mime)
|
||||
}
|
||||
|
||||
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile): Boolean {
|
||||
if (!preferences.splitTallImages().get()) return true
|
||||
|
||||
val filename = String.format("%03d", page.number)
|
||||
val imageFile = tmpDir.listFiles()?.find { it.name!!.startsWith(filename) }
|
||||
?: throw Error(context.getString(R.string.download_notifier_split_page_not_found, page.number))
|
||||
val imageFilePath = imageFile.filePath
|
||||
?: throw Error(context.getString(R.string.download_notifier_split_page_path_not_found, page.number))
|
||||
|
||||
// check if the original page was previously splitted before then skip.
|
||||
if (imageFile.name!!.contains("__")) return true
|
||||
|
||||
return try {
|
||||
ImageUtil.splitTallImage(imageFile, imageFilePath)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the download was successful.
|
||||
*
|
||||
@@ -495,16 +521,10 @@ class Downloader(
|
||||
dirname: String,
|
||||
) {
|
||||
// Ensure that the chapter folder has all the images.
|
||||
val downloadedImages = tmpDir.listFiles().orEmpty().filterNot { it.name!!.endsWith(".tmp") }
|
||||
val downloadedImages = tmpDir.listFiles().orEmpty().filterNot { it.name!!.endsWith(".tmp") || (it.name!!.contains("__") && !it.name!!.contains("__001.jpg")) }
|
||||
|
||||
download.status = if (downloadedImages.size == download.pages!!.size) {
|
||||
Download.State.DOWNLOADED
|
||||
} else {
|
||||
Download.State.ERROR
|
||||
}
|
||||
|
||||
// Only rename the directory if it's downloaded.
|
||||
if (download.status == Download.State.DOWNLOADED) {
|
||||
// Only rename the directory if it's downloaded.
|
||||
if (preferences.saveChaptersAsCBZ().get()) {
|
||||
archiveChapter(mangaDir, dirname, tmpDir)
|
||||
} else {
|
||||
@@ -513,6 +533,10 @@ class Downloader(
|
||||
cache.addChapter(dirname, mangaDir, download.manga)
|
||||
|
||||
DiskUtil.createNoMediaFile(tmpDir, context)
|
||||
|
||||
Download.State.DOWNLOADED
|
||||
} else {
|
||||
Download.State.ERROR
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
@@ -193,7 +194,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
val file = File(path)
|
||||
file.delete()
|
||||
|
||||
DiskUtil.scanMedia(context, file)
|
||||
DiskUtil.scanMedia(context, file.toUri())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,6 +65,8 @@ object PreferenceKeys {
|
||||
|
||||
const val dohProvider = "doh_provider"
|
||||
|
||||
const val defaultUserAgent = "default_user_agent"
|
||||
|
||||
const val defaultChapterFilterByRead = "default_chapter_filter_by_read"
|
||||
|
||||
const val defaultChapterFilterByDownloaded = "default_chapter_filter_by_downloaded"
|
||||
|
||||
@@ -59,7 +59,7 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun lockAppAfter() = flowPrefs.getInt("lock_app_after", 0)
|
||||
|
||||
fun lastAppUnlock() = flowPrefs.getLong("last_app_unlock", 0)
|
||||
fun lastAppClosed() = flowPrefs.getLong("last_app_closed", 0)
|
||||
|
||||
fun secureScreen() = flowPrefs.getEnum("secure_screen_v2", Values.SecureScreenMode.INCOGNITO)
|
||||
|
||||
@@ -215,6 +215,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun saveChaptersAsCBZ() = flowPrefs.getBoolean("save_chapter_as_cbz", true)
|
||||
|
||||
fun splitTallImages() = flowPrefs.getBoolean("split_tall_images", false)
|
||||
|
||||
fun folderPerManga() = prefs.getBoolean(Keys.folderPerManga, false)
|
||||
|
||||
fun numberOfBackups() = flowPrefs.getInt("backup_slots", 2)
|
||||
@@ -308,6 +310,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun dohProvider() = prefs.getInt(Keys.dohProvider, -1)
|
||||
|
||||
fun defaultUserAgent() = flowPrefs.getString(Keys.defaultUserAgent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44")
|
||||
|
||||
fun lastSearchQuerySearchSettings() = flowPrefs.getString("last_search_query", "")
|
||||
|
||||
fun filterChapterByRead() = prefs.getInt(Keys.defaultChapterFilterByRead, Manga.SHOW_ALL)
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||
@@ -82,7 +83,7 @@ class ImageSaver(
|
||||
}
|
||||
}
|
||||
|
||||
DiskUtil.scanMedia(context, destFile)
|
||||
DiskUtil.scanMedia(context, destFile.toUri())
|
||||
|
||||
return destFile.getUriCompat(context)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
@@ -256,13 +257,21 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
|
||||
.appendPath("my_list_status")
|
||||
.build()
|
||||
|
||||
fun refreshTokenRequest(refreshToken: String): Request {
|
||||
fun refreshTokenRequest(oauth: OAuth): Request {
|
||||
val formBody: RequestBody = FormBody.Builder()
|
||||
.add("client_id", clientId)
|
||||
.add("refresh_token", refreshToken)
|
||||
.add("refresh_token", oauth.refresh_token)
|
||||
.add("grant_type", "refresh_token")
|
||||
.build()
|
||||
return POST("$baseOAuthUrl/token", body = formBody)
|
||||
|
||||
// Add the Authorization header manually as this particular
|
||||
// request is called by the interceptor itself so it doesn't reach
|
||||
// the part where the token is added automatically.
|
||||
val headers = Headers.Builder()
|
||||
.add("Authorization", "Bearer ${oauth.access_token}")
|
||||
.build()
|
||||
|
||||
return POST("$baseOAuthUrl/token", body = formBody, headers = headers)
|
||||
}
|
||||
|
||||
private fun getPkceChallengeCode(): String {
|
||||
|
||||
+16
-4
@@ -1,9 +1,10 @@
|
||||
package eu.kanade.tachiyomi.data.track.myanimelist
|
||||
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import eu.kanade.tachiyomi.network.parseAs
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import okhttp3.internal.closeQuietly
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
|
||||
@@ -24,11 +25,22 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t
|
||||
}
|
||||
// Refresh access token if expired
|
||||
if (oauth != null && oauth!!.isExpired()) {
|
||||
chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!.refresh_token)).use {
|
||||
if (it.isSuccessful) {
|
||||
setAuth(json.decodeFromString(it.body!!.string()))
|
||||
val newOauth = runCatching {
|
||||
val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!))
|
||||
|
||||
if (oauthResponse.isSuccessful) {
|
||||
oauthResponse.parseAs<OAuth>()
|
||||
} else {
|
||||
oauthResponse.closeQuietly()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (newOauth.getOrNull() == null) {
|
||||
throw IOException("Failed to refresh the access token")
|
||||
}
|
||||
|
||||
setAuth(newOauth.getOrNull())
|
||||
}
|
||||
if (oauth == null) {
|
||||
throw IOException("No authentication token")
|
||||
|
||||
@@ -48,6 +48,7 @@ class AppUpdateChecker {
|
||||
when (result) {
|
||||
is AppUpdateResult.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
||||
is AppUpdateResult.NewUpdateFdroidInstallation -> AppUpdateNotifier(context).promptFdroidUpdate()
|
||||
else -> {}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
@@ -29,6 +29,7 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
fun promptUpdate(release: GithubRelease) {
|
||||
val intent = Intent(context, AppUpdateService::class.java).apply {
|
||||
putExtra(AppUpdateService.EXTRA_DOWNLOAD_URL, release.getDownloadLink())
|
||||
putExtra(AppUpdateService.EXTRA_DOWNLOAD_TITLE, release.version)
|
||||
}
|
||||
val updateIntent = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
|
||||
@@ -116,6 +117,7 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
setOnlyAlertOnce(false)
|
||||
setProgress(0, 0, false)
|
||||
setContentIntent(installIntent)
|
||||
setOngoing(true)
|
||||
|
||||
clearActions()
|
||||
addAction(
|
||||
|
||||
@@ -147,7 +147,7 @@ class AppUpdateService : Service() {
|
||||
* @param context the application context.
|
||||
* @param url the url to the new update.
|
||||
*/
|
||||
fun start(context: Context, url: String, title: String = context.getString(R.string.app_name)) {
|
||||
fun start(context: Context, url: String, title: String? = context.getString(R.string.app_name)) {
|
||||
if (!isRunning(context)) {
|
||||
val intent = Intent(context, AppUpdateService::class.java).apply {
|
||||
putExtra(EXTRA_DOWNLOAD_TITLE, title)
|
||||
|
||||
@@ -4,7 +4,5 @@ sealed class LoadResult {
|
||||
|
||||
class Success(val extension: Extension.Installed) : LoadResult()
|
||||
class Untrusted(val extension: Extension.Untrusted) : LoadResult()
|
||||
class Error(val message: String? = null) : LoadResult() {
|
||||
constructor(exception: Throwable) : this(exception.message)
|
||||
}
|
||||
object Error : LoadResult()
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ import android.content.IntentFilter
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import eu.kanade.tachiyomi.extension.model.LoadResult
|
||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import logcat.LogPriority
|
||||
|
||||
/**
|
||||
* Broadcast receiver that listens for the system's packages installed, updated or removed, and only
|
||||
@@ -52,6 +54,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
is LoadResult.Success -> listener.onExtensionInstalled(result.extension)
|
||||
is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,8 +63,8 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
when (val result = getExtensionFromIntent(context, intent)) {
|
||||
is LoadResult.Success -> listener.onExtensionUpdated(result.extension)
|
||||
// Not needed as a package can't be upgraded if the signature is different
|
||||
is LoadResult.Untrusted -> {
|
||||
}
|
||||
is LoadResult.Untrusted -> {}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +96,10 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
*/
|
||||
private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult {
|
||||
val pkgName = getPackageNameFromIntent(intent)
|
||||
?: return LoadResult.Error("Package name not found")
|
||||
if (pkgName == null) {
|
||||
logcat(LogPriority.WARN) { "Package name not found" }
|
||||
return LoadResult.Error
|
||||
}
|
||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
||||
}
|
||||
|
||||
|
||||
@@ -80,10 +80,12 @@ internal object ExtensionLoader {
|
||||
context.packageManager.getPackageInfo(pkgName, PACKAGE_FLAGS)
|
||||
} catch (error: PackageManager.NameNotFoundException) {
|
||||
// Unlikely, but the package may have been uninstalled at this point
|
||||
return LoadResult.Error(error)
|
||||
logcat(LogPriority.ERROR, error)
|
||||
return LoadResult.Error
|
||||
}
|
||||
if (!isPackageAnExtension(pkgInfo)) {
|
||||
return LoadResult.Error("Tried to load a package that wasn't a extension")
|
||||
logcat(LogPriority.WARN) { "Tried to load a package that wasn't a extension ($pkgName)" }
|
||||
return LoadResult.Error
|
||||
}
|
||||
return loadExtension(context, pkgName, pkgInfo)
|
||||
}
|
||||
@@ -102,7 +104,8 @@ internal object ExtensionLoader {
|
||||
pkgManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
|
||||
} catch (error: PackageManager.NameNotFoundException) {
|
||||
// Unlikely, but the package may have been uninstalled at this point
|
||||
return LoadResult.Error(error)
|
||||
logcat(LogPriority.ERROR, error)
|
||||
return LoadResult.Error
|
||||
}
|
||||
|
||||
val extName = pkgManager.getApplicationLabel(appInfo).toString().substringAfter("Tachiyomi: ")
|
||||
@@ -112,7 +115,7 @@ internal object ExtensionLoader {
|
||||
if (versionName.isNullOrEmpty()) {
|
||||
val exception = Exception("Missing versionName for extension $extName")
|
||||
logcat(LogPriority.WARN, exception)
|
||||
return LoadResult.Error(exception)
|
||||
return LoadResult.Error
|
||||
}
|
||||
|
||||
// Validate lib version
|
||||
@@ -123,13 +126,14 @@ internal object ExtensionLoader {
|
||||
"$LIB_VERSION_MIN to $LIB_VERSION_MAX are allowed",
|
||||
)
|
||||
logcat(LogPriority.WARN, exception)
|
||||
return LoadResult.Error(exception)
|
||||
return LoadResult.Error
|
||||
}
|
||||
|
||||
val signatureHash = getSignatureHash(pkgInfo)
|
||||
|
||||
if (signatureHash == null) {
|
||||
return LoadResult.Error("Package $pkgName isn't signed")
|
||||
logcat(LogPriority.WARN) { "Package $pkgName isn't signed" }
|
||||
return LoadResult.Error
|
||||
} else if (signatureHash !in trustedSignatures) {
|
||||
val extension = Extension.Untrusted(extName, pkgName, versionName, versionCode, signatureHash)
|
||||
logcat(LogPriority.WARN) { "Extension $pkgName isn't trusted" }
|
||||
@@ -138,7 +142,8 @@ internal object ExtensionLoader {
|
||||
|
||||
val isNsfw = appInfo.metaData.getInt(METADATA_NSFW) == 1
|
||||
if (!loadNsfwSource && isNsfw) {
|
||||
return LoadResult.Error("NSFW extension $pkgName not allowed")
|
||||
logcat(LogPriority.WARN) { "NSFW extension $pkgName not allowed" }
|
||||
return LoadResult.Error
|
||||
}
|
||||
|
||||
val hasReadme = appInfo.metaData.getInt(METADATA_HAS_README, 0) == 1
|
||||
@@ -165,7 +170,7 @@ internal object ExtensionLoader {
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($it)" }
|
||||
return LoadResult.Error(e)
|
||||
return LoadResult.Error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,4 +64,8 @@ open /* SY <-- */ class NetworkHelper(context: Context) {
|
||||
.addInterceptor(CloudflareInterceptor(context))
|
||||
.build()
|
||||
}
|
||||
|
||||
val defaultUserAgent by lazy {
|
||||
preferences.defaultUserAgent().get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ class ProgressResponseBody(private val responseBody: ResponseBody, private val p
|
||||
source(responseBody.source()).buffer()
|
||||
}
|
||||
|
||||
override fun contentType(): MediaType {
|
||||
return responseBody.contentType()!!
|
||||
override fun contentType(): MediaType? {
|
||||
return responseBody.contentType()
|
||||
}
|
||||
|
||||
override fun contentLength(): Long {
|
||||
|
||||
@@ -9,7 +9,6 @@ import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.WebViewClientCompat
|
||||
@@ -109,7 +108,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
|
||||
|
||||
// Avoid sending empty User-Agent, Chromium WebView will reset to default if empty
|
||||
webview.settings.userAgentString = request.header("User-Agent")
|
||||
?: HttpSource.DEFAULT_USER_AGENT
|
||||
?: networkHelper.defaultUserAgent
|
||||
|
||||
webview.webViewClient = object : WebViewClientCompat() {
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package eu.kanade.tachiyomi.network.interceptor
|
||||
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class UserAgentInterceptor : Interceptor {
|
||||
|
||||
private val networkHelper: NetworkHelper by injectLazy()
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val originalRequest = chain.request()
|
||||
|
||||
@@ -12,7 +16,7 @@ class UserAgentInterceptor : Interceptor {
|
||||
val newRequest = originalRequest
|
||||
.newBuilder()
|
||||
.removeHeader("User-Agent")
|
||||
.addHeader("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
||||
.addHeader("User-Agent", networkHelper.defaultUserAgent)
|
||||
.build()
|
||||
chain.proceed(newRequest)
|
||||
} else {
|
||||
|
||||
@@ -20,11 +20,13 @@ import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.EpubFile
|
||||
import eu.kanade.tachiyomi.util.system.ImageUtil
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import kotlinx.serialization.json.encodeToStream
|
||||
import logcat.LogPriority
|
||||
import rx.Observable
|
||||
import tachiyomi.source.model.ChapterInfo
|
||||
import tachiyomi.source.model.MangaInfo
|
||||
@@ -286,41 +288,46 @@ class LocalSource(
|
||||
}
|
||||
|
||||
private fun updateCover(chapter: SChapter, manga: SManga): File? {
|
||||
return when (val format = getFormat(chapter)) {
|
||||
is Format.Directory -> {
|
||||
val entry = format.file.listFiles()
|
||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
return try {
|
||||
when (val format = getFormat(chapter)) {
|
||||
is Format.Directory -> {
|
||||
val entry = format.file.listFiles()
|
||||
?.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
?.find { !it.isDirectory && ImageUtil.isImage(it.name) { FileInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, it.inputStream()) }
|
||||
}
|
||||
is Format.Zip -> {
|
||||
ZipFile(format.file).use { zip ->
|
||||
val entry = zip.entries().toList()
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
entry?.let { updateCover(context, manga, it.inputStream()) }
|
||||
}
|
||||
is Format.Zip -> {
|
||||
ZipFile(format.file).use { zip ->
|
||||
val entry = zip.entries().toList()
|
||||
.sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
|
||||
}
|
||||
}
|
||||
is Format.Rar -> {
|
||||
Archive(format.file).use { archive ->
|
||||
val entry = archive.fileHeaders
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
|
||||
}
|
||||
}
|
||||
is Format.Epub -> {
|
||||
EpubFile(format.file).use { epub ->
|
||||
val entry = epub.getImagesFromPages()
|
||||
.firstOrNull()
|
||||
?.let { epub.getEntry(it) }
|
||||
|
||||
entry?.let { updateCover(context, manga, epub.getInputStream(it)) }
|
||||
entry?.let { updateCover(context, manga, zip.getInputStream(it)) }
|
||||
}
|
||||
}
|
||||
is Format.Rar -> {
|
||||
Archive(format.file).use { archive ->
|
||||
val entry = archive.fileHeaders
|
||||
.sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) }
|
||||
.find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } }
|
||||
|
||||
entry?.let { updateCover(context, manga, archive.getInputStream(it)) }
|
||||
}
|
||||
}
|
||||
is Format.Epub -> {
|
||||
EpubFile(format.file).use { epub ->
|
||||
val entry = epub.getImagesFromPages()
|
||||
.firstOrNull()
|
||||
?.let { epub.getEntry(it) }
|
||||
|
||||
entry?.let { updateCover(context, manga, epub.getInputStream(it)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e) { "Error updating cover for ${manga.title}" }
|
||||
null
|
||||
}
|
||||
.also { coverCache.clearMemoryCache() }
|
||||
}
|
||||
@@ -398,7 +405,6 @@ class LocalSource(
|
||||
}
|
||||
}
|
||||
|
||||
// Create a .nomedia file
|
||||
DiskUtil.createNoMediaFile(UniFile.fromFile(mangaDir), context)
|
||||
|
||||
manga.thumbnail_url = coverFile.absolutePath
|
||||
|
||||
@@ -253,7 +253,7 @@ open class SourceManager(private val context: Context) {
|
||||
),
|
||||
).associateBy { it.originalSourceQualifiedClassName }
|
||||
|
||||
val currentDelegatedSources = ListenMutableMap(mutableMapOf<Long, DelegatedSource>(), ::handleSourceLibrary)
|
||||
val currentDelegatedSources: MutableMap<Long, DelegatedSource> = ListenMutableMap(mutableMapOf(), ::handleSourceLibrary)
|
||||
|
||||
data class DelegatedSource(
|
||||
val sourceName: String,
|
||||
@@ -264,19 +264,10 @@ open class SourceManager(private val context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
class ListenMutableMap<K, V>(private val internalMap: MutableMap<K, V>, val listener: () -> Unit) : MutableMap<K, V> {
|
||||
override val size: Int
|
||||
get() = internalMap.size
|
||||
override fun containsKey(key: K): Boolean = internalMap.containsKey(key)
|
||||
override fun containsValue(value: V): Boolean = internalMap.containsValue(value)
|
||||
override fun get(key: K): V? = internalMap[key]
|
||||
override fun isEmpty(): Boolean = internalMap.isEmpty()
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
|
||||
get() = internalMap.entries
|
||||
override val keys: MutableSet<K>
|
||||
get() = internalMap.keys
|
||||
override val values: MutableCollection<V>
|
||||
get() = internalMap.values
|
||||
private class ListenMutableMap<K, V>(
|
||||
private val internalMap: MutableMap<K, V>,
|
||||
private val listener: () -> Unit,
|
||||
) : MutableMap<K, V> by internalMap {
|
||||
override fun clear() {
|
||||
val clearResult = internalMap.clear()
|
||||
listener()
|
||||
|
||||
@@ -28,6 +28,11 @@ interface SManga : Serializable {
|
||||
|
||||
var initialized: Boolean
|
||||
|
||||
fun getGenres(): List<String>? {
|
||||
if (genre.isNullOrBlank()) return null
|
||||
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
|
||||
}
|
||||
|
||||
// SY -->
|
||||
val originalTitle: String
|
||||
get() = (this as? MangaImpl)?.ogTitle ?: title
|
||||
@@ -104,7 +109,7 @@ fun SManga.toMangaInfo(): MangaInfo {
|
||||
artist = this.artist ?: "",
|
||||
author = this.author ?: "",
|
||||
description = this.description ?: "",
|
||||
genres = this.genre?.split(", ") ?: emptyList(),
|
||||
genres = this.getGenres() ?: emptyList(),
|
||||
status = this.status,
|
||||
cover = this.thumbnail_url ?: "",
|
||||
)
|
||||
|
||||
@@ -99,7 +99,7 @@ abstract class HttpSource : CatalogueSource {
|
||||
* Headers builder for requests. Implementations can override this method for custom headers.
|
||||
*/
|
||||
protected open fun headersBuilder() = Headers.Builder().apply {
|
||||
add("User-Agent", DEFAULT_USER_AGENT)
|
||||
add("User-Agent", network.defaultUserAgent)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,8 +417,4 @@ abstract class HttpSource : CatalogueSource {
|
||||
this.delegate = delegate
|
||||
}
|
||||
// EXH <--
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.124 Safari/537.36 Edg/102.0.1245.44"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,8 +835,8 @@ class EHentai(
|
||||
)
|
||||
},
|
||||
AutoCompleteTags(
|
||||
EHTags.getNamespaces0Tags().map { "$it:" } + EHTags.getAllTags(),
|
||||
EHTags.getNamespaces0Tags().map { "$it:" },
|
||||
EHTags.getNamespaces().map { "$it:" } + EHTags.getAllTags(),
|
||||
EHTags.getNamespaces().map { "$it:" },
|
||||
excludePrefix,
|
||||
),
|
||||
if (preferences.exhWatchedListDefaultState().get()) {
|
||||
|
||||
@@ -121,16 +121,16 @@ class MangaDex(delegate: HttpSource, val context: Context) :
|
||||
MangaPlusHandler(network.client)
|
||||
}
|
||||
private val comikeyHandler by lazy {
|
||||
ComikeyHandler(network.cloudflareClient)
|
||||
ComikeyHandler(network.cloudflareClient, network.defaultUserAgent)
|
||||
}
|
||||
private val bilibiliHandler by lazy {
|
||||
BilibiliHandler(network.cloudflareClient)
|
||||
}
|
||||
private val azukHandler by lazy {
|
||||
AzukiHandler(network.client)
|
||||
AzukiHandler(network.client, network.defaultUserAgent)
|
||||
}
|
||||
private val mangaHotHandler by lazy {
|
||||
MangaHotHandler(network.client)
|
||||
MangaHotHandler(network.client, network.defaultUserAgent)
|
||||
}
|
||||
private val pageHandler by lazy {
|
||||
PageHandler(
|
||||
|
||||
@@ -98,7 +98,7 @@ abstract class DialogController : Controller {
|
||||
/**
|
||||
* Dismiss the dialog and pop this controller
|
||||
*/
|
||||
private fun dismissDialog() {
|
||||
protected fun dismissDialog() {
|
||||
if (dismissed) {
|
||||
return
|
||||
}
|
||||
|
||||
+11
-10
@@ -59,16 +59,17 @@ abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*
|
||||
val searchAutoComplete: SearchView.SearchAutoComplete = searchView.findViewById(
|
||||
R.id.search_src_text,
|
||||
)
|
||||
searchAutoComplete.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
searchAutoComplete.addTextChangedListener(
|
||||
object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
editable.getSpans(0, editable.length, CharacterStyle::class.java)
|
||||
.forEach { editable.removeSpan(it) }
|
||||
}
|
||||
},
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
editable.getSpans(0, editable.length, CharacterStyle::class.java)
|
||||
.forEach { editable.removeSpan(it) }
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
searchView.queryTextEvents()
|
||||
@@ -134,12 +135,12 @@ abstract class SearchableNucleusController<VB : ViewBinding, P : BasePresenter<*
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
onSearchMenuItemActionExpand(item)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
val localSearchView = searchItem.actionView as SearchView
|
||||
|
||||
// if it is blank the flow event won't trigger so we would stay in a COLLAPSING state
|
||||
|
||||
@@ -119,6 +119,6 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
|
||||
// SY <--
|
||||
|
||||
return preferences.lockAppAfter().get() <= 0 ||
|
||||
Date().time >= preferences.lastAppUnlock().get() + 60 * 1000 * preferences.lockAppAfter().get()
|
||||
Date().time >= preferences.lastAppClosed().get() + 60 * 1000 * preferences.lockAppAfter().get()
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -247,9 +247,13 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
|
||||
}
|
||||
|
||||
private fun createUrl(url: String, pkgName: String, pkgFactory: String?, path: String = ""): String {
|
||||
return when {
|
||||
!pkgFactory.isNullOrEmpty() -> "$url/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory$path"
|
||||
else -> "$url/src/${pkgName.replace(".", "/")}$path"
|
||||
return if (!pkgFactory.isNullOrEmpty()) {
|
||||
when (path.isEmpty()) {
|
||||
true -> "$url/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory"
|
||||
else -> "$url/multisrc/overrides/$pkgFactory/" + (pkgName.split(".").lastOrNull() ?: "") + path
|
||||
}
|
||||
} else {
|
||||
url + "/src/" + pkgName.replace(".", "/") + path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -474,8 +474,8 @@ class MigrationListController(bundle: Bundle? = null) :
|
||||
}
|
||||
|
||||
private fun MenuItem.setIconTint(enabled: Boolean, color: Int) {
|
||||
icon.mutate()
|
||||
icon.setTint(color)
|
||||
icon?.mutate()
|
||||
icon?.setTint(color)
|
||||
isEnabled = enabled
|
||||
}
|
||||
|
||||
|
||||
+52
-38
@@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.AddDuplicateMangaDialog
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
@@ -483,19 +484,20 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
* @param genreName the name of the genre
|
||||
*/
|
||||
fun searchWithGenre(genreName: String) {
|
||||
presenter.sourceFilters = presenter.source.getFilterList()
|
||||
val defaultFilters = presenter.source.getFilterList()
|
||||
|
||||
var filterList: FilterList? = null
|
||||
var genreExists = false
|
||||
|
||||
filter@ for (sourceFilter in presenter.sourceFilters) {
|
||||
filter@ for (sourceFilter in defaultFilters) {
|
||||
if (sourceFilter is Filter.Group<*>) {
|
||||
for (filter in sourceFilter.state) {
|
||||
if (filter is Filter<*> && filter.name.equals(genreName, true)) {
|
||||
when (filter) {
|
||||
is Filter.TriState -> filter.state = 1
|
||||
is Filter.CheckBox -> filter.state = true
|
||||
else -> {}
|
||||
}
|
||||
filterList = presenter.sourceFilters
|
||||
genreExists = true
|
||||
break@filter
|
||||
}
|
||||
}
|
||||
@@ -505,19 +507,20 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
|
||||
if (index != -1) {
|
||||
sourceFilter.state = index
|
||||
filterList = presenter.sourceFilters
|
||||
genreExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filterList != null) {
|
||||
if (genreExists) {
|
||||
presenter.sourceFilters = defaultFilters
|
||||
filterSheet?.setFilters(presenter.filterItems)
|
||||
|
||||
showProgressBar()
|
||||
|
||||
adapter?.clear()
|
||||
presenter.restartPager("", filterList)
|
||||
presenter.restartPager("", defaultFilters)
|
||||
} else {
|
||||
searchWithQuery(genreName)
|
||||
}
|
||||
@@ -740,6 +743,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
override fun onItemLongClick(position: Int) {
|
||||
val activity = activity ?: return
|
||||
val manga = (adapter?.getItem(position) as? SourceItem?)?.manga ?: return
|
||||
val duplicateManga = presenter.getDuplicateLibraryManga(manga)
|
||||
|
||||
if (manga.favorite) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
@@ -755,43 +759,53 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
if (duplicateManga != null) {
|
||||
AddDuplicateMangaDialog(this, duplicateManga) { addToLibrary(manga, position) }
|
||||
.showDialog(router)
|
||||
} else {
|
||||
addToLibrary(manga, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
// Default category set
|
||||
defaultCategory != null -> {
|
||||
presenter.moveMangaToCategory(manga, defaultCategory)
|
||||
private fun addToLibrary(newManga: Manga, position: Int) {
|
||||
val activity = activity ?: return
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_added_library))
|
||||
}
|
||||
when {
|
||||
// Default category set
|
||||
defaultCategory != null -> {
|
||||
presenter.moveMangaToCategory(newManga, defaultCategory)
|
||||
|
||||
// Automatic 'Default' or no categories
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> {
|
||||
presenter.moveMangaToCategory(manga, null)
|
||||
presenter.changeMangaFavorite(newManga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_added_library))
|
||||
}
|
||||
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_added_library))
|
||||
}
|
||||
// Automatic 'Default' or no categories
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> {
|
||||
presenter.moveMangaToCategory(newManga, null)
|
||||
|
||||
// Choose a category
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
val preselected = categories.map {
|
||||
if (it.id in ids) {
|
||||
QuadStateTextView.State.CHECKED.ordinal
|
||||
} else {
|
||||
QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
}.toTypedArray()
|
||||
presenter.changeMangaFavorite(newManga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_added_library))
|
||||
}
|
||||
|
||||
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
|
||||
.showDialog(router)
|
||||
}
|
||||
// Choose a category
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds(newManga)
|
||||
val preselected = categories.map {
|
||||
if (it.id in ids) {
|
||||
QuadStateTextView.State.CHECKED.ordinal
|
||||
} else {
|
||||
QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(this, listOf(newManga), categories, preselected)
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,6 +435,10 @@ open class BrowseSourcePresenter(
|
||||
return db.getCategories().executeAsBlocking()
|
||||
}
|
||||
|
||||
fun getDuplicateLibraryManga(manga: Manga): Manga? {
|
||||
return db.getDuplicateLibraryManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
|
||||
*
|
||||
|
||||
@@ -28,8 +28,7 @@ class DownloadHeaderHolder(view: View, adapter: FlexibleAdapter<*>) : Expandable
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
binding.container.isDragged = false
|
||||
mAdapter as DownloadAdapter
|
||||
mAdapter.expandAll()
|
||||
mAdapter.downloadItemListener.onItemReleased(position)
|
||||
(mAdapter as DownloadAdapter).downloadItemListener.onItemReleased(position)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ class LibraryController(
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
createOptionsMenu(menu, inflater, R.menu.library, R.id.action_search)
|
||||
// Mutate the filter icon because it needs to be tinted and the resource is shared.
|
||||
menu.findItem(R.id.action_filter).icon.mutate()
|
||||
menu.findItem(R.id.action_filter).icon?.mutate()
|
||||
|
||||
// SY -->
|
||||
menu.findItem(R.id.action_sync_favorites).isVisible = preferences.isHentaiEnabled().get()
|
||||
@@ -472,7 +472,7 @@ class LibraryController(
|
||||
// Tint icon if there's a filter active
|
||||
if (settingsSheet.filters.hasActiveFilters()) {
|
||||
val filterColor = activity!!.getResourceColor(R.attr.colorFilterActive)
|
||||
filterItem.icon.setTint(filterColor)
|
||||
filterItem.icon?.setTint(filterColor)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -451,6 +451,7 @@ class LibrarySettingsSheet(
|
||||
unreadBadge -> preferences.unreadBadge().set((item.checked))
|
||||
localBadge -> preferences.localBadge().set((item.checked))
|
||||
languageBadge -> preferences.languageBadge().set((item.checked))
|
||||
else -> {}
|
||||
}
|
||||
adapter.notifyItemChanged(item)
|
||||
}
|
||||
@@ -473,6 +474,7 @@ class LibrarySettingsSheet(
|
||||
item.checked = !item.checked
|
||||
when (item) {
|
||||
startReadingButton -> preferences.startReadingButton().set((item.checked))
|
||||
else -> Unit
|
||||
}
|
||||
adapter.notifyItemChanged(item)
|
||||
}
|
||||
@@ -498,6 +500,7 @@ class LibrarySettingsSheet(
|
||||
when (item) {
|
||||
showTabs -> preferences.categoryTabs().set(item.checked)
|
||||
showNumberOfItems -> preferences.categoryNumberOfItems().set(item.checked)
|
||||
else -> {}
|
||||
}
|
||||
adapter.notifyItemChanged(item)
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
@Deprecated("Deprecated in favor for SortModeSetting")
|
||||
object LibrarySort {
|
||||
|
||||
const val ALPHA = 0
|
||||
const val LAST_READ = 1
|
||||
const val LAST_CHECKED = 2
|
||||
const val UNREAD = 3
|
||||
const val TOTAL = 4
|
||||
const val LATEST_CHAPTER = 6
|
||||
const val CHAPTER_FETCH_DATE = 10
|
||||
const val DATE_ADDED = 8
|
||||
|
||||
// SY -->
|
||||
const val DRAG_AND_DROP = 7
|
||||
const val TAG_LIST = 9
|
||||
|
||||
// SY <--
|
||||
|
||||
@Deprecated("Removed in favor of searching by source")
|
||||
const val SOURCE = 5
|
||||
}
|
||||
@@ -520,7 +520,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
// Binding sometimes isn't actually instantiated yet somehow
|
||||
nav?.setOnItemSelectedListener(null)
|
||||
binding?.toolbar.setNavigationOnClickListener(null)
|
||||
binding?.toolbar?.setNavigationOnClickListener(null)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package eu.kanade.tachiyomi.ui.manga
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class AddDuplicateMangaDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
private lateinit var libraryManga: Manga
|
||||
private lateinit var onAddToLibrary: () -> Unit
|
||||
|
||||
constructor(
|
||||
target: Controller,
|
||||
libraryManga: Manga,
|
||||
onAddToLibrary: () -> Unit,
|
||||
) : this() {
|
||||
targetController = target
|
||||
|
||||
this.libraryManga = libraryManga
|
||||
this.onAddToLibrary = onAddToLibrary
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val source = sourceManager.getOrStub(libraryManga.source)
|
||||
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setMessage(activity?.getString(R.string.confirm_manga_add_duplicate, source.name))
|
||||
.setPositiveButton(activity?.getString(R.string.action_add)) { _, _ ->
|
||||
onAddToLibrary()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setNeutralButton(activity?.getString(R.string.action_show_manga)) { _, _ ->
|
||||
dismissDialog()
|
||||
router.pushController(MangaController(libraryManga.id!!).withFadeTransaction())
|
||||
}
|
||||
.setCancelable(true)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
@@ -673,18 +673,8 @@ class MangaController :
|
||||
|
||||
private fun showAddDuplicateDialog(newManga: Manga, libraryManga: Manga) {
|
||||
activity?.let {
|
||||
val source = sourceManager.getOrStub(libraryManga.source)
|
||||
MaterialAlertDialogBuilder(it).apply {
|
||||
setMessage(activity?.getString(R.string.confirm_manga_add_duplicate, source.name))
|
||||
setPositiveButton(activity?.getString(R.string.action_add)) { _, _ ->
|
||||
addToLibrary(newManga)
|
||||
}
|
||||
setNegativeButton(activity?.getString(R.string.action_cancel)) { _, _ -> }
|
||||
setNeutralButton(activity?.getString(R.string.action_show_manga)) { _, _ ->
|
||||
router.pushController(MangaController(libraryManga).withFadeTransaction())
|
||||
}
|
||||
setCancelable(true)
|
||||
}.create().show()
|
||||
AddDuplicateMangaDialog(this, libraryManga) { addToLibrary(newManga) }
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,7 @@ class ChaptersSettingsSheet(
|
||||
downloaded -> presenter.setDownloadedFilter(newState)
|
||||
unread -> presenter.setUnreadFilter(newState)
|
||||
bookmarked -> presenter.setBookmarkedFilter(newState)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
initModels()
|
||||
|
||||
@@ -19,6 +19,7 @@ class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundl
|
||||
constructor(update: AppUpdateResult.NewUpdate) : this(
|
||||
bundleOf(
|
||||
BODY_KEY to update.release.info,
|
||||
VERSION_KEY to update.release.version,
|
||||
RELEASE_URL_KEY to update.release.releaseLink,
|
||||
DOWNLOAD_URL_KEY to update.release.getDownloadLink(),
|
||||
),
|
||||
@@ -36,7 +37,8 @@ class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundl
|
||||
applicationContext?.let { context ->
|
||||
// Start download
|
||||
val url = args.getString(DOWNLOAD_URL_KEY)!!
|
||||
AppUpdateService.start(context, url)
|
||||
val version = args.getString(VERSION_KEY)
|
||||
AppUpdateService.start(context, url, version)
|
||||
}
|
||||
}
|
||||
.setNeutralButton(R.string.update_check_open) { _, _ ->
|
||||
@@ -55,5 +57,6 @@ class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundl
|
||||
}
|
||||
|
||||
private const val BODY_KEY = "NewUpdateDialogController.body"
|
||||
private const val VERSION_KEY = "NewUpdateDialogController.version"
|
||||
private const val RELEASE_URL_KEY = "NewUpdateDialogController.release_url"
|
||||
private const val DOWNLOAD_URL_KEY = "NewUpdateDialogController.download_url"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.loader
|
||||
|
||||
import android.content.Context
|
||||
import com.github.junrar.exception.UnsupportedRarV5Exception
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
@@ -116,7 +117,11 @@ class ChapterLoader(
|
||||
when (format) {
|
||||
is LocalSource.Format.Directory -> DirectoryPageLoader(format.file)
|
||||
is LocalSource.Format.Zip -> ZipPageLoader(format.file)
|
||||
is LocalSource.Format.Rar -> RarPageLoader(format.file)
|
||||
is LocalSource.Format.Rar -> try {
|
||||
RarPageLoader(format.file)
|
||||
} catch (e: UnsupportedRarV5Exception) {
|
||||
error(context.getString(R.string.loader_rar5_error))
|
||||
}
|
||||
is LocalSource.Format.Epub -> EpubPageLoader(format.file)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ data class ReaderChapter(val chapter: Chapter) {
|
||||
var state: State =
|
||||
State.Wait
|
||||
set(value) {
|
||||
field = value
|
||||
stateRelay.call(value)
|
||||
}
|
||||
field = value
|
||||
stateRelay.call(value)
|
||||
}
|
||||
|
||||
private val stateRelay by lazy { BehaviorRelay.create(state) }
|
||||
|
||||
|
||||
@@ -34,27 +34,28 @@ class ReaderSettingsSheet(
|
||||
behavior.halfExpandedRatio = 0.25f
|
||||
|
||||
val filterTabIndex = getTabViews().indexOf(colorFilterSettings)
|
||||
binding.tabs.addOnTabSelectedListener(object : SimpleTabSelectedListener() {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
val isFilterTab = tab?.position == filterTabIndex
|
||||
binding.tabs.addOnTabSelectedListener(
|
||||
object : SimpleTabSelectedListener() {
|
||||
override fun onTabSelected(tab: TabLayout.Tab?) {
|
||||
val isFilterTab = tab?.position == filterTabIndex
|
||||
|
||||
// Remove dimmed backdrop so color filter changes can be previewed
|
||||
backgroundDimAnimator.run {
|
||||
if (isFilterTab) {
|
||||
if (animatedFraction < 1f) {
|
||||
start()
|
||||
// Remove dimmed backdrop so color filter changes can be previewed
|
||||
backgroundDimAnimator.run {
|
||||
if (isFilterTab) {
|
||||
if (animatedFraction < 1f) {
|
||||
start()
|
||||
}
|
||||
} else if (animatedFraction > 0f) {
|
||||
reverse()
|
||||
}
|
||||
} else if (animatedFraction > 0f) {
|
||||
reverse()
|
||||
}
|
||||
|
||||
// Hide toolbars
|
||||
if (activity.menuVisible != !isFilterTab) {
|
||||
activity.setMenuVisibility(!isFilterTab)
|
||||
}
|
||||
}
|
||||
|
||||
// Hide toolbars
|
||||
if (activity.menuVisible != !isFilterTab) {
|
||||
activity.setMenuVisibility(!isFilterTab)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if (showColorFilterSettings) {
|
||||
|
||||
@@ -249,6 +249,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||
ZoomStartPosition.LEFT -> setScaleAndCenter(scale, PointF(0F, 0F))
|
||||
ZoomStartPosition.RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0F))
|
||||
ZoomStartPosition.CENTER -> setScaleAndCenter(scale, center.also { it?.y = 0F })
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +311,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
this@ReaderPageImageView.onViewClicked()
|
||||
return super.onSingleTapConfirmed(e)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ImageSpan
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.core.view.isVisible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
LinearLayout(context, attrs) {
|
||||
@@ -21,10 +30,11 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
||||
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
|
||||
fun bind(transition: ChapterTransition) {
|
||||
fun bind(transition: ChapterTransition, downloadManager: DownloadManager, manga: Manga?) {
|
||||
manga ?: return
|
||||
when (transition) {
|
||||
is ChapterTransition.Prev -> bindPrevChapterTransition(transition)
|
||||
is ChapterTransition.Next -> bindNextChapterTransition(transition)
|
||||
is ChapterTransition.Prev -> bindPrevChapterTransition(transition, downloadManager, manga)
|
||||
is ChapterTransition.Next -> bindNextChapterTransition(transition, downloadManager, manga)
|
||||
}
|
||||
missingChapterWarning(transition)
|
||||
}
|
||||
@@ -32,20 +42,30 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
||||
/**
|
||||
* Binds a previous chapter transition on this view and subscribes to the page load status.
|
||||
*/
|
||||
private fun bindPrevChapterTransition(transition: ChapterTransition) {
|
||||
val prevChapter = transition.to
|
||||
private fun bindPrevChapterTransition(
|
||||
transition: ChapterTransition,
|
||||
downloadManager: DownloadManager,
|
||||
manga: Manga,
|
||||
) {
|
||||
val prevChapter = transition.to?.chapter
|
||||
|
||||
val hasPrevChapter = prevChapter != null
|
||||
binding.lowerText.isVisible = hasPrevChapter
|
||||
if (hasPrevChapter) {
|
||||
binding.lowerText.isVisible = prevChapter != null
|
||||
if (prevChapter != null) {
|
||||
binding.upperText.textAlignment = TEXT_ALIGNMENT_TEXT_START
|
||||
val isPrevDownloaded = downloadManager.isChapterDownloaded(
|
||||
prevChapter,
|
||||
manga,
|
||||
)
|
||||
val isCurrentDownloaded = transition.from.pageLoader is DownloadPageLoader
|
||||
binding.upperText.text = buildSpannedString {
|
||||
bold { append(context.getString(R.string.transition_previous)) }
|
||||
append("\n${prevChapter!!.chapter.name}")
|
||||
append("\n${prevChapter.name}")
|
||||
if (isPrevDownloaded) addDLImageSpan()
|
||||
}
|
||||
binding.lowerText.text = buildSpannedString {
|
||||
bold { append(context.getString(R.string.transition_current)) }
|
||||
append("\n${transition.from.chapter.name}")
|
||||
if (isCurrentDownloaded) addDLImageSpan()
|
||||
}
|
||||
} else {
|
||||
binding.upperText.textAlignment = TEXT_ALIGNMENT_CENTER
|
||||
@@ -56,20 +76,30 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
||||
/**
|
||||
* Binds a next chapter transition on this view and subscribes to the load status.
|
||||
*/
|
||||
private fun bindNextChapterTransition(transition: ChapterTransition) {
|
||||
val nextChapter = transition.to
|
||||
private fun bindNextChapterTransition(
|
||||
transition: ChapterTransition,
|
||||
downloadManager: DownloadManager,
|
||||
manga: Manga,
|
||||
) {
|
||||
val nextChapter = transition.to?.chapter
|
||||
|
||||
val hasNextChapter = nextChapter != null
|
||||
binding.lowerText.isVisible = hasNextChapter
|
||||
if (hasNextChapter) {
|
||||
binding.lowerText.isVisible = nextChapter != null
|
||||
if (nextChapter != null) {
|
||||
binding.upperText.textAlignment = TEXT_ALIGNMENT_TEXT_START
|
||||
val isCurrentDownloaded = transition.from.pageLoader is DownloadPageLoader
|
||||
val isNextDownloaded = downloadManager.isChapterDownloaded(
|
||||
nextChapter,
|
||||
manga,
|
||||
)
|
||||
binding.upperText.text = buildSpannedString {
|
||||
bold { append(context.getString(R.string.transition_finished)) }
|
||||
append("\n${transition.from.chapter.name}")
|
||||
if (isCurrentDownloaded) addDLImageSpan()
|
||||
}
|
||||
binding.lowerText.text = buildSpannedString {
|
||||
bold { append(context.getString(R.string.transition_next)) }
|
||||
append("\n${nextChapter!!.chapter.name}")
|
||||
append("\n${nextChapter.name}")
|
||||
if (isNextDownloaded) addDLImageSpan()
|
||||
}
|
||||
} else {
|
||||
binding.upperText.textAlignment = TEXT_ALIGNMENT_CENTER
|
||||
@@ -77,6 +107,17 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
|
||||
}
|
||||
}
|
||||
|
||||
private fun SpannableStringBuilder.addDLImageSpan() {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.ic_offline_pin_24dp)?.mutate()
|
||||
?.apply {
|
||||
val size = binding.lowerText.textSize + 4.dpToPx
|
||||
setTint(binding.lowerText.currentTextColor)
|
||||
setBounds(0, 0, size.roundToInt(), size.roundToInt())
|
||||
} ?: return
|
||||
append(" ")
|
||||
inSpans(ImageSpan(icon)) { append("image") }
|
||||
}
|
||||
|
||||
private fun missingChapterWarning(transition: ChapterTransition) {
|
||||
if (transition.to == null) {
|
||||
binding.warning.isVisible = false
|
||||
|
||||
@@ -24,6 +24,7 @@ import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import tachiyomi.decoder.ImageDecoder
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -332,7 +333,7 @@ class PagerPageHolder(
|
||||
.subscribe({}, {})
|
||||
}
|
||||
|
||||
private fun process(page: ReaderPage, imageStream: InputStream): InputStream {
|
||||
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
|
||||
if (!viewer.config.dualPageSplit) {
|
||||
return imageStream
|
||||
}
|
||||
@@ -341,7 +342,7 @@ class PagerPageHolder(
|
||||
return splitInHalf(imageStream)
|
||||
}
|
||||
|
||||
val isDoublePage = ImageUtil.isDoublePage(imageStream)
|
||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
||||
if (!isDoublePage) {
|
||||
return imageStream
|
||||
}
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ class PagerTransitionHolder(
|
||||
addView(transitionView)
|
||||
addView(pagesContainer)
|
||||
|
||||
transitionView.bind(transition)
|
||||
transitionView.bind(transition, viewer.downloadManager, viewer.activity.presenter.manga)
|
||||
|
||||
transition.to?.let { observeStatus(it) }
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
|
||||
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
|
||||
@@ -21,6 +22,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
@@ -29,6 +31,8 @@ import kotlin.math.min
|
||||
@Suppress("LeakingThis")
|
||||
abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
|
||||
|
||||
val downloadManager: DownloadManager by injectLazy()
|
||||
|
||||
val scope = MainScope()
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,7 @@ class WebtoonFrame(context: Context) : FrameLayout(context) {
|
||||
* Scale listener used to delegate events to the recycler view.
|
||||
*/
|
||||
inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
|
||||
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
|
||||
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
|
||||
recycler?.onScaleBegin()
|
||||
return true
|
||||
}
|
||||
@@ -71,13 +71,13 @@ class WebtoonFrame(context: Context) : FrameLayout(context) {
|
||||
* Fling listener used to delegate events to the recycler view.
|
||||
*/
|
||||
inner class FlingListener : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
override fun onDown(e: MotionEvent): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent?,
|
||||
e1: MotionEvent,
|
||||
e2: MotionEvent,
|
||||
velocityX: Float,
|
||||
velocityY: Float,
|
||||
): Boolean {
|
||||
|
||||
@@ -23,6 +23,7 @@ import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.InputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -272,12 +273,12 @@ class WebtoonPageHolder(
|
||||
addSubscription(readImageHeaderSubscription)
|
||||
}
|
||||
|
||||
private fun process(imageStream: InputStream): InputStream {
|
||||
private fun process(imageStream: BufferedInputStream): InputStream {
|
||||
if (!viewer.config.dualPageSplit) {
|
||||
return imageStream
|
||||
}
|
||||
|
||||
val isDoublePage = ImageUtil.isDoublePage(imageStream)
|
||||
val isDoublePage = ImageUtil.isWideImage(imageStream)
|
||||
if (!isDoublePage) {
|
||||
return imageStream
|
||||
}
|
||||
|
||||
+1
-1
@@ -63,7 +63,7 @@ class WebtoonTransitionHolder(
|
||||
* Binds the given [transition] with this view holder, subscribing to its state.
|
||||
*/
|
||||
fun bind(transition: ChapterTransition) {
|
||||
transitionView.bind(transition)
|
||||
transitionView.bind(transition, viewer.downloadManager, viewer.activity.presenter.manga)
|
||||
|
||||
transition.to?.let { observeStatus(it, transition) }
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.WebtoonLayoutManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
|
||||
@@ -24,6 +25,7 @@ import kotlinx.coroutines.cancel
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -32,6 +34,8 @@ import kotlin.math.min
|
||||
*/
|
||||
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : BaseViewer {
|
||||
|
||||
val downloadManager: DownloadManager by injectLazy()
|
||||
|
||||
private val scope = MainScope()
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.startAuthentication
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import logcat.LogPriority
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Blank activity with a BiometricPrompt.
|
||||
@@ -39,7 +38,6 @@ class UnlockActivity : BaseActivity() {
|
||||
) {
|
||||
super.onAuthenticationSucceeded(activity, result)
|
||||
SecureActivityDelegate.locked = false
|
||||
preferences.lastAppUnlock().set(Date().time)
|
||||
finish()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -219,6 +219,28 @@ class SettingsAdvancedController : SettingsController() {
|
||||
true
|
||||
}
|
||||
}
|
||||
editTextPreference {
|
||||
key = Keys.defaultUserAgent
|
||||
titleRes = R.string.pref_user_agent_string
|
||||
text = preferences.defaultUserAgent().get()
|
||||
summary = network.defaultUserAgent
|
||||
|
||||
onChange {
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
true
|
||||
}
|
||||
}
|
||||
if (preferences.defaultUserAgent().isSet()) {
|
||||
preference {
|
||||
key = "pref_reset_user_agent"
|
||||
titleRes = R.string.pref_reset_user_agent_string
|
||||
|
||||
onClick {
|
||||
preferences.defaultUserAgent().delete()
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
|
||||
@@ -24,6 +24,7 @@ import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
@@ -72,6 +73,12 @@ class SettingsDownloadController : SettingsController() {
|
||||
bindTo(preferences.saveChaptersAsCBZ())
|
||||
titleRes = R.string.save_chapter_as_cbz
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(preferences.splitTallImages())
|
||||
titleRes = R.string.split_tall_images
|
||||
summaryRes = R.string.split_tall_images_summary
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_delete_chapters
|
||||
|
||||
|
||||
@@ -121,13 +121,13 @@ class SettingsMainController : SettingsController() {
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
preferences.lastSearchQuerySearchSettings().set("") // reset saved search query
|
||||
router.pushController(SettingsSearchController().withFadeTransaction())
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
return true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -74,11 +74,11 @@ class SettingsSearchController :
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
router.popCurrentController()
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -166,12 +166,12 @@ class WebViewActivity : BaseActivity() {
|
||||
|
||||
menu.findItem(R.id.action_web_back).apply {
|
||||
isEnabled = binding.webview.canGoBack()
|
||||
icon.setTint(if (binding.webview.canGoBack()) iconTintColor else translucentIconTintColor)
|
||||
icon?.setTint(if (binding.webview.canGoBack()) iconTintColor else translucentIconTintColor)
|
||||
}
|
||||
|
||||
menu.findItem(R.id.action_web_forward).apply {
|
||||
isEnabled = binding.webview.canGoForward()
|
||||
icon.setTint(if (binding.webview.canGoForward()) iconTintColor else translucentIconTintColor)
|
||||
icon?.setTint(if (binding.webview.canGoForward()) iconTintColor else translucentIconTintColor)
|
||||
}
|
||||
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
|
||||
@@ -46,8 +46,8 @@ object ChapterRecognition {
|
||||
// Get chapter title with lower case
|
||||
var name = chapter.name.lowercase()
|
||||
|
||||
// Remove comma's from chapter.
|
||||
name = name.replace(',', '.')
|
||||
// Remove comma's or hyphens.
|
||||
name = name.replace(',', '.').replace('-', '.')
|
||||
|
||||
// Remove unwanted white spaces.
|
||||
unwantedWhiteSpace.findAll(name).let {
|
||||
|
||||
@@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.util.chapter
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
|
||||
|
||||
fun getChapterSort(manga: Manga, sortDescending: Boolean = manga.sortDescending()): (Chapter, Chapter) -> Int {
|
||||
return when (manga.sorting) {
|
||||
@@ -11,13 +10,13 @@ fun getChapterSort(manga: Manga, sortDescending: Boolean = manga.sortDescending(
|
||||
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
|
||||
}
|
||||
Manga.CHAPTER_SORTING_NUMBER -> when (sortDescending) {
|
||||
true -> { c1, c2 -> c2.chapter_number.toString().compareToCaseInsensitiveNaturalOrder(c1.chapter_number.toString()) }
|
||||
false -> { c1, c2 -> c1.chapter_number.toString().compareToCaseInsensitiveNaturalOrder(c2.chapter_number.toString()) }
|
||||
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
|
||||
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
|
||||
}
|
||||
Manga.CHAPTER_SORTING_UPLOAD_DATE -> when (sortDescending) {
|
||||
true -> { c1, c2 -> c2.date_upload.compareTo(c1.date_upload) }
|
||||
false -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) }
|
||||
}
|
||||
else -> throw NotImplementedError("Unimplemented sorting method")
|
||||
else -> throw NotImplementedError("Invalid chapter sorting method: ${manga.sorting}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package eu.kanade.tachiyomi.util.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Environment
|
||||
import android.os.StatFs
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.Hash
|
||||
import java.io.File
|
||||
@@ -74,21 +73,11 @@ object DiskUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given file so that it can be shown in gallery apps, for example.
|
||||
*/
|
||||
fun scanMedia(context: Context, file: File) {
|
||||
scanMedia(context, file.toUri())
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given file so that it can be shown in gallery apps, for example.
|
||||
*/
|
||||
fun scanMedia(context: Context, uri: Uri) {
|
||||
val action = Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
|
||||
val mediaScanIntent = Intent(action)
|
||||
mediaScanIntent.data = uri
|
||||
context.sendBroadcast(mediaScanIntent)
|
||||
MediaScannerConnection.scanFile(context, arrayOf(uri.path), null, null)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,6 +47,7 @@ import logcat.LogPriority
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_DP = 720
|
||||
@@ -166,6 +167,9 @@ fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermissio
|
||||
}
|
||||
}
|
||||
|
||||
val getDisplayMaxHeightInPx: Int
|
||||
get() = Resources.getSystem().displayMetrics.let { max(it.heightPixels, it.widthPixels) }
|
||||
|
||||
/**
|
||||
* Converts to dp.
|
||||
*/
|
||||
@@ -258,7 +262,7 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
||||
}
|
||||
|
||||
fun Context.defaultBrowserPackageName(): String? {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("http://"))
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||
return packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
?.activityInfo?.packageName
|
||||
?.takeUnless { it in DeviceUtil.invalidDefaultBrowsers }
|
||||
@@ -315,8 +319,8 @@ fun Context.isNightMode(): Boolean {
|
||||
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:appcompat/appcompat/src/main/java/androidx/appcompat/app/AppCompatDelegateImpl.java;l=348;drc=e28752c96fc3fb4d3354781469a1af3dbded4898
|
||||
*/
|
||||
fun Context.createReaderThemeContext(): Context {
|
||||
val prefs = Injekt.get<PreferencesHelper>()
|
||||
val isDarkBackground = when (prefs.readerTheme().get()) {
|
||||
val preferences = Injekt.get<PreferencesHelper>()
|
||||
val isDarkBackground = when (preferences.readerTheme().get()) {
|
||||
1, 2 -> true // Black, Gray
|
||||
3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
|
||||
else -> false // White
|
||||
@@ -329,7 +333,7 @@ fun Context.createReaderThemeContext(): Context {
|
||||
|
||||
val wrappedContext = ContextThemeWrapper(this, R.style.Theme_Tachiyomi)
|
||||
wrappedContext.applyOverrideConfiguration(overrideConf)
|
||||
ThemingDelegate.getThemeResIds(prefs.appTheme().get(), prefs.themeDarkAmoled().get())
|
||||
ThemingDelegate.getThemeResIds(preferences.appTheme().get(), preferences.themeDarkAmoled().get())
|
||||
.forEach { wrappedContext.theme.applyStyle(it, true) }
|
||||
return wrappedContext
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.BitmapRegionDecoder
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Rect
|
||||
@@ -17,16 +18,23 @@ import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.applyCanvas
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.core.graphics.get
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import com.hippo.unifile.UniFile
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.decoder.Format
|
||||
import tachiyomi.decoder.ImageDecoder
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.URLConnection
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
object ImageUtil {
|
||||
|
||||
@@ -76,8 +84,7 @@ object ImageUtil {
|
||||
Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
|
||||
else -> false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
} catch (e: Exception) { /* Do Nothing */ }
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -109,19 +116,12 @@ object ImageUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the image is a double-page spread
|
||||
* Check whether the image is wide (which we consider a double-page spread).
|
||||
*
|
||||
* @return true if the width is greater than the height
|
||||
*/
|
||||
fun isDoublePage(imageStream: InputStream): Boolean {
|
||||
imageStream.mark(imageStream.available() + 1)
|
||||
|
||||
val imageBytes = imageStream.readBytes()
|
||||
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options)
|
||||
|
||||
imageStream.reset()
|
||||
|
||||
fun isWideImage(imageStream: BufferedInputStream): Boolean {
|
||||
val options = extractImageOptions(imageStream)
|
||||
return options.outWidth > options.outHeight
|
||||
}
|
||||
|
||||
@@ -188,6 +188,111 @@ object ImageUtil {
|
||||
RIGHT, LEFT
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the image is considered a tall image.
|
||||
*
|
||||
* @return true if the height:width ratio is greater than 3.
|
||||
*/
|
||||
private fun isTallImage(imageStream: InputStream): Boolean {
|
||||
val options = extractImageOptions(imageStream, resetAfterExtraction = false)
|
||||
return (options.outHeight / options.outWidth) > 3
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits tall images to improve performance of reader
|
||||
*/
|
||||
fun splitTallImage(imageFile: UniFile, imageFilePath: String): Boolean {
|
||||
if (isAnimatedAndSupported(imageFile.openInputStream()) || !isTallImage(imageFile.openInputStream())) {
|
||||
return true
|
||||
}
|
||||
|
||||
val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { inJustDecodeBounds = false }
|
||||
// Values are stored as they get modified during split loop
|
||||
val imageHeight = options.outHeight
|
||||
val imageWidth = options.outWidth
|
||||
|
||||
val splitHeight = (getDisplayMaxHeightInPx * 1.5).toInt()
|
||||
// -1 so it doesn't try to split when imageHeight = getDisplayHeightInPx
|
||||
val partCount = (imageHeight - 1) / splitHeight + 1
|
||||
|
||||
val optimalSplitHeight = imageHeight / partCount
|
||||
|
||||
val splitDataList = (0 until partCount).fold(mutableListOf<SplitData>()) { list, index ->
|
||||
list.apply {
|
||||
// Only continue if the list is empty or there is image remaining
|
||||
if (isEmpty() || imageHeight > last().bottomOffset) {
|
||||
val topOffset = index * optimalSplitHeight
|
||||
var outputImageHeight = min(optimalSplitHeight, imageHeight - topOffset)
|
||||
|
||||
val remainingHeight = imageHeight - (topOffset + outputImageHeight)
|
||||
// If remaining height is smaller or equal to 1/3th of
|
||||
// optimal split height then include it in current page
|
||||
if (remainingHeight <= (optimalSplitHeight / 3)) {
|
||||
outputImageHeight += remainingHeight
|
||||
}
|
||||
add(SplitData(index, topOffset, outputImageHeight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val bitmapRegionDecoder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
BitmapRegionDecoder.newInstance(imageFile.openInputStream())
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
BitmapRegionDecoder.newInstance(imageFile.openInputStream(), false)
|
||||
}
|
||||
|
||||
if (bitmapRegionDecoder == null) {
|
||||
logcat { "Failed to create new instance of BitmapRegionDecoder" }
|
||||
return false
|
||||
}
|
||||
|
||||
logcat {
|
||||
"Splitting image with height of $imageHeight into $partCount part " +
|
||||
"with estimated ${optimalSplitHeight}px height per split"
|
||||
}
|
||||
|
||||
return try {
|
||||
splitDataList.forEach { splitData ->
|
||||
val splitPath = splitImagePath(imageFilePath, splitData.index)
|
||||
|
||||
val region = Rect(0, splitData.topOffset, imageWidth, splitData.bottomOffset)
|
||||
|
||||
FileOutputStream(splitPath).use { outputStream ->
|
||||
val splitBitmap = bitmapRegionDecoder.decodeRegion(region, options)
|
||||
splitBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
|
||||
splitBitmap.recycle()
|
||||
}
|
||||
logcat {
|
||||
"Success: Split #${splitData.index + 1} with topOffset=${splitData.topOffset} " +
|
||||
"height=${splitData.outputImageHeight} bottomOffset=${splitData.bottomOffset}"
|
||||
}
|
||||
}
|
||||
imageFile.delete()
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
// Image splits were not successfully saved so delete them and keep the original image
|
||||
splitDataList
|
||||
.map { splitImagePath(imageFilePath, it.index) }
|
||||
.forEach { File(it).delete() }
|
||||
logcat(LogPriority.ERROR, e)
|
||||
false
|
||||
} finally {
|
||||
bitmapRegionDecoder.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
private fun splitImagePath(imageFilePath: String, index: Int) =
|
||||
imageFilePath.substringBeforeLast(".") + "__${"%03d".format(index + 1)}.jpg"
|
||||
|
||||
data class SplitData(
|
||||
val index: Int,
|
||||
val topOffset: Int,
|
||||
val outputImageHeight: Int,
|
||||
) {
|
||||
val bottomOffset = topOffset + outputImageHeight
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm for determining what background to accompany a comic/manga page
|
||||
*/
|
||||
@@ -212,14 +317,14 @@ object ImageUtil {
|
||||
val leftOffsetX = left - offsetX
|
||||
val rightOffsetX = right + offsetX
|
||||
|
||||
val topLeftPixel = image.getPixel(left, top)
|
||||
val topRightPixel = image.getPixel(right, top)
|
||||
val midLeftPixel = image.getPixel(left, midY)
|
||||
val midRightPixel = image.getPixel(right, midY)
|
||||
val topCenterPixel = image.getPixel(midX, top)
|
||||
val botLeftPixel = image.getPixel(left, bot)
|
||||
val bottomCenterPixel = image.getPixel(midX, bot)
|
||||
val botRightPixel = image.getPixel(right, bot)
|
||||
val topLeftPixel = image[left, top]
|
||||
val topRightPixel = image[right, top]
|
||||
val midLeftPixel = image[left, midY]
|
||||
val midRightPixel = image[right, midY]
|
||||
val topCenterPixel = image[midX, top]
|
||||
val botLeftPixel = image[left, bot]
|
||||
val bottomCenterPixel = image[midX, bot]
|
||||
val botRightPixel = image[right, bot]
|
||||
|
||||
val topLeftIsDark = topLeftPixel.isDark()
|
||||
val topRightIsDark = topRightPixel.isDark()
|
||||
@@ -272,8 +377,8 @@ object ImageUtil {
|
||||
var whiteStreak = false
|
||||
val notOffset = x == left || x == right
|
||||
inner@ for ((index, y) in (0 until image.height step image.height / 25).withIndex()) {
|
||||
val pixel = image.getPixel(x, y)
|
||||
val pixelOff = image.getPixel(x + (if (x < image.width / 2) -offsetX else offsetX), y)
|
||||
val pixel = image[x, y]
|
||||
val pixelOff = image[x + (if (x < image.width / 2) -offsetX else offsetX), y]
|
||||
if (pixel.isWhite()) {
|
||||
whitePixelsStreak++
|
||||
whitePixels++
|
||||
@@ -364,8 +469,8 @@ object ImageUtil {
|
||||
val topCornersIsDark = topLeftIsDark && topRightIsDark
|
||||
val botCornersIsDark = botLeftIsDark && botRightIsDark
|
||||
|
||||
val topOffsetCornersIsDark = image.getPixel(leftOffsetX, top).isDark() && image.getPixel(rightOffsetX, top).isDark()
|
||||
val botOffsetCornersIsDark = image.getPixel(leftOffsetX, bot).isDark() && image.getPixel(rightOffsetX, bot).isDark()
|
||||
val topOffsetCornersIsDark = image[leftOffsetX, top].isDark() && image[rightOffsetX, top].isDark()
|
||||
val botOffsetCornersIsDark = image[leftOffsetX, bot].isDark() && image[rightOffsetX, bot].isDark()
|
||||
|
||||
val gradient = when {
|
||||
darkBG && botCornersIsWhite -> {
|
||||
@@ -394,15 +499,31 @@ object ImageUtil {
|
||||
)
|
||||
}
|
||||
|
||||
private fun Int.isDark(): Boolean =
|
||||
private fun @receiver:ColorInt Int.isDark(): Boolean =
|
||||
red < 40 && blue < 40 && green < 40 && alpha > 200
|
||||
|
||||
private fun Int.isCloseTo(other: Int): Boolean =
|
||||
private fun @receiver:ColorInt Int.isCloseTo(other: Int): Boolean =
|
||||
abs(red - other.red) < 30 && abs(green - other.green) < 30 && abs(blue - other.blue) < 30
|
||||
|
||||
private fun Int.isWhite(): Boolean =
|
||||
private fun @receiver:ColorInt Int.isWhite(): Boolean =
|
||||
red + blue + green > 740
|
||||
|
||||
/**
|
||||
* Used to check an image's dimensions without loading it in the memory.
|
||||
*/
|
||||
private fun extractImageOptions(
|
||||
imageStream: InputStream,
|
||||
resetAfterExtraction: Boolean = true,
|
||||
): BitmapFactory.Options {
|
||||
imageStream.mark(imageStream.available() + 1)
|
||||
|
||||
val imageBytes = imageStream.readBytes()
|
||||
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options)
|
||||
if (resetAfterExtraction) imageStream.reset()
|
||||
return options
|
||||
}
|
||||
|
||||
// Android doesn't include some mappings
|
||||
private val SUPPLEMENTARY_MIMETYPE_MAPPING = mapOf(
|
||||
// https://issuetracker.google.com/issues/182703810
|
||||
|
||||
@@ -115,12 +115,13 @@ class TachiyomiBottomNavigationView @JvmOverloads constructor(
|
||||
.setInterpolator(interpolator)
|
||||
.setDuration(duration)
|
||||
.applySystemAnimatorScale(context)
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
currentAnimator = null
|
||||
postInvalidate()
|
||||
}
|
||||
},
|
||||
.setListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
currentAnimator = null
|
||||
postInvalidate()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,12 +37,13 @@ class ThemesPreference @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
recycler?.adapter = adapter
|
||||
|
||||
// Retain scroll position on activity recreate after changing theme
|
||||
recycler?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
lastScrollPosition = recyclerView.computeHorizontalScrollOffset()
|
||||
}
|
||||
},
|
||||
recycler?.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
lastScrollPosition = recyclerView.computeHorizontalScrollOffset()
|
||||
}
|
||||
},
|
||||
)
|
||||
lastScrollPosition?.let { scrollToOffset(it) }
|
||||
}
|
||||
|
||||
@@ -45,11 +45,12 @@ class BottomSheetViewPager @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
init {
|
||||
addOnPageChangeListener(object : SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
requestLayout()
|
||||
}
|
||||
},
|
||||
addOnPageChangeListener(
|
||||
object : SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
requestLayout()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.all.Hitomi
|
||||
import eu.kanade.tachiyomi.source.online.all.NHentai
|
||||
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||
import eu.kanade.tachiyomi.ui.library.setting.DisplayModeSetting
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortDirectionSetting
|
||||
import eu.kanade.tachiyomi.ui.library.setting.SortModeSetting
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.util.preference.minusAssign
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.log.xLogE
|
||||
import exh.log.xLogW
|
||||
@@ -308,36 +308,40 @@ object EXHMigrations {
|
||||
}
|
||||
}
|
||||
if (oldVersion under 20) {
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0)
|
||||
val oldSortingDirection = prefs.getBoolean(PreferenceKeys.librarySortingDirection, true)
|
||||
try {
|
||||
val oldSortingMode = prefs.getInt(PreferenceKeys.librarySortingMode, 0 /* ALPHABETICAL */)
|
||||
val oldSortingDirection = prefs.getBoolean(PreferenceKeys.librarySortingDirection, true)
|
||||
|
||||
val newSortingMode = when (oldSortingMode) {
|
||||
LibrarySort.ALPHA -> SortModeSetting.ALPHABETICAL
|
||||
LibrarySort.LAST_READ -> SortModeSetting.LAST_READ
|
||||
LibrarySort.LAST_CHECKED -> SortModeSetting.LAST_CHECKED
|
||||
LibrarySort.UNREAD -> SortModeSetting.UNREAD
|
||||
LibrarySort.TOTAL -> SortModeSetting.TOTAL_CHAPTERS
|
||||
LibrarySort.LATEST_CHAPTER -> SortModeSetting.LATEST_CHAPTER
|
||||
LibrarySort.CHAPTER_FETCH_DATE -> SortModeSetting.DATE_FETCHED
|
||||
LibrarySort.DATE_ADDED -> SortModeSetting.DATE_ADDED
|
||||
LibrarySort.DRAG_AND_DROP -> SortModeSetting.DRAG_AND_DROP
|
||||
LibrarySort.TAG_LIST -> SortModeSetting.TAG_LIST
|
||||
else -> SortModeSetting.ALPHABETICAL
|
||||
}
|
||||
val newSortingMode = when (oldSortingMode) {
|
||||
0 -> SortModeSetting.ALPHABETICAL
|
||||
1 -> SortModeSetting.LAST_READ
|
||||
2 -> SortModeSetting.LAST_CHECKED
|
||||
3 -> SortModeSetting.UNREAD
|
||||
4 -> SortModeSetting.TOTAL_CHAPTERS
|
||||
6 -> SortModeSetting.LATEST_CHAPTER
|
||||
7 -> SortModeSetting.DRAG_AND_DROP
|
||||
8 -> SortModeSetting.DATE_ADDED
|
||||
9 -> SortModeSetting.TAG_LIST
|
||||
10 -> SortModeSetting.DATE_FETCHED
|
||||
else -> SortModeSetting.ALPHABETICAL
|
||||
}
|
||||
|
||||
val newSortingDirection = when (oldSortingDirection) {
|
||||
true -> SortDirectionSetting.ASCENDING
|
||||
else -> SortDirectionSetting.DESCENDING
|
||||
}
|
||||
val newSortingDirection = when (oldSortingDirection) {
|
||||
true -> SortDirectionSetting.ASCENDING
|
||||
else -> SortDirectionSetting.DESCENDING
|
||||
}
|
||||
|
||||
prefs.edit(commit = true) {
|
||||
remove(PreferenceKeys.librarySortingMode)
|
||||
remove(PreferenceKeys.librarySortingDirection)
|
||||
}
|
||||
prefs.edit(commit = true) {
|
||||
remove(PreferenceKeys.librarySortingMode)
|
||||
remove(PreferenceKeys.librarySortingDirection)
|
||||
}
|
||||
|
||||
prefs.edit {
|
||||
putString(PreferenceKeys.librarySortingMode, newSortingMode.name)
|
||||
putString(PreferenceKeys.librarySortingDirection, newSortingDirection.name)
|
||||
prefs.edit {
|
||||
putString(PreferenceKeys.librarySortingMode, newSortingMode.name)
|
||||
putString(PreferenceKeys.librarySortingDirection, newSortingDirection.name)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logcat(throwable = e) { "Already done migration" }
|
||||
}
|
||||
}
|
||||
if (oldVersion under 21) {
|
||||
|
||||
@@ -6,31 +6,32 @@ import exh.eh.tags.Character
|
||||
import exh.eh.tags.Cosplayer
|
||||
import exh.eh.tags.Female
|
||||
import exh.eh.tags.Group
|
||||
import exh.eh.tags.Group2
|
||||
import exh.eh.tags.Language
|
||||
import exh.eh.tags.Male
|
||||
import exh.eh.tags.Mixed
|
||||
import exh.eh.tags.Other
|
||||
import exh.eh.tags.Parody
|
||||
import exh.eh.tags.ReClass
|
||||
import exh.eh.tags.Reclass
|
||||
|
||||
object EHTags {
|
||||
|
||||
fun getAllTags() = listOf(
|
||||
fun getAllTags(): List<String> = listOf(
|
||||
Female.getTags(),
|
||||
Male.getTags(),
|
||||
Language.getTags(),
|
||||
ReClass.getTags(),
|
||||
Reclass.getTags(),
|
||||
Mixed.getTags(),
|
||||
Other.getTags(),
|
||||
Cosplayer.getTags(),
|
||||
Parody.getTags(),
|
||||
Character.getTags(),
|
||||
Group.getTags(),
|
||||
Group2.getTags(),
|
||||
Artist.getTags(),
|
||||
Artist2.getTags(),
|
||||
).flatten().flatten()
|
||||
|
||||
fun getNamespaces0Tags() = listOf(
|
||||
fun getNamespaces(): List<String> = listOf(
|
||||
"reclass",
|
||||
"language",
|
||||
"parody",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,46 +1,121 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Cosplayer : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"cosplayer:abaoyeshituniang",
|
||||
"cosplayer:ai lei jiang",
|
||||
"cosplayer:akane araragi",
|
||||
"cosplayer:akemi101xoxo",
|
||||
"cosplayer:aleksandra bodler",
|
||||
"cosplayer:alicekyo",
|
||||
"cosplayer:alin ma",
|
||||
"cosplayer:alisa kiss",
|
||||
"cosplayer:alodia gosiengfiao",
|
||||
"cosplayer:amanda welp",
|
||||
"cosplayer:anastasia komori",
|
||||
"cosplayer:aokotan",
|
||||
"cosplayer:arisa mizuhara",
|
||||
"cosplayer:arty huang",
|
||||
"cosplayer:ashiya noriko",
|
||||
"cosplayer:atsuki",
|
||||
"cosplayer:ayaka matsunaga",
|
||||
"cosplayer:bailey jay",
|
||||
"cosplayer:banbanko",
|
||||
"cosplayer:bishoujomom",
|
||||
"cosplayer:bobbi starr",
|
||||
"cosplayer:carry key",
|
||||
"cosplayer:charles dera",
|
||||
"cosplayer:chunmomo",
|
||||
"cosplayer:comonun",
|
||||
"cosplayer:danielle vedovelli",
|
||||
"cosplayer:darling cute",
|
||||
"cosplayer:dillion harper",
|
||||
"cosplayer:donnaloli",
|
||||
"cosplayer:evenink",
|
||||
"cosplayer:fe galvao",
|
||||
"cosplayer:fluffy nemu",
|
||||
"cosplayer:franxcos",
|
||||
"cosplayer:g44 wa kizutsukanai",
|
||||
"cosplayer:gumiho hannya",
|
||||
"cosplayer:hane ame",
|
||||
"cosplayer:helly von valentine",
|
||||
"cosplayer:hessakai",
|
||||
"cosplayer:hey shika",
|
||||
"cosplayer:higurashi rin",
|
||||
"cosplayer:himeecosplay",
|
||||
"cosplayer:hinaughtya",
|
||||
"cosplayer:holly wolf",
|
||||
"cosplayer:imokawa naoko",
|
||||
"cosplayer:iori moe",
|
||||
"cosplayer:ishikawa asami",
|
||||
"cosplayer:jaycee",
|
||||
"cosplayer:jessica nigri",
|
||||
"cosplayer:jill",
|
||||
"cosplayer:kalinka fox",
|
||||
"cosplayer:kanda midori",
|
||||
"cosplayer:kaya huang",
|
||||
"cosplayer:kimmie mi",
|
||||
"cosplayer:kitami eri",
|
||||
"cosplayer:koyama rikako",
|
||||
"cosplayer:kqueentsun",
|
||||
"cosplayer:kurumi.",
|
||||
"cosplayer:kuuko w",
|
||||
"cosplayer:lenfried",
|
||||
"cosplayer:lewdoart",
|
||||
"cosplayer:lovelyspacekitten",
|
||||
"cosplayer:marie-claude bourbonnais",
|
||||
"cosplayer:meikoui",
|
||||
"cosplayer:miih cosplay",
|
||||
"cosplayer:mikomin",
|
||||
"cosplayer:misa daidai",
|
||||
"cosplayer:mizhimaoqiu",
|
||||
"cosplayer:mochizuki eiko",
|
||||
"cosplayer:momoiro reku",
|
||||
"cosplayer:momokun",
|
||||
"cosplayer:nadyasonika",
|
||||
"cosplayer:neroko kaigan",
|
||||
"cosplayer:niannian d",
|
||||
"cosplayer:nicky",
|
||||
"cosplayer:niyeye",
|
||||
"cosplayer:nora fawn",
|
||||
"cosplayer:octokuro",
|
||||
"cosplayer:oichi",
|
||||
"cosplayer:okada yui",
|
||||
"cosplayer:penkarui",
|
||||
"cosplayer:punk macarroni",
|
||||
"cosplayer:queenie",
|
||||
"cosplayer:rio-chan",
|
||||
"cosplayer:rioko",
|
||||
"cosplayer:rocksy light",
|
||||
"cosplayer:rolyatistaylor",
|
||||
"cosplayer:saiwari ph",
|
||||
"cosplayer:saku",
|
||||
"cosplayer:sakurai hinoki",
|
||||
"cosplayer:sandykuroneko",
|
||||
"cosplayer:saotome love",
|
||||
"cosplayer:sawaka",
|
||||
"cosplayer:sexyflowerwater",
|
||||
"cosplayer:shibuya kaho",
|
||||
"cosplayer:shiro kitsune",
|
||||
"cosplayer:siao ding",
|
||||
"cosplayer:smoettii",
|
||||
"cosplayer:sneaky",
|
||||
"cosplayer:soa lianna",
|
||||
"cosplayer:son yeeun",
|
||||
"cosplayer:sunnyvier",
|
||||
"cosplayer:tanaka hitomi",
|
||||
"cosplayer:tenleid",
|
||||
"cosplayer:todopokie",
|
||||
"cosplayer:tsubaki zakuro",
|
||||
"cosplayer:tsuki desu",
|
||||
"cosplayer:tyouduki maryou",
|
||||
"cosplayer:uchida mahiro",
|
||||
"cosplayer:valery himera",
|
||||
"cosplayer:velvet",
|
||||
"cosplayer:wildhoney423",
|
||||
"cosplayer:yume",
|
||||
"cosplayer:yunocos69",
|
||||
"cosplayer:yuzupyon",
|
||||
"cosplayer:zara durose",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Female : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"female:abortion",
|
||||
"female:absorption",
|
||||
"female:adventitious mouth",
|
||||
"female:adventitious penis",
|
||||
"female:adventitious vagina",
|
||||
"female:afro",
|
||||
"female:age progression",
|
||||
"female:age regression",
|
||||
"female:ahegao",
|
||||
@@ -17,10 +19,13 @@ object Female : TagList {
|
||||
"female:anal birth",
|
||||
"female:anal intercourse",
|
||||
"female:anal prolapse",
|
||||
"female:analphagia",
|
||||
"female:angel",
|
||||
"female:animal on animal",
|
||||
"female:animal on furry",
|
||||
"female:animegao",
|
||||
"female:anorexic",
|
||||
"female:apparel bukkake",
|
||||
"female:apron",
|
||||
"female:armpit licking",
|
||||
"female:armpit sex",
|
||||
@@ -33,8 +38,10 @@ object Female : TagList {
|
||||
"female:bald",
|
||||
"female:ball sucking",
|
||||
"female:balljob",
|
||||
"female:balls expansion",
|
||||
"female:bandages",
|
||||
"female:bandaid",
|
||||
"female:bat girl",
|
||||
"female:bbw",
|
||||
"female:bdsm",
|
||||
"female:bear",
|
||||
@@ -47,6 +54,8 @@ object Female : TagList {
|
||||
"female:big balls",
|
||||
"female:big breasts",
|
||||
"female:big clit",
|
||||
"female:big lips",
|
||||
"female:big muscles",
|
||||
"female:big nipples",
|
||||
"female:big penis",
|
||||
"female:big vagina",
|
||||
@@ -68,6 +77,7 @@ object Female : TagList {
|
||||
"female:bodystocking",
|
||||
"female:bodysuit",
|
||||
"female:bondage",
|
||||
"female:braces",
|
||||
"female:brain fuck",
|
||||
"female:breast expansion",
|
||||
"female:breast feeding",
|
||||
@@ -80,6 +90,7 @@ object Female : TagList {
|
||||
"female:butler",
|
||||
"female:cannibalism",
|
||||
"female:cashier",
|
||||
"female:cat",
|
||||
"female:catfight",
|
||||
"female:catgirl",
|
||||
"female:cbt",
|
||||
@@ -95,11 +106,16 @@ object Female : TagList {
|
||||
"female:christmas",
|
||||
"female:clamp",
|
||||
"female:clit growth",
|
||||
"female:clit insertion",
|
||||
"female:clit stimulation",
|
||||
"female:clone",
|
||||
"female:closed eyes",
|
||||
"female:clothed male nude female",
|
||||
"female:clothed paizuri",
|
||||
"female:clown",
|
||||
"female:coach",
|
||||
"female:cock ring",
|
||||
"female:cockphagia",
|
||||
"female:cockslapping",
|
||||
"female:collar",
|
||||
"female:condom",
|
||||
@@ -115,11 +131,13 @@ object Female : TagList {
|
||||
"female:crossdressing",
|
||||
"female:crotch tattoo",
|
||||
"female:crown",
|
||||
"female:crying",
|
||||
"female:cum bath",
|
||||
"female:cum in eye",
|
||||
"female:cum swap",
|
||||
"female:cumflation",
|
||||
"female:cunnilingus",
|
||||
"female:cuntbusting",
|
||||
"female:dark nipples",
|
||||
"female:dark sclera",
|
||||
"female:dark skin",
|
||||
@@ -129,6 +147,8 @@ object Female : TagList {
|
||||
"female:deer girl",
|
||||
"female:defloration",
|
||||
"female:demon girl",
|
||||
"female:denki anma",
|
||||
"female:detached sleeves",
|
||||
"female:diaper",
|
||||
"female:dick growth",
|
||||
"female:dickgirl on dickgirl",
|
||||
@@ -138,6 +158,8 @@ object Female : TagList {
|
||||
"female:dog",
|
||||
"female:dog girl",
|
||||
"female:doll joints",
|
||||
"female:dolphin",
|
||||
"female:domination loss",
|
||||
"female:donkey",
|
||||
"female:double anal",
|
||||
"female:double blowjob",
|
||||
@@ -169,6 +191,7 @@ object Female : TagList {
|
||||
"female:farting",
|
||||
"female:females only",
|
||||
"female:femdom",
|
||||
"female:fff threesome",
|
||||
"female:fft threesome",
|
||||
"female:filming",
|
||||
"female:fingering",
|
||||
@@ -183,11 +206,13 @@ object Female : TagList {
|
||||
"female:foot insertion",
|
||||
"female:foot licking",
|
||||
"female:footjob",
|
||||
"female:forced exposure",
|
||||
"female:forniphilia",
|
||||
"female:fox",
|
||||
"female:fox girl",
|
||||
"female:freckles",
|
||||
"female:frog",
|
||||
"female:frog girl",
|
||||
"female:frottage",
|
||||
"female:fundoshi",
|
||||
"female:furry",
|
||||
@@ -199,11 +224,15 @@ object Female : TagList {
|
||||
"female:gender change",
|
||||
"female:gender morph",
|
||||
"female:ghost",
|
||||
"female:giant sperm",
|
||||
"female:giantess",
|
||||
"female:gigantic breasts",
|
||||
"female:gijinka",
|
||||
"female:giraffe girl",
|
||||
"female:glasses",
|
||||
"female:glory hole",
|
||||
"female:gloves",
|
||||
"female:goblin",
|
||||
"female:gokkun",
|
||||
"female:gothic lolita",
|
||||
"female:granddaughter",
|
||||
@@ -213,12 +242,14 @@ object Female : TagList {
|
||||
"female:guro",
|
||||
"female:gyaru",
|
||||
"female:gymshorts",
|
||||
"female:haigure",
|
||||
"female:hair buns",
|
||||
"female:hairjob",
|
||||
"female:hairy",
|
||||
"female:hairy armpits",
|
||||
"female:handicapped",
|
||||
"female:handjob",
|
||||
"female:hanging",
|
||||
"female:harem",
|
||||
"female:harness",
|
||||
"female:harpy",
|
||||
@@ -237,18 +268,24 @@ object Female : TagList {
|
||||
"female:human cattle",
|
||||
"female:human on furry",
|
||||
"female:humiliation",
|
||||
"female:hyena girl",
|
||||
"female:impregnation",
|
||||
"female:incest",
|
||||
"female:infantilism",
|
||||
"female:inflation",
|
||||
"female:insect",
|
||||
"female:insect girl",
|
||||
"female:internal urination",
|
||||
"female:inverted nipples",
|
||||
"female:invisible",
|
||||
"female:kangaroo",
|
||||
"female:kappa",
|
||||
"female:kemonomimi",
|
||||
"female:kigurumi pajama",
|
||||
"female:kimono",
|
||||
"female:kindergarten uniform",
|
||||
"female:kissing",
|
||||
"female:kneepit sex",
|
||||
"female:kunoichi",
|
||||
"female:lab coat",
|
||||
"female:lactation",
|
||||
@@ -258,6 +295,7 @@ object Female : TagList {
|
||||
"female:layer cake",
|
||||
"female:leash",
|
||||
"female:leg lock",
|
||||
"female:legjob",
|
||||
"female:leotard",
|
||||
"female:lingerie",
|
||||
"female:lioness",
|
||||
@@ -266,7 +304,10 @@ object Female : TagList {
|
||||
"female:lolicon",
|
||||
"female:long tongue",
|
||||
"female:low bestiality",
|
||||
"female:low guro",
|
||||
"female:low lolicon",
|
||||
"female:low scat",
|
||||
"female:low smegma",
|
||||
"female:machine",
|
||||
"female:maggot",
|
||||
"female:magical girl",
|
||||
@@ -290,20 +331,25 @@ object Female : TagList {
|
||||
"female:minigirl",
|
||||
"female:monkey",
|
||||
"female:monkey girl",
|
||||
"female:monoeye",
|
||||
"female:monster girl",
|
||||
"female:moral degeneration",
|
||||
"female:mother",
|
||||
"female:mouse",
|
||||
"female:mouse girl",
|
||||
"female:mouth mask",
|
||||
"female:multimouth blowjob",
|
||||
"female:multiple arms",
|
||||
"female:multiple assjob",
|
||||
"female:multiple breasts",
|
||||
"female:multiple footjob",
|
||||
"female:multiple handjob",
|
||||
"female:multiple nipples",
|
||||
"female:multiple orgasms",
|
||||
"female:multiple paizuri",
|
||||
"female:multiple penises",
|
||||
"female:multiple straddling",
|
||||
"female:multiple vaginas",
|
||||
"female:muscle",
|
||||
"female:muscle growth",
|
||||
"female:nakadashi",
|
||||
@@ -315,6 +361,7 @@ object Female : TagList {
|
||||
"female:nipple birth",
|
||||
"female:nipple expansion",
|
||||
"female:nipple fuck",
|
||||
"female:nipple stimulation",
|
||||
"female:nose fuck",
|
||||
"female:nose hook",
|
||||
"female:nun",
|
||||
@@ -327,12 +374,16 @@ object Female : TagList {
|
||||
"female:oppai loli",
|
||||
"female:orc",
|
||||
"female:orgasm denial",
|
||||
"female:otter girl",
|
||||
"female:oyakodon",
|
||||
"female:paizuri",
|
||||
"female:panda girl",
|
||||
"female:pantyhose",
|
||||
"female:pantyjob",
|
||||
"female:parasite",
|
||||
"female:pasties",
|
||||
"female:penis birth",
|
||||
"female:personality excretion",
|
||||
"female:petplay",
|
||||
"female:petrification",
|
||||
"female:phimosis",
|
||||
@@ -364,13 +415,16 @@ object Female : TagList {
|
||||
"female:rape",
|
||||
"female:real doll",
|
||||
"female:reptile",
|
||||
"female:retractable penis",
|
||||
"female:rhinoceros",
|
||||
"female:rimjob",
|
||||
"female:robot",
|
||||
"female:ryona",
|
||||
"female:saliva",
|
||||
"female:sarashi",
|
||||
"female:scar",
|
||||
"female:scat",
|
||||
"female:scat insertion",
|
||||
"female:school gym uniform",
|
||||
"female:school swimsuit",
|
||||
"female:schoolboy uniform",
|
||||
@@ -378,22 +432,29 @@ object Female : TagList {
|
||||
"female:scrotal lingerie",
|
||||
"female:selfcest",
|
||||
"female:sex toys",
|
||||
"female:shapening",
|
||||
"female:shared senses",
|
||||
"female:shark",
|
||||
"female:shark girl",
|
||||
"female:shaved head",
|
||||
"female:sheep",
|
||||
"female:sheep girl",
|
||||
"female:shemale",
|
||||
"female:shibari",
|
||||
"female:shimaidon",
|
||||
"female:shimapan",
|
||||
"female:shrinking",
|
||||
"female:sister",
|
||||
"female:skinsuit",
|
||||
"female:skunk girl",
|
||||
"female:slave",
|
||||
"female:sleeping",
|
||||
"female:slime",
|
||||
"female:slime girl",
|
||||
"female:slug",
|
||||
"female:small breasts",
|
||||
"female:small penis",
|
||||
"female:smalldom",
|
||||
"female:smegma",
|
||||
"female:smell",
|
||||
"female:smoking",
|
||||
@@ -401,6 +462,7 @@ object Female : TagList {
|
||||
"female:snake",
|
||||
"female:snake girl",
|
||||
"female:snuff",
|
||||
"female:sockjob",
|
||||
"female:sole dickgirl",
|
||||
"female:sole female",
|
||||
"female:solo action",
|
||||
@@ -412,8 +474,10 @@ object Female : TagList {
|
||||
"female:squirting",
|
||||
"female:ssbbw",
|
||||
"female:stewardess",
|
||||
"female:stirrup legwear",
|
||||
"female:stockings",
|
||||
"female:stomach deformation",
|
||||
"female:straitjacket",
|
||||
"female:strap-on",
|
||||
"female:stretching",
|
||||
"female:stuck in wall",
|
||||
@@ -427,10 +491,13 @@ object Female : TagList {
|
||||
"female:table masturbation",
|
||||
"female:tail",
|
||||
"female:tail plug",
|
||||
"female:tailjob",
|
||||
"female:tailphagia",
|
||||
"female:tall girl",
|
||||
"female:tanlines",
|
||||
"female:teacher",
|
||||
"female:tentacles",
|
||||
"female:thick eyebrows",
|
||||
"female:thigh high boots",
|
||||
"female:tiara",
|
||||
"female:tickling",
|
||||
@@ -443,11 +510,13 @@ object Female : TagList {
|
||||
"female:tracksuit",
|
||||
"female:trampling",
|
||||
"female:transformation",
|
||||
"female:transparent clothing",
|
||||
"female:tribadism",
|
||||
"female:triple anal",
|
||||
"female:triple penetration",
|
||||
"female:triple vaginal",
|
||||
"female:ttf threesome",
|
||||
"female:ttt threesome",
|
||||
"female:tube",
|
||||
"female:turtle",
|
||||
"female:tutor",
|
||||
@@ -472,7 +541,10 @@ object Female : TagList {
|
||||
"female:waiter",
|
||||
"female:waitress",
|
||||
"female:weight gain",
|
||||
"female:wet clothes",
|
||||
"female:whale",
|
||||
"female:whip",
|
||||
"female:wingjob",
|
||||
"female:wings",
|
||||
"female:witch",
|
||||
"female:wolf",
|
||||
@@ -484,6 +556,7 @@ object Female : TagList {
|
||||
"female:x-ray",
|
||||
"female:yandere",
|
||||
"female:yuri",
|
||||
"female:zebra",
|
||||
"female:zombie",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Group : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"group:---",
|
||||
"group:... mou ii desu.",
|
||||
"group:...with my tears.",
|
||||
@@ -72,6 +71,7 @@ object Group : TagList {
|
||||
"group:38.5 c",
|
||||
"group:3d adult comics",
|
||||
"group:3d bdsm dungeon",
|
||||
"group:3d live",
|
||||
"group:3d pose shuu",
|
||||
"group:3darlings",
|
||||
"group:3dfiends",
|
||||
@@ -402,6 +402,7 @@ object Group : TagList {
|
||||
"group:anakichi",
|
||||
"group:anal crisis",
|
||||
"group:analog store",
|
||||
"group:analyst freedom",
|
||||
"group:ananwanco",
|
||||
"group:anata o aishite yamazu",
|
||||
"group:anatawo haijindesu",
|
||||
@@ -514,6 +515,7 @@ object Group : TagList {
|
||||
"group:arikui paradise",
|
||||
"group:arinko.",
|
||||
"group:arisan-antenna",
|
||||
"group:aristogracy",
|
||||
"group:ariyon dou",
|
||||
"group:ark emerald",
|
||||
"group:ark nantoka",
|
||||
@@ -586,6 +588,7 @@ object Group : TagList {
|
||||
"group:asunaro-shiki bakudan",
|
||||
"group:at 1level",
|
||||
"group:at down",
|
||||
"group:at e.com",
|
||||
"group:at kenkyuujo",
|
||||
"group:at m-gun",
|
||||
"group:at mztm",
|
||||
@@ -690,6 +693,7 @@ object Group : TagList {
|
||||
"group:balklash.",
|
||||
"group:ball colon s",
|
||||
"group:ballet cohort",
|
||||
"group:bambi",
|
||||
"group:banana koubou",
|
||||
"group:banana musume",
|
||||
"group:banana no kawa",
|
||||
@@ -752,6 +756,7 @@ object Group : TagList {
|
||||
"group:bery manjhr",
|
||||
"group:bessungou",
|
||||
"group:beta houkai",
|
||||
"group:beta kikaku hanbai",
|
||||
"group:beta na kanzume",
|
||||
"group:betaneta",
|
||||
"group:betsu ni suki janai yo",
|
||||
@@ -829,6 +834,7 @@ object Group : TagList {
|
||||
"group:blue topaz",
|
||||
"group:blue24",
|
||||
"group:bluebox",
|
||||
"group:bluehistory",
|
||||
"group:bluehot plus",
|
||||
"group:bluejelly",
|
||||
"group:bluemage",
|
||||
@@ -1038,6 +1044,7 @@ object Group : TagList {
|
||||
"group:chikutakudoh",
|
||||
"group:chikuwano kimochi",
|
||||
"group:chikyuugai seimeitai mokyu",
|
||||
"group:child box",
|
||||
"group:childmaid",
|
||||
"group:childwife",
|
||||
"group:chill-out",
|
||||
@@ -1143,6 +1150,7 @@ object Group : TagList {
|
||||
"group:circle yuki",
|
||||
"group:circlesprocket",
|
||||
"group:citoron soft",
|
||||
"group:citric acid1350",
|
||||
"group:clammbon",
|
||||
"group:cleanliness.",
|
||||
"group:cleari tei",
|
||||
@@ -1408,6 +1416,7 @@ object Group : TagList {
|
||||
"group:dolcecanto",
|
||||
"group:dollproject",
|
||||
"group:dom joshidan",
|
||||
"group:donaora",
|
||||
"group:donburi beya",
|
||||
"group:dondondon",
|
||||
"group:dont understand",
|
||||
@@ -1458,6 +1467,7 @@ object Group : TagList {
|
||||
"group:drill",
|
||||
"group:drill biyori",
|
||||
"group:dro-ya",
|
||||
"group:dropwortbell",
|
||||
"group:drug slash tag slash 21",
|
||||
"group:dual beat",
|
||||
"group:dualtail",
|
||||
@@ -1531,6 +1541,7 @@ object Group : TagList {
|
||||
"group:enshu spirits",
|
||||
"group:entgegen",
|
||||
"group:enuhuo",
|
||||
"group:enyidou",
|
||||
"group:ephese",
|
||||
"group:epic lust",
|
||||
"group:epicureansyndrome",
|
||||
@@ -1726,6 +1737,7 @@ object Group : TagList {
|
||||
"group:fuwa fuwa pinkchan",
|
||||
"group:fuwamoko honpo",
|
||||
"group:fuwaten",
|
||||
"group:fuyuzora izumo",
|
||||
"group:fuzukikai",
|
||||
"group:g area",
|
||||
"group:g equals kundow",
|
||||
@@ -1768,9 +1780,11 @@ object Group : TagList {
|
||||
"group:garaku dusk",
|
||||
"group:garakuta shoujo",
|
||||
"group:garakuta-ya",
|
||||
"group:garasu hokou",
|
||||
"group:garbage",
|
||||
"group:gardening bulldog",
|
||||
"group:garnet-works.",
|
||||
"group:garunansa mk-2",
|
||||
"group:garyuh-chitai",
|
||||
"group:gas ketsu jinsei",
|
||||
"group:gasshuukoku netamekoru",
|
||||
@@ -1885,6 +1899,7 @@ object Group : TagList {
|
||||
"group:gpen",
|
||||
"group:gpx",
|
||||
"group:grand cross",
|
||||
"group:grand plie.",
|
||||
"group:graphic l",
|
||||
"group:gravidan",
|
||||
"group:great acta",
|
||||
@@ -1987,6 +2002,9 @@ object Group : TagList {
|
||||
"group:halleluya.",
|
||||
"group:hallenchi planet",
|
||||
"group:hallucigenia",
|
||||
)
|
||||
|
||||
override fun getTags2(): List<String> = listOf(
|
||||
"group:halopack",
|
||||
"group:ham string",
|
||||
"group:ham.",
|
||||
@@ -2002,13 +2020,12 @@ object Group : TagList {
|
||||
"group:hamurabi-dou",
|
||||
"group:hamustar",
|
||||
"group:hamusuta-nonikomi",
|
||||
"group:hana ni arashi.",
|
||||
"group:hana tabako",
|
||||
)
|
||||
|
||||
override fun getTags2() = listOf(
|
||||
"group:hana to ribon",
|
||||
"group:hana x mezo",
|
||||
"group:hanafubuki gorilla",
|
||||
"group:hanahubu",
|
||||
"group:hanaji koubou",
|
||||
"group:hanamachi shimaiten",
|
||||
"group:hanamaru mugen gym",
|
||||
@@ -2037,6 +2054,7 @@ object Group : TagList {
|
||||
"group:happo ryuu",
|
||||
"group:happy birthday",
|
||||
"group:happy flame time",
|
||||
"group:happy log",
|
||||
"group:happy strawberry",
|
||||
"group:happydrop",
|
||||
"group:happywest",
|
||||
@@ -2136,6 +2154,7 @@ object Group : TagList {
|
||||
"group:hetaretch",
|
||||
"group:hetaruya",
|
||||
"group:heya no sumi.",
|
||||
"group:hgt labo",
|
||||
"group:hi at skip",
|
||||
"group:hi plus us",
|
||||
"group:hi-b",
|
||||
@@ -2199,6 +2218,7 @@ object Group : TagList {
|
||||
"group:hiroq",
|
||||
"group:hirouguma",
|
||||
"group:hisagoya",
|
||||
"group:hisou and anchoku",
|
||||
"group:hisuitei",
|
||||
"group:hisyoku no tansansui",
|
||||
"group:hito no fundoshi",
|
||||
@@ -2229,6 +2249,7 @@ object Group : TagList {
|
||||
"group:hokoushayou shingou",
|
||||
"group:hokuroza",
|
||||
"group:holiday school",
|
||||
"group:home not found",
|
||||
"group:homepie koubou",
|
||||
"group:homerun chaya",
|
||||
"group:homuras r comics",
|
||||
@@ -2248,6 +2269,7 @@ object Group : TagList {
|
||||
"group:hook",
|
||||
"group:hooliganism",
|
||||
"group:horiishi horuto",
|
||||
"group:horonabe ken",
|
||||
"group:horrorbabecentral",
|
||||
"group:horsetail",
|
||||
"group:hoshi no hako",
|
||||
@@ -2367,6 +2389,7 @@ object Group : TagList {
|
||||
"group:imobatake",
|
||||
"group:imokenpi",
|
||||
"group:imomushi kyouiku madoguchi",
|
||||
"group:inakahaishin",
|
||||
"group:inaridou shoten",
|
||||
"group:inc satsujinsha",
|
||||
"group:inceton games",
|
||||
@@ -2395,6 +2418,7 @@ object Group : TagList {
|
||||
"group:intoku.info",
|
||||
"group:inudrill.",
|
||||
"group:inukamedou",
|
||||
"group:inukichi club",
|
||||
"group:inukorohouse",
|
||||
"group:inunabe",
|
||||
"group:inuteikoku",
|
||||
@@ -2414,6 +2438,7 @@ object Group : TagList {
|
||||
"group:isada-ke",
|
||||
"group:isami kaihatsu jigyoudan",
|
||||
"group:isamu. no oheya",
|
||||
"group:isanayoruho",
|
||||
"group:ishikari shake nabe doukoukai",
|
||||
"group:ishikorodou",
|
||||
"group:ishimuraya",
|
||||
@@ -2460,6 +2485,7 @@ object Group : TagList {
|
||||
"group:jamadai oukoku",
|
||||
"group:janculsoft",
|
||||
"group:jangarian",
|
||||
"group:jar of elements",
|
||||
"group:jasmin universal village",
|
||||
"group:jeepney.cony",
|
||||
"group:jei c1on-ri",
|
||||
@@ -2535,6 +2561,7 @@ object Group : TagList {
|
||||
"group:k.a.d",
|
||||
"group:k.f.d.",
|
||||
"group:k.o.store",
|
||||
"group:k.z.z. gundan",
|
||||
"group:k2 company",
|
||||
"group:k2 tomo no kai",
|
||||
"group:k3",
|
||||
@@ -2555,6 +2582,7 @@ object Group : TagList {
|
||||
"group:kaeri no kai 2",
|
||||
"group:kaeru soft",
|
||||
"group:kagaku-shitsu.",
|
||||
"group:kage mitsu",
|
||||
"group:kagisawadou",
|
||||
"group:kagishippo",
|
||||
"group:kagiyama baking co ltd",
|
||||
@@ -2584,6 +2612,7 @@ object Group : TagList {
|
||||
"group:kaki purin",
|
||||
"group:kakiabura",
|
||||
"group:kakinotanehitotsubu",
|
||||
"group:kaku shoujo",
|
||||
"group:kakunetu neko punch",
|
||||
"group:kakuzato-ichi",
|
||||
"group:kamaboko higii",
|
||||
@@ -2612,6 +2641,7 @@ object Group : TagList {
|
||||
"group:kangaroo kick",
|
||||
"group:kanimiso pan",
|
||||
"group:kanimiso-tei",
|
||||
"group:kankitudou",
|
||||
"group:kankituteien",
|
||||
"group:kanmi ningyou",
|
||||
"group:kanmidokoro usb",
|
||||
@@ -2840,12 +2870,14 @@ object Group : TagList {
|
||||
"group:kokkishin",
|
||||
"group:koko sou iu mise janainde",
|
||||
"group:kokochikyuu",
|
||||
"group:kokonji honpo",
|
||||
"group:kokonoe",
|
||||
"group:kokonokaya",
|
||||
"group:kokonokiya",
|
||||
"group:kokoro ha koi iro",
|
||||
"group:kokoro no bookmark",
|
||||
"group:kokou no gokutsubushi",
|
||||
"group:koks k yokochou",
|
||||
"group:koku-from-shojo",
|
||||
"group:kokumaro chousei tounyuu",
|
||||
"group:kokuritsu hinanjo",
|
||||
@@ -2916,6 +2948,7 @@ object Group : TagList {
|
||||
"group:kouni yuu",
|
||||
"group:kousaien",
|
||||
"group:kousoku gurihari-tei",
|
||||
"group:kousoku purin",
|
||||
"group:kouzaka-san to makino jimusho",
|
||||
"group:kouzu shoukai",
|
||||
"group:kouzuya",
|
||||
@@ -3047,8 +3080,10 @@ object Group : TagList {
|
||||
"group:lab-ideas",
|
||||
"group:labomagi",
|
||||
"group:lagrangian-point",
|
||||
"group:lagunaseca",
|
||||
"group:laikaloid",
|
||||
"group:lala la",
|
||||
"group:lalapaloosa",
|
||||
"group:lamchat",
|
||||
"group:lamia advisers",
|
||||
"group:landurchin",
|
||||
@@ -3056,6 +3091,7 @@ object Group : TagList {
|
||||
"group:lantern chord",
|
||||
"group:lapis lazuli",
|
||||
"group:lapislazuli triple star",
|
||||
"group:laser beam",
|
||||
"group:lathimania kyouwakoku",
|
||||
"group:latte chaba",
|
||||
"group:lavenderblue",
|
||||
@@ -3171,6 +3207,7 @@ object Group : TagList {
|
||||
"group:m kichibeya",
|
||||
"group:m plus dilore",
|
||||
"group:m slash k club",
|
||||
"group:m-i-p",
|
||||
"group:m-keifu",
|
||||
"group:m-koujou",
|
||||
"group:m-lab.",
|
||||
@@ -3460,6 +3497,7 @@ object Group : TagList {
|
||||
"group:mindcontrolcomics",
|
||||
"group:mine slash mine",
|
||||
"group:minemine kikaku",
|
||||
"group:minimum fuusen",
|
||||
"group:minimum lab",
|
||||
"group:miniomlet ongakudan",
|
||||
"group:minisuka fx",
|
||||
@@ -3481,6 +3519,7 @@ object Group : TagList {
|
||||
"group:misin koujou",
|
||||
"group:misonodenpatou",
|
||||
"group:misoyahonpo",
|
||||
"group:misssail",
|
||||
"group:misuterutein",
|
||||
"group:misutta",
|
||||
"group:misuzu dennou gijutsukenkyuujo",
|
||||
@@ -3531,6 +3570,7 @@ object Group : TagList {
|
||||
"group:moe hentai",
|
||||
"group:moe hina",
|
||||
"group:moe shoujo ryouiki",
|
||||
"group:moegara",
|
||||
"group:moeyuki soft",
|
||||
"group:moezilla-gumi",
|
||||
"group:mofu mofu sheep",
|
||||
@@ -3582,6 +3622,7 @@ object Group : TagList {
|
||||
"group:moon night kitten",
|
||||
"group:moon ruler",
|
||||
"group:moonlegacy",
|
||||
"group:moonlight laboratory",
|
||||
"group:moonrevenge",
|
||||
"group:moonshell",
|
||||
"group:moonsorrow",
|
||||
@@ -3639,6 +3680,7 @@ object Group : TagList {
|
||||
"group:mozuku dokokai",
|
||||
"group:mp",
|
||||
"group:mp0",
|
||||
"group:mr. hokke",
|
||||
"group:mr.jack plus",
|
||||
"group:mr.k",
|
||||
"group:mr.outside",
|
||||
@@ -3765,6 +3807,7 @@ object Group : TagList {
|
||||
"group:namakura dou",
|
||||
"group:namanecotei",
|
||||
"group:namasute koubou",
|
||||
"group:namazuchaya",
|
||||
"group:namekataya",
|
||||
"group:nami-nami restaurant",
|
||||
"group:namida no teinen taishoku",
|
||||
@@ -3906,6 +3949,7 @@ object Group : TagList {
|
||||
"group:nekozamedan",
|
||||
"group:nekuronomikon",
|
||||
"group:nel-zel formula",
|
||||
"group:nemu wa yakiniku ga tabetai",
|
||||
"group:nemurineko",
|
||||
"group:nenashigusa no ie",
|
||||
"group:nendo ningyo",
|
||||
@@ -3961,6 +4005,9 @@ object Group : TagList {
|
||||
"group:nikomark",
|
||||
"group:nikomi omurice",
|
||||
"group:nikoniko company",
|
||||
)
|
||||
|
||||
override fun getTags3(): List<String> = listOf(
|
||||
"group:niku ringo",
|
||||
"group:nikubenki seisakusho",
|
||||
"group:nikudan",
|
||||
@@ -4006,9 +4053,6 @@ object Group : TagList {
|
||||
"group:niwatori",
|
||||
"group:niwatori-ya",
|
||||
"group:niwatoritowani",
|
||||
)
|
||||
|
||||
override fun getTags3() = listOf(
|
||||
"group:niy koubou",
|
||||
"group:niziro",
|
||||
"group:niziyumedokoro",
|
||||
@@ -4025,6 +4069,7 @@ object Group : TagList {
|
||||
"group:nocohica",
|
||||
"group:nodobotoke kingyo",
|
||||
"group:nogusaw puzzle",
|
||||
"group:noir jou entrance",
|
||||
"group:nokishita no rakuen.",
|
||||
"group:nomerikomu",
|
||||
"group:nomigoro.",
|
||||
@@ -4040,7 +4085,6 @@ object Group : TagList {
|
||||
"group:norakurari.",
|
||||
"group:noraneko koubou",
|
||||
"group:noraneko-no-tama",
|
||||
"group:nori5rou",
|
||||
"group:norihee ginjou",
|
||||
"group:noritama-gozen",
|
||||
"group:norn",
|
||||
@@ -4175,6 +4219,7 @@ object Group : TagList {
|
||||
"group:omusubi koubou",
|
||||
"group:on my way",
|
||||
"group:on-show",
|
||||
"group:onabe no naka.",
|
||||
"group:onaka suita domei",
|
||||
"group:onasuga 99-yen",
|
||||
"group:one dollar",
|
||||
@@ -4220,6 +4265,7 @@ object Group : TagList {
|
||||
"group:ororiya enpitsudo",
|
||||
"group:osanagokoro no kimi ni",
|
||||
"group:osaru-san panic",
|
||||
"group:oshaburi tengoku",
|
||||
"group:oshigoto no jikan",
|
||||
"group:osouzaiya-san",
|
||||
"group:osova",
|
||||
@@ -4439,6 +4485,7 @@ object Group : TagList {
|
||||
"group:pockyfactory",
|
||||
"group:poco black",
|
||||
"group:poco poco",
|
||||
"group:poino",
|
||||
"group:poisonblues",
|
||||
"group:poiyo dimension",
|
||||
"group:pokachutei",
|
||||
@@ -4464,6 +4511,7 @@ object Group : TagList {
|
||||
"group:poppin stompin",
|
||||
"group:popship",
|
||||
"group:popularplus",
|
||||
"group:porcini",
|
||||
"group:porno maker",
|
||||
"group:potato house",
|
||||
"group:poteto dango",
|
||||
@@ -4544,7 +4592,6 @@ object Group : TagList {
|
||||
"group:rabbit kuukan",
|
||||
"group:rabbits",
|
||||
"group:rabitan",
|
||||
"group:rabu.",
|
||||
"group:raccoondog",
|
||||
"group:radiant slash h plus",
|
||||
"group:radiostar",
|
||||
@@ -4797,6 +4844,7 @@ object Group : TagList {
|
||||
"group:sakura mangekyou",
|
||||
"group:sakura naomiki",
|
||||
"group:sakura nigou",
|
||||
"group:sakura no tomoru hie",
|
||||
"group:sakura prin",
|
||||
"group:sakura zensen",
|
||||
"group:sakuradou",
|
||||
@@ -4827,6 +4875,7 @@ object Group : TagList {
|
||||
"group:sangenshokudou",
|
||||
"group:sanjuuhachi shiki kikanjuu",
|
||||
"group:sankaku apron",
|
||||
"group:sankaku button",
|
||||
"group:sankaku doumei",
|
||||
"group:sankokudo",
|
||||
"group:sanma kizoku",
|
||||
@@ -5081,6 +5130,7 @@ object Group : TagList {
|
||||
"group:shonen gekikuukan",
|
||||
"group:shonnaka-dou",
|
||||
"group:shootouts",
|
||||
"group:short kami",
|
||||
"group:shortcut koubou",
|
||||
"group:shosekido",
|
||||
"group:shouchuu mac",
|
||||
@@ -5110,6 +5160,7 @@ object Group : TagList {
|
||||
"group:shuryousha",
|
||||
"group:shuto reccara",
|
||||
"group:shuueisha",
|
||||
"group:shuukyuu 8-ka",
|
||||
"group:shuukyuu itsukasei",
|
||||
"group:shuutaisei",
|
||||
"group:shyness over drive",
|
||||
@@ -5143,9 +5194,9 @@ object Group : TagList {
|
||||
"group:siop",
|
||||
"group:sioyaki",
|
||||
"group:sioyude",
|
||||
"group:sirisiri denbu club",
|
||||
"group:sirius soft",
|
||||
"group:siro house",
|
||||
"group:siropome",
|
||||
"group:sirouto plan",
|
||||
"group:sirubedou",
|
||||
"group:sisinabeya",
|
||||
@@ -5261,6 +5312,7 @@ object Group : TagList {
|
||||
"group:st. rio",
|
||||
"group:st. rororo",
|
||||
"group:staccato squirrel",
|
||||
"group:stamina teishoku",
|
||||
"group:standard azarashi",
|
||||
"group:star link",
|
||||
"group:star-dreamer tei",
|
||||
@@ -5402,7 +5454,6 @@ object Group : TagList {
|
||||
"group:sushi-go-round",
|
||||
"group:suteinu nursery",
|
||||
"group:sutekiplan",
|
||||
"group:suzuki masahisa",
|
||||
"group:suzuki shouten",
|
||||
"group:suzunaridou",
|
||||
"group:suzupony",
|
||||
@@ -5421,6 +5472,7 @@ object Group : TagList {
|
||||
"group:syamisen koubou",
|
||||
"group:synthetic garden",
|
||||
"group:syonen-kikakugai.",
|
||||
"group:syounan rakujin society",
|
||||
"group:syounen heroine",
|
||||
"group:syounen kouraku",
|
||||
"group:syouseki kessyou",
|
||||
@@ -5474,6 +5526,7 @@ object Group : TagList {
|
||||
"group:takara no suzunari",
|
||||
"group:takashi-ya",
|
||||
"group:takayashiki kaihatsu",
|
||||
"group:take4 project",
|
||||
"group:takeda syouten",
|
||||
"group:takegamiya",
|
||||
"group:takeshidou-chou",
|
||||
@@ -5494,6 +5547,7 @@ object Group : TagList {
|
||||
"group:tamatamasanmyaku",
|
||||
"group:tamokuteki hall",
|
||||
"group:tamokuteki kuukan",
|
||||
"group:tanajou",
|
||||
"group:tanaka shouten",
|
||||
"group:tanaura honpo",
|
||||
"group:tanetsuke ichinengo",
|
||||
@@ -5526,6 +5580,7 @@ object Group : TagList {
|
||||
"group:team tanabe",
|
||||
"group:team z and 3n",
|
||||
"group:team zero",
|
||||
"group:team4000",
|
||||
"group:tears of ymir",
|
||||
"group:teatime",
|
||||
"group:tecchitecchi",
|
||||
@@ -5534,9 +5589,11 @@ object Group : TagList {
|
||||
"group:tedaingu",
|
||||
"group:teddy-plaza",
|
||||
"group:teemonk",
|
||||
"group:teiesuya",
|
||||
"group:teihatu syouzyo titai",
|
||||
"group:teikiatu de ikou",
|
||||
"group:teikoku club",
|
||||
"group:tekirororock",
|
||||
"group:tekken neko gourmet",
|
||||
"group:tekoman-dou",
|
||||
"group:telomere limiter",
|
||||
@@ -5718,6 +5775,7 @@ object Group : TagList {
|
||||
"group:toumei tsuushin",
|
||||
"group:toushi ryoku kenkyuujo",
|
||||
"group:toutaku tuyagadou",
|
||||
"group:touyou bujutsu gakkou",
|
||||
"group:touyu okiba kari",
|
||||
"group:touyu stand",
|
||||
"group:touzoku tachi no rakuda no mure",
|
||||
@@ -5918,6 +5976,7 @@ object Group : TagList {
|
||||
"group:usagi youjinbou",
|
||||
"group:usagijiru",
|
||||
"group:usagikoara",
|
||||
"group:usaginoheya",
|
||||
"group:usagitei",
|
||||
"group:usako kf",
|
||||
"group:usamimi syndrome",
|
||||
@@ -5949,337 +6008,5 @@ object Group : TagList {
|
||||
"group:vagina dentata",
|
||||
"group:valiant",
|
||||
"group:valkyria",
|
||||
"group:valkyrieharlem",
|
||||
"group:vanilla fuumi shoujo",
|
||||
"group:vanilla star",
|
||||
"group:vanilla-dou max",
|
||||
"group:variety pot",
|
||||
"group:vashadow",
|
||||
"group:vc productions",
|
||||
"group:venom",
|
||||
"group:veronica no ha",
|
||||
"group:violence asia team",
|
||||
"group:visual biscuits",
|
||||
"group:vitamin gohan",
|
||||
"group:vitamin soft",
|
||||
"group:vitamin x",
|
||||
"group:vivi-sectr",
|
||||
"group:vivid color",
|
||||
"group:vivid dot",
|
||||
"group:vivido",
|
||||
"group:viweb",
|
||||
"group:voltcompany.",
|
||||
"group:vpans extasy",
|
||||
"group:vulcannu",
|
||||
"group:w at nd",
|
||||
"group:wabi sabi wasabi",
|
||||
"group:wagamama dou",
|
||||
"group:wakaba syokei",
|
||||
"group:wakarase seisaku iinkai",
|
||||
"group:wakatobi",
|
||||
"group:wakusei-teki shukou",
|
||||
"group:wakuwaku boycott",
|
||||
"group:wakuwaku doubutsuen",
|
||||
"group:walhalla illusion",
|
||||
"group:wamusho",
|
||||
"group:wanko-tei",
|
||||
"group:wankyoku canvas",
|
||||
"group:wanwandoh",
|
||||
"group:warabimochi",
|
||||
"group:waretama",
|
||||
"group:warp loop",
|
||||
"group:washikul.",
|
||||
"group:washokudeniku.",
|
||||
"group:wata 120 percent",
|
||||
"group:wataame",
|
||||
"group:watagashi maker",
|
||||
"group:watanabe tou",
|
||||
"group:wataru kuya",
|
||||
"group:water ducts",
|
||||
"group:waterfall",
|
||||
"group:waterspoon",
|
||||
"group:waterstudio",
|
||||
"group:waterwheel",
|
||||
"group:weather report",
|
||||
"group:webstudioofflimits",
|
||||
"group:wellca",
|
||||
"group:wendybell",
|
||||
"group:west vision",
|
||||
"group:whirl-wind",
|
||||
"group:white out",
|
||||
"group:white plus dk",
|
||||
"group:white rocket",
|
||||
)
|
||||
|
||||
override fun getTags4() = listOf(
|
||||
"group:whitecloth",
|
||||
"group:whitesoft",
|
||||
"group:whitewill",
|
||||
"group:wi-fe hacker",
|
||||
"group:wicked heart",
|
||||
"group:wild goat",
|
||||
"group:will tame",
|
||||
"group:willow soft",
|
||||
"group:wind mail",
|
||||
"group:wind of the keep valley",
|
||||
"group:windarteam",
|
||||
"group:winger in mind",
|
||||
"group:winghills",
|
||||
"group:wish",
|
||||
"group:wish kibou no tsubasa",
|
||||
"group:witchflame",
|
||||
"group:witching hour entertainment",
|
||||
"group:wizard",
|
||||
"group:wolf fang dou",
|
||||
"group:wonderland 203",
|
||||
"group:wope-retta",
|
||||
"group:world of porncraft",
|
||||
"group:world temperament",
|
||||
"group:worstworks",
|
||||
"group:wriggle souzeme tomonokai",
|
||||
"group:x model",
|
||||
"group:x-tei",
|
||||
"group:xephs artwork",
|
||||
"group:xl-toons",
|
||||
"group:xx koubou",
|
||||
"group:xxxxxxx",
|
||||
"group:xyzyroh",
|
||||
"group:y slash s slash k",
|
||||
"group:y.d.l",
|
||||
"group:y.m.sensha",
|
||||
"group:yaboudo project",
|
||||
"group:yabougumi",
|
||||
"group:yabukaradou",
|
||||
"group:yaburi dokoro",
|
||||
"group:yadapot",
|
||||
"group:yadokugaeru",
|
||||
"group:yadoo van yahdoo",
|
||||
"group:yagi q-syah",
|
||||
"group:yago no ana",
|
||||
"group:yajilshi plus",
|
||||
"group:yajirushi key",
|
||||
"group:yajiya",
|
||||
"group:yakata",
|
||||
"group:yakimisomura",
|
||||
"group:yakinasu teishoku",
|
||||
"group:yakiniku teishoku",
|
||||
"group:yakisaketeishoku",
|
||||
"group:yakisoba pants",
|
||||
"group:yakisoba rengo",
|
||||
"group:yakiubu",
|
||||
"group:yakousei fan club",
|
||||
"group:yaku 40 man sarad",
|
||||
"group:yakumi sarai",
|
||||
"group:yakusyo",
|
||||
"group:yakutai",
|
||||
"group:yam x 2 dai teikoku",
|
||||
"group:yamada ichizoku.",
|
||||
"group:yamadamaya",
|
||||
"group:yamadaya",
|
||||
"group:yamagiwa art cg studio",
|
||||
"group:yamaguchirou",
|
||||
"group:yamakawa denenhuukei",
|
||||
"group:yamami no yado",
|
||||
"group:yamamori gohan",
|
||||
"group:yamanaka no naka",
|
||||
"group:yamano murao",
|
||||
"group:yami ni ugomeku",
|
||||
"group:yamikumo tsuushin",
|
||||
"group:yamotodou rakugakiichi",
|
||||
"group:yanagiba dai",
|
||||
"group:yanagisegawa",
|
||||
"group:yanasegawabeya",
|
||||
"group:yangyang nickbow",
|
||||
"group:yanmarumaa",
|
||||
"group:yaou keikaku",
|
||||
"group:yaoyorozu-kobo",
|
||||
"group:yaoyorozudo",
|
||||
"group:yasai batake",
|
||||
"group:yaseuma lo-ru",
|
||||
"group:yashiya",
|
||||
"group:yashock kaigi",
|
||||
"group:yasrin-do",
|
||||
"group:yasudajuku",
|
||||
"group:yasuomi-craft",
|
||||
"group:yasyokutei",
|
||||
"group:yatuumi no kagami",
|
||||
"group:yawaraka okashiya",
|
||||
"group:yaya hinata-ya",
|
||||
"group:yes sir.",
|
||||
"group:yggdrasil",
|
||||
"group:yo-metdo",
|
||||
"group:yoban left",
|
||||
"group:yobigakka",
|
||||
"group:yohsyuan",
|
||||
"group:yojouhan factory",
|
||||
"group:yojouhan shobou",
|
||||
"group:yojouhan toshi",
|
||||
"group:yokan musume",
|
||||
"group:yokazetei",
|
||||
"group:yokohama zza koubou",
|
||||
"group:yokoshimanchi.",
|
||||
"group:yokoshimaya",
|
||||
"group:yokoshoku ice",
|
||||
"group:yomamagoto",
|
||||
"group:yomosue doukoukai",
|
||||
"group:yomothuhirasaka",
|
||||
"group:yonatan black mutou",
|
||||
"group:yonbangai garo",
|
||||
"group:yonjuichi",
|
||||
"group:yonmasuya",
|
||||
"group:yonurime",
|
||||
"group:yorando",
|
||||
"group:yoru no benkyoukai",
|
||||
"group:yoru no okazu shokudou",
|
||||
"group:yoruyama no kyuukeijo",
|
||||
"group:yoseatume tekina nanika",
|
||||
"group:yoshida gorou shoukai",
|
||||
"group:yoshiga dokoro",
|
||||
"group:yoshii tech sha",
|
||||
"group:yotukuro",
|
||||
"group:you",
|
||||
"group:you you tsuushin",
|
||||
"group:you you you",
|
||||
"group:youchi-na-ochakai",
|
||||
"group:youen bijo no garou",
|
||||
"group:youkai ankake",
|
||||
"group:youkai tamanokoshi",
|
||||
"group:youkandou",
|
||||
"group:youki m.k.c.",
|
||||
"group:youmusya",
|
||||
"group:yours-wow",
|
||||
"group:yousei allergen",
|
||||
"group:youseimangasya",
|
||||
"group:youtoujirushi",
|
||||
"group:youtsuu transmitter",
|
||||
"group:youyukai",
|
||||
"group:yowatari kouba",
|
||||
"group:yoyude ikemasu",
|
||||
"group:yozorairodrops",
|
||||
"group:ys company",
|
||||
"group:yu-ta.18",
|
||||
"group:yu-yu-tei",
|
||||
"group:yuasa rengou",
|
||||
"group:yubidou",
|
||||
"group:yudenakya nama-beer",
|
||||
"group:yudokuya",
|
||||
"group:yuhshiki",
|
||||
"group:yukagenikaga",
|
||||
"group:yukeyuke ryuseigo",
|
||||
"group:yuki daruma koujou",
|
||||
"group:yuki no iori",
|
||||
"group:yukijirushi nyuugyou",
|
||||
"group:yukikagerou",
|
||||
"group:yukimi honpo",
|
||||
"group:yukimura",
|
||||
"group:yukinko okeya",
|
||||
"group:yukino koubou",
|
||||
"group:yukirinrin",
|
||||
"group:yukyu-kyuka",
|
||||
"group:yume bouei shoujo tai",
|
||||
"group:yume yori suteki na",
|
||||
"group:yume-zakura",
|
||||
"group:yumeiro-goromo",
|
||||
"group:yumeizukosya",
|
||||
"group:yumemigachi campus",
|
||||
"group:yumemigokoti",
|
||||
"group:yumemiru-kikai",
|
||||
"group:yumenamakon",
|
||||
"group:yumenokage",
|
||||
"group:yumeoikyounouta",
|
||||
"group:yumeoukoku",
|
||||
"group:yunabon",
|
||||
"group:yurayuraseyuura",
|
||||
"group:yureika blade",
|
||||
"group:yuri dokidoki",
|
||||
"group:yuri equal 18l",
|
||||
"group:yuriai kojinshi kai",
|
||||
"group:yurinyurin",
|
||||
"group:yuriru-rarika",
|
||||
"group:yurumebox",
|
||||
"group:yuruyakatou",
|
||||
"group:yuruyuru seisakusho",
|
||||
"group:yusuzumi",
|
||||
"group:yuu adashino suisan",
|
||||
"group:yuu heya",
|
||||
"group:yuubin basha",
|
||||
"group:yuudachitei",
|
||||
"group:yuuendou",
|
||||
"group:yuugai tosho kikaku",
|
||||
"group:yuugen kaisha sokuhou seisakusho",
|
||||
"group:yuugensangyou sukimakaze",
|
||||
"group:yuugure koubou",
|
||||
"group:yuuhodou",
|
||||
"group:yuujikouji",
|
||||
"group:yuukakumin",
|
||||
"group:yuuki kagoubutsu",
|
||||
"group:yuukyuu shinden",
|
||||
"group:yuukyuu suisenkan",
|
||||
"group:yuunagi gaibutai",
|
||||
"group:yuuriko",
|
||||
"group:yuusha kandenchi",
|
||||
"group:yuutopia",
|
||||
"group:yuuya-yuu",
|
||||
"group:yuyake box",
|
||||
"group:yuzu soft",
|
||||
"group:yuzucha",
|
||||
"group:yuzuen",
|
||||
"group:yuzumomo jam",
|
||||
"group:yuzuonsen",
|
||||
"group:yuzuponz",
|
||||
"group:yuzurihaya",
|
||||
"group:z",
|
||||
"group:z-tabukuroneko house",
|
||||
"group:z-vector",
|
||||
"group:za da carjya",
|
||||
"group:zakkin kougyou",
|
||||
"group:zan-sei",
|
||||
"group:zankirow",
|
||||
"group:zankoku doumei",
|
||||
"group:zankoku na kami ga shihai suru",
|
||||
"group:zantetuken",
|
||||
"group:zasshu-ken",
|
||||
"group:zassoubatake",
|
||||
"group:zassoya",
|
||||
"group:zatouichi",
|
||||
"group:zatuyou gakari",
|
||||
"group:zawameki jambo",
|
||||
"group:zeiniku shoujotai",
|
||||
"group:zenmai koubou",
|
||||
"group:zenmai kourogi",
|
||||
"group:zennihon do-m jitsuryoku kentei kousa",
|
||||
"group:zenoside",
|
||||
"group:zenra restaurant",
|
||||
"group:zensekai yakenohara doumei",
|
||||
"group:zenshuu bougyo",
|
||||
"group:zensoku zenkai.",
|
||||
"group:zensun habaku",
|
||||
"group:zenzidou kosyubenjo",
|
||||
"group:zero byte",
|
||||
"group:zero equals mono",
|
||||
"group:zero ni kaeru tsuki",
|
||||
"group:zero-one",
|
||||
"group:zero-sen",
|
||||
"group:zero-xx",
|
||||
"group:zerocool",
|
||||
"group:zeroinfinityone",
|
||||
"group:zeryishi",
|
||||
"group:zettai shoujo",
|
||||
"group:zettaitensei",
|
||||
"group:zgf",
|
||||
"group:zi",
|
||||
"group:zion",
|
||||
"group:zmey no soukutsu",
|
||||
"group:zokubutsu.zip",
|
||||
"group:zombie to yukaina nakamatachi",
|
||||
"group:zonzon sharp 4",
|
||||
"group:zooerastia",
|
||||
"group:zouri no sato",
|
||||
"group:zozalist",
|
||||
"group:zugaikotsu marudashi",
|
||||
"group:zvizva-dan",
|
||||
"group:zydan",
|
||||
"group:zyulokuya",
|
||||
"group:zzz comics",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Group2 : TagList {
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"group:valkyrieharlem",
|
||||
"group:vanilla fuumi shoujo",
|
||||
"group:vanilla star",
|
||||
"group:vanilla-dou max",
|
||||
"group:variety pot",
|
||||
"group:vashadow",
|
||||
"group:vc productions",
|
||||
"group:venom",
|
||||
"group:veronica no ha",
|
||||
"group:violence asia team",
|
||||
"group:visual biscuits",
|
||||
"group:vitamin gohan",
|
||||
"group:vitamin soft",
|
||||
"group:vitamin x",
|
||||
"group:vivi-sectr",
|
||||
"group:vivid color",
|
||||
"group:vivid dot",
|
||||
"group:vivido",
|
||||
"group:viweb",
|
||||
"group:voltcompany.",
|
||||
"group:vpans extasy",
|
||||
"group:vulcannu",
|
||||
"group:w at nd",
|
||||
"group:wabi sabi wasabi",
|
||||
"group:wagamama dou",
|
||||
"group:wakaba syokei",
|
||||
"group:wakarase seisaku iinkai",
|
||||
"group:wakatobi",
|
||||
"group:wakusei-teki shukou",
|
||||
"group:wakuwaku boycott",
|
||||
"group:wakuwaku doubutsuen",
|
||||
"group:walhalla illusion",
|
||||
"group:wamusho",
|
||||
"group:wanko-tei",
|
||||
"group:wankyoku canvas",
|
||||
"group:wanwandoh",
|
||||
"group:warabimochi",
|
||||
"group:waretama",
|
||||
"group:warp loop",
|
||||
"group:wasa wasa",
|
||||
"group:washikul.",
|
||||
"group:washokudeniku.",
|
||||
"group:wata 120 percent",
|
||||
"group:wataame",
|
||||
"group:watagashi maker",
|
||||
"group:watanabe tou",
|
||||
"group:wataru kuya",
|
||||
"group:water ducts",
|
||||
"group:waterfall",
|
||||
"group:waterspoon",
|
||||
"group:waterstudio",
|
||||
"group:waterwheel",
|
||||
"group:weather report",
|
||||
"group:webstudioofflimits",
|
||||
"group:wellca",
|
||||
"group:wendybell",
|
||||
"group:west vision",
|
||||
"group:whirl-wind",
|
||||
"group:white out",
|
||||
"group:white plus dk",
|
||||
"group:white rocket",
|
||||
"group:whitecloth",
|
||||
"group:whitesoft",
|
||||
"group:whitewill",
|
||||
"group:wi-fe hacker",
|
||||
"group:wicked heart",
|
||||
"group:wild goat",
|
||||
"group:will tame",
|
||||
"group:willow soft",
|
||||
"group:wind mail",
|
||||
"group:wind of the keep valley",
|
||||
"group:windarteam",
|
||||
"group:winger in mind",
|
||||
"group:winghills",
|
||||
"group:wish",
|
||||
"group:wish kibou no tsubasa",
|
||||
"group:witchflame",
|
||||
"group:witching hour entertainment",
|
||||
"group:wizard",
|
||||
"group:wolf fang dou",
|
||||
"group:wonderland 203",
|
||||
"group:wope-retta",
|
||||
"group:world of porncraft",
|
||||
"group:world temperament",
|
||||
"group:worstworks",
|
||||
"group:wriggle souzeme tomonokai",
|
||||
"group:x model",
|
||||
"group:x-tei",
|
||||
"group:xephs artwork",
|
||||
"group:xl-toons",
|
||||
"group:xx koubou",
|
||||
"group:xxxxxxx",
|
||||
"group:xyzyroh",
|
||||
"group:y slash s slash k",
|
||||
"group:y.d.l",
|
||||
"group:y.m.sensha",
|
||||
"group:yaboudo project",
|
||||
"group:yabougumi",
|
||||
"group:yabukaradou",
|
||||
"group:yaburi dokoro",
|
||||
"group:yadapot",
|
||||
"group:yadokugaeru",
|
||||
"group:yadoo van yahdoo",
|
||||
"group:yagi q-syah",
|
||||
"group:yago no ana",
|
||||
"group:yajilshi plus",
|
||||
"group:yajirushi key",
|
||||
"group:yajiya",
|
||||
"group:yakata",
|
||||
"group:yakimisomura",
|
||||
"group:yakinasu teishoku",
|
||||
"group:yakiniku teishoku",
|
||||
"group:yakisaketeishoku",
|
||||
"group:yakisoba pants",
|
||||
"group:yakisoba rengo",
|
||||
"group:yakiubu",
|
||||
"group:yakousei fan club",
|
||||
"group:yaku 40 man sarad",
|
||||
"group:yakumi sarai",
|
||||
"group:yakusyo",
|
||||
"group:yakutai",
|
||||
"group:yam x 2 dai teikoku",
|
||||
"group:yamada ichizoku.",
|
||||
"group:yamadamaya",
|
||||
"group:yamadaya",
|
||||
"group:yamagiwa art cg studio",
|
||||
"group:yamaguchirou",
|
||||
"group:yamakawa denenhuukei",
|
||||
"group:yamami no yado",
|
||||
"group:yamamori gohan",
|
||||
"group:yamanaka no naka",
|
||||
"group:yamano murao",
|
||||
"group:yamato nadeshiko club",
|
||||
"group:yami ni ugomeku",
|
||||
"group:yamikumo tsuushin",
|
||||
"group:yamotodou rakugakiichi",
|
||||
"group:yanagiba dai",
|
||||
"group:yanagisegawa",
|
||||
"group:yanasegawabeya",
|
||||
"group:yangyang nickbow",
|
||||
"group:yanmarumaa",
|
||||
"group:yaou keikaku",
|
||||
"group:yaoyorozu-kobo",
|
||||
"group:yaoyorozudo",
|
||||
"group:yasai batake",
|
||||
"group:yaseuma lo-ru",
|
||||
"group:yashiya",
|
||||
"group:yashock kaigi",
|
||||
"group:yasrin-do",
|
||||
"group:yasudajuku",
|
||||
"group:yasuomi-craft",
|
||||
"group:yasyokutei",
|
||||
"group:yatomomin",
|
||||
"group:yatuumi no kagami",
|
||||
"group:yawaraka okashiya",
|
||||
"group:yaya hinata-ya",
|
||||
"group:yes sir.",
|
||||
"group:yggdrasil",
|
||||
"group:yo-metdo",
|
||||
"group:yoban left",
|
||||
"group:yobigakka",
|
||||
"group:yohsyuan",
|
||||
"group:yojouhan factory",
|
||||
"group:yojouhan shobou",
|
||||
"group:yojouhan toshi",
|
||||
"group:yokan musume",
|
||||
"group:yokazetei",
|
||||
"group:yokohama zza koubou",
|
||||
"group:yokoshimanchi.",
|
||||
"group:yokoshimaya",
|
||||
"group:yokoshoku ice",
|
||||
"group:yoku mireba beta",
|
||||
"group:yomamagoto",
|
||||
"group:yomosue doukoukai",
|
||||
"group:yomothuhirasaka",
|
||||
"group:yonatan black mutou",
|
||||
"group:yonbangai garo",
|
||||
"group:yonjuichi",
|
||||
"group:yonmasuya",
|
||||
"group:yonurime",
|
||||
"group:yorando",
|
||||
"group:yoru no benkyoukai",
|
||||
"group:yoru no okazu shokudou",
|
||||
"group:yoruyama no kyuukeijo",
|
||||
"group:yoseatume tekina nanika",
|
||||
"group:yoshida gorou shoukai",
|
||||
"group:yoshiga dokoro",
|
||||
"group:yoshii tech sha",
|
||||
"group:yotukuro",
|
||||
"group:you",
|
||||
"group:you you tsuushin",
|
||||
"group:you you you",
|
||||
"group:youchi-na-ochakai",
|
||||
"group:youen bijo no garou",
|
||||
"group:yougensya",
|
||||
"group:youkai ankake",
|
||||
"group:youkai tamanokoshi",
|
||||
"group:youkandou",
|
||||
"group:youki m.k.c.",
|
||||
"group:youmusya",
|
||||
"group:yours-wow",
|
||||
"group:yousei allergen",
|
||||
"group:youseimangasya",
|
||||
"group:youtoujirushi",
|
||||
"group:youtsuu transmitter",
|
||||
"group:youyukai",
|
||||
"group:yowatari kouba",
|
||||
"group:yoyude ikemasu",
|
||||
"group:yozorairodrops",
|
||||
"group:ys company",
|
||||
"group:yu-ta.18",
|
||||
"group:yu-yu-tei",
|
||||
"group:yuasa rengou",
|
||||
"group:yubidou",
|
||||
"group:yudenakya nama-beer",
|
||||
"group:yudokuya",
|
||||
"group:yuhshiki",
|
||||
"group:yukagenikaga",
|
||||
"group:yukan high zakura",
|
||||
"group:yukeyuke ryuseigo",
|
||||
"group:yuki daruma koujou",
|
||||
"group:yuki no iori",
|
||||
"group:yukijirushi nyuugyou",
|
||||
"group:yukikagerou",
|
||||
"group:yukimi honpo",
|
||||
"group:yukimura",
|
||||
"group:yukinko okeya",
|
||||
"group:yukino koubou",
|
||||
"group:yukirinrin",
|
||||
"group:yukyu-kyuka",
|
||||
"group:yume bouei shoujo tai",
|
||||
"group:yume yori suteki na",
|
||||
"group:yume-zakura",
|
||||
"group:yumeiro-goromo",
|
||||
"group:yumeizukosya",
|
||||
"group:yumemigachi campus",
|
||||
"group:yumemigokoti",
|
||||
"group:yumemiru-kikai",
|
||||
"group:yumenamakon",
|
||||
"group:yumenokage",
|
||||
"group:yumeoikyounouta",
|
||||
"group:yumeoukoku",
|
||||
"group:yunabon",
|
||||
"group:yurayuraseyuura",
|
||||
"group:yureika blade",
|
||||
"group:yuri dokidoki",
|
||||
"group:yuri equal 18l",
|
||||
"group:yuriai kojinshi kai",
|
||||
"group:yurinyurin",
|
||||
"group:yuriru-rarika",
|
||||
"group:yurumebox",
|
||||
"group:yuruyakatou",
|
||||
"group:yuruyuru seisakusho",
|
||||
"group:yusuzumi",
|
||||
"group:yuu adashino suisan",
|
||||
"group:yuu heya",
|
||||
"group:yuubin basha",
|
||||
"group:yuudachitei",
|
||||
"group:yuuendou",
|
||||
"group:yuugai tosho kikaku",
|
||||
"group:yuugen kaisha sokuhou seisakusho",
|
||||
"group:yuugensangyou sukimakaze",
|
||||
"group:yuugure koubou",
|
||||
"group:yuuhodou",
|
||||
"group:yuujikouji",
|
||||
"group:yuukakumin",
|
||||
"group:yuuki kagoubutsu",
|
||||
"group:yuukyuu shinden",
|
||||
"group:yuukyuu suisenkan",
|
||||
"group:yuunagi gaibutai",
|
||||
"group:yuuriko",
|
||||
"group:yuusha kandenchi",
|
||||
"group:yuutopia",
|
||||
"group:yuuya-yuu",
|
||||
"group:yuyake box",
|
||||
"group:yuzu soft",
|
||||
"group:yuzucha",
|
||||
"group:yuzuen",
|
||||
"group:yuzumomo jam",
|
||||
"group:yuzuonsen",
|
||||
"group:yuzuponz",
|
||||
"group:yuzurihaya",
|
||||
"group:z",
|
||||
"group:z-tabukuroneko house",
|
||||
"group:z-vector",
|
||||
"group:za da carjya",
|
||||
"group:zakkin kougyou",
|
||||
"group:zan-sei",
|
||||
"group:zankirow",
|
||||
"group:zankoku doumei",
|
||||
"group:zankoku na kami ga shihai suru",
|
||||
"group:zantetuken",
|
||||
"group:zasshu-ken",
|
||||
"group:zassoubatake",
|
||||
"group:zassoya",
|
||||
"group:zatouichi",
|
||||
"group:zatuyou gakari",
|
||||
"group:zawameki jambo",
|
||||
"group:zeiniku shoujotai",
|
||||
"group:zenmai koubou",
|
||||
"group:zenmai kourogi",
|
||||
"group:zennihon do-m jitsuryoku kentei kousa",
|
||||
"group:zenoside",
|
||||
"group:zenra restaurant",
|
||||
"group:zenryoku sissou cat",
|
||||
"group:zensekai yakenohara doumei",
|
||||
"group:zenshuu bougyo",
|
||||
"group:zensoku zenkai.",
|
||||
"group:zensun habaku",
|
||||
"group:zenzidou kosyubenjo",
|
||||
"group:zero byte",
|
||||
"group:zero equals mono",
|
||||
"group:zero ni kaeru tsuki",
|
||||
"group:zero-one",
|
||||
"group:zero-sen",
|
||||
"group:zero-xx",
|
||||
"group:zerocool",
|
||||
"group:zeroinfinityone",
|
||||
"group:zeryishi",
|
||||
"group:zettai shoujo",
|
||||
"group:zettaitensei",
|
||||
"group:zgf",
|
||||
"group:zi",
|
||||
"group:zion",
|
||||
"group:zmey no soukutsu",
|
||||
"group:zokubutsu.zip",
|
||||
"group:zombie to yukaina nakamatachi",
|
||||
"group:zonzon sharp 4",
|
||||
"group:zooerastia",
|
||||
"group:zouri no sato",
|
||||
"group:zozalist",
|
||||
"group:zugaikotsu marudashi",
|
||||
"group:zvizva-dan",
|
||||
"group:zydan",
|
||||
"group:zyulokuya",
|
||||
"group:zzz comics",
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Language : TagList {
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"language:arabic",
|
||||
"language:bulgarian",
|
||||
"language:catalan",
|
||||
|
||||
@@ -1,28 +1,39 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Male : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"male:abortion",
|
||||
"male:absorption",
|
||||
"male:adventitious penis",
|
||||
"male:afro",
|
||||
"male:age progression",
|
||||
"male:age regression",
|
||||
"male:ahegao",
|
||||
"male:albino",
|
||||
"male:alien",
|
||||
"male:all the way through",
|
||||
"male:amputee",
|
||||
"male:anal",
|
||||
"male:anal birth",
|
||||
"male:anal intercourse",
|
||||
"male:anal prolapse",
|
||||
"male:analphagia",
|
||||
"male:angel",
|
||||
"male:animal on animal",
|
||||
"male:animal on furry",
|
||||
"male:animegao",
|
||||
"male:anorexic",
|
||||
"male:apparel bukkake",
|
||||
"male:apron",
|
||||
"male:armpit licking",
|
||||
"male:armpit sex",
|
||||
"male:asphyxiation",
|
||||
"male:ass expansion",
|
||||
"male:assjob",
|
||||
"male:autofellatio",
|
||||
"male:bald",
|
||||
"male:ball sucking",
|
||||
"male:balljob",
|
||||
"male:balls expansion",
|
||||
"male:bandages",
|
||||
"male:bat boy",
|
||||
@@ -30,10 +41,14 @@ object Male : TagList {
|
||||
"male:bdsm",
|
||||
"male:bear",
|
||||
"male:bear boy",
|
||||
"male:beauty mark",
|
||||
"male:bestiality",
|
||||
"male:big areolae",
|
||||
"male:big ass",
|
||||
"male:big balls",
|
||||
"male:big breasts",
|
||||
"male:big lips",
|
||||
"male:big muscles",
|
||||
"male:big nipples",
|
||||
"male:big penis",
|
||||
"male:bike shorts",
|
||||
@@ -47,12 +62,16 @@ object Male : TagList {
|
||||
"male:bloomers",
|
||||
"male:blowjob",
|
||||
"male:blowjob face",
|
||||
"male:body modification",
|
||||
"male:body painting",
|
||||
"male:body swap",
|
||||
"male:body writing",
|
||||
"male:bodystocking",
|
||||
"male:bodysuit",
|
||||
"male:bondage",
|
||||
"male:braces",
|
||||
"male:brain fuck",
|
||||
"male:breast expansion",
|
||||
"male:breast feeding",
|
||||
"male:bride",
|
||||
"male:brother",
|
||||
@@ -62,11 +81,13 @@ object Male : TagList {
|
||||
"male:burping",
|
||||
"male:business suit",
|
||||
"male:butler",
|
||||
"male:camel",
|
||||
"male:cannibalism",
|
||||
"male:cashier",
|
||||
"male:cat",
|
||||
"male:catboy",
|
||||
"male:cbt",
|
||||
"male:centaur",
|
||||
"male:cervix prolapse",
|
||||
"male:chastity belt",
|
||||
"male:cheating",
|
||||
@@ -75,13 +96,20 @@ object Male : TagList {
|
||||
"male:chinese dress",
|
||||
"male:chloroform",
|
||||
"male:christmas",
|
||||
"male:clamp",
|
||||
"male:clit insertion",
|
||||
"male:clit stimulation",
|
||||
"male:clone",
|
||||
"male:closed eyes",
|
||||
"male:clothed female nude male",
|
||||
"male:clown",
|
||||
"male:coach",
|
||||
"male:cock ring",
|
||||
"male:cockphagia",
|
||||
"male:cockslapping",
|
||||
"male:collar",
|
||||
"male:condom",
|
||||
"male:conjoined",
|
||||
"male:coprophagia",
|
||||
"male:corruption",
|
||||
"male:corset",
|
||||
@@ -90,25 +118,39 @@ object Male : TagList {
|
||||
"male:cowman",
|
||||
"male:crab",
|
||||
"male:crossdressing",
|
||||
"male:crotch tattoo",
|
||||
"male:crown",
|
||||
"male:crying",
|
||||
"male:cum bath",
|
||||
"male:cumflation",
|
||||
"male:cunnilingus",
|
||||
"male:cuntboy",
|
||||
"male:dark nipples",
|
||||
"male:dark sclera",
|
||||
"male:dark skin",
|
||||
"male:deepthroat",
|
||||
"male:deer",
|
||||
"male:deer boy",
|
||||
"male:demon",
|
||||
"male:denki anma",
|
||||
"male:detached sleeves",
|
||||
"male:diaper",
|
||||
"male:dick growth",
|
||||
"male:dickgirl on male",
|
||||
"male:dicknipples",
|
||||
"male:dilf",
|
||||
"male:dinosaur",
|
||||
"male:dog",
|
||||
"male:dog boy",
|
||||
"male:doll joints",
|
||||
"male:dolphin",
|
||||
"male:domination loss",
|
||||
"male:donkey",
|
||||
"male:double anal",
|
||||
"male:double blowjob",
|
||||
"male:double penetration",
|
||||
"male:dougi",
|
||||
"male:draenei",
|
||||
"male:dragon",
|
||||
"male:drill hair",
|
||||
"male:drugs",
|
||||
@@ -126,6 +168,7 @@ object Male : TagList {
|
||||
"male:eye-covering bang",
|
||||
"male:eyemask",
|
||||
"male:eyepatch",
|
||||
"male:facesitting",
|
||||
"male:facial hair",
|
||||
"male:fairy",
|
||||
"male:farting",
|
||||
@@ -138,8 +181,10 @@ object Male : TagList {
|
||||
"male:fisting",
|
||||
"male:focus paizuri",
|
||||
"male:food on body",
|
||||
"male:foot insertion",
|
||||
"male:foot licking",
|
||||
"male:footjob",
|
||||
"male:forced exposure",
|
||||
"male:forniphilia",
|
||||
"male:fox",
|
||||
"male:fox boy",
|
||||
@@ -156,8 +201,13 @@ object Male : TagList {
|
||||
"male:gender morph",
|
||||
"male:ghost",
|
||||
"male:giant",
|
||||
"male:giant sperm",
|
||||
"male:gijinka",
|
||||
"male:giraffe boy",
|
||||
"male:glasses",
|
||||
"male:glory hole",
|
||||
"male:gloves",
|
||||
"male:goat",
|
||||
"male:goblin",
|
||||
"male:gokkun",
|
||||
"male:gorilla",
|
||||
@@ -169,42 +219,63 @@ object Male : TagList {
|
||||
"male:gyaru-oh",
|
||||
"male:gymshorts",
|
||||
"male:hair buns",
|
||||
"male:hairjob",
|
||||
"male:hairy",
|
||||
"male:hairy armpits",
|
||||
"male:handjob",
|
||||
"male:hanging",
|
||||
"male:harem",
|
||||
"male:harpy",
|
||||
"male:headphones",
|
||||
"male:heterochromia",
|
||||
"male:hijab",
|
||||
"male:hood",
|
||||
"male:horns",
|
||||
"male:horse",
|
||||
"male:horse boy",
|
||||
"male:horse cock",
|
||||
"male:hotpants",
|
||||
"male:huge penis",
|
||||
"male:human on furry",
|
||||
"male:humiliation",
|
||||
"male:hyena boy",
|
||||
"male:impregnation",
|
||||
"male:incest",
|
||||
"male:infantilism",
|
||||
"male:inflation",
|
||||
"male:insect",
|
||||
"male:insect boy",
|
||||
"male:internal urination",
|
||||
"male:inverted nipples",
|
||||
"male:invisible",
|
||||
"male:josou seme",
|
||||
"male:kangaroo",
|
||||
"male:kappa",
|
||||
"male:kemonomimi",
|
||||
"male:kigurumi pajama",
|
||||
"male:kimono",
|
||||
"male:kindergarten uniform",
|
||||
"male:kissing",
|
||||
"male:kunoichi",
|
||||
"male:lab coat",
|
||||
"male:lactation",
|
||||
"male:large insertions",
|
||||
"male:large tattoo",
|
||||
"male:latex",
|
||||
"male:layer cake",
|
||||
"male:leash",
|
||||
"male:leg lock",
|
||||
"male:leotard",
|
||||
"male:lingerie",
|
||||
"male:lion",
|
||||
"male:living clothes",
|
||||
"male:lizard guy",
|
||||
"male:long tongue",
|
||||
"male:low bestiality",
|
||||
"male:low guro",
|
||||
"male:low scat",
|
||||
"male:low shotacon",
|
||||
"male:low smegma",
|
||||
"male:machine",
|
||||
"male:maggot",
|
||||
"male:magical girl",
|
||||
@@ -213,33 +284,46 @@ object Male : TagList {
|
||||
"male:males only",
|
||||
"male:masked face",
|
||||
"male:masturbation",
|
||||
"male:mecha boy",
|
||||
"male:merman",
|
||||
"male:mesuiki",
|
||||
"male:metal armor",
|
||||
"male:midget",
|
||||
"male:miko",
|
||||
"male:military",
|
||||
"male:milking",
|
||||
"male:mind break",
|
||||
"male:mind control",
|
||||
"male:miniguy",
|
||||
"male:minotaur",
|
||||
"male:mmm threesome",
|
||||
"male:monkey",
|
||||
"male:monkey boy",
|
||||
"male:monoeye",
|
||||
"male:monster",
|
||||
"male:moral degeneration",
|
||||
"male:mouse",
|
||||
"male:mouse boy",
|
||||
"male:mouth mask",
|
||||
"male:multimouth blowjob",
|
||||
"male:multiple arms",
|
||||
"male:multiple assjob",
|
||||
"male:multiple footjob",
|
||||
"male:multiple handjob",
|
||||
"male:multiple orgasms",
|
||||
"male:multiple penises",
|
||||
"male:multiple straddling",
|
||||
"male:muscle",
|
||||
"male:muscle growth",
|
||||
"male:nakadashi",
|
||||
"male:navel fuck",
|
||||
"male:nazi",
|
||||
"male:necrophilia",
|
||||
"male:netorare",
|
||||
"male:ninja",
|
||||
"male:nipple birth",
|
||||
"male:nipple fuck",
|
||||
"male:nipple stimulation",
|
||||
"male:nose fuck",
|
||||
"male:nose hook",
|
||||
"male:nun",
|
||||
@@ -248,94 +332,131 @@ object Male : TagList {
|
||||
"male:oil",
|
||||
"male:old man",
|
||||
"male:onahole",
|
||||
"male:oni",
|
||||
"male:orc",
|
||||
"male:orgasm denial",
|
||||
"male:ostrich",
|
||||
"male:otokofutanari",
|
||||
"male:otter boy",
|
||||
"male:oyakodon",
|
||||
"male:paizuri",
|
||||
"male:panda boy",
|
||||
"male:panther",
|
||||
"male:pantyhose",
|
||||
"male:pantyjob",
|
||||
"male:parasite",
|
||||
"male:pasties",
|
||||
"male:pegasus",
|
||||
"male:pegging",
|
||||
"male:penis birth",
|
||||
"male:personality excretion",
|
||||
"male:petplay",
|
||||
"male:petrification",
|
||||
"male:phimosis",
|
||||
"male:piercing",
|
||||
"male:pig",
|
||||
"male:pig man",
|
||||
"male:pillory",
|
||||
"male:pirate",
|
||||
"male:piss drinking",
|
||||
"male:pixie cut",
|
||||
"male:plant boy",
|
||||
"male:pole dancing",
|
||||
"male:policeman",
|
||||
"male:ponytail",
|
||||
"male:possession",
|
||||
"male:pregnant",
|
||||
"male:prehensile hair",
|
||||
"male:priest",
|
||||
"male:prolapse",
|
||||
"male:prostate massage",
|
||||
"male:prostitution",
|
||||
"male:pubic stubble",
|
||||
"male:public use",
|
||||
"male:pussyboys only",
|
||||
"male:rabbit",
|
||||
"male:raccoon boy",
|
||||
"male:randoseru",
|
||||
"male:rape",
|
||||
"male:reptile",
|
||||
"male:retractable penis",
|
||||
"male:rhinoceros",
|
||||
"male:rimjob",
|
||||
"male:robot",
|
||||
"male:ryona",
|
||||
"male:saliva",
|
||||
"male:scar",
|
||||
"male:scat",
|
||||
"male:school gym uniform",
|
||||
"male:school swimsuit",
|
||||
"male:schoolboy uniform",
|
||||
"male:schoolgirl uniform",
|
||||
"male:scrotal lingerie",
|
||||
"male:selfcest",
|
||||
"male:sex toys",
|
||||
"male:shared senses",
|
||||
"male:shark",
|
||||
"male:shark boy",
|
||||
"male:shaved head",
|
||||
"male:sheep",
|
||||
"male:sheep boy",
|
||||
"male:shibari",
|
||||
"male:shimaidon",
|
||||
"male:shimapan",
|
||||
"male:shotacon",
|
||||
"male:shrinking",
|
||||
"male:skinsuit",
|
||||
"male:skunk boy",
|
||||
"male:slave",
|
||||
"male:sleeping",
|
||||
"male:slime",
|
||||
"male:slime boy",
|
||||
"male:slug",
|
||||
"male:small penis",
|
||||
"male:smalldom",
|
||||
"male:smegma",
|
||||
"male:smell",
|
||||
"male:smoking",
|
||||
"male:snake",
|
||||
"male:snake boy",
|
||||
"male:snuff",
|
||||
"male:sockjob",
|
||||
"male:sole male",
|
||||
"male:sole pussyboy",
|
||||
"male:solo action",
|
||||
"male:spanking",
|
||||
"male:speculum",
|
||||
"male:spider",
|
||||
"male:spider boy",
|
||||
"male:squid boy",
|
||||
"male:squirrel boy",
|
||||
"male:ssbbm",
|
||||
"male:steward",
|
||||
"male:stewardess",
|
||||
"male:stirrup legwear",
|
||||
"male:stockings",
|
||||
"male:stomach deformation",
|
||||
"male:straitjacket",
|
||||
"male:strap-on",
|
||||
"male:stretching",
|
||||
"male:stuck in wall",
|
||||
"male:sumata",
|
||||
"male:sundress",
|
||||
"male:sunglasses",
|
||||
"male:sweating",
|
||||
"male:swimsuit",
|
||||
"male:swinging",
|
||||
"male:syringe",
|
||||
"male:table masturbation",
|
||||
"male:tail",
|
||||
"male:tail plug",
|
||||
"male:tailjob",
|
||||
"male:tailphagia",
|
||||
"male:tall man",
|
||||
"male:tanlines",
|
||||
"male:teacher",
|
||||
"male:tentacles",
|
||||
"male:thick eyebrows",
|
||||
"male:thigh high boots",
|
||||
"male:tiara",
|
||||
"male:tickling",
|
||||
@@ -348,16 +469,23 @@ object Male : TagList {
|
||||
"male:tracksuit",
|
||||
"male:trampling",
|
||||
"male:transformation",
|
||||
"male:triple anal",
|
||||
"male:triple penetration",
|
||||
"male:tube",
|
||||
"male:turtle",
|
||||
"male:tutor",
|
||||
"male:twins",
|
||||
"male:twintails",
|
||||
"male:unbirth",
|
||||
"male:uncle",
|
||||
"male:underwater",
|
||||
"male:unicorn",
|
||||
"male:unusual insertions",
|
||||
"male:unusual pupils",
|
||||
"male:unusual teeth",
|
||||
"male:urethra insertion",
|
||||
"male:urination",
|
||||
"male:vacbed",
|
||||
"male:vampire",
|
||||
"male:very long hair",
|
||||
"male:virginity",
|
||||
@@ -366,6 +494,9 @@ object Male : TagList {
|
||||
"male:voyeurism",
|
||||
"male:waiter",
|
||||
"male:waitress",
|
||||
"male:weight gain",
|
||||
"male:wet clothes",
|
||||
"male:whale",
|
||||
"male:whip",
|
||||
"male:wings",
|
||||
"male:witch",
|
||||
@@ -374,8 +505,11 @@ object Male : TagList {
|
||||
"male:wooden horse",
|
||||
"male:worm",
|
||||
"male:wormhole",
|
||||
"male:wrestling",
|
||||
"male:x-ray",
|
||||
"male:yandere",
|
||||
"male:yaoi",
|
||||
"male:zebra",
|
||||
"male:zombie",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Mixed : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"mixed:animal on animal",
|
||||
"mixed:body swap",
|
||||
"mixed:ffm threesome",
|
||||
@@ -16,6 +15,7 @@ object Mixed : TagList {
|
||||
"mixed:multiple assjob",
|
||||
"mixed:multiple footjob",
|
||||
"mixed:multiple handjob",
|
||||
"mixed:nudism",
|
||||
"mixed:oyakodon",
|
||||
"mixed:shimaidon",
|
||||
"mixed:ttm threesome",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Other : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"other:3d",
|
||||
"other:3d imageset",
|
||||
"other:already uploaded",
|
||||
"other:anaglyph",
|
||||
"other:animated",
|
||||
@@ -13,10 +13,12 @@ object Other : TagList {
|
||||
"other:comic",
|
||||
"other:compilation",
|
||||
"other:dakimakura",
|
||||
"other:defaced",
|
||||
"other:figure",
|
||||
"other:forbidden content",
|
||||
"other:full censorship",
|
||||
"other:full color",
|
||||
"other:game manual",
|
||||
"other:game sprite",
|
||||
"other:goudoushi",
|
||||
"other:hardcore",
|
||||
@@ -33,10 +35,10 @@ object Other : TagList {
|
||||
"other:nudity only",
|
||||
"other:out of order",
|
||||
"other:paperchild",
|
||||
"other:poor grammar",
|
||||
"other:realporn",
|
||||
"other:redraw",
|
||||
"other:replaced",
|
||||
"other:rough grammar",
|
||||
"other:rough translation",
|
||||
"other:sample",
|
||||
"other:scanmark",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object Parody : TagList {
|
||||
override fun getTags1() = listOf(
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"parody:.hack",
|
||||
"parody:.hackg.u.",
|
||||
"parody:.hacklegend of the twilight",
|
||||
@@ -31,6 +31,7 @@ object Parody : TagList {
|
||||
"parody:a town where you live",
|
||||
"parody:a vampyre story",
|
||||
"parody:a.d.police",
|
||||
"parody:a.i. ga tomaranai",
|
||||
"parody:abenobashi mahou shoutengai",
|
||||
"parody:acca 13-ku kansatsu-ka",
|
||||
"parody:accel world",
|
||||
@@ -66,8 +67,10 @@ object Parody : TagList {
|
||||
"parody:akira",
|
||||
"parody:aku no musume",
|
||||
"parody:aku no onna kanbu",
|
||||
"parody:akuma no memumemu-chan",
|
||||
"parody:akuyaku reijou nanode last boss o kattemimashita",
|
||||
"parody:aladdin",
|
||||
"parody:albatross koukairoku",
|
||||
"parody:aldnoah.zero",
|
||||
"parody:alice gear aegis",
|
||||
"parody:alice in wonderland",
|
||||
@@ -124,6 +127,7 @@ object Parody : TagList {
|
||||
"parody:ar nosurge",
|
||||
"parody:ar tonelico",
|
||||
"parody:ar tonelico qoga",
|
||||
"parody:araburu kisetsu no otome-domo yo",
|
||||
"parody:araiguma rascal",
|
||||
"parody:arasaa mama no watashi de ii no",
|
||||
"parody:arcana famiglia",
|
||||
@@ -194,6 +198,7 @@ object Parody : TagList {
|
||||
"parody:backbeard-sama ga miteru",
|
||||
"parody:bagi the monster of mighty nature",
|
||||
"parody:baka to test to shoukanjuu",
|
||||
"parody:bakemono no ko",
|
||||
"parody:bakemonogatari",
|
||||
"parody:bakuen campus guardress",
|
||||
"parody:bakugan",
|
||||
@@ -206,6 +211,7 @@ object Parody : TagList {
|
||||
"parody:ballroom e youkoso",
|
||||
"parody:bamboo blade",
|
||||
"parody:band yarouze",
|
||||
"parody:banished from the heros party i decided to live a quiet life in the countryside",
|
||||
"parody:banjo-kazooie",
|
||||
"parody:banner of the stars",
|
||||
"parody:baribari densetsu",
|
||||
@@ -271,6 +277,7 @@ object Parody : TagList {
|
||||
"parody:bloodstained",
|
||||
"parody:bloody roar",
|
||||
"parody:blue dragon",
|
||||
"parody:blue skin no mori",
|
||||
"parody:blue spring ride",
|
||||
"parody:blue submarine no. 6",
|
||||
"parody:bna brand new animal",
|
||||
@@ -355,6 +362,7 @@ object Parody : TagList {
|
||||
"parody:childs play",
|
||||
"parody:chio-chan no tsuugakuro",
|
||||
"parody:chip n dale rescue rangers",
|
||||
"parody:cho aniki",
|
||||
"parody:chobits",
|
||||
"parody:chogattai majutsu robot ginguiser",
|
||||
"parody:chokotto sister",
|
||||
@@ -428,6 +436,7 @@ object Parody : TagList {
|
||||
"parody:daiakuji",
|
||||
"parody:daibanchou -big bang age-",
|
||||
"parody:daicon",
|
||||
"parody:daihanjou manpuku marche",
|
||||
"parody:daikoukai jidai",
|
||||
"parody:daisenryaku",
|
||||
"parody:daiya no ace",
|
||||
@@ -440,6 +449,7 @@ object Parody : TagList {
|
||||
"parody:dantalian no shoka",
|
||||
"parody:daphne in the brilliant blue",
|
||||
"parody:dark chronicle",
|
||||
"parody:dark messiah",
|
||||
"parody:dark water",
|
||||
"parody:darker than black",
|
||||
"parody:darkstalkers",
|
||||
@@ -457,6 +467,7 @@ object Parody : TagList {
|
||||
"parody:defenders",
|
||||
"parody:defense devil",
|
||||
"parody:defense of the ancients",
|
||||
"parody:delicious party precure",
|
||||
"parody:demento",
|
||||
"parody:demi-chan wa kataritai",
|
||||
"parody:demonbane",
|
||||
@@ -480,6 +491,7 @@ object Parody : TagList {
|
||||
"parody:devil may cry",
|
||||
"parody:devil summoner soul hackers",
|
||||
"parody:devil survivor",
|
||||
"parody:devilman lady",
|
||||
"parody:dexters laboratory",
|
||||
"parody:di gi charat",
|
||||
"parody:diablo",
|
||||
@@ -550,6 +562,7 @@ object Parody : TagList {
|
||||
"parody:dragonaut",
|
||||
"parody:dragonica",
|
||||
"parody:dragons crown",
|
||||
"parody:dragons raiden",
|
||||
"parody:drakengard",
|
||||
"parody:drawn together",
|
||||
"parody:dream c club",
|
||||
@@ -619,6 +632,7 @@ object Parody : TagList {
|
||||
"parody:family project",
|
||||
"parody:fancy lala",
|
||||
"parody:fantastic four",
|
||||
"parody:fantasy bishoujo juniku ojisan to",
|
||||
"parody:fantasy earth zero",
|
||||
"parody:far east of eden kabuki klash",
|
||||
"parody:fatal frame",
|
||||
@@ -706,6 +720,7 @@ object Parody : TagList {
|
||||
"parody:fushigi no umi no nadia",
|
||||
"parody:fushigi yuugi",
|
||||
"parody:fushigiboshi no futagohime",
|
||||
"parody:futaba channel",
|
||||
"parody:futakoi",
|
||||
"parody:futari ecchi",
|
||||
"parody:futari wa precure splash star",
|
||||
@@ -723,6 +738,7 @@ object Parody : TagList {
|
||||
"parody:ga-rei",
|
||||
"parody:gad guard",
|
||||
"parody:gaiking",
|
||||
"parody:gaikotsu kishi-sama tadaima isekai e odekakechuu",
|
||||
"parody:gakkou gurashi",
|
||||
"parody:gakkou no kaidan",
|
||||
"parody:gakuen alice",
|
||||
@@ -733,6 +749,7 @@ object Parody : TagList {
|
||||
"parody:galaxy cyclone braiger",
|
||||
"parody:galaxy express 999",
|
||||
"parody:galaxy fraulein yuna",
|
||||
"parody:galleria no chika meikyuu to majo no ryodan",
|
||||
"parody:gan kon",
|
||||
"parody:ganbare goemon",
|
||||
"parody:gangsta.",
|
||||
@@ -753,6 +770,7 @@ object Parody : TagList {
|
||||
"parody:gen 13",
|
||||
"parody:gen colon lock",
|
||||
"parody:genji tsuushin agedama",
|
||||
"parody:genkai tokki monster monpiece",
|
||||
"parody:genmu no tou to tsurugi no okite",
|
||||
"parody:genmu senki leda",
|
||||
"parody:genroh",
|
||||
@@ -824,6 +842,7 @@ object Parody : TagList {
|
||||
"parody:gundam 00",
|
||||
"parody:gundam 0083",
|
||||
"parody:gundam age",
|
||||
"parody:gundam breaker mobile",
|
||||
"parody:gundam build fighters",
|
||||
"parody:gundam g no reconguista",
|
||||
"parody:gundam seed",
|
||||
@@ -856,6 +875,7 @@ object Parody : TagList {
|
||||
"parody:hakumei to mikochi",
|
||||
"parody:hakuouki",
|
||||
"parody:hakushaku to yousei",
|
||||
"parody:hakushon daimaou",
|
||||
"parody:half-life",
|
||||
"parody:halo",
|
||||
"parody:hamtaro",
|
||||
@@ -906,8 +926,10 @@ object Parody : TagList {
|
||||
"parody:highschool dxd",
|
||||
"parody:highschool of the dead",
|
||||
"parody:higurashi no naku koro ni",
|
||||
"parody:higyaku no noel",
|
||||
"parody:hikarian",
|
||||
"parody:hikaru no go",
|
||||
"parody:hikounin sentai akibaranger",
|
||||
"parody:hime chen otogi chikku idol lilpri",
|
||||
"parody:hime kishi lilia",
|
||||
"parody:hime-chans ribbon",
|
||||
@@ -918,6 +940,7 @@ object Parody : TagList {
|
||||
"parody:history kikan",
|
||||
"parody:historys strongest disciple kenichi",
|
||||
"parody:hitomi no karte",
|
||||
"parody:hitomi-chan wa hitomishiri",
|
||||
"parody:hitomi-sensei no hokenshitsu",
|
||||
"parody:hitori bocchi no marumaru seikatsu",
|
||||
"parody:hitsugi katsugi no kuro.",
|
||||
@@ -978,6 +1001,7 @@ object Parody : TagList {
|
||||
"parody:ikoku meiro no croisee",
|
||||
"parody:imouto sae ireba ii.",
|
||||
"parody:in search of the lost future",
|
||||
"parody:inaka ni kaeru to yakeni natsuita kasshoku ponytail shota ga iru",
|
||||
"parody:inazuma eleven",
|
||||
"parody:inazuman",
|
||||
"parody:inda no himekishi janne",
|
||||
@@ -998,6 +1022,7 @@ object Parody : TagList {
|
||||
"parody:is",
|
||||
"parody:isekai izakaya nobu",
|
||||
"parody:isekai maou to shoukan shoujo no dorei majutsu",
|
||||
"parody:isekai meikyuu de harem o",
|
||||
"parody:isekai no seikishi monogatari",
|
||||
"parody:isekai shokudou",
|
||||
"parody:isekai wa smartphone to tomo ni.",
|
||||
@@ -1007,6 +1032,7 @@ object Parody : TagList {
|
||||
"parody:its not my fault that im not popular",
|
||||
"parody:iwa kakeru",
|
||||
"parody:ixion saga dt",
|
||||
"parody:iya na kao sare nagara opantsu misete moraitai",
|
||||
"parody:izuna legend of the unemployed ninja",
|
||||
"parody:jackie chan adventures",
|
||||
"parody:jaja uma grooming up",
|
||||
@@ -1035,9 +1061,11 @@ object Parody : TagList {
|
||||
"parody:johnny bravo",
|
||||
"parody:johnny test",
|
||||
"parody:jojos bizarre adventure",
|
||||
"parody:joshikousei girls high",
|
||||
"parody:joshikousei no mudazukai",
|
||||
"parody:joukamachi no dandelion",
|
||||
"parody:jouki toshi no tantei shoujo",
|
||||
"parody:ju-on",
|
||||
"parody:jubei-chan",
|
||||
"parody:jumping rabbit",
|
||||
"parody:jungle de ikou",
|
||||
@@ -1060,11 +1088,13 @@ object Parody : TagList {
|
||||
"parody:k.o. beast",
|
||||
"parody:kaerunyo panyorn",
|
||||
"parody:kage no densetsu",
|
||||
"parody:kageki shojo",
|
||||
"parody:kagihime monogatari eikyuu alice rondo",
|
||||
"parody:kaguya-sama wa kokurasetai",
|
||||
"parody:kaichou wa maid-sama",
|
||||
"parody:kaifuku jutsushi no yarinaoshi",
|
||||
"parody:kaiji",
|
||||
"parody:kaijin kaihatsubu no kuroitsu-san",
|
||||
"parody:kaiketsu zorro",
|
||||
"parody:kaitou tenshi twin angel",
|
||||
"parody:kaizoku sentai gokaiger",
|
||||
@@ -1080,6 +1110,7 @@ object Parody : TagList {
|
||||
"parody:kami-tachi ni hirowareta otoko",
|
||||
"parody:kamikaze kaitou jeanne",
|
||||
"parody:kamisama dolls",
|
||||
"parody:kamisama hajimemashita",
|
||||
"parody:kamisama minarai himitsu no cocotama",
|
||||
"parody:kamisama ni natta hi",
|
||||
"parody:kampfer",
|
||||
@@ -1103,6 +1134,7 @@ object Parody : TagList {
|
||||
"parody:katekyo hitman reborn",
|
||||
"parody:katsute mahou shoujo to aku wa tekitai shite ita.",
|
||||
"parody:katte ni kaizou",
|
||||
"parody:kawaii dake ja nai shikimori-san",
|
||||
"parody:kawaikereba hentai demo suki ni natte kuremasu ka",
|
||||
"parody:kaze no na wa amnesia",
|
||||
"parody:kaze no yojimbo",
|
||||
@@ -1113,6 +1145,7 @@ object Parody : TagList {
|
||||
"parody:kemeko deluxe",
|
||||
"parody:kemono friends",
|
||||
"parody:kemono jihen",
|
||||
"parody:kenja no deshi o nanoru kenja",
|
||||
"parody:kenja no mago",
|
||||
"parody:kenka banchou otome",
|
||||
"parody:kenkou zenrakei suieibu umishou",
|
||||
@@ -1146,6 +1179,7 @@ object Parody : TagList {
|
||||
"parody:kingdom hearts",
|
||||
"parody:kinnikuman",
|
||||
"parody:kino no tabi",
|
||||
"parody:kinsou no vermeil",
|
||||
"parody:kira kira",
|
||||
"parody:kirakira precure a la mode",
|
||||
"parody:kirarin revolution",
|
||||
@@ -1173,6 +1207,7 @@ object Parody : TagList {
|
||||
"parody:komi-san wa komyushou desu.",
|
||||
"parody:konjiki no word master",
|
||||
"parody:kono aozora ni yakusoku o",
|
||||
"parody:kono healer mendokusai",
|
||||
"parody:kono oto tomare",
|
||||
"parody:kono subarashii sekai ni syukufuku o",
|
||||
"parody:konto koroshiya 1989",
|
||||
@@ -1216,9 +1251,11 @@ object Parody : TagList {
|
||||
"parody:kyoukai no rinne",
|
||||
"parody:kyoukai senjou no horizon",
|
||||
"parody:kyouryuu wakusei",
|
||||
"parody:kyuuketsuki sugu shinu",
|
||||
"parody:kyuukyoku choujin r",
|
||||
"parody:kyuukyoku hentai kamen",
|
||||
"parody:kyuukyoku shinka shita full dive rpg ga genjitsu yori mo kusogee dattara",
|
||||
"parody:kyuukyuu sentai gogofive",
|
||||
"parody:kyuushu sentai danjija",
|
||||
"parody:la corda doro",
|
||||
"parody:la pucelle",
|
||||
@@ -1232,6 +1269,7 @@ object Parody : TagList {
|
||||
"parody:le ranch",
|
||||
"parody:league of legends",
|
||||
"parody:left 4 dead",
|
||||
"parody:legend of legaia",
|
||||
"parody:legend of lyon flare",
|
||||
"parody:legend of queen opala",
|
||||
"parody:legend of the cryptids",
|
||||
@@ -1326,6 +1364,8 @@ object Parody : TagList {
|
||||
"parody:mahouka koukou no rettousei",
|
||||
"parody:mahoutsukai no yakusoku",
|
||||
"parody:mahoutsukai no yome",
|
||||
"parody:mahoutsukai reimeiki",
|
||||
"parody:mai mai miracle",
|
||||
"parody:mai-hime",
|
||||
"parody:mai-otome",
|
||||
"parody:mairimashita iruma-kun",
|
||||
@@ -1383,6 +1423,7 @@ object Parody : TagList {
|
||||
"parody:mayoi neko overrun",
|
||||
"parody:maze runner",
|
||||
"parody:mazinger z",
|
||||
"parody:me me me",
|
||||
"parody:mecha mote",
|
||||
"parody:mechakko dotakon",
|
||||
"parody:medabots",
|
||||
@@ -1423,6 +1464,7 @@ object Parody : TagList {
|
||||
"parody:mirmo de pon",
|
||||
"parody:miss machiko",
|
||||
"parody:mission impossible",
|
||||
"parody:misumisou",
|
||||
"parody:mitsudomoe",
|
||||
"parody:mitsume ga tooru",
|
||||
"parody:mitsumete knight",
|
||||
@@ -1479,7 +1521,6 @@ object Parody : TagList {
|
||||
"parody:muv-luv alternative total eclipse",
|
||||
"parody:mx0",
|
||||
"parody:my dad the rock star",
|
||||
"parody:my dress-up darling",
|
||||
"parody:my hero academia",
|
||||
"parody:my life as a teenage robot",
|
||||
"parody:my little pony friendship is magic",
|
||||
@@ -1508,6 +1549,7 @@ object Parody : TagList {
|
||||
"parody:nazo no kanojo x",
|
||||
"parody:nee chanto shiyou yo",
|
||||
"parody:nee summer",
|
||||
"parody:needy streamer overload",
|
||||
"parody:nejimaki seirei senki tenkyou no alderamin",
|
||||
"parody:nekketsu saikyou go-saurer",
|
||||
"parody:nekome kozou",
|
||||
@@ -1563,6 +1605,7 @@ object Parody : TagList {
|
||||
"parody:oda nobuna no yabou",
|
||||
"parody:odin sphere",
|
||||
"parody:odoru daisousasen",
|
||||
"parody:odyssey",
|
||||
"parody:oira uchuu no tankoufu",
|
||||
"parody:ojama yurei-kun",
|
||||
"parody:ojamajo doremi",
|
||||
@@ -1636,6 +1679,7 @@ object Parody : TagList {
|
||||
"parody:parappa the rapper",
|
||||
"parody:parasite eve",
|
||||
"parody:parasyte",
|
||||
"parody:paripi koumei",
|
||||
"parody:pastel",
|
||||
"parody:pastel yumi",
|
||||
"parody:patlabor",
|
||||
@@ -1709,6 +1753,7 @@ object Parody : TagList {
|
||||
"parody:pu-li-ru-la",
|
||||
"parody:puella magi madoka magica",
|
||||
"parody:pumpkin scissors",
|
||||
"parody:punch-out",
|
||||
"parody:puppet princess of marl kingdom",
|
||||
"parody:pussy saga",
|
||||
"parody:puyo puyo",
|
||||
@@ -1740,6 +1785,7 @@ object Parody : TagList {
|
||||
"parody:red pride of eden",
|
||||
"parody:redline",
|
||||
"parody:redwall",
|
||||
"parody:refrain no chika meikyuu to majo no ryodan",
|
||||
"parody:regalia the three sacred stars",
|
||||
"parody:reibaishi izuna",
|
||||
"parody:remi nobodys girl",
|
||||
@@ -1822,6 +1868,7 @@ object Parody : TagList {
|
||||
"parody:sarah and duck",
|
||||
"parody:sasameki koto",
|
||||
"parody:sasami magical girls club",
|
||||
"parody:satsukare",
|
||||
"parody:savage reign",
|
||||
"parody:sayonara zetsubou sensei",
|
||||
"parody:school days",
|
||||
@@ -1838,6 +1885,7 @@ object Parody : TagList {
|
||||
"parody:seiken densetsu",
|
||||
"parody:seiken densetsu 2",
|
||||
"parody:seiken densetsu 3",
|
||||
"parody:seiken densetsu ds",
|
||||
"parody:seikesshou albatross",
|
||||
"parody:seikon no qwaser",
|
||||
"parody:seirei no moribito",
|
||||
@@ -1869,12 +1917,14 @@ object Parody : TagList {
|
||||
"parody:sensei no bulge",
|
||||
"parody:sentimental graffiti",
|
||||
"parody:sentouin hakenshimasu",
|
||||
"parody:senyoku no sigrdrifa",
|
||||
"parody:serial experiments lain",
|
||||
"parody:seto no hanayome",
|
||||
"parody:seven mortal sins",
|
||||
"parody:seven of seven",
|
||||
"parody:sewayaki kitsune no senko-san",
|
||||
"parody:sexfriend",
|
||||
"parody:shachiku-san wa youjo yuurei ni iyasaretai.",
|
||||
"parody:shadow of the colossus",
|
||||
"parody:shakugan no shana",
|
||||
"parody:shakunetsu no nirai kanai",
|
||||
@@ -1912,6 +1962,7 @@ object Parody : TagList {
|
||||
"parody:shinryaku ika musume",
|
||||
"parody:shinseiki inma seiden",
|
||||
"parody:shinsekai yori",
|
||||
"parody:shiroi suna no aquatope",
|
||||
"parody:shironeko project",
|
||||
"parody:shisha no teikoku",
|
||||
"parody:shishunki renaissance david-kun",
|
||||
@@ -1951,9 +2002,13 @@ object Parody : TagList {
|
||||
"parody:snow white and the seven dwarfs",
|
||||
"parody:sokihei m.d. geist",
|
||||
"parody:solatorobo",
|
||||
)
|
||||
|
||||
override fun getTags2(): List<String> = listOf(
|
||||
"parody:soltyrei",
|
||||
"parody:sonic soldier borgman",
|
||||
"parody:sonic the hedgehog",
|
||||
"parody:sono bisque doll wa koi o suru",
|
||||
"parody:sono hanabira ni kuchizuke o",
|
||||
"parody:sora no iro mizu no iro",
|
||||
"parody:sora no kanata no dystopia",
|
||||
@@ -1967,6 +2022,7 @@ object Parody : TagList {
|
||||
"parody:soredemo machi wa mawatteiru",
|
||||
"parody:soredemo tsuma o aishiteru",
|
||||
"parody:soromon no kagi",
|
||||
"parody:sou-bou-tei kowasubeshi",
|
||||
"parody:soukou kijo iris",
|
||||
"parody:soukyuu no fafner",
|
||||
"parody:soul cradle",
|
||||
@@ -2002,9 +2058,6 @@ object Parody : TagList {
|
||||
"parody:star gladiator",
|
||||
"parody:star ocean",
|
||||
"parody:star ocean 2",
|
||||
)
|
||||
|
||||
override fun getTags2() = listOf(
|
||||
"parody:star ocean 3",
|
||||
"parody:star ocean 4",
|
||||
"parody:star trek",
|
||||
@@ -2055,6 +2108,7 @@ object Parody : TagList {
|
||||
"parody:sword art online",
|
||||
"parody:sword art online alternative gun gale online",
|
||||
"parody:sword girls",
|
||||
"parody:sword of the stranger",
|
||||
"parody:sword world rpg",
|
||||
"parody:sym-bionic titan",
|
||||
"parody:t.u.f.f. puppy",
|
||||
@@ -2087,6 +2141,7 @@ object Parody : TagList {
|
||||
"parody:tantei opera milky holmes",
|
||||
"parody:tantei wa mou shindeiru.",
|
||||
"parody:tari tari",
|
||||
"parody:tartaros",
|
||||
"parody:tasogare otome x amnesia",
|
||||
"parody:tasogare sakaba uwabami breakers",
|
||||
"parody:tatakae tarantella",
|
||||
@@ -2236,6 +2291,7 @@ object Parody : TagList {
|
||||
"parody:the x-files",
|
||||
"parody:they are my noble masters",
|
||||
"parody:this ugly yet beautiful world",
|
||||
"parody:thomas the tank engine and friends",
|
||||
"parody:threads of fate",
|
||||
"parody:three from prostokvashino",
|
||||
"parody:thundercats",
|
||||
@@ -2244,8 +2300,7 @@ object Parody : TagList {
|
||||
"parody:tiny toons",
|
||||
"parody:to heart",
|
||||
"parody:to love-ru",
|
||||
"parody:toaru kagaku no railgun",
|
||||
"parody:toaru majutsu no index",
|
||||
"parody:toaru project",
|
||||
"parody:tobe isami",
|
||||
"parody:togainu no chi",
|
||||
"parody:toheart2",
|
||||
@@ -2259,6 +2314,7 @@ object Parody : TagList {
|
||||
"parody:tokyo mew mew",
|
||||
"parody:tokyo red hood",
|
||||
"parody:tom and jerry",
|
||||
"parody:tom clancys ghost recon",
|
||||
"parody:tom clancys rainbow six",
|
||||
"parody:tomb raider",
|
||||
"parody:tomodachi no imouto ga ore ni dake uzai",
|
||||
@@ -2306,6 +2362,7 @@ object Parody : TagList {
|
||||
"parody:twinkle review",
|
||||
"parody:uchi no musume ni te o dasuna",
|
||||
"parody:uchouten kazoku",
|
||||
"parody:uchuu eiyuu monogatari",
|
||||
"parody:uchuu kaizoku sara",
|
||||
"parody:uchuu kazoku carlvinson",
|
||||
"parody:uchuu no kishi tekkaman",
|
||||
@@ -2332,6 +2389,7 @@ object Parody : TagList {
|
||||
"parody:uta no prince-sama",
|
||||
"parody:utawarerumono",
|
||||
"parody:utawarerumono itsuwari no kamen",
|
||||
"parody:utsukushiki sei no dendoushi rei rei",
|
||||
"parody:uzaki-chan wa asobitai",
|
||||
"parody:va-11 hall-a",
|
||||
"parody:valkyria chronicles",
|
||||
@@ -2412,6 +2470,7 @@ object Parody : TagList {
|
||||
"parody:witchblade",
|
||||
"parody:witchs weapon",
|
||||
"parody:with you",
|
||||
"parody:wixoss diva a live",
|
||||
"parody:wizard of oz",
|
||||
"parody:wolfs rain",
|
||||
"parody:wooser no sonohigurashi",
|
||||
@@ -2437,6 +2496,7 @@ object Parody : TagList {
|
||||
"parody:yagate kimi ni naru",
|
||||
"parody:yahari ore no seishun love come wa machigatteiru",
|
||||
"parody:yakitate japan",
|
||||
"parody:yakumo-san wa ezuke ga shitai.",
|
||||
"parody:yakushiji ryouko no kaiki jikenbo",
|
||||
"parody:yakusoku no neverland",
|
||||
"parody:yama no susume",
|
||||
@@ -2453,6 +2513,7 @@ object Parody : TagList {
|
||||
"parody:yiik",
|
||||
"parody:yin yang yo",
|
||||
"parody:yoake mae yori ruriiro na",
|
||||
"parody:yofukashi no uta",
|
||||
"parody:yokohama kaidashi kikou",
|
||||
"parody:yomawari",
|
||||
"parody:yondemasuyo azazel-san",
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package exh.eh.tags
|
||||
|
||||
object ReClass : TagList {
|
||||
|
||||
override fun getTags1() = listOf(
|
||||
object Reclass : TagList {
|
||||
override fun getTags1(): List<String> = listOf(
|
||||
"reclass:artistcg",
|
||||
"reclass:asianporn",
|
||||
"reclass:cosplay",
|
||||
@@ -7,12 +7,9 @@ interface TagList {
|
||||
|
||||
fun getTags3(): List<String> = emptyList()
|
||||
|
||||
fun getTags4(): List<String> = emptyList()
|
||||
|
||||
fun getTags() = listOf(
|
||||
fun getTags(): List<List<String>> = listOf(
|
||||
getTags1(),
|
||||
getTags2(),
|
||||
getTags3(),
|
||||
getTags4(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package exh.md.handlers
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@@ -13,11 +12,11 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
class AzukiHandler(currentClient: OkHttpClient) {
|
||||
class AzukiHandler(currentClient: OkHttpClient, userAgent: String) {
|
||||
val baseUrl = "https://www.azuki.co"
|
||||
private val apiUrl = "https://production.api.azuki.co"
|
||||
val headers = Headers.Builder()
|
||||
.add("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
||||
.add("User-Agent", userAgent)
|
||||
.build()
|
||||
|
||||
val client: OkHttpClient = currentClient
|
||||
|
||||
@@ -3,7 +3,6 @@ package exh.md.handlers
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.booleanOrNull
|
||||
@@ -16,11 +15,11 @@ import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
class ComikeyHandler(cloudflareClient: OkHttpClient) {
|
||||
class ComikeyHandler(cloudflareClient: OkHttpClient, userAgent: String) {
|
||||
val baseUrl = "https://comikey.com"
|
||||
private val apiUrl = "$baseUrl/sapi"
|
||||
val headers = Headers.Builder()
|
||||
.add("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
||||
.add("User-Agent", userAgent)
|
||||
.build()
|
||||
|
||||
val client: OkHttpClient = cloudflareClient
|
||||
|
||||
@@ -3,7 +3,6 @@ package exh.md.handlers
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
@@ -12,11 +11,11 @@ import okhttp3.Headers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
|
||||
class MangaHotHandler(currentClient: OkHttpClient) {
|
||||
class MangaHotHandler(currentClient: OkHttpClient, userAgent: String) {
|
||||
val baseUrl = "https://mangahot.jp"
|
||||
private val apiUrl = "https://api.mangahot.jp"
|
||||
val headers = Headers.Builder()
|
||||
.add("User-Agent", HttpSource.DEFAULT_USER_AGENT)
|
||||
.add("User-Agent", userAgent)
|
||||
.build()
|
||||
|
||||
val client: OkHttpClient = currentClient
|
||||
|
||||
@@ -1,49 +1,52 @@
|
||||
package exh.md.utils
|
||||
|
||||
@Suppress("unused")
|
||||
enum class MdLang(val lang: String, val prettyPrint: String, val extLang: String = lang) {
|
||||
ENGLISH("en", "English"),
|
||||
JAPANESE("ja", "Japanese"),
|
||||
POLISH("pl", "Polish"),
|
||||
SERBO_CROATIAN("rs", "Serbo-Croatian", "sh"),
|
||||
DUTCH("nl", "Dutch"),
|
||||
ITALIAN("it", "IT"),
|
||||
RUSSIAN("ru", "Russian"),
|
||||
GERMAN("de", "German"),
|
||||
HUNGARIAN("hu", "Hungarian"),
|
||||
FRENCH("fr", "French"),
|
||||
FINNISH("fi", "Finnish"),
|
||||
VIETNAMESE("vi", "Vietnamese"),
|
||||
GREEK("el", "Greek"),
|
||||
BULGARIAN("bg", "BULGARIN"),
|
||||
SPANISH_ES("es", "Spanish (Es)"),
|
||||
PORTUGUESE_BR("pt-br", "Portuguese (Br)", "pt-BR"),
|
||||
PORTUGUESE("pt", "Portuguese (Pt)"),
|
||||
SWEDISH("sv", "Swedish"),
|
||||
ARABIC("ar", "Arabic"),
|
||||
DANISH("da", "Danish"),
|
||||
CHINESE_SIMPLIFIED("zh", "Chinese (Simp)", "zh-Hans"),
|
||||
BENGALI("bn", "Bengali"),
|
||||
ROMANIAN("ro", "Romanian"),
|
||||
CZECH("cs", "Czech"),
|
||||
MONGOLIAN("mn", "Mongolian"),
|
||||
TURKISH("tr", "Turkish"),
|
||||
INDONESIAN("id", "Indonesian"),
|
||||
KOREAN("kr", "Korean", "ko"),
|
||||
SPANISH_LATAM("es-la", "Spanish (LATAM)", "es-419"),
|
||||
PERSIAN("fa", "Persian"),
|
||||
MALAY("ms", "Malay"),
|
||||
THAI("th", "Thai"),
|
||||
CATALAN("ca", "Catalan"),
|
||||
FILIPINO("tl", "Filipino", "fil"),
|
||||
CHINESE_TRAD("zh-hk", "Chinese (Trad)", "zh-Hant"),
|
||||
UKRAINIAN("uk", "Ukrainian"),
|
||||
BURMESE("my", "Burmese"),
|
||||
LINTHUANIAN("lt", "Lithuanian"),
|
||||
HEBREW("he", "Hebrew"),
|
||||
HINDI("hi", "Hindi"),
|
||||
NORWEGIAN("no", "Norwegian"),
|
||||
NEPALI("ne", "Nepali")
|
||||
enum class MdLang(val lang: String, val extLang: String = lang) {
|
||||
ENGLISH("en"),
|
||||
JAPANESE("ja"),
|
||||
POLISH("pl"),
|
||||
SERBO_CROATIAN("rs", "sh"),
|
||||
DUTCH("nl"),
|
||||
ITALIAN("it"),
|
||||
RUSSIAN("ru"),
|
||||
GERMAN("de"),
|
||||
HUNGARIAN("hu"),
|
||||
FRENCH("fr"),
|
||||
FINNISH("fi"),
|
||||
VIETNAMESE("vi"),
|
||||
GREEK("el"),
|
||||
BULGARIAN("bg"),
|
||||
SPANISH_ES("es"),
|
||||
PORTUGUESE_BR("pt-br", "pt-BR"),
|
||||
PORTUGUESE("pt"),
|
||||
SWEDISH("sv"),
|
||||
ARABIC("ar"),
|
||||
DANISH("da"),
|
||||
CHINESE_SIMPLIFIED("zh", "zh-Hans"),
|
||||
BENGALI("bn"),
|
||||
ROMANIAN("ro"),
|
||||
CZECH("cs"),
|
||||
MONGOLIAN("mn"),
|
||||
TURKISH("tr"),
|
||||
INDONESIAN("id"),
|
||||
KOREAN("kr", "ko"),
|
||||
SPANISH_LATAM("es-la", "es-419"),
|
||||
PERSIAN("fa"),
|
||||
MALAY("ms"),
|
||||
THAI("th"),
|
||||
CATALAN("ca"),
|
||||
FILIPINO("tl", "fil"),
|
||||
CHINESE_TRAD("zh-hk", "zh-Hant"),
|
||||
UKRAINIAN("uk"),
|
||||
BURMESE("my"),
|
||||
LINTHUANIAN("lt"),
|
||||
HEBREW("he"),
|
||||
HINDI("hi"),
|
||||
NORWEGIAN("no"),
|
||||
NEPALI("ne"),
|
||||
LATIN("la"),
|
||||
TAMIL("ta"),
|
||||
KAZAKH("kk"),
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM17,18H7v-2h10V18zM10.3,14L7,10.7l1.4,-1.4l1.9,1.9l5.3,-5.3L17,7.3L10.3,14z" />
|
||||
</vector>
|
||||
@@ -9,8 +9,7 @@
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="40dp"
|
||||
android:padding="8dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal">
|
||||
|
||||
<TextView
|
||||
@@ -20,10 +19,12 @@
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/dedupe_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:paddingVertical="16dp"
|
||||
android:paddingHorizontal="16dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user