Compare commits

...

18 Commits

Author SHA1 Message Date
Jobobby04 b9583a31c9 Update readme
Remote Dispatch Action Initiator / ping-pong (push) Failing after 10s
2020-08-02 16:29:13 -04:00
Jobobby04 926fa85ccd Release 1.1.1 2020-08-02 16:26:07 -04:00
Jobobby04 b91252df67 HBrowse url matching sorta fixed 2020-08-02 16:24:54 -04:00
arkon 3893c90eb2 Make download badges lighter to improve contrast (closes #3571)
(cherry picked from commit 2e9d89574d)
2020-08-02 15:11:29 -04:00
Jozef Hollý d5f4783aca Translated using Weblate (Russian) (#3549)
Currently translated at 100.0% (565 of 565 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (565 of 565 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/

Translated using Weblate (Indonesian)

Currently translated at 100.0% (565 of 565 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/id/

Translated using Weblate (Hindi)

Currently translated at 100.0% (565 of 565 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/

Translated using Weblate (Spanish)

Currently translated at 100.0% (565 of 565 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/

Translated using Weblate (Russian)

Currently translated at 100.0% (564 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/

Translated using Weblate (Georgian)

Currently translated at 9.3% (53 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ka/

Translated using Weblate (Tagalog)

Currently translated at 73.9% (417 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tl/

Translated using Weblate (Serbian)

Currently translated at 79.9% (451 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sr/

Translated using Weblate (Thai)

Currently translated at 58.1% (328 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/th/

Translated using Weblate (Norwegian Bokmål)

Currently translated at 88.6% (500 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nb_NO/

Translated using Weblate (Czech)

Currently translated at 64.0% (361 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/

Translated using Weblate (Korean)

Currently translated at 57.9% (327 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ko/

Translated using Weblate (Hungarian)

Currently translated at 35.6% (201 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hu/

Translated using Weblate (Bengali)

Currently translated at 60.4% (341 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/bn/

Translated using Weblate (Czech)

Currently translated at 63.8% (360 of 564 strings)

Translation: Tachiyomi/Strings
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/

Co-authored-by: Hosted Weblate <hosted@weblate.org>
(cherry picked from commit 569c99496b)
2020-08-02 15:11:12 -04:00
arkon b0bcfa9db0 Fix crash when filter groups contain items with identical names (closes #3568)
(cherry picked from commit ea3b8767de)
2020-08-02 15:11:01 -04:00
arkon 01ea86ab90 Move download warnings/errors to separate notification channel
(cherry picked from commit 8e8c30c1eb)
2020-08-02 15:10:52 -04:00
arkon 475299d9b3 Revert "Downgrade coroutines and flow-preferences"
This reverts commit b47ee8857b.

(cherry picked from commit d921ba81c8)
2020-08-02 15:10:43 -04:00
arkon 951bb1f3c6 Fix downloads not working for custom SD card paths (closes #3564)
(cherry picked from commit ad9f646102)
2020-08-02 15:10:34 -04:00
arkon 1f7e69e13c Don't show completed notification if download error notification was shown
(cherry picked from commit 2ef277bcef)
2020-08-02 15:10:23 -04:00
arkon 5fbaa7d6be Fix history item icon tint in light blue theme
(cherry picked from commit 9e396e1624)
2020-08-02 15:09:22 -04:00
Jobobby04 cce1b135c9 Tweak HBrowse migration 2020-08-02 15:08:47 -04:00
Jobobby04 b344a3944e Fix certain HBrowse manga 2020-08-02 15:06:49 -04:00
arkon 7f416bda7c Fix dividers in migrate list
(cherry picked from commit 9708d84e60)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrationSourcesController.kt
2020-08-02 00:58:13 -04:00
arkon 3b08c7fdea Fix last used source pinned status
(cherry picked from commit 4efc195548)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcePresenter.kt
2020-08-02 00:55:28 -04:00
Jobobby04 e346d95b0e Delegate HBrowse 2020-08-02 00:50:52 -04:00
arkon 0fe8990f99 Filter out chapter entries with duplicate URLs (fixes #3552)
(cherry picked from commit 0d15cbe334)
2020-08-01 16:40:43 -04:00
jobobby04 35ed8e2d34 Update README.md 2020-08-01 15:29:14 -04:00
51 changed files with 308 additions and 1086 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
I acknowledge that:
- I have updated to the latest version of the app (stable is v1.1.0)
- I have updated to the latest version of the app (stable is v1.1.1)
- I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/inorichi/tachiyomi-extensions
+1 -1
View File
@@ -9,7 +9,7 @@ labels: "bug"
I acknowledge that:
- I have updated to the latest version of the app (stable is v1.1.0)
- I have updated to the latest version of the app (stable is v1.1.1)
- I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/inorichi/tachiyomi-extensions
+1 -1
View File
@@ -9,7 +9,7 @@ labels: "feature"
I acknowledge that:
- I have updated to the latest version of the app (stable is v1.1.0)
- I have updated to the latest version of the app (stable is v1.1.1)
- I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/inorichi/tachiyomi-extensions
+4 -1
View File
@@ -34,6 +34,9 @@ Features of TachiyomiSY include:
* New E-Hentai/ExHentai features, such as language settings and watched list settings
* Comfortable grid view
* Custom categories for sources, liked the pinned sources, but you can make your own versions and put any sources in them
* Manga info edit
* Enhanced views for internal and integrated sources
* Enhanced usability for internal and delegated sources
Inherited from TachiyomiAZ or TachiyomiEH and are included and possibly modified in TachiyomiSY
* Source migration, migrate all your manga from one source to another
@@ -42,12 +45,12 @@ Inherited from TachiyomiAZ or TachiyomiEH and are included and possibly modified
* * nHentai
* * Hitomi.la
* * 8Muses
* * HBrowse
* * Perv Eden
* Additional features for some extensions, features include custom description, opening in app, batch add to library:
* * Puruin
* * Tsumino
* * HentaiCafe (Foolside)
* * HBrowse
* Saving searches
* Autoscroll
* Page preload customization
+4 -5
View File
@@ -42,8 +42,8 @@ android {
minSdkVersion AndroidConfig.minSdk
targetSdkVersion AndroidConfig.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 3
versionName "1.1.0"
versionCode 4
versionName "1.1.1"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@@ -217,7 +217,7 @@ dependencies {
implementation 'io.requery:sqlite-android:3.31.0'
// Preferences
implementation 'com.github.tfcporciuncula:flow-preferences:1.1.1'
implementation 'com.github.tfcporciuncula:flow-preferences:1.3.0'
// Model View Presenter
final nucleus_version = '3.0.0'
@@ -296,8 +296,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// Do not update until we bump to Kotlin 1.4, see https://github.com/Kotlin/kotlinx.coroutines/issues/2049
final coroutines_version = '1.3.6'
final coroutines_version = '1.3.8'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutines_version"
@@ -0,0 +1,32 @@
package eu.kanade.tachiyomi.data.database.resolvers
import android.content.ContentValues
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.tables.MangaTable
// [EXH]
class MangaUrlPutResolver : PutResolver<Manga>() {
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(manga)
val contentValues = mapToContentValues(manga)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_ID} = ?")
.whereArgs(manga.id)
.build()
fun mapToContentValues(manga: Manga) = ContentValues(1).apply {
put(MangaTable.COL_URL, manga.url)
}
}
@@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.util.lang.chop
import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.notificationManager
import java.util.regex.Pattern
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
/**
@@ -25,12 +24,22 @@ internal class DownloadNotifier(private val context: Context) {
private val preferences: PreferencesHelper by injectLazy()
private val progressNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
private val progressNotificationBuilder by lazy {
context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
}
}
private val completeNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_COMPLETE) {
setAutoCancel(false)
private val completeNotificationBuilder by lazy {
context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_COMPLETE) {
setAutoCancel(false)
}
}
private val errorNotificationBuilder by lazy {
context.notificationBuilder(Notifications.CHANNEL_DOWNLOADER_ERROR) {
setAutoCancel(false)
}
}
/**
@@ -53,7 +62,7 @@ internal class DownloadNotifier(private val context: Context) {
*
* @param id the id of the notification.
*/
private fun NotificationCompat.Builder.show(id: Int = Notifications.ID_DOWNLOAD_CHAPTER) {
private fun NotificationCompat.Builder.show(id: Int) {
context.notificationManager.notify(id, build())
}
@@ -71,7 +80,7 @@ internal class DownloadNotifier(private val context: Context) {
* those can only be dismissed by the user.
*/
fun dismiss() {
context.notificationManager.cancel(Notifications.ID_DOWNLOAD_CHAPTER)
context.notificationManager.cancel(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
}
/**
@@ -112,14 +121,15 @@ internal class DownloadNotifier(private val context: Context) {
}
setProgress(download.pages!!.size, download.downloadedImages, false)
show(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
}
progressNotificationBuilder.show()
}
/**
* Show notification when download is paused.
*/
fun onDownloadPaused() {
fun onPaused() {
with(progressNotificationBuilder) {
setContentTitle(context.getString(R.string.chapter_paused))
setContentText(context.getString(R.string.download_notifier_download_paused))
@@ -141,8 +151,9 @@ internal class DownloadNotifier(private val context: Context) {
context.getString(R.string.action_cancel_all),
NotificationReceiver.clearDownloadsPendingBroadcast(context)
)
show(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
}
progressNotificationBuilder.show()
// Reset initial values
isDownloading = false
@@ -151,18 +162,21 @@ internal class DownloadNotifier(private val context: Context) {
/**
* This function shows a notification to inform download tasks are done.
*/
fun downloadFinished() {
// Create notification
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.download_notifier_downloader_title))
setContentText(context.getString(R.string.download_notifier_download_finish))
setSmallIcon(android.R.drawable.stat_sys_download_done)
clearActions()
setAutoCancel(true)
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
setProgress(0, 0, false)
fun onComplete() {
if (!errorThrown) {
// Create notification
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.download_notifier_downloader_title))
setContentText(context.getString(R.string.download_notifier_download_finish))
setSmallIcon(android.R.drawable.stat_sys_download_done)
clearActions()
setAutoCancel(true)
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
setProgress(0, 0, false)
show(Notifications.ID_DOWNLOAD_CHAPTER_COMPLETE)
}
}
completeNotificationBuilder.show(Notifications.ID_DOWNLOAD_CHAPTER_COMPLETE)
// Reset states to default
errorThrown = false
@@ -175,7 +189,7 @@ internal class DownloadNotifier(private val context: Context) {
* @param reason the text to show.
*/
fun onWarning(reason: String) {
with(completeNotificationBuilder) {
with(errorNotificationBuilder) {
setContentTitle(context.getString(R.string.download_notifier_downloader_title))
setContentText(reason)
setSmallIcon(android.R.drawable.stat_sys_warning)
@@ -183,8 +197,9 @@ internal class DownloadNotifier(private val context: Context) {
clearActions()
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
setProgress(0, 0, false)
show(Notifications.ID_DOWNLOAD_CHAPTER_ERROR)
}
completeNotificationBuilder.show()
// Reset download information
isDownloading = false
@@ -199,7 +214,7 @@ internal class DownloadNotifier(private val context: Context) {
*/
fun onError(error: String? = null, chapter: String? = null) {
// Create notification
with(completeNotificationBuilder) {
with(errorNotificationBuilder) {
setContentTitle(
chapter
?: context.getString(R.string.download_notifier_downloader_title)
@@ -210,8 +225,9 @@ internal class DownloadNotifier(private val context: Context) {
setAutoCancel(false)
setContentIntent(NotificationHandler.openDownloadManagerPendingActivity(context))
setProgress(0, 0, false)
show(Notifications.ID_DOWNLOAD_CHAPTER_ERROR)
}
completeNotificationBuilder.show(Notifications.ID_DOWNLOAD_CHAPTER_ERROR)
// Reset download information
errorThrown = true
@@ -83,7 +83,7 @@ class DownloadService : Service() {
*/
override fun onCreate() {
super.onCreate()
startForeground(Notifications.ID_DOWNLOAD_CHAPTER, getPlaceholderNotification())
startForeground(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS, getPlaceholderNotification())
wakeLock = acquireWakeLock(javaClass.name)
runningRelay.call(true)
subscriptions = CompositeSubscription()
@@ -137,9 +137,9 @@ class Downloader(
} else {
if (notifier.paused) {
notifier.paused = false
notifier.onDownloadPaused()
notifier.onPaused()
} else {
notifier.downloadFinished()
notifier.onComplete()
}
}
}
@@ -32,10 +32,11 @@ object Notifications {
*/
private const val GROUP_DOWNLOADER = "group_downloader"
const val CHANNEL_DOWNLOADER_PROGRESS = "downloader_progress_channel"
const val ID_DOWNLOAD_CHAPTER = -201
const val ID_DOWNLOAD_CHAPTER_PROGRESS = -201
const val CHANNEL_DOWNLOADER_COMPLETE = "downloader_complete_channel"
const val ID_DOWNLOAD_CHAPTER_ERROR = -202
const val ID_DOWNLOAD_CHAPTER_COMPLETE = -203
const val CHANNEL_DOWNLOADER_ERROR = "downloader_error_channel"
const val ID_DOWNLOAD_CHAPTER_ERROR = -202
/**
* Notification channel and ids used by the library updater.
@@ -104,6 +105,13 @@ object Notifications {
group = GROUP_DOWNLOADER
setShowBadge(false)
},
NotificationChannel(
CHANNEL_DOWNLOADER_ERROR, context.getString(R.string.channel_errors),
NotificationManager.IMPORTANCE_LOW
).apply {
group = GROUP_DOWNLOADER
setShowBadge(false)
},
NotificationChannel(
CHANNEL_NEW_CHAPTERS, context.getString(R.string.channel_new_chapters),
NotificationManager.IMPORTANCE_DEFAULT
@@ -21,7 +21,6 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EIGHTMUSES_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HBROWSE_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
@@ -89,7 +88,6 @@ class ExtensionManager(
NHENTAI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_nhentai_source)
HITOMI_SOURCE_ID -> context.getDrawable(R.mipmap.ic_hitomi_source)
EIGHTMUSES_SOURCE_ID -> context.getDrawable(R.mipmap.ic_8muses_source)
HBROWSE_SOURCE_ID -> context.getDrawable(R.mipmap.ic_hbrowse_source)
MERGED_SOURCE_ID -> context.getDrawable(R.mipmap.ic_merged_source)
else -> null
}
@@ -141,7 +141,6 @@ open class SourceManager(private val context: Context) {
exSrcs += NHentai(context)
exSrcs += Hitomi(context)
exSrcs += EightMuses(context)
exSrcs += HBrowse(context)
return exSrcs
}
// SY <--
@@ -200,7 +199,13 @@ open class SourceManager(private val context: Context) {
"eu.kanade.tachiyomi.extension.all.mangadex",
MangaDex::class,
true
)*/
)*/,
DelegatedSource(
"HBrowse",
1401584337232758222,
"eu.kanade.tachiyomi.extension.en.hbrowse.HBrowse",
HBrowse::class
)
).associateBy { it.originalSourceQualifiedClassName }
var currentDelegatedSources = mutableMapOf<String, DelegatedSource>()
@@ -2,330 +2,54 @@ package eu.kanade.tachiyomi.source.online.english
import android.content.Context
import android.net.Uri
import com.github.salomonbrys.kotson.array
import com.github.salomonbrys.kotson.string
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservable
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.LewdSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.asJsoup
import exh.HBROWSE_SOURCE_ID
import exh.metadata.metadata.HBrowseSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.search.Namespace
import exh.search.SearchEngine
import exh.search.Text
import exh.source.DelegatedHttpSource
import exh.ui.metadata.adapters.HBrowseDescriptionAdapter
import exh.util.await
import exh.util.dropBlank
import exh.util.urlImportFetchSearchManga
import hu.akarnokd.rxjava.interop.RxJavaInterop
import info.debatty.java.stringsimilarity.Levenshtein
import kotlin.math.ceil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.rx2.asSingle
import okhttp3.CookieJar
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import rx.schedulers.Schedulers
class HBrowse(val context: Context) : HttpSource(), LewdSource<HBrowseSearchMetadata, Document>, UrlImportableSource {
/**
* An ISO 639-1 compliant language code (two letters in lower case).
*/
override val lang: String = "en"
/**
* Base url of the website without the trailing slash, like: http://mysite.com
*/
override val baseUrl = HBrowseSearchMetadata.BASE_URL
override val name: String = "HBrowse"
override val supportsLatest = true
class HBrowse(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate),
LewdSource<HBrowseSearchMetadata, Document>,
UrlImportableSource {
override val metaClass = HBrowseSearchMetadata::class
override val lang = "en"
override val id: Long = HBROWSE_SOURCE_ID
// Support direct URL importing
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
urlImportFetchSearchManga(context, query) {
super.fetchSearchManga(page, query, filters)
}
override fun headersBuilder() = Headers.Builder()
.add("Cookie", BASE_COOKIES)
private val clientWithoutCookies = client.newBuilder()
.cookieJar(CookieJar.NO_COOKIES)
.build()
private val nonRedirectingClientWithoutCookies = clientWithoutCookies.newBuilder()
.followRedirects(false)
.build()
private val searchEngine = SearchEngine()
/**
* Returns the request for the popular manga given the page.
*
* @param page the page number to retrieve.
*/
override fun popularMangaRequest(page: Int) = GET("$baseUrl/browse/title/rank/DESC/$page", headers)
private fun parseListing(response: Response): MangasPage {
val doc = response.asJsoup()
val main = doc.selectFirst("#main")
val items = main.select(".thumbTable > tbody")
val manga = items.map { mangaEle ->
SManga.create().apply {
val thumbElement = mangaEle.selectFirst(".thumbImg")
url = "/" + thumbElement.parent().attr("href").split("/").dropBlank().first()
title = thumbElement.parent().attr("title").substringAfter('\'').substringBeforeLast('\'')
thumbnail_url = baseUrl + thumbElement.attr("src")
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.flatMap {
parseToManga(manga, it.asJsoup()).andThen(Observable.just(manga))
}
}
val hasNextPage = doc.selectFirst("#main > p > a[title~=jump]:nth-last-child(1)") != null
return MangasPage(
manga,
hasNextPage
)
}
/**
* Returns an observable containing a page with a list of manga. Normally it's not needed to
* override this method.
*
* @param page the page number to retrieve.
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return urlImportFetchSearchManga(context, query) {
fetchSearchMangaInternal(page, query, filters)
}
}
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun popularMangaParse(response: Response) = parseListing(response)
/**
* Returns the request for the search manga given the page.
*
* @param page the page number to retrieve.
* @param query the search query.
* @param filters the list of filters to apply.
*/
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Should not be called!")
private fun fetchSearchMangaInternal(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return RxJavaInterop.toV1Single(
GlobalScope.async(Dispatchers.IO) {
val modeFilter = filters.filterIsInstance<ModeFilter>().firstOrNull()
val sortFilter = filters.filterIsInstance<SortFilter>().firstOrNull()
var base: String? = null
var isSortFilter = false
// <NS, VALUE, EXCLUDED>
var tagQuery: List<Triple<String, String, Boolean>>? = null
if (sortFilter != null) {
sortFilter.state?.let { state ->
if (query.isNotBlank()) {
throw IllegalArgumentException("Cannot use sorting while text/tag search is active!")
}
isSortFilter = true
base = "/browse/title/${SortFilter.SORT_OPTIONS[state.index].first}/${if (state.ascending) "ASC" else "DESC"}"
}
}
if (base == null) {
base = if (modeFilter != null && modeFilter.state == 1) {
tagQuery = searchEngine.parseQuery(query, false).map {
when (it) {
is Text -> {
var minDist = Int.MAX_VALUE.toDouble()
// ns, value
var minContent: Pair<String, String> = "" to ""
for (ns in ALL_TAGS) {
val (v, d) = ns.value.nearest(it.rawTextOnly(), minDist)
if (d < minDist) {
minDist = d
minContent = ns.key to v
}
}
minContent
}
is Namespace -> {
// Map ns aliases
val mappedNs = NS_MAPPINGS[it.namespace] ?: it.namespace
var key = mappedNs
if (!ALL_TAGS.containsKey(key)) key = ALL_TAGS.keys.sorted().nearest(mappedNs).first
// Find nearest NS
val nsContents = ALL_TAGS[key]
key to nsContents!!.nearest(it.tag?.rawTextOnly() ?: "").first
}
else -> error("Unknown type!")
}.let { p ->
Triple(p.first, p.second, it.excluded)
}
}
"/result"
} else {
"/search"
}
}
base += "/$page"
if (isSortFilter) {
parseListing(
client.newCall(GET(baseUrl + base, headers))
.asObservableSuccess()
.toSingle()
.await(Schedulers.io())
)
} else {
val body = if (tagQuery != null) {
FormBody.Builder()
.add("type", "advance")
.apply {
tagQuery.forEach {
add(it.first + "_" + it.second, if (it.third) "n" else "y")
}
}
} else {
FormBody.Builder()
.add("type", "search")
.add("needle", query)
}
val processRequest = POST(
"$baseUrl/content/process.php",
headers,
body = body.build()
)
val processResponse = nonRedirectingClientWithoutCookies.newCall(processRequest)
.asObservable()
.toSingle()
.await(Schedulers.io())
if (!processResponse.isRedirect) {
throw IllegalStateException("Unexpected process response code!")
}
val sessId = processResponse.headers("Set-Cookie").find {
it.startsWith("PHPSESSID")
} ?: throw IllegalStateException("Missing server session cookie!")
val response = clientWithoutCookies.newCall(
GET(
baseUrl + base,
headersBuilder()
.set("Cookie", BASE_COOKIES + " " + sessId.substringBefore(';'))
.build()
)
)
.asObservableSuccess()
.toSingle()
.await(Schedulers.io())
val doc = response.asJsoup()
val manga = doc.select(".browseDescription").map {
SManga.create().apply {
val first = it.child(0)
url = first.attr("href")
title = first.attr("title").substringAfter('\'').removeSuffix("'").replace('_', ' ')
thumbnail_url = HBrowseSearchMetadata.guessThumbnailUrl(url.substring(1))
}
}
val hasNextPage = doc.selectFirst("#main > p > a[title~=jump]:nth-last-child(1)") != null
MangasPage(
manga,
hasNextPage
)
}
}.asSingle(GlobalScope.coroutineContext)
).toObservable()
}
// Collection must be sorted and cannot be sorted
private fun List<String>.nearest(string: String, maxDist: Double = Int.MAX_VALUE.toDouble()): Pair<String, Double> {
val idx = binarySearch(string)
return if (idx < 0) {
val l = Levenshtein()
var minSoFar = maxDist
var minIndexSoFar = 0
forEachIndexed { index, s ->
val d = l.distance(string, s, ceil(minSoFar).toInt())
if (d < minSoFar) {
minSoFar = d
minIndexSoFar = index
}
}
get(minIndexSoFar) to minSoFar
} else {
get(idx) to 0.0
}
}
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun searchMangaParse(response: Response) = parseListing(response)
/**
* Returns the request for latest manga given the page.
*
* @param page the page number to retrieve.
*/
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/browse/title/date/DESC/$page", headers)
/**
* Parses the response from the site and returns a [MangasPage] object.
*
* @param response the response from the site.
*/
override fun latestUpdatesParse(response: Response) = parseListing(response)
/**
* Parses the response from the site and returns the details of a manga.
*
* @param response the response from the site.
*/
override fun mangaDetailsParse(response: Response): SManga {
throw UnsupportedOperationException("Should not be called!")
}
override fun parseIntoMetadata(metadata: HBrowseSearchMetadata, input: Document) {
val tables = parseIntoTables(input)
with(metadata) {
hbId = Uri.parse(input.location()).pathSegments.first().toLong()
hbUrl = input.location().removePrefix("$baseUrl/thumbnails")
hbId = hbUrl!!.removePrefix("/").substringBefore("/").toLong()
tags.clear()
(tables[""]!! + tables["categories"]!!).forEach { (k, v) ->
((tables[""] ?: error("")) + (tables["categories"] ?: error(""))).forEach { (k, v) ->
when (val lowercaseNs = k.toLowerCase()) {
"title" -> title = v.text()
"length" -> length = v.text().substringBefore(" ").toInt()
@@ -343,35 +67,6 @@ class HBrowse(val context: Context) : HttpSource(), LewdSource<HBrowseSearchMeta
}
}
/**
* Returns an observable with the updated details for a manga. Normally it's not needed to
* override this method.
*
* @param manga the manga to be updated.
*/
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(mangaDetailsRequest(manga))
.asObservableSuccess()
.flatMap {
parseToManga(manga, it.asJsoup()).andThen(Observable.just(manga))
}
}
/**
* Parses the response from the site and returns a list of chapters.
*
* @param response the response from the site.
*/
override fun chapterListParse(response: Response): List<SChapter> {
return parseIntoTables(response.asJsoup())["read manga online"]?.map { (key, value) ->
SChapter.create().apply {
url = value.selectFirst(".listLink").attr("href")
name = key
}
} ?: emptyList()
}
private fun parseIntoTables(doc: Document): Map<String, Map<String, Element>> {
return doc.select("#main > .listTable").map { ele ->
val tableName = ele.previousElementSibling()?.text()?.toLowerCase() ?: ""
@@ -381,606 +76,16 @@ class HBrowse(val context: Context) : HttpSource(), LewdSource<HBrowseSearchMeta
}.toMap()
}
/**
* Parses the response from the site and returns a list of pages.
*
* @param response the response from the site.
*/
override fun pageListParse(response: Response): List<Page> {
val doc = response.asJsoup()
val basePath = listOf("data") + response.request.url.pathSegments
val scripts = doc.getElementsByTag("script").map { it.data() }
for (script in scripts) {
val totalPages = TOTAL_PAGES_REGEX.find(script)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: continue
val pageList = PAGE_LIST_REGEX.find(script)?.groupValues?.getOrNull(1) ?: continue
return JsonParser.parseString(pageList).array.take(totalPages).map {
it.string
}.mapIndexed { index, pageName ->
Page(
index,
pageName,
"$baseUrl/${basePath.joinToString("/")}/$pageName"
)
}
}
return emptyList()
}
class HelpFilter : Filter.HelpDialog(
"Usage instructions",
markdown =
"""
### Modes
There are three available filter modes:
- Text search
- Tag search
- Sort mode
You can only use a single mode at a time. Switch between the text and tag search modes using the dropdown menu. Switch to sorting mode by selecting a sorting option.
### Text search
Search for galleries by title, artist or origin.
### Tag search
Search for galleries by tag (e.g. search for a specific genre, type, setting, etc). Uses nhentai/e-hentai syntax. Refer to the "Search" section on [this page](https://nhentai.net/info/) for more information.
### Sort mode
View a list of all galleries sorted by a specific parameter. Exit sorting mode by resetting the filters using the reset button near the bottom of the screen.
### Tag list
""".trimIndent() + "\n$TAGS_AS_MARKDOWN"
)
class ModeFilter : Filter.Select<String>(
"Mode",
arrayOf(
"Text search",
"Tag search"
)
)
class SortFilter : Filter.Sort("Sort", SORT_OPTIONS.map { it.second }.toTypedArray()) {
companion object {
// internal to display
val SORT_OPTIONS = listOf(
"length" to "Length",
"date" to "Date added",
"rank" to "Rank"
)
}
}
override fun getFilterList() = FilterList(
HelpFilter(),
ModeFilter(),
SortFilter()
)
/**
* Parses the response from the site and returns the absolute url to the source image.
*
* @param response the response from the site.
*/
override fun imageUrlParse(response: Response): String {
throw UnsupportedOperationException("Should not be called!")
}
override val matchingHosts = listOf(
"www.hbrowse.com",
"hbrowse.com"
)
override fun mapUrlToMangaUrl(uri: Uri): String? {
return "$baseUrl/${uri.pathSegments.first()}"
return "/${uri.pathSegments.first()}/c00001/"
}
override fun getDescriptionAdapter(controller: MangaController): HBrowseDescriptionAdapter {
return HBrowseDescriptionAdapter(controller)
}
companion object {
private val PAGE_LIST_REGEX = Regex("list *= *(\\[.*]);")
private val TOTAL_PAGES_REGEX = Regex("totalPages *= *([0-9]*);")
private const val BASE_COOKIES = "thumbnails=1;"
private val NS_MAPPINGS = mapOf(
"set" to "setting",
"loc" to "setting",
"location" to "setting",
"fet" to "fetish",
"relation" to "relationship",
"male" to "malebody",
"female" to "femalebody",
"pos" to "position"
)
private val ALL_TAGS = mapOf(
"genre" to listOf(
"action",
"adventure",
"anime",
"bizarre",
"comedy",
"drama",
"fantasy",
"gore",
"historic",
"horror",
"medieval",
"modern",
"myth",
"psychological",
"romance",
"school_life",
"scifi",
"supernatural",
"video_game",
"visual_novel"
),
"type" to listOf(
"anthology",
"bestiality",
"dandere",
"deredere",
"deviant",
"fully_colored",
"furry",
"futanari",
"gender_bender",
"guro",
"harem",
"incest",
"kuudere",
"lolicon",
"long_story",
"netorare",
"non-con",
"partly_colored",
"reverse_harem",
"ryona",
"short_story",
"shotacon",
"transgender",
"tsundere",
"uncensored",
"vanilla",
"yandere",
"yaoi",
"yuri"
),
"setting" to listOf(
"amusement_park",
"attic",
"automobile",
"balcony",
"basement",
"bath",
"beach",
"bedroom",
"cabin",
"castle",
"cave",
"church",
"classroom",
"deck",
"dining_room",
"doctors",
"dojo",
"doorway",
"dream",
"dressing_room",
"dungeon",
"elevator",
"festival",
"gym",
"haunted_building",
"hospital",
"hotel",
"hot_springs",
"kitchen",
"laboratory",
"library",
"living_room",
"locker_room",
"mansion",
"office",
"other",
"outdoor",
"outer_space",
"park",
"pool",
"prison",
"public",
"restaurant",
"restroom",
"roof",
"sauna",
"school",
"school_nurses_office",
"shower",
"shrine",
"storage_room",
"store",
"street",
"teachers_lounge",
"theater",
"tight_space",
"toilet",
"train",
"transit",
"virtual_reality",
"warehouse",
"wilderness"
),
"fetish" to listOf(
"androphobia",
"apron",
"assertive_girl",
"bikini",
"bloomers",
"breast_expansion",
"business_suit",
"chastity_device",
"chinese_dress",
"christmas",
"collar",
"corset",
"cosplay_(female)",
"cosplay_(male)",
"crossdressing_(female)",
"crossdressing_(male)",
"eye_patch",
"food",
"giantess",
"glasses",
"gothic_lolita",
"gyaru",
"gynophobia",
"high_heels",
"hot_pants",
"impregnation",
"kemonomimi",
"kimono",
"knee_high_socks",
"lab_coat",
"latex",
"leotard",
"lingerie",
"maid_outfit",
"mother_and_daughter",
"none",
"nonhuman_girl",
"olfactophilia",
"pregnant",
"rich_girl",
"school_swimsuit",
"shy_girl",
"sisters",
"sleeping_girl",
"sporty",
"stockings",
"strapon",
"student_uniform",
"swimsuit",
"tanned",
"tattoo",
"time_stop",
"twins_(coed)",
"twins_(female)",
"twins_(male)",
"uniform",
"wedding_dress"
),
"role" to listOf(
"alien",
"android",
"angel",
"athlete",
"bride",
"bunnygirl",
"cheerleader",
"delinquent",
"demon",
"doctor",
"dominatrix",
"escort",
"foreigner",
"ghost",
"housewife",
"idol",
"magical_girl",
"maid",
"mamono",
"massagist",
"miko",
"mythical_being",
"neet",
"nekomimi",
"newlywed",
"ninja",
"normal",
"nun",
"nurse",
"office_lady",
"other",
"police",
"priest",
"princess",
"queen",
"school_nurse",
"scientist",
"sorcerer",
"student",
"succubus",
"teacher",
"tomboy",
"tutor",
"waitress",
"warrior",
"witch"
),
"relationship" to listOf(
"acquaintance",
"anothers_daughter",
"anothers_girlfriend",
"anothers_mother",
"anothers_sister",
"anothers_wife",
"aunt",
"babysitter",
"childhood_friend",
"classmate",
"cousin",
"customer",
"daughter",
"daughter-in-law",
"employee",
"employer",
"enemy",
"fiance",
"friend",
"friends_daughter",
"friends_girlfriend",
"friends_mother",
"friends_sister",
"friends_wife",
"girlfriend",
"landlord",
"manager",
"master",
"mother",
"mother-in-law",
"neighbor",
"niece",
"none",
"older_sister",
"patient",
"pet",
"physician",
"relative",
"relatives_friend",
"relatives_girlfriend",
"relatives_wife",
"servant",
"server",
"sister-in-law",
"slave",
"stepdaughter",
"stepmother",
"stepsister",
"stranger",
"student",
"teacher",
"tutee",
"tutor",
"twin",
"underclassman",
"upperclassman",
"wife",
"workmate",
"younger_sister"
),
"maleBody" to listOf(
"adult",
"animal",
"animal_ears",
"bald",
"beard",
"dark_skin",
"elderly",
"exaggerated_penis",
"fat",
"furry",
"goatee",
"hairy",
"half_animal",
"horns",
"large_penis",
"long_hair",
"middle_age",
"monster",
"muscular",
"mustache",
"none",
"short",
"short_hair",
"skinny",
"small_penis",
"tail",
"tall",
"tanned",
"tan_line",
"teenager",
"wings",
"young"
),
"femaleBody" to listOf(
"adult",
"animal_ears",
"bald",
"big_butt",
"chubby",
"dark_skin",
"elderly",
"elf_ears",
"exaggerated_breasts",
"fat",
"furry",
"hairy",
"hair_bun",
"half_animal",
"halo",
"hime_cut",
"horns",
"large_breasts",
"long_hair",
"middle_age",
"monster_girl",
"muscular",
"none",
"pigtails",
"ponytail",
"short",
"short_hair",
"skinny",
"small_breasts",
"tail",
"tall",
"tanned",
"tan_line",
"teenager",
"twintails",
"wings",
"young"
),
"grouping" to listOf(
"foursome_(1_female)",
"foursome_(1_male)",
"foursome_(mixed)",
"foursome_(only_female)",
"one_on_one",
"one_on_one_(2_females)",
"one_on_one_(2_males)",
"orgy_(1_female)",
"orgy_(1_male)",
"orgy_(mainly_female)",
"orgy_(mainly_male)",
"orgy_(mixed)",
"orgy_(only_female)",
"orgy_(only_male)",
"solo_(female)",
"solo_(male)",
"threesome_(1_female)",
"threesome_(1_male)",
"threesome_(only_female)",
"threesome_(only_male)"
),
"scene" to listOf(
"adultery",
"ahegao",
"anal_(female)",
"anal_(male)",
"aphrodisiac",
"armpit_sex",
"asphyxiation",
"blackmail",
"blowjob",
"bondage",
"breast_feeding",
"breast_sucking",
"bukkake",
"cheating_(female)",
"cheating_(male)",
"chikan",
"clothed_sex",
"consensual",
"cunnilingus",
"defloration",
"discipline",
"dominance",
"double_penetration",
"drunk",
"enema",
"exhibitionism",
"facesitting",
"fingering_(female)",
"fingering_(male)",
"fisting",
"footjob",
"grinding",
"groping",
"handjob",
"humiliation",
"hypnosis",
"intercrural",
"interracial_sex",
"interspecies_sex",
"lactation",
"lotion",
"masochism",
"masturbation",
"mind_break",
"nonhuman",
"orgy",
"paizuri",
"phone_sex",
"props",
"rape",
"reverse_rape",
"rimjob",
"sadism",
"scat",
"sex_toys",
"spanking",
"squirt",
"submission",
"sumata",
"swingers",
"tentacles",
"voyeurism",
"watersports",
"x-ray_blowjob",
"x-ray_sex"
),
"position" to listOf(
"69",
"acrobat",
"arch",
"bodyguard",
"butterfly",
"cowgirl",
"dancer",
"deck_chair",
"deep_stick",
"doggy",
"drill",
"ex_sex",
"jockey",
"lap_dance",
"leg_glider",
"lotus",
"mastery",
"missionary",
"none",
"other",
"pile_driver",
"prison_guard",
"reverse_piggyback",
"rodeo",
"spoons",
"standing",
"teaspoons",
"unusual",
"victory"
)
).mapValues { it.value.sorted() }
private val TAGS_AS_MARKDOWN = ALL_TAGS.map { (ns, values) ->
"#### $ns\n" + values.map { "- $it" }.joinToString("\n")
}.joinToString("\n\n")
}
}
@@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.browse.source
package eu.kanade.tachiyomi.ui.browse
import android.content.Context
import android.graphics.Canvas
@@ -23,8 +23,8 @@ class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoratio
for (i in 0 until childCount - 1) {
val child = parent.getChildAt(i)
val holder = parent.getChildViewHolder(child)
if (holder is SourceHolder &&
parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder
if (holder is SourceListItem &&
parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceListItem
) {
val top = child.bottom + child.marginBottom
val bottom = top + divider.intrinsicHeight
@@ -0,0 +1,5 @@
package eu.kanade.tachiyomi.ui.browse
import eu.kanade.tachiyomi.ui.base.holder.SlicedHolder
interface SourceListItem : SlicedHolder
@@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.extension.model.Extension
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.SourceDividerItemDecoration
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsController
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
@@ -75,7 +76,7 @@ open class ExtensionController :
// Create recycler and set adapter.
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter
binding.recycler.addItemDecoration(ExtensionDividerItemDecoration(view.context))
binding.recycler.addItemDecoration(SourceDividerItemDecoration(view.context))
adapter?.fastScroller = binding.fastScroller
}
@@ -1,48 +0,0 @@
package eu.kanade.tachiyomi.ui.browse.extension
import android.content.Context
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.View
import androidx.core.view.marginBottom
import androidx.recyclerview.widget.RecyclerView
class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() {
private val divider: Drawable
init {
val a = context.obtainStyledAttributes(intArrayOf(android.R.attr.listDivider))
divider = a.getDrawable(0)!!
a.recycle()
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val childCount = parent.childCount
for (i in 0 until childCount - 1) {
val child = parent.getChildAt(i)
val holder = parent.getChildViewHolder(child)
if (holder is ExtensionHolder &&
parent.getChildViewHolder(parent.getChildAt(i + 1)) is ExtensionHolder
) {
val top = child.bottom + child.marginBottom
val bottom = top + divider.intrinsicHeight
val left = parent.paddingStart + holder.margin
val right = parent.width - parent.paddingEnd - holder.margin
divider.setBounds(left, top, right, bottom)
divider.draw(c)
}
}
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
outRect.set(0, 0, 0, divider.intrinsicHeight)
}
}
@@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.extension.model.InstallStep
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.holder.SlicedHolder
import eu.kanade.tachiyomi.ui.browse.SourceListItem
import eu.kanade.tachiyomi.util.system.LocaleHelper
import io.github.mthli.slice.Slice
import kotlinx.android.synthetic.main.extension_card_item.card
@@ -22,6 +23,7 @@ import uy.kohesive.injekt.api.get
class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
BaseFlexibleViewHolder(view, adapter),
SourceListItem,
SlicedHolder {
override val slice = Slice(card).apply {
@@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.browse.SourceDividerItemDecoration
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.source.SourceDividerItemDecoration
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -12,9 +12,9 @@ import eu.kanade.tachiyomi.databinding.MigrationSourcesControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.SourceDividerItemDecoration
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrationMangaController
import eu.kanade.tachiyomi.ui.browse.source.SourceDividerItemDecoration
import eu.kanade.tachiyomi.util.lang.launchUI
import exh.util.await
import kotlinx.coroutines.Dispatchers
@@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.holder.SlicedHolder
import eu.kanade.tachiyomi.ui.browse.SourceListItem
import io.github.mthli.slice.Slice
import kotlinx.android.synthetic.main.source_main_controller_card_item.card
import kotlinx.android.synthetic.main.source_main_controller_card_item.image
@@ -14,6 +15,7 @@ import kotlinx.android.synthetic.main.source_main_controller_card_item.title
class SourceHolder(view: View, override val adapter: SourceAdapter) :
BaseFlexibleViewHolder(view, adapter),
SourceListItem,
SlicedHolder {
override val slice = Slice(card).apply {
@@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.SourceDividerItemDecoration
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
@@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.base.holder.SlicedHolder
import eu.kanade.tachiyomi.ui.browse.SourceListItem
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setVectorCompat
import io.github.mthli.slice.Slice
@@ -18,6 +19,7 @@ import kotlinx.android.synthetic.main.source_main_controller_card_item.title
class SourceHolder(private val view: View, override val adapter: SourceAdapter /* SY --> */, private val showButtons: Boolean /* SY <-- */) :
BaseFlexibleViewHolder(view, adapter),
SourceListItem,
SlicedHolder {
override val slice = Slice(card).apply {
@@ -149,7 +149,10 @@ class SourcePresenter(
}
private fun updateLastUsedSource(sourceId: Long) {
val source = (sourceManager.get(sourceId) as? CatalogueSource)?.let { SourceItem(it, showButtons = controllerMode == SourceController.Mode.CATALOGUE) }
val source = (sourceManager.get(sourceId) as? CatalogueSource)?.let {
val isPinned = it.id.toString() in preferences.pinnedSources().get()
SourceItem(it, null, isPinned, controllerMode == SourceController.Mode.CATALOGUE)
}
source?.let { view?.setLastUsedSource(it) }
}
@@ -16,11 +16,14 @@ class TriStateSectionItem(filter: Filter.TriState) : TriStateItem(filter), ISect
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return filter == (other as TriStateSectionItem).filter
other as TriStateSectionItem
if (head != other.head) return false
return filter == other.filter
}
override fun hashCode(): Int {
return filter.hashCode()
return filter.hashCode() + (head?.hashCode() ?: 0)
}
}
@@ -37,11 +40,14 @@ class TextSectionItem(filter: Filter.Text) : TextItem(filter), ISectionable<Text
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return filter == (other as TextSectionItem).filter
other as TextSectionItem
if (head != other.head) return false
return filter == other.filter
}
override fun hashCode(): Int {
return filter.hashCode()
return filter.hashCode() + (head?.hashCode() ?: 0)
}
}
@@ -58,11 +64,14 @@ class CheckboxSectionItem(filter: Filter.CheckBox) : CheckboxItem(filter), ISect
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return filter == (other as CheckboxSectionItem).filter
other as CheckboxSectionItem
if (head != other.head) return false
return filter == other.filter
}
override fun hashCode(): Int {
return filter.hashCode()
return filter.hashCode() + (head?.hashCode() ?: 0)
}
}
@@ -79,11 +88,14 @@ class SelectSectionItem(filter: Filter.Select<*>) : SelectItem(filter), ISection
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return filter == (other as SelectSectionItem).filter
other as SelectSectionItem
if (head != other.head) return false
return filter == other.filter
}
override fun hashCode(): Int {
return filter.hashCode()
return filter.hashCode() + (head?.hashCode() ?: 0)
}
}
@@ -50,7 +50,6 @@ import exh.EH_SOURCE_ID
import exh.EIGHTMUSES_SOURCE_ID
import exh.EXHMigrations
import exh.EXH_SOURCE_ID
import exh.HBROWSE_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PERV_EDEN_EN_SOURCE_ID
@@ -235,9 +234,6 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
if (EIGHTMUSES_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += EIGHTMUSES_SOURCE_ID
}
if (HBROWSE_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += HBROWSE_SOURCE_ID
}
}
// SY -->
@@ -309,7 +309,7 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
R.id.action_settings -> ReaderSettingsSheet(this).show()
R.id.action_custom_filter -> {
val sheet = ReaderColorFilterSheet(this)
// Remove dimmed backdrop so changes can be previewd
// Remove dimmed backdrop so changes can be previewed
.apply { window?.setDimAmount(0f) }
// Hide toolbars while sheet is open for better preview
@@ -35,7 +35,6 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.EH_SOURCE_ID
import exh.EIGHTMUSES_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HBROWSE_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PERV_EDEN_EN_SOURCE_ID
@@ -175,9 +174,6 @@ class SettingsAdvancedController : SettingsController() {
if (EIGHTMUSES_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += EIGHTMUSES_SOURCE_ID
}
if (HBROWSE_SOURCE_ID !in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES += HBROWSE_SOURCE_ID
}
} else {
if (EH_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
@@ -200,9 +196,6 @@ class SettingsAdvancedController : SettingsController() {
if (EIGHTMUSES_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= EIGHTMUSES_SOURCE_ID
}
if (HBROWSE_SOURCE_ID in BlacklistedSources.HIDDEN_SOURCES) {
BlacklistedSources.HIDDEN_SOURCES -= HBROWSE_SOURCE_ID
}
}
true
}
@@ -38,13 +38,15 @@ fun syncChaptersWithSource(
// Chapters from db.
val dbChapters = db.getChapters(manga).executeAsBlocking()
val sourceChapters = rawSourceChapters.mapIndexed { i, sChapter ->
Chapter.create().apply {
copyFrom(sChapter)
manga_id = manga.id
source_order = i
val sourceChapters = rawSourceChapters
.distinctBy { it.url }
.mapIndexed { i, sChapter ->
Chapter.create().apply {
copyFrom(sChapter)
manga_id = manga.id
source_order = i
}
}
}
// Chapters from the source not in db.
val toAdd = mutableListOf<Chapter>()
@@ -34,11 +34,14 @@ object DiskUtil {
* Gets the available space for the disk that a file path points to, in bytes.
*/
fun getAvailableStorageSpace(f: UniFile): Long {
val stat = StatFs(f.filePath)
val availBlocks = stat.availableBlocksLong
val blockSize = stat.blockSizeLong
val stat = try {
StatFs(f.filePath)
} catch (_: Exception) {
// Assume that exception is thrown when path is on external storage
StatFs(Environment.getExternalStorageDirectory().path)
}
return availBlocks * blockSize
return stat.availableBlocksLong * stat.blockSizeLong
}
/**
+4 -2
View File
@@ -2,6 +2,7 @@ package exh
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.HentaiCafe
import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino
@@ -22,13 +23,14 @@ val PURURIN_SOURCE_ID = delegatedSourceId<Pururin>()
val TSUMINO_SOURCE_ID = delegatedSourceId<Tsumino>()
const val HITOMI_SOURCE_ID = LEWD_SOURCE_SERIES + 10
const val EIGHTMUSES_SOURCE_ID = LEWD_SOURCE_SERIES + 11
const val HBROWSE_SOURCE_ID = LEWD_SOURCE_SERIES + 12
val HBROWSE_SOURCE_ID = delegatedSourceId<HBrowse>()
const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69
private val DELEGATED_LEWD_SOURCES = listOf(
HentaiCafe::class,
Pururin::class,
Tsumino::class
Tsumino::class,
HBrowse::class
)
val LIBRARY_UPDATE_EXCLUDED_SOURCES = listOf(
+46
View File
@@ -2,6 +2,8 @@ package exh
import android.content.Context
import com.elvishew.xlog.XLog
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.data.backup.models.DHistory
import eu.kanade.tachiyomi.data.database.DatabaseHelper
@@ -9,6 +11,8 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.resolvers.MangaUrlPutResolver
import eu.kanade.tachiyomi.data.database.tables.MangaTable
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.updater.UpdaterJob
@@ -43,6 +47,44 @@ object EXHMigrations {
LibraryUpdateJob.setupTask(context)
return false
}
if (oldVersion < 4) {
db.inTransaction {
// Migrate Tsumino source IDs
db.lowLevel().executeSQL(
RawQuery.builder()
.query(
"""
UPDATE ${MangaTable.TABLE}
SET ${MangaTable.COL_SOURCE} = $HBROWSE_SOURCE_ID
WHERE ${MangaTable.COL_SOURCE} = 6912
""".trimIndent()
)
.affectsTables(MangaTable.TABLE)
.build()
)
// Migrate BHrowse URLs
val hBrowseManga = db.db.get()
.listOfObjects(Manga::class.java)
.withQuery(
Query.builder()
.table(MangaTable.TABLE)
.where("${MangaTable.COL_SOURCE} = $HBROWSE_SOURCE_ID")
.build()
)
.prepare()
.executeAsBlocking()
hBrowseManga.forEach {
it.url = it.url + "/c00001/"
}
db.db.put()
.objects(hBrowseManga)
// Extremely slow without the resolver :/
.withPutResolver(MangaUrlPutResolver())
.prepare()
.executeAsBlocking()
}
}
// if (oldVersion < 1) { }
// do stuff here when releasing changed crap
@@ -70,6 +112,10 @@ object EXHMigrations {
manga.source = TSUMINO_SOURCE_ID!!
}
if (manga.source == 6912L) {
manga.source = HBROWSE_SOURCE_ID!!
}
// Migrate nhentai URLs
if (manga.source == NHENTAI_SOURCE_ID) {
manga.url = getUrlWithoutDomain(manga.url)
@@ -9,13 +9,19 @@ import exh.metadata.metadata.base.RaisedSearchMetadata
class HBrowseSearchMetadata : RaisedSearchMetadata() {
var hbId: Long? = null
var hbUrl: String? = null
var thumbnail: String? = null
var title: String? by titleDelegate(TITLE_TYPE_MAIN)
// Length in pages
var length: Int? = null
override fun copyTo(manga: SManga) {
manga.url = "/$hbId"
hbUrl?.let {
manga.url = it
}
title?.let {
manga.title = it
@@ -44,6 +50,8 @@ class HBrowseSearchMetadata : RaisedSearchMetadata() {
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
val pairs = mutableListOf<Pair<String, String>>()
hbId?.let { pairs += Pair(context.getString(R.string.id), it.toString()) }
hbUrl?.let { pairs += Pair(context.getString(R.string.url), it) }
thumbnail?.let { pairs += Pair(context.getString(R.string.thumbnail_url), it) }
title?.let { pairs += Pair(context.getString(R.string.title), it) }
length?.let { pairs += Pair(context.getString(R.string.page_count), it.toString()) }
return pairs
+9 -5
View File
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/holder"
style="@style/Theme.Widget.CardView.Item"
android:padding="0dp">
@@ -19,7 +20,8 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,3:2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<LinearLayout
android:layout_width="0dp"
@@ -38,13 +40,15 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.Medium" />
android:textAppearance="@style/TextAppearance.Medium"
tools:text="Title" />
<TextView
android:id="@+id/manga_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp" />
android:layout_marginTop="4dp"
tools:text="Subtitle" />
</LinearLayout>
@@ -59,7 +63,7 @@
app:layout_constraintEnd_toStartOf="@+id/resume"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_delete_24dp"
app:tint="?attr/colorOnPrimary" />
app:tint="?android:attr/textColorPrimary" />
<ImageButton
android:id="@+id/resume"
@@ -73,7 +77,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_play_arrow_24dp"
app:tint="?attr/colorOnPrimary" />
app:tint="?android:attr/textColorPrimary" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog bulletedList="true">
<changelogversion versionName="1.1.1" changeDate="Aug 2,2020">
<changelogtext>[b]Based on Tachiyomi stable 0.10.2(from 0.10.1)[/b] </changelogtext>
<changelogtext>Delegated HBrowse, if you used that source please install the extension to continue using your manga</changelogtext>
</changelogversion>
<changelogversion versionName="1.1.0" changeDate="Aug 1,2020">
<changelogtext>[b]Based on Tachiyomi stable 0.10.1(from a 0.9.2 preview)[/b] </changelogtext>
<changelogtext>Made the app translatable! Feel free to ask how to in the Tachiyomi discord or open a issue if you want to help with translations</changelogtext>
+4 -2
View File
@@ -86,7 +86,7 @@
<string name="portrait">ঘুরান</string>
<string name="landscape">আড়াআড়ি ভাবে</string>
<string name="default_columns">ডিফল্ট</string>
<string name="pref_library_update_interval">"মাংগাশালা আপডেট কাল"</string>
<string name="pref_library_update_interval">মাংগাশালা আপডেট কাল</string>
<string name="update_never">সারগ্রন্থ</string>
<string name="update_1hour">ঘন্টায়</string>
<string name="update_2hour">প্রতি ২ ঘন্টায়</string>
@@ -178,7 +178,9 @@
<string name="pref_backup_slots">স্বয়ংক্রিয় ব্যাকআপের উচ্চমাত্রা</string>
<string name="backup_created">পুনরুদ্ধার তৈরী হয়েছে</string>
<string name="restore_completed">পুনরুদ্ধার হয়েছে</string>
<string name="backup_restore_content">পুনরুদ্ধারের সন্ধান করার জন্য উৎস ব্যবহার করা হয়, ক্যারিয়ারের খরচগুলি প্রযোজ্য হতে পারে। এছাড়াও নিশ্চিত করুন যে আপনি পুনরুদ্ধারের আগে উৎসগুলোতে যথাযথভাবে লগ-ইন করা আছেন যেখানে প্রোয়জন হয়।</string>
<string name="backup_restore_content">পুনরুদ্ধারের সন্ধান করার জন্য উৎস ব্যবহার করা হয়, ক্যারিয়ারের খরচগুলি প্রযোজ্য হতে পারে। এছাড়াও নিশ্চিত করুন যে আপনি
\n
\nপুনরুদ্ধারের আগে উৎসগুলোতে যথাযথভাবে লগ-ইন করা আছেন যেখানে প্রোয়জন হয়।</string>
<string name="backup_choice">আপনি কি ব্যাকআপ করতে ইচ্ছুক?</string>
<string name="restoring_backup">ব্যাকআপ পুনরুদ্ধার হচ্ছে</string>
<string name="creating_backup">ব্যাকআপ তৈরী হচ্ছে</string>
+3 -1
View File
@@ -195,7 +195,7 @@
<string name="update_check_notification_file_download">Stáhnout aktualizaci</string>
<string name="update_check_notification_download_complete">Stahování dokončeno</string>
<string name="update_check_notification_download_error">Chyba při stahování</string>
<string name="update_check_notification_update_available">Dostupná aktualizace</string>
<string name="update_check_notification_update_available">Dostupná aktualizace!</string>
<string name="information_no_recent">Žádné nedávné kapitoly</string>
<string name="information_no_recent_manga">Žádné nedávno čtené mangy</string>
<string name="download_notifier_title_error">Chyba</string>
@@ -349,6 +349,7 @@
<string name="pref_restore_backup_summ">Obnovit knihovnu ze záložního souboru</string>
<string name="pref_backup_slots">Maximální automatické zálohování</string>
<string name="backup_restore_content">Obnovení používá zdroje pro načítání dat, mohou být účtovány poplatky od operátora (v případě použití datového připojení).
\n
\nTaké se před obnovením ujistěte, že jste správně přihlášeni do zdrojů, které to vyžadují.</string>
<string name="choices_reset">Volby dialogu se vynulují</string>
<string name="login_title">Přihlásit se do %1$s</string>
@@ -401,4 +402,5 @@
<string name="logout_title">Odhlásit z %1$s\?</string>
<string name="logout_success">Byl jste odhlášen</string>
<string name="paused">Pozastaveno</string>
<string name="label_more">Více</string>
</resources>
+1
View File
@@ -634,4 +634,5 @@
<string name="group_downloader">Descargas</string>
<string name="channel_complete">Completo</string>
<string name="channel_progress">Progreso</string>
<string name="channel_errors">Errores</string>
</resources>
+1
View File
@@ -603,4 +603,5 @@
<string name="channel_complete">पूर्ण</string>
<string name="channel_progress">प्रगती</string>
<string name="download_notifier_download_finish">डाउनलोड सम्पन्न हुआ</string>
<string name="channel_errors">त्रुटियाँ</string>
</resources>
+2 -2
View File
@@ -191,7 +191,7 @@
<string name="action_select_inverse">Kiválasztás megfordítása</string>
<string name="action_sort_last_checked">Utoljára ellenőrizve</string>
<string name="action_menu">Menü</string>
<string name="confirm_exit">A befejezéshez nyomd meg újfent a Vissza gombot!</string>
<string name="confirm_exit">A befejezéshez nyomd meg újfent a Vissza gombot</string>
<string name="unlock_app">Tachiyomi feloldása</string>
<string name="information_empty_library">A könyvtárad üres. Adj hozzá új sorozatokat a Böngészőből.</string>
<string name="information_no_recent">Nincs új frissítés</string>
@@ -205,7 +205,7 @@
<string name="download_notifier_page_error">Egy oldal hiányzik a könyvtárból</string>
<string name="download_notifier_unknown_error">Egy váratlan hiba miatt nem sikerült letölteni a fejezetet</string>
<string name="download_notifier_title_error">Hiba</string>
<string name="information_webview_outdated">A jobb kompatibilitás érdekében kérlek, frissítsd a WebView alkalmazást!</string>
<string name="information_webview_outdated">A jobb kompatibilitás érdekében kérlek, frissítsd a WebView alkalmazást</string>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">Egy új bővítményfrissítés érhető el</item>
<item quantity="other">%d bővítményfrissítés érhető el</item>
+1
View File
@@ -592,4 +592,5 @@
<string name="channel_complete">Lengkap</string>
<string name="channel_progress">Kemajuan</string>
<string name="download_notifier_download_finish">Unduhan selesai</string>
<string name="channel_errors">error</string>
</resources>
+21 -14
View File
@@ -87,7 +87,7 @@
<string name="action_webview_back">უკან</string>
<string name="action_webview_forward">წინ</string>
<string name="action_webview_refresh">განახლება</string>
<string name="loading">იტვირთება...</string>
<string name="loading">იტვირთება</string>
<string name="app_not_available">აპლიკაცია არ არის ხელმისაწვდომი</string>
<string name="short_recent_updates">განახლებები</string>
<string name="pref_category_general">ზოგადი</string>
@@ -302,7 +302,7 @@
<string name="logout_success">გამოსვლა წარმატებით მოხერხდა</string>
<string name="invalid_login">შესვლა ვერ მოხერხდა</string>
<string name="unknown_error">დაფიქსირდა უცნობი შეცდომა</string>
<string name="library_search_hint">სათაური ან ავტორი...</string>
<string name="library_search_hint">სათაური ან ავტორი</string>
<string name="updating_category">კატეგორია ახლდება</string>
<string name="local_source_badge">ლოკალური</string>
<string name="confirm_delete_manga">დარწმუნებული ხართ რომ გსურთ წაშალოთ შერჩეული მანგა?</string>
@@ -320,7 +320,7 @@
<string name="last_used_source">ბოლოს გამოყენებული</string>
<string name="invalid_combination">ნაგულისხმევის მონიშვნა შეუძლებელია სხვა კატეგორიასთან ერთად</string>
<string name="added_to_library">მანგა დაემატა თქვენს ბიბლიოთეკას</string>
<string name="action_global_search_hint">გლობალური ძებნა...</string>
<string name="action_global_search_hint">გლობალური ძებნა</string>
<string name="latest">უკანასკნელი</string>
<string name="browse">დათვალიერება</string>
<string name="manga_detail_tab">ინფორმაცია</string>
@@ -338,7 +338,8 @@
<string name="manga_info_expand">მეტი ინფორმაციის ჩვენება</string>
<string name="manga_info_collapse">ნაკლები ინფორმაციის ჩვენება</string>
<string name="delete_downloads_for_manga">გადმოწერილი თავების წაშლა</string>
<string name="copied_to_clipboard">დაკოპირებულია ბუფერში:\\n%1$s</string>
<string name="copied_to_clipboard">დაკოპირებულია ბუფერში:
\n%1$s</string>
<string name="source_not_installed">წყარო არ არის დაინსტალირებული: %1$s</string>
<string name="snack_add_to_library">დავამატო მანგა ბიბლიოთეკაში?</string>
<string name="manga_chapters_tab">თავები</string>
@@ -392,9 +393,9 @@
<string name="snack_categories_deleted">კატეგორიები წაიშალა</string>
<string name="dialog_with_checkbox_remove_description">ეს წაშლის ამ თავის წაკითხვის თარიღს. დარწმუნებული ხართ?</string>
<string name="picture_saved">სურათი შენახულია</string>
<string name="saving_picture">სურათი ინახება...</string>
<string name="saving_picture">სურათი ინახება</string>
<string name="options">პარამეტრები</string>
<string name="downloading">გადმოწერა...</string>
<string name="downloading">გადმოწერა</string>
<string name="download_progress">გადმოწერილია %1$d%%</string>
<string name="chapter_progress">გვერდი: %1$d</string>
<string name="chapter_subtitle">თავი: %1$d</string>
@@ -409,7 +410,7 @@
<string name="transition_previous">წინა:</string>
<string name="transition_no_next">შემდეგი თავი არ არის</string>
<string name="transition_no_previous">წინა თავი არ არის</string>
<string name="transition_pages_loading">გვერდები იტვირთება...</string>
<string name="transition_pages_loading">გვერდები იტვირთება</string>
<string name="transition_pages_error">გვერდების ჩატვირთვა ვერ მოხერხდა: %1$s</string>
<string name="page_list_empty_error">ვერცერთი გვერდი ვერ მოიძევნა</string>
<string name="updating_library">ბიბლიოთეკის განახლება</string>
@@ -427,7 +428,7 @@
<string name="notification_chapters_single_and_more">თავი %1$s და %2$d მეტი</string>
<string name="notification_chapters_multiple">თავები %1$s</string>
<string name="notification_cover_update_failed">ვერ მოხერხდა ყდის განახლება</string>
<string name="notification_first_add_to_library">"გთხოვთ ჯერ დაამატოთ მანგა თქვენს ბიბლიოთეკაში "</string>
<string name="notification_first_add_to_library">გთხოვთ ჯერ დაამატოთ მანგა თქვენს ბიბლიოთეკაში</string>
<string name="notification_not_connected_to_ac_title">სინქრონიზაცია გაუქმებულია</string>
<string name="notification_no_connection_title">სინქრონიზაცია გაუქმებულია</string>
<string name="notification_not_connected_to_ac_body">არ არის შეერთებული დენის წყაროსთან</string>
@@ -437,10 +438,10 @@
<string name="file_select_icon">ამოირჩიე მალსახმობის ნიშნაკი</string>
<string name="update_check_confirm">გადმოწერა</string>
<string name="update_check_ignore">დაიგნორება</string>
<string name="update_check_no_new_updates">"განახლება ვერ მოიძებნა "</string>
<string name="update_check_look_for_updates">განახლების ძებნა...</string>
<string name="update_check_no_new_updates">განახლება ვერ მოიძებნა</string>
<string name="update_check_look_for_updates">განახლების ძებნა</string>
<string name="update_check_notification_file_download">განახლების გადმოწერა</string>
<string name="update_check_notification_download_in_progress">გადმოწერა...</string>
<string name="update_check_notification_download_in_progress">გადმოწერა</string>
<string name="update_check_notification_download_complete">გადმოწერა დასრულებულია</string>
<string name="update_check_notification_download_error">გადმოწერის შეცდომა</string>
<string name="update_check_notification_update_available">ხელმისაწვდომია ახალი ვერსია!</string>
@@ -513,10 +514,16 @@
<string name="download_notifier_page_error">გვერდი გამოტოვებულია დირექტორიაში</string>
<string name="action_enable_all">ჩართე ყველა</string>
<string name="action_disable_all">გამორთე ყველა</string>
<string name="secure_screen_summary">"აპლიკაციებს შორის გადართვისას შემადგენლობის დამალვა და სკრინშოტების დაბლოკვა "</string>
<string name="untrusted_extension_message">ეს დამატება ხელმოწერილია უცნობი სერთიფიკატის მიერ და არ არის აქტივირებული\\n\\nმავნე დამატებას შეუძლია წაიკითხოს შესვლის მონაცემები შენახული ამ აპლიკაციაში ან გაუშვას თავისი კოდი \\n\\nამ სერტიფიკატის ნდობით თქვენ თქვენს თავზე იღებთ რისკებს და პასუხისმგებლობას.</string>
<string name="secure_screen_summary">აპლიკაციებს შორის გადართვისას შემადგენლობის დამალვა და სკრინშოტების დაბლოკვა</string>
<string name="untrusted_extension_message">ეს დამატება ხელმოწერილია უცნობი სერთიფიკატის მიერ და არ არის აქტივირებული\\n\\nმავნე დამატებას შეუძლია
\n
\nწაიკითხოს შესვლის მონაცემები შენახული ამ აპლიკაციაში ან გაუშვას თავისი კოდი \\n\\nამ სერტიფიკატის ნდობით თქვენ
\n
\nთქვენს თავზე იღებთ რისკებს და პასუხისმგებლობას.</string>
<string name="tracking_info">ცალმხრივი სინქრონიზაცია თვალყურის სადევნებელ სერვისებში თავების პროგრესის განსაახლებლად. მიადევნე თვალყური ინდივიდუალურ მანგებს მათი ჩანართებიდან</string>
<string name="backup_restore_content">აღდგენა იყენებს წყაროებს მონაცემების გადმოსაწერად.\\n\\nდარწმუნდი რომ გაქვს დაინსტალირებული ყველა საჭირო დამატება და ხარ დალოგინებული წყაროებში და თვალყურის სადევნებელ სერვისებში ააღდგენამდე.</string>
<string name="backup_restore_content">აღდგენა იყენებს წყაროებს მონაცემების გადმოსაწერად.\\n\\nდარწმუნდი რომ გაქვს დაინსტალირებული ყველა
\n
\nსაჭირო დამატება და ხარ დალოგინებული წყაროებში და თვალყურის სადევნებელ სერვისებში ააღდგენამდე.</string>
<string name="pref_refresh_library_tracking_summary">ანახლებს სტატუსს, შეფასებას და ბოლო თავს წაკითხულს თვალყურის სადევნებელ სერვისებიდან</string>
<string name="download_notifier_page_ready_error">გვერდი ვერ ჩაიტვირთა</string>
<string name="filter_mode_overlay">გადაფარება</string>
+4 -4
View File
@@ -283,12 +283,12 @@
<string name="update_check_confirm">다운로드</string>
<string name="update_check_ignore">무시</string>
<string name="update_check_no_new_updates">새로운 업데이트 없음</string>
<string name="update_check_look_for_updates">"업데이트를 찾는중 "</string>
<string name="update_check_look_for_updates">업데이트를 찾는중</string>
<string name="update_check_notification_file_download">업데이트 다운로드</string>
<string name="update_check_notification_download_in_progress">다운로드 진행중</string>
<string name="update_check_notification_download_in_progress">다운로드 진행중</string>
<string name="update_check_notification_download_complete">다운로드 완료됨</string>
<string name="update_check_notification_download_error">다운로드 오류</string>
<string name="update_check_notification_update_available">업데이트 이용 가능</string>
<string name="update_check_notification_update_available">업데이트 이용 가능!</string>
<string name="description_cover">만화 표지</string>
<string name="information_no_downloads">다운로드 없음</string>
<string name="information_no_recent">최신 챕터 없음</string>
@@ -339,7 +339,7 @@
<string name="invalid_combination">기본 카테고리는 다른 카테고리와 함께 선택될 수 없습니다</string>
<string name="added_to_library">만화가 서재에 추가되었습니다</string>
<string name="browse">찾아보기</string>
<string name="manga_not_in_db">이 만화는 데이터베이스에서 제거되었습니다!</string>
<string name="manga_not_in_db">이 만화는 데이터베이스에서 제거되었습니다</string>
<string name="licensed">판권작</string>
<string name="copied_to_clipboard">클립보드에 %1$s 복사됨</string>
<string name="manga_chapter_no_title">제목 없음</string>
+5 -5
View File
@@ -205,21 +205,21 @@
<string name="creating_backup">Oppretter sikkerhetskopi</string>
<string name="pref_clear_chapter_cache">Tøm kapittelhurtiglager</string>
<string name="used_cache">Brukt: %1$s</string>
<string name="cache_deleted">Hurtiglager tømt. %1$d filer har blitt slettet.</string>
<string name="cache_deleted">Hurtiglager tømt. %1$d filer har blitt slettet</string>
<string name="cache_delete_error">Feil oppstod under tømming av hurtiglager</string>
<string name="pref_clear_cookies">Tøm kaker</string>
<string name="cookies_cleared">Kaker tømt</string>
<string name="choices_reset">Dialogvinduvalg tilbakestilt</string>
<string name="pref_clear_database">Tøm database</string>
<string name="pref_clear_database_summary">Slett manga og kapittel som ikke finnes i biblioteket ditt</string>
<string name="clear_database_confirmation">Er du sikker\? Leste kapittel og framdrift for manga som ikke er i biblioteket vil gå tapt.</string>
<string name="clear_database_confirmation">Er du sikker\? Leste kapittel og framdrift for manga som ikke er i biblioteket vil gå tapt</string>
<string name="clear_database_completed">Oppføringer slettet</string>
<string name="pref_refresh_library_tracking">Gjenopprett sporingsmetadata</string>
<string name="pref_refresh_library_tracking_summary">Oppdaterer status, poengsum og sist leste kapittel fra sporingstjenestene</string>
<string name="version">Versjon</string>
<string name="build_time">Byggtid</string>
<string name="pref_enable_acra">Send kræsjrapporter</string>
<string name="pref_acra_summary">Hjelper til med fiksing av feil. Ingen sensitiv data vil bli sendt.</string>
<string name="pref_acra_summary">Hjelper til med fiksing av feil. Ingen sensitiv data vil bli sendt</string>
<string name="login_title">Innlogging for %1$s</string>
<string name="username">Brukernavn</string>
<string name="password">Passord</string>
@@ -295,7 +295,7 @@
<string name="track_start_date">Startet</string>
<string name="track_type">Type</string>
<string name="track_author">Forfatter</string>
<string name="error_category_exists">En kategori ved dette navnet finnes allerede.</string>
<string name="error_category_exists">En kategori ved dette navnet finnes allerede!</string>
<string name="snack_categories_deleted">Kategorier slettet</string>
<string name="dialog_with_checkbox_remove_description">Lesningsdato for dette kapittelet vil bli fjernet. Er du sikker\?</string>
<string name="dialog_with_checkbox_reset">Tilbakestill alle kapittel for denne mangaen</string>
@@ -348,7 +348,7 @@
\nForsikre deg om at du har installert alle nødvendige utvidelser og er logget inn i kilder og sporingstjenester før du gjenoppretter.</string>
<string name="confirm_set_image_as_cover">Bruk dette bildet som omslag\?</string>
<string name="migration_info">Trykk for å velge kilde å migrere fra</string>
<string name="download_queue_error">Kunne ikke laste ned kapitler. Du kan prøve igjen i nedlastingsdelen.</string>
<string name="download_queue_error">Kunne ikke laste ned kapitler. Du kan prøve igjen i nedlastingsdelen</string>
<string name="notification_update_progress">Oppdateringsfremdrift: %1$d/%2$d</string>
<string name="notification_first_add_to_library">Legg til mangaen i biblioteket ditt før du gjør dette</string>
<string name="notification_not_connected_to_ac_title">Synkronisering avbrutt</string>
@@ -603,4 +603,5 @@
<string name="group_backup_restore">Backup e restauração</string>
<string name="group_downloader">Downloads</string>
<string name="channel_complete">Concluído</string>
<string name="channel_errors">Erros</string>
</resources>
+2 -1
View File
@@ -49,7 +49,7 @@
<string name="action_stop">Стоп</string>
<string name="action_update">Обновить</string>
<string name="action_update_library">Обновить библиотеку</string>
<string name="all">Всё</string>
<string name="all">Все</string>
<string name="backup">Резервная копия</string>
<string name="black_background">Черный</string>
<string name="build_time">Дата сборки</string>
@@ -621,4 +621,5 @@
<string name="group_downloader">Загрузки</string>
<string name="channel_complete">Завершено</string>
<string name="channel_progress">Прогресс</string>
<string name="channel_errors">Ошибки</string>
</resources>
+1 -1
View File
@@ -176,7 +176,7 @@
<string name="rotation_lock">Zaključano</string>
<string name="rotation_force_portrait">Forsirano vertikalno</string>
<string name="rotation_force_landscape">Forsirano horizontalno</string>
<string name="color_filter_r_value">"R "</string>
<string name="color_filter_r_value">R</string>
<string name="color_filter_g_value">G</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
+1 -1
View File
@@ -251,7 +251,7 @@
<string name="action_global_search_hint">ค้นหาทั้งหมด…</string>
<string name="latest">ล่าสุด</string>
<string name="browse">เปิดดู</string>
<string name="manga_not_in_db">มังงะนี้ถูกลบออกจากฐานข้อมูล!</string>
<string name="manga_not_in_db">มังงะนี้ถูกลบออกจากฐานข้อมูล</string>
<string name="manga_detail_tab">ข้อมูล</string>
<string name="description">คำอธิบาย</string>
<string name="ongoing">ยังดำเนินการอยู่</string>
+3 -2
View File
@@ -210,6 +210,7 @@
<string name="backup_created">Nagawa na ang backup</string>
<string name="restore_completed">Nakumpleto na ang pagre-restore</string>
<string name="backup_restore_content">Ginagamit ng pagre-restore ang source para makakuha ng data, maaring magamit ang inyong data.
\n
\nSiguraduhin din na ikaw ay maayos na naka-login sa mga source that nangangailangan nito bago mag-restore.</string>
<string name="pref_color_filter_mode">Blend mode ng color filter</string>
<string name="filter_mode_default">Default</string>
@@ -264,7 +265,7 @@
<string name="action_global_search_hint">Pangkalahatang paghahanap…</string>
<string name="latest">Pinakabago</string>
<string name="browse">Mag-browse</string>
<string name="manga_not_in_db">Ang manga na ito ay tinanggal mula sa database!</string>
<string name="manga_not_in_db">Ang manga na ito ay tinanggal mula sa database.</string>
<string name="manga_detail_tab">Impo</string>
<string name="description">Paglalarawan</string>
<string name="ongoing">Nagpapatuloy</string>
@@ -364,7 +365,7 @@
<string name="update_check_no_new_updates">Walang mga bagong update</string>
<string name="update_check_look_for_updates">Naghahanap ng mga update…</string>
<string name="update_check_notification_file_download">Download update</string>
<string name="update_check_notification_download_in_progress">Kasalukuyang nagda-download</string>
<string name="update_check_notification_download_in_progress">Kasalukuyang nagda-download</string>
<string name="update_check_notification_download_complete">Nakumpleto ang pag-download</string>
<string name="update_check_notification_download_error">Error sa pag-download</string>
<string name="update_check_notification_update_available">Mayroong update</string>
+1 -1
View File
@@ -74,6 +74,6 @@
<color name="md_blue_grey_900">#263238</color>
<color name="md_blue_grey_800">#37474F</color>
<color name="pale_green">#99CC99</color>
<color name="pale_green">#AAE1AA</color>
</resources>
+1
View File
@@ -678,6 +678,7 @@
<string name="channel_common">Common</string>
<string name="channel_progress">Progress</string>
<string name="channel_complete">Complete</string>
<string name="channel_errors">Errors</string>
<string name="channel_library">Library</string>
<string name="group_downloader">Downloads</string>
<string name="group_backup_restore">Backup and restore</string>