Remove HBrowse and Tsumino sources from delegated sources (#1622)

This commit is contained in:
Cuong-Tran 2026-05-13 12:55:15 +07:00 committed by GitHub
parent 0293507d1a
commit dc3d034109
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
41 changed files with 27 additions and 742 deletions

View file

@ -121,7 +121,6 @@ Custom sources:
Additional features for some extensions, features include custom description, opening in app, batch add to library, and a bunch of other things based on the source:
* 8Muses (EroMuse)
* HBrowse
* Mangadex
* NHentai
* Puruin

View file

@ -333,22 +333,6 @@
<data android:pathPattern="/g/..*" />
</intent-filter>
<!-- Tsumino -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="tsumino.com" />
<data android:host="www.tsumino.com" />
<data android:pathPattern="/Read/View/..*" />
<data android:pathPattern="/Book/Info/..*" />
</intent-filter>
<!-- Pururin -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -363,21 +347,6 @@
<data android:pathPattern="/gallery/..*" />
</intent-filter>
<!-- HBrowse -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:scheme="http" />
<data android:host="hbrowse.com" />
<data android:host="www.hbrowse.com" />
<!--<data android:pathPattern="/gallery/..*" />-->
</intent-filter>
<!-- Mangadex -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />

View file

@ -16701,9 +16701,7 @@ HSPLeu/kanade/tachiyomi/source/online/all/MergedSource$special$$inlined$injectLa
PLeu/kanade/tachiyomi/source/online/all/MergedSource$special$$inlined$injectLazy$9;-><init>()V
Leu/kanade/tachiyomi/source/online/all/NHentai;
Leu/kanade/tachiyomi/source/online/english/EightMuses;
Leu/kanade/tachiyomi/source/online/english/HBrowse;
Leu/kanade/tachiyomi/source/online/english/Pururin;
Leu/kanade/tachiyomi/source/online/english/Tsumino;
Leu/kanade/tachiyomi/ui/base/activity/BaseActivity;
HSPLeu/kanade/tachiyomi/ui/base/activity/BaseActivity;-><init>()V
PLeu/kanade/tachiyomi/ui/base/activity/BaseActivity;-><init>()V

View file

@ -94,9 +94,7 @@ import eu.kanade.tachiyomi.source.online.all.Lanraragi
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino
import eu.kanade.tachiyomi.ui.manga.ChapterList
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel
import eu.kanade.tachiyomi.ui.manga.MergedMangaData
@ -108,12 +106,10 @@ import exh.source.getMainSource
import exh.source.isEhBasedManga
import exh.ui.metadata.adapters.EHentaiDescription
import exh.ui.metadata.adapters.EightMusesDescription
import exh.ui.metadata.adapters.HBrowseDescription
import exh.ui.metadata.adapters.LanraragiDescription
import exh.ui.metadata.adapters.MangaDexDescription
import exh.ui.metadata.adapters.NHentaiDescription
import exh.ui.metadata.adapters.PururinDescription
import exh.ui.metadata.adapters.TsuminoDescription
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.service.missingChaptersCount
import tachiyomi.domain.library.service.LibraryPreferences
@ -1438,15 +1434,9 @@ fun metadataDescription(source: Source): MetadataDescriptionComposable? {
is EightMuses -> { state, openMetadataViewer, _ ->
EightMusesDescription(state, openMetadataViewer)
}
is HBrowse -> { state, openMetadataViewer, _ ->
HBrowseDescription(state, openMetadataViewer)
}
is Pururin -> { state, openMetadataViewer, _ ->
PururinDescription(state, openMetadataViewer)
}
is Tsumino -> { state, openMetadataViewer, _ ->
TsuminoDescription(state, openMetadataViewer)
}
is Lanraragi -> { state, openMetadataViewer, _ ->
LanraragiDescription(state, openMetadataViewer)
}

View file

@ -13,9 +13,7 @@ import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino
import exh.log.xLogD
import exh.source.BlacklistedSources
import exh.source.DelegatedHttpSource
@ -24,10 +22,8 @@ import exh.source.EIGHTMUSES_SOURCE_ID
import exh.source.EXHENTAI_EXT_SOURCES
import exh.source.EnhancedHttpSource
import exh.source.ExhPreferences
import exh.source.HBROWSE_SOURCE_ID
import exh.source.MERGED_SOURCE_ID
import exh.source.PURURIN_SOURCE_ID
import exh.source.TSUMINO_SOURCE_ID
import exh.source.handleSourceLibrary
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -282,12 +278,6 @@ class AndroidSourceManager(
"eu.kanade.tachiyomi.extension.en.pururin.Pururin",
Pururin::class,
),
DelegatedSource(
"Tsumino",
TSUMINO_SOURCE_ID,
"eu.kanade.tachiyomi.extension.en.tsumino.Tsumino",
Tsumino::class,
),
DelegatedSource(
"MangaDex",
fillInSourceId,
@ -295,12 +285,6 @@ class AndroidSourceManager(
MangaDex::class,
true,
),
DelegatedSource(
"HBrowse",
HBROWSE_SOURCE_ID,
"eu.kanade.tachiyomi.extension.en.hbrowse.HBrowse",
HBrowse::class,
),
DelegatedSource(
"8Muses",
EIGHTMUSES_SOURCE_ID,

View file

@ -1,95 +0,0 @@
package eu.kanade.tachiyomi.source.online.english
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.HBrowseSearchMetadata
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.util.urlImportFetchSearchManga
import exh.util.urlImportFetchSearchMangaSuspend
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class HBrowse(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate),
MetadataSource<HBrowseSearchMetadata, Document>,
UrlImportableSource,
NamespaceSource {
override val metaClass = HBrowseSearchMetadata::class
override fun newMetaInstance() = HBrowseSearchMetadata()
override val lang = "en"
// Support direct URL importing
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
urlImportFetchSearchManga(context, query) {
@Suppress("DEPRECATION")
super<DelegatedHttpSource>.fetchSearchManga(page, query, filters)
}
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
return urlImportFetchSearchMangaSuspend(context, query) {
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
}
}
override suspend fun getMangaDetails(manga: SManga): SManga {
val response = client.newCall(mangaDetailsRequest(manga)).awaitSuccess()
return parseToManga(manga, response.asJsoup())
}
override suspend fun parseIntoMetadata(metadata: HBrowseSearchMetadata, input: Document) {
val tables = parseIntoTables(input)
with(metadata) {
hbUrl = input.location().removePrefix("$baseUrl/thumbnails")
hbId = hbUrl!!.removePrefix("/").substringBefore("/").toLong()
tags.clear()
((tables[""] ?: error("")) + (tables["categories"] ?: error(""))).forEach { (k, v) ->
when (val lowercaseNs = k.lowercase()) {
"title" -> title = v.text()
"length" -> length = v.text().substringBefore(" ").toInt()
else -> {
v.getElementsByTag("a").forEach {
tags += RaisedTag(
lowercaseNs,
it.text(),
HBrowseSearchMetadata.TAG_TYPE_DEFAULT,
)
}
}
}
}
}
}
private fun parseIntoTables(doc: Document): Map<String, Map<String, Element>> {
return doc.select("#main > .listTable").associate { ele ->
val tableName = ele.previousElementSibling()?.text()?.lowercase().orEmpty()
tableName to ele.select("tr")
.filter { element -> element.childrenSize() > 1 }
.associate {
it.child(0).text() to it.child(1)
}
}
}
override val matchingHosts = listOf(
"www.hbrowse.com",
"hbrowse.com",
)
override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
return uri.pathSegments.firstOrNull()?.let { "/$it/c00001/" }
}
}

View file

@ -1,151 +0,0 @@
package eu.kanade.tachiyomi.source.online.english
import android.content.Context
import android.net.Uri
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.source.online.UrlImportableSource
import eu.kanade.tachiyomi.util.asJsoup
import exh.metadata.metadata.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.metadata.metadata.TsuminoSearchMetadata
import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT
import exh.metadata.metadata.base.RaisedTag
import exh.source.DelegatedHttpSource
import exh.util.dropBlank
import exh.util.trimAll
import exh.util.urlImportFetchSearchManga
import exh.util.urlImportFetchSearchMangaSuspend
import org.jsoup.nodes.Document
import rx.Observable
import java.text.SimpleDateFormat
import java.util.Locale
class Tsumino(delegate: HttpSource, val context: Context) :
DelegatedHttpSource(delegate),
MetadataSource<TsuminoSearchMetadata, Document>,
UrlImportableSource,
NamespaceSource {
override val metaClass = TsuminoSearchMetadata::class
override fun newMetaInstance() = TsuminoSearchMetadata()
override val lang = "en"
// Support direct URL importing
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
urlImportFetchSearchManga(context, query) {
@Suppress("DEPRECATION")
super<DelegatedHttpSource>.fetchSearchManga(page, query, filters)
}
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
return urlImportFetchSearchMangaSuspend(context, query) {
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
}
}
override suspend fun mapUrlToMangaUrl(uri: Uri): String? {
val lcFirstPathSegment = uri.pathSegments.firstOrNull()?.lowercase(Locale.ROOT) ?: return null
if (lcFirstPathSegment != "read" && lcFirstPathSegment != "book" && lcFirstPathSegment != "entry") {
return null
}
return "https://tsumino.com/Book/Info/${uri.lastPathSegment}"
}
override suspend fun getMangaDetails(manga: SManga): SManga {
val response = client.newCall(mangaDetailsRequest(manga)).awaitSuccess()
return parseToManga(manga, response.asJsoup())
}
override suspend fun parseIntoMetadata(metadata: TsuminoSearchMetadata, input: Document) {
with(metadata) {
tmId = TsuminoSearchMetadata.tmIdFromUrl(input.location())!!.toInt()
tags.clear()
input.select("meta[property=og:title]").firstOrNull()?.attr("content")?.let {
title = it.trim()
}
input.getElementById("Artist")?.children()?.first()?.attr("data-define")?.trim()?.let { artistString ->
artistString.split("|").trimAll().dropBlank().forEach {
tags.add(RaisedTag("artist", it, TAG_TYPE_DEFAULT))
}
tags.add(RaisedTag("artist", artistString, TAG_TYPE_VIRTUAL))
artist = artistString
}
input.getElementById("Uploader")?.children()?.first()?.text()?.trim()?.let {
uploader = it
}
input.getElementById("Uploaded")?.text()?.let {
uploadDate = TM_DATE_FORMAT.parse(it.trim())!!.time
}
input.getElementById("Pages")?.text()?.let {
length = it.trim().toIntOrNull()
}
input.getElementById("Rating")?.text()?.let {
ratingString = it.trim()
val ratingString = ratingString
if (!ratingString.isNullOrBlank()) {
averageRating = RATING_FLOAT_REGEX.find(ratingString)?.groups?.get(1)?.value?.toFloatOrNull()
userRatings = RATING_USERS_REGEX.find(ratingString)?.groups?.get(1)?.value?.toLongOrNull()
favorites = RATING_FAVORITES_REGEX.find(ratingString)?.groups?.get(1)?.value?.toLongOrNull()
}
}
input.getElementById("Category")?.children()?.first()?.attr("data-define")?.let {
category = it.trim()
tags.add(RaisedTag("genre", it, TAG_TYPE_VIRTUAL))
}
input.getElementById("Collection")?.children()?.first()?.attr("data-define")?.let {
collection = it.trim()
tags.add(RaisedTag("collection", it, TAG_TYPE_DEFAULT))
}
input.getElementById("Group")?.children()?.first()?.attr("data-define")?.let {
group = it.trim()
tags.add(RaisedTag("group", it, TAG_TYPE_DEFAULT))
}
parody = input.getElementById("Parody")?.children()?.map {
val entry = it.attr("data-define").trim()
tags.add(RaisedTag("parody", entry, TAG_TYPE_DEFAULT))
entry
}.orEmpty()
character = input.getElementById("Character")?.children()?.map {
val entry = it.attr("data-define").trim()
tags.add(RaisedTag("character", entry, TAG_TYPE_DEFAULT))
entry
}.orEmpty()
input.getElementById("Tag")?.children()?.let { tagElements ->
tags.addAll(
tagElements.map {
RaisedTag("tags", it.attr("data-define").trim(), TAG_TYPE_DEFAULT)
},
)
}
}
}
override val matchingHosts = listOf(
"www.tsumino.com",
"tsumino.com",
)
companion object {
val TM_DATE_FORMAT = SimpleDateFormat("yyyy MMM dd", Locale.US)
val RATING_FLOAT_REGEX = "([0-9].*) \\(".toRegex()
val RATING_USERS_REGEX = "\\(([0-9].*) users".toRegex()
val RATING_FAVORITES_REGEX = "/ ([0-9].*) favs".toRegex()
}
}

View file

@ -177,7 +177,7 @@ object EXHMigrations {
out += "#" + uri.fragment
}
out
} catch (e: URISyntaxException) {
} catch (_: URISyntaxException) {
orig
}
}

View file

@ -5,9 +5,7 @@ import eu.kanade.tachiyomi.source.online.all.Lanraragi
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.source.online.all.NHentai
import eu.kanade.tachiyomi.source.online.english.EightMuses
import eu.kanade.tachiyomi.source.online.english.HBrowse
import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino
/**
* Source helpers
@ -16,8 +14,6 @@ import eu.kanade.tachiyomi.source.online.english.Tsumino
private val DELEGATED_METADATA_SOURCES by lazy {
listOf(
Pururin::class,
Tsumino::class,
HBrowse::class,
EightMuses::class,
NHentai::class,
Lanraragi::class,

View file

@ -1,66 +0,0 @@
package exh.ui.metadata.adapters
import android.view.LayoutInflater
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import eu.kanade.presentation.theme.colorscheme.AndroidViewColorScheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterHbBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel.State
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import exh.metadata.metadata.HBrowseSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import tachiyomi.core.common.i18n.pluralStringResource
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.sy.SYMR
@Composable
fun HBrowseDescription(state: State.Success, openMetadataViewer: () -> Unit) {
val context = LocalContext.current
// KMK -->
val colorScheme = AndroidViewColorScheme(MaterialTheme.colorScheme)
val iconColor = colorScheme.iconColor
val textColor = LocalContentColor.current.toArgb()
// KMK <--
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { factoryContext ->
DescriptionAdapterHbBinding.inflate(LayoutInflater.from(factoryContext)).root
},
update = {
val meta = state.meta
if (meta == null || meta !is HBrowseSearchMetadata) return@AndroidView
val binding = DescriptionAdapterHbBinding.bind(it)
binding.pages.text =
context.pluralStringResource(SYMR.plurals.num_pages, meta.length ?: 0, meta.length ?: 0)
// KMK -->
binding.pages.bindDrawable(context, R.drawable.ic_baseline_menu_book_24, iconColor, 4.dpToPx)
binding.pages.setTextColor(textColor)
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp, iconColor)
binding.moreInfo.text = context.stringResource(SYMR.strings.more_info)
binding.moreInfo.setTextColor(iconColor)
// KMK <--
binding.pages.setOnLongClickListener {
context.copyToClipboard(
binding.pages.text.toString(),
binding.pages.text.toString(),
)
true
}
binding.moreInfo.setOnClickListener {
openMetadataViewer()
}
},
)
}

View file

@ -1,119 +0,0 @@
package exh.ui.metadata.adapters
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.view.LayoutInflater
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import eu.kanade.presentation.theme.colorscheme.AndroidViewColorScheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.DescriptionAdapterTsBinding
import eu.kanade.tachiyomi.ui.manga.MangaScreenModel.State
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.dpToPx
import exh.metadata.metadata.TsuminoSearchMetadata
import exh.ui.metadata.adapters.MetadataUIUtil.bindDrawable
import exh.util.SourceTagsUtil.genreTextColor
import tachiyomi.core.common.i18n.pluralStringResource
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import java.util.Date
import kotlin.math.round
@Composable
fun TsuminoDescription(state: State.Success, openMetadataViewer: () -> Unit) {
val context = LocalContext.current
// KMK -->
val colorScheme = AndroidViewColorScheme(MaterialTheme.colorScheme)
val iconColor = colorScheme.iconColor
val ratingBarColor = colorScheme.ratingBarColor
val ratingBarSecondaryColor = colorScheme.ratingBarSecondaryColor
val textColor = LocalContentColor.current.toArgb()
// KMK <--
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { factoryContext ->
DescriptionAdapterTsBinding.inflate(LayoutInflater.from(factoryContext)).root
},
update = {
val meta = state.meta
if (meta == null || meta !is TsuminoSearchMetadata) return@AndroidView
val binding = DescriptionAdapterTsBinding.bind(it)
binding.genre.text = meta.category?.let { genre -> MetadataUIUtil.getGenreAndColour(context, genre) }
?.let { (genre, name) ->
binding.genre.setBackgroundColor(genre.color)
// KMK -->
binding.genre.setTextColor(genreTextColor(genre))
// KMK <--
name
} ?: meta.category ?: context.stringResource(MR.strings.unknown)
binding.favorites.text = (meta.favorites ?: 0).toString()
// KMK -->
binding.favorites.bindDrawable(context, R.drawable.ic_book_24dp, iconColor)
binding.favorites.setTextColor(textColor)
// KMK <--
binding.whenPosted.text = TsuminoSearchMetadata.TSUMINO_DATE_FORMAT.format(Date(meta.uploadDate ?: 0))
// KMK -->
binding.whenPosted.setTextColor(textColor)
// KMK <--
binding.uploader.text = meta.uploader ?: context.stringResource(MR.strings.unknown)
// KMK -->
binding.uploader.setTextColor(textColor)
// KMK <--
binding.pages.text =
context.pluralStringResource(SYMR.plurals.num_pages, meta.length ?: 0, meta.length ?: 0)
// KMK -->
binding.pages.bindDrawable(context, R.drawable.ic_baseline_menu_book_24, iconColor, 4.dpToPx)
binding.pages.setTextColor(textColor)
// KMK <--
binding.ratingBar.rating = meta.averageRating ?: 0F
@SuppressLint("SetTextI18n")
binding.rating.text =
(round((meta.averageRating ?: 0F) * 100.0) / 100.0).toString() + " - " +
MetadataUIUtil.getRatingString(context, meta.averageRating?.times(2))
// KMK -->
binding.ratingBar.supportProgressTintList = ColorStateList.valueOf(ratingBarColor)
binding.ratingBar.supportSecondaryProgressTintList = ColorStateList.valueOf(ratingBarSecondaryColor)
binding.rating.setTextColor(textColor)
binding.moreInfo.bindDrawable(context, R.drawable.ic_info_24dp, iconColor)
binding.moreInfo.text = context.stringResource(SYMR.strings.more_info)
binding.moreInfo.setTextColor(iconColor)
// KMK <--
listOf(
binding.favorites,
binding.genre,
binding.pages,
binding.rating,
binding.uploader,
binding.whenPosted,
).forEach { textView ->
textView.setOnLongClickListener {
context.copyToClipboard(
textView.text.toString(),
textView.text.toString(),
)
true
}
}
binding.moreInfo.setOnClickListener {
openMetadataViewer()
}
},
)
}

View file

@ -1,29 +0,0 @@
package mihon.core.migration.migrations
import eu.kanade.domain.manga.interactor.UpdateManga
import exh.source.HBROWSE_OLD_ID
import exh.source.HBROWSE_SOURCE_ID
import mihon.core.migration.MigrateUtils
import mihon.core.migration.Migration
import mihon.core.migration.MigrationContext
import tachiyomi.core.common.util.lang.withIOContext
import tachiyomi.domain.manga.interactor.GetMangaBySource
import tachiyomi.domain.manga.model.MangaUpdate
class DelegateHBrowseMigration : Migration {
override val version: Float = 4f
override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext {
val getMangaBySource = migrationContext.get<GetMangaBySource>() ?: return@withIOContext false
val updateManga = migrationContext.get<UpdateManga>() ?: return@withIOContext false
MigrateUtils.updateSourceId(migrationContext, HBROWSE_SOURCE_ID, HBROWSE_OLD_ID)
// Migrate BHrowse URLs
val hBrowseManga = getMangaBySource.await(HBROWSE_SOURCE_ID)
val mangaUpdates = hBrowseManga.map {
MangaUpdate(it.id, url = it.url + "/c00001/")
}
updateManga.awaitAll(mangaUpdates)
return@withIOContext true
}
}

View file

@ -8,7 +8,6 @@ val migrations: List<Migration>
SetupLibraryUpdateMigration(),
SetupEHentaiUpdateMigration(),
SetupSyncDataMigration(),
// DelegateHBrowseMigration(),
// DelegateNHentaiMigration(),
// MergedMangaRewriteMigration(),
// LogoutFromMALMigration(),

View file

@ -121,7 +121,7 @@
<string name="action_copy_to_clipboard_combined_page">نسخ الصفحة المدمجة إلى الحافظة</string>
<string name="auto_update_app_disable_auto_download">عدم التحميل التلقائي للتحديث</string>
<string name="action_remove_merged">إزالة العناصر الأصلية من المكتبة؟</string>
<string name="batch_add_description">الدعم: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">الدعم: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_open_folder">فتح مجلد</string>
<string name="action_update">تحديث</string>
<string name="action_filter_unfinished_manga">مانجا غير مكتملة</string>

View file

@ -136,7 +136,7 @@
<string name="current_">বৰ্তমান: %1$s</string>
<string name="label_to_be_updated">আপডেট কৰিবলগীয়া</string>
<string name="sponsor_me">মোক স্পনছৰ কৰক</string>
<string name="batch_add_description">সমৰ্থন: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">সমৰ্থন: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_discord_rpc">ডিস্ক’ৰ্ড ৰিচ প্ৰেজেন্স</string>
<string name="active_account">সক্ৰিয় একাউণ্ট</string>
<string name="pref_category_connections">সংযোগ</string>

View file

@ -192,7 +192,7 @@
<string name="job_failed_schedule_backup_check">Failed to schedule automatic backup: %s</string>
<string name="label_to_be_updated">To be updated</string>
<string name="sponsor_me">Sponsor Me</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<!-- Connections section -->
<string name="special_services">Special Services</string>
<string name="pref_category_connections">Connections</string>

View file

@ -115,7 +115,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopírovat kombinovanou stránku do schránky</string>
<string name="auto_update_app_disable_auto_download">Automaticky nestahovat aktualizaci</string>
<string name="action_remove_merged">Odstranit původní položky z knihovny?</string>
<string name="batch_add_description">Podpora: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Podpora: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="custom_theme_palette_bubblegum">Bubblegum</string>
<string name="discord_accounts">Discord účty</string>
<string name="pref_discord_custom_message_summary">Vlastní zpráva, která bude zobrazena na Discordu</string>

View file

@ -115,7 +115,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopier kombineret side til udklipsholder</string>
<string name="auto_update_app_disable_auto_download">Download ikke automatisk opdatering</string>
<string name="action_remove_merged">Fjern originale indgange fra biblioteket?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="theme_sapphire">Sapphire</string>
<string name="special_services">Særlige Tjenester</string>
<string name="pref_discord_incognito_categories_details">Indgange i inkluderede kategorier vil ikke blive vist i Discord</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">Kombinierte Seite in Zwischenablage kopieren</string>
<string name="auto_update_app_disable_auto_download">Update nicht automatisch herunterladen</string>
<string name="action_remove_merged">Originale Einträge aus der Bibliothek entfernen?</string>
<string name="batch_add_description">Unterstützung: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Unterstützung: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_panorama_cover_flow">Panorama-Cover Flow</string>
<string name="theme_doom">Untergang</string>
<string name="pref_custom_color">Paletten-Personalisierer</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">Αντιγραφή συνδυασμένης σελίδας στο πρόχειρο</string>
<string name="auto_update_app_disable_auto_download">Να μην γίνεται αυτόματη λήψη ενημέρωσης</string>
<string name="action_remove_merged">Αφαιρέστε τις αρχικές καταχωρήσεις από τη βιβλιοθήκη?</string>
<string name="batch_add_description">Υποστήριξη: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Υποστήριξη: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="theme_sapphire">Sapphire</string>
<string name="special_services">Ειδικές Υπηρεσίες</string>
<string name="connections_discord">Discord</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">Copiar página combinada al portapapeles</string>
<string name="auto_update_app_disable_auto_download">No descargar actualización automática</string>
<string name="action_remove_merged">¿Eliminar entradas originales de la biblioteca?</string>
<string name="batch_add_description">Soporte: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Soporte: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_open_folder">Abrir carpeta</string>
<string name="action_update">Actualizar</string>
<string name="error_opening_folder">Fallo al abrir la carpeta</string>

View file

@ -114,7 +114,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopioi yhdistetty sivu leikepöydälle</string>
<string name="auto_update_app_disable_auto_download">Älä lataa automaattisesti päivitystä</string>
<string name="action_remove_merged">Poistetaanko alkuperäiset merkinnät kirjastosta?</string>
<string name="batch_add_description">Tuki: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Tuki: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_discord_rpc">Discord Rich Presence</string>
<string name="login_failed">Kirjautuminen epäonnistui</string>
<string name="theme_sapphire">Sapphire</string>

View file

@ -97,7 +97,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopyahin ang pinagsamang pahina sa clipboard</string>
<string name="auto_update_app_disable_auto_download">Huwag awtomatikong i-download ang update</string>
<string name="action_remove_merged">Tanggalin ang orihinal na mga tala galing sa aklatan?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_theme_cover_based_style_fruitsalad">Fruit Salad</string>
<string name="pref_theme_cover_based_style_vibrant">Makulay</string>
<string name="pref_theme_cover_based_style_expressive">Masining</string>

View file

@ -114,7 +114,7 @@
<string name="action_copy_to_clipboard_combined_page">Copier la page combinée dans le presse-papiers</string>
<string name="auto_update_app_disable_auto_download">Ne pas télécharger automatiquement la mise à jour</string>
<string name="action_remove_merged">Supprimer les entrées originales de la bibliothèque?</string>
<string name="batch_add_description">Compatible : MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Compatible : MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_theme_cover_based_style_tonalspot">Tonal</string>
<string name="pref_theme_cover_based_style_monochrome">Monochrome</string>
<string name="theme_doom">Doom</string>

View file

@ -68,7 +68,7 @@
<string name="release_page">Release page</string>
<string name="auto_update_app_disable_auto_download">Don\'t auto download update</string>
<string name="action_remove_merged">Hapus entri asli dari perpustakaan?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_hide">Sembunyikan</string>
<string name="action_show_hidden_categories">Tampilkan kategori tersembunyi</string>
<string name="action_panorama_cover">Penutup panorama</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">Copia la pagina combinata negli appunti</string>
<string name="auto_update_app_disable_auto_download">Non scaricare automaticamente l\'aggiornamento</string>
<string name="action_remove_merged">Rimuovere le voci originali dalla libreria?</string>
<string name="batch_add_description">Supporto: MangaDex, E-H, ExH, nH, 8Muse, Tsumino</string>
<string name="batch_add_description">Supporto: MangaDex, E-H, ExH, nH, 8Muse</string>
<string name="custom_theme_palette_patagonia">Patagonia</string>
<string name="custom_theme_palette_pop">Pop</string>
<string name="custom_theme_palette_outrun">Superare</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">結合ページをクリップボードにコピー</string>
<string name="auto_update_app_disable_auto_download">更新プログラムを自動的にダウンロードしない</string>
<string name="action_remove_merged">ライブラリから元の作品を削除しますか?</string>
<string name="batch_add_description">サポート: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">サポート: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_open_folder">フォルダーを開く</string>
<string name="error_opening_folder">フォルダーを開けませんでした</string>
<string name="scale_type_16_9">16:9</string>

View file

@ -115,7 +115,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopieer gecombineerde pagina naar klembord</string>
<string name="auto_update_app_disable_auto_download">Update niet automatisch downloaden</string>
<string name="action_remove_merged">Originele invoergegevens uit bibliotheek verwijderen?</string>
<string name="batch_add_description">Ondersteuning: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Ondersteuning: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_webdav_username">Gebruikersnaam</string>
<string name="action_open_folder">Map openen</string>
<string name="action_update">Updaten</string>

View file

@ -114,7 +114,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopiuj połączoną stronę do schowka</string>
<string name="auto_update_app_disable_auto_download">Nie pobieraj automatycznie aktualizacji</string>
<string name="action_remove_merged">Usunąć oryginalne wpisy z biblioteki?</string>
<string name="batch_add_description">Wsparcie: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Wsparcie: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="theme_doom">Fatum</string>
<string name="action_manual_install">Ręczna instalacja</string>
<string name="theme_mocha">Mokka</string>

View file

@ -118,7 +118,7 @@
<string name="action_copy_to_clipboard_combined_page">Copiar combinação de páginas para a área de transferência</string>
<string name="auto_update_app_disable_auto_download">Não baixar a atualização automaticamente</string>
<string name="action_remove_merged">Remover entradas originais da biblioteca?</string>
<string name="batch_add_description">Suporte: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Suporte: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_update">Atualizar</string>
<string name="scale_type_16_9">16:9</string>
<string name="scale_type_20_9">20:9</string>

View file

@ -118,7 +118,7 @@
<string name="action_copy_to_clipboard_combined_page">Copiar página combinada para área de transferência</string>
<string name="auto_update_app_disable_auto_download">Não baixar a atualização automaticamente</string>
<string name="action_remove_merged">Remover entradas originais da biblioteca?</string>
<string name="batch_add_description">Suporte: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Suporte: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_manual_install">Instalação manual</string>
<string name="action_open_folder">Abrir pasta</string>
<string name="action_clear_manga">Limpar mangá</string>

View file

@ -115,7 +115,7 @@
<string name="action_copy_to_clipboard_combined_page">Copiază pagina combinată în clipboard</string>
<string name="auto_update_app_disable_auto_download">Nu descărca automat</string>
<string name="action_remove_merged">Eliminaţi intrările originale din bibliotecă?</string>
<string name="batch_add_description">Suport: MangaDex, E-H, ExH, nH, 8Muse, Tsumino</string>
<string name="batch_add_description">Suport: MangaDex, E-H, ExH, nH, 8Muse</string>
<string name="special_services">Servicii speciale</string>
<string name="pref_category_connections">Conexiuni</string>
<string name="active_account">Cont activ</string>

View file

@ -119,7 +119,7 @@
<string name="action_copy_to_clipboard_combined_page">Скопировать объединенную страницу в буфер обмена</string>
<string name="auto_update_app_disable_auto_download">Не скачивать автоматически</string>
<string name="action_remove_merged">Удалить оригинальные тайтлы из библиотеки?</string>
<string name="batch_add_description">Поддержка: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Поддержка: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="pref_top_align_cover">Выровнять обложку к верху</string>
<string name="pref_top_align_cover_summary">Показать обложку выровненной к вершине рядом с информацией о манге</string>
<string name="theme_doom">Дум</string>

View file

@ -116,7 +116,7 @@
<string name="action_copy_to_clipboard_combined_page">Kopiera kombinerad sida till urklipp</string>
<string name="auto_update_app_disable_auto_download">Uppdatera inte automatisk nedladdning</string>
<string name="action_remove_merged">Ta bort ursprungliga poster från biblioteket?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_manual_install">Manuell installation</string>
<string name="discord_status_scrolling">Rullar igenom</string>
<string name="discord_status_messing">Ringer runt med</string>

View file

@ -100,7 +100,7 @@
<string name="release_page">Release page</string>
<string name="auto_update_app_disable_auto_download">Otomatik indirmeyi devre dışı bırak</string>
<string name="action_remove_merged">Remove original entries from library?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="auto_update_app">Uygulamayı otomatik güncelle</string>
<string name="label_library_update_errors">Kütüphane güncelleme hataları (%d)</string>
<string name="option_label_library_update_errors">Kütüphane güncelleme hataları</string>

View file

@ -118,7 +118,7 @@
<string name="action_copy_to_clipboard_combined_page">Скопіювати комбіновану сторінку в буфер обміну</string>
<string name="auto_update_app_disable_auto_download">Не завантажувати автоматично оновлення</string>
<string name="action_remove_merged">Видалити оригінальні записи з бібліотеки?</string>
<string name="batch_add_description">Підтримка: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Підтримка: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_open_folder">Відкрити папку</string>
<string name="action_clear_manga">Очистити мангу</string>
<string name="action_update">Оновити</string>

View file

@ -122,7 +122,7 @@
<string name="action_copy_to_clipboard_combined_page">Sao chép trang đã kết hợp vào clipboard</string>
<string name="auto_update_app_disable_auto_download">Không tự động tải xuống bản cập nhật</string>
<string name="action_remove_merged">Xóa các truyện gốc ra khỏi thư viện?</string>
<string name="batch_add_description">Hỗ trợ: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Hỗ trợ: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_manual_install">Cài đặt thủ công</string>
<string name="action_open_folder">Mở thư mục</string>
<string name="action_clear_manga">Xóa manga</string>

View file

@ -122,7 +122,7 @@
<string name="action_copy_to_clipboard_combined_page">复制合并页面到剪贴板</string>
<string name="auto_update_app_disable_auto_download">不自动下载更新</string>
<string name="action_remove_merged">从书架删除原始条目?</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses, Tsumino</string>
<string name="batch_add_description">Support: MangaDex, E-H, ExH, nH, 8Muses</string>
<string name="action_open_folder">打开文件夹</string>
<string name="error_opening_folder">打开文件夹时出错</string>
<string name="action_manual_install">手动安装</string>

View file

@ -122,7 +122,7 @@
<string name="action_copy_to_clipboard_combined_page">將雙頁複製到剪貼簿</string>
<string name="auto_update_app_disable_auto_download">不自動下載更新</string>
<string name="action_remove_merged">要從書櫃移除原始條目嗎?</string>
<string name="batch_add_description">支援MangaDex、E-H、ExH、nH、8Muses、Tsumino</string>
<string name="batch_add_description">支援MangaDex、E-H、ExH、nH、8Muses</string>
<string name="action_manual_install">手動下載</string>
<string name="pref_top_align_cover">將封面與頂部對齊</string>
<string name="pref_top_align_cover_summary">將封面與頂部對齊並顯示漫畫資訊</string>

View file

@ -1,76 +0,0 @@
package exh.metadata.metadata
import android.content.Context
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.copy
import kotlinx.serialization.Serializable
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
@Serializable
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 createMangaInfo(manga: SManga): SManga {
val key = hbUrl
val title = title
// Guess thumbnail URL if manga does not have thumbnail URL
val cover = if (manga.thumbnail_url.isNullOrBlank()) {
guessThumbnailUrl(hbId.toString())
} else {
null
}
val artist = tags.ofNamespace(ARTIST_NAMESPACE).joinToString { it.name }
val genres = tagsToGenreString()
val description = null
return manga.copy(
url = key ?: manga.url,
title = title ?: manga.title,
thumbnail_url = cover ?: manga.thumbnail_url,
artist = artist,
genre = genres,
description = description,
)
}
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
return with(context) {
listOfNotNull(
getItem(hbId) { stringResource(SYMR.strings.id) },
getItem(hbUrl) { stringResource(SYMR.strings.url) },
getItem(thumbnail) { stringResource(SYMR.strings.thumbnail_url) },
getItem(title) { stringResource(MR.strings.title) },
getItem(length) { stringResource(SYMR.strings.page_count) },
)
}
}
companion object {
const val BASE_URL = "https://www.hbrowse.com"
private const val TITLE_TYPE_MAIN = 0
const val TAG_TYPE_DEFAULT = 0
const val ARTIST_NAMESPACE = "artist"
fun guessThumbnailUrl(hbid: String): String {
return "$BASE_URL/thumbnails/${hbid}_1.jpg#guessed"
}
}
}

View file

@ -1,114 +0,0 @@
package exh.metadata.metadata
import android.content.Context
import androidx.core.net.toUri
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.model.copy
import exh.metadata.MetadataUtil
import exh.util.nullIfEmpty
import kotlinx.serialization.Serializable
import tachiyomi.core.common.i18n.stringResource
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.util.Locale
@Serializable
class TsuminoSearchMetadata : RaisedSearchMetadata() {
var tmId: Int? = null
var title by titleDelegate(TITLE_TYPE_MAIN)
var artist: String? = null
var uploadDate: Long? = null
var length: Int? = null
var ratingString: String? = null
var averageRating: Float? = null
var userRatings: Long? = null
var favorites: Long? = null
var category: String? = null
var collection: String? = null
var group: String? = null
var parody: List<String> = emptyList()
var character: List<String> = emptyList()
override fun createMangaInfo(manga: SManga): SManga {
val title = title
val cover = tmId?.let { BASE_URL.replace("www", "content") + thumbUrlFromId(it.toString()) }
val artist = artist
val status = SManga.UNKNOWN
// Copy tags -> genres
val genres = tagsToGenreString()
val description = null
return manga.copy(
title = title ?: manga.title,
thumbnail_url = cover ?: manga.thumbnail_url,
artist = artist ?: manga.artist,
status = status,
genre = genres,
description = description,
)
}
override fun getExtraInfoPairs(context: Context): List<Pair<String, String>> {
return with(context) {
listOfNotNull(
getItem(tmId) { stringResource(SYMR.strings.id) },
getItem(title) { stringResource(MR.strings.title) },
getItem(uploader) { stringResource(SYMR.strings.uploader) },
getItem(
uploadDate,
{
MetadataUtil.EX_DATE_FORMAT
.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneId.systemDefault()))
},
) {
stringResource(SYMR.strings.date_posted)
},
getItem(length) { stringResource(SYMR.strings.page_count) },
getItem(ratingString) { stringResource(SYMR.strings.rating_string) },
getItem(averageRating) { stringResource(SYMR.strings.average_rating) },
getItem(userRatings) { stringResource(SYMR.strings.total_ratings) },
getItem(favorites) { stringResource(SYMR.strings.total_favorites) },
getItem(category) { stringResource(SYMR.strings.genre) },
getItem(collection) { stringResource(SYMR.strings.collection) },
getItem(group) { stringResource(SYMR.strings.group) },
getItem(parody.nullIfEmpty(), { it.joinToString() }) { stringResource(SYMR.strings.parodies) },
getItem(character.nullIfEmpty(), { it.joinToString() }) { stringResource(SYMR.strings.characters) },
)
}
}
companion object {
private const val TITLE_TYPE_MAIN = 0
const val TAG_TYPE_DEFAULT = 0
val BASE_URL = "https://www.tsumino.com"
val TSUMINO_DATE_FORMAT = SimpleDateFormat("yyyy-MM-dd", Locale.US)
fun tmIdFromUrl(url: String) = url.toUri().lastPathSegment
fun thumbUrlFromId(id: String) = "/thumbs/$id/1"
}
}