diff --git a/app/src/main/java/moe/apex/breadboard/image/Image.kt b/app/src/main/java/moe/apex/breadboard/image/Image.kt index 22d6c77b..4457e45e 100644 --- a/app/src/main/java/moe/apex/breadboard/image/Image.kt +++ b/app/src/main/java/moe/apex/breadboard/image/Image.kt @@ -6,6 +6,7 @@ import moe.apex.breadboard.preferences.ImageSource import moe.apex.breadboard.tag.TagCategory import moe.apex.breadboard.tag.TagGroup import moe.apex.breadboard.util.MigrationOnlyField +import moe.apex.breadboard.util.PixivArtwork @Serializable @@ -21,7 +22,8 @@ data class ImageMetadata( val uncategorisedTags: List? = null, val groupedTags: List = emptyList(), val rating: ImageRating, - val pixivId: Int? = null, + @SerialName("pixivId") + private val pixivArtworkId: Int? = null, @MigrationOnlyField @SerialName("artist") @Deprecated( @@ -36,8 +38,18 @@ data class ImageMetadata( val tags: List get() = groupedTags.fold(emptyList()) { acc, tagGroup -> acc + tagGroup.tags } + val pixivId: Int? + get() = pixivArtworkId ?: pixivArtwork?.id + + val pixivArtwork: PixivArtwork? + get() = PixivArtwork.fromUrl(source) ?: pixivArtworkId?.let { PixivArtwork(it, 0) } + val pixivUrl: String? - get() = pixivId?.let { "https://www.pixiv.net/en/artworks/$it" } + get() = pixivArtwork?.let { + val id = if (it.index == 0) it.id.toString() + else "${it.id}#${it.index}" + "https://www.pixiv.net/artworks/$id" + } } diff --git a/app/src/main/java/moe/apex/breadboard/image/ImageBoard.kt b/app/src/main/java/moe/apex/breadboard/image/ImageBoard.kt index 6439fb78..df524588 100644 --- a/app/src/main/java/moe/apex/breadboard/image/ImageBoard.kt +++ b/app/src/main/java/moe/apex/breadboard/image/ImageBoard.kt @@ -9,7 +9,6 @@ import moe.apex.breadboard.tag.TagCategory import moe.apex.breadboard.tag.TagGroup import moe.apex.breadboard.tag.TagSuggestion import moe.apex.breadboard.util.decodeHtml -import moe.apex.breadboard.util.extractPixivId import org.json.JSONArray import org.json.JSONException import org.json.JSONObject @@ -195,7 +194,6 @@ interface GelbooruBasedImageBoard : ImageBoard { } val metaRating = getRatingFromString(e.getString("rating")) - val metaPixivId = extractPixivId(metaSource) val metadata = ImageMetadata( parentId = metaParentId, hasChildren = null, // Not available for Gelbooru-based image boards @@ -203,7 +201,6 @@ interface GelbooruBasedImageBoard : ImageBoard { source = metaSource, groupedTags = metaGroupedTags, rating = metaRating, - pixivId = metaPixivId, ) return Image(id, fileName, fileFormat, previewUrl, fileUrl, sampleUrl, imageSource, aspectRatio, metadata) @@ -459,7 +456,7 @@ object Danbooru : ImageBoard { source = metaSource, groupedTags = metaGroupedTags, rating = metaRating, - pixivId = metaPixivId, + pixivArtworkId = metaPixivId, ) return Image(id, fileName, fileFormat, previewUrl, fileUrl, sampleUrl, ImageSource.DANBOORU, aspectRatio, metadata) @@ -543,14 +540,12 @@ object Yandere : ImageBoard { TagCategory.GENERAL.group(e.getString("tags").decodeHtml().split(" ")), ) val metaRating = getRatingFromString(e.getString("rating")) - val metaPixivId = extractPixivId(metaSource) val metadata = ImageMetadata( parentId = metaParentId, hasChildren = metaHasChildren, source = metaSource, groupedTags = metaGroupedTags, rating = metaRating, - pixivId = metaPixivId, ) return Image(id, fileName, fileFormat, previewUrl, fileUrl, sampleUrl, ImageSource.YANDERE, aspectRatio, metadata) diff --git a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt index c65ca444..a791868d 100644 --- a/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt +++ b/app/src/main/java/moe/apex/breadboard/preferences/Pref.kt @@ -47,9 +47,9 @@ import moe.apex.breadboard.image.AI_TAG_NAMES import moe.apex.breadboard.tag.TagCategory import moe.apex.breadboard.util.AgeVerification import moe.apex.breadboard.util.MigrationOnlyField +import moe.apex.breadboard.util.PixivArtwork import moe.apex.breadboard.util.SecretsManager import moe.apex.breadboard.util.availableRatingsForSource -import moe.apex.breadboard.util.extractPixivId import moe.apex.breadboard.util.decodeHtml import java.io.IOException @@ -404,7 +404,7 @@ class UserPreferencesRepository(private val dataStore: DataStore) { uncategorisedTags = null, groupedTags = if (!image.metadata.uncategorisedTags.isNullOrEmpty()) listOf(TagCategory.GENERAL.group(image.metadata.tags)) else emptyList(), - pixivId = image.metadata.pixivId ?: extractPixivId(image.metadata.source) + pixivArtworkId = image.metadata.pixivId ?: PixivArtwork.fromUrl(image.metadata.source)?.id ) ) ) diff --git a/app/src/main/java/moe/apex/breadboard/util/Links.kt b/app/src/main/java/moe/apex/breadboard/util/Links.kt index 8c5a6d4e..3222dc2f 100644 --- a/app/src/main/java/moe/apex/breadboard/util/Links.kt +++ b/app/src/main/java/moe/apex/breadboard/util/Links.kt @@ -16,13 +16,35 @@ private val links = mapOf( fun fixLink(link: String): String { val uri = link.toUri() for ((originalHost, fixedHost) in links) { - if (uri.host == originalHost) { - return uri.buildUpon().authority(fixedHost).build().toString() + val newHost = if (uri.host == originalHost) { + fixedHost } else if (uri.host!!.endsWith(".$originalHost")) { val subdomain = uri.host!!.substringBeforeLast(".$originalHost") - val newHost = "$subdomain.$fixedHost" - return uri.buildUpon().authority(newHost).build().toString() + "$subdomain.$fixedHost" + } else { + continue } + + var newPath: String? = null + var newFragment: String? = null + + /* The phixiv fixer cannot take the official pixiv image index syntax into account since + URI fragments are client-sided, so we have to use its own path syntax for indexed images. */ + if (fixedHost == "phixiv.net" && "/artworks/\\d+$".toRegex().containsMatchIn(uri.path ?: "")) { + val index = uri.fragment?.toIntOrNull().takeIf { it != 0 } + if (index != null) { + newPath = "${uri.path}/${index + 1}" + newFragment = "" + } + } + + return uri + .buildUpon() + .authority(newHost) + .path(newPath ?: uri.path) + .fragment(newFragment ?: uri.fragment) + .build() + .toString() } return link } diff --git a/app/src/main/java/moe/apex/breadboard/util/Pixiv.kt b/app/src/main/java/moe/apex/breadboard/util/Pixiv.kt index ee9f6861..163cfbf1 100644 --- a/app/src/main/java/moe/apex/breadboard/util/Pixiv.kt +++ b/app/src/main/java/moe/apex/breadboard/util/Pixiv.kt @@ -4,34 +4,39 @@ package moe.apex.breadboard.util private val PIXIV_CURRENT_RX = // https://i.pximg.net/img-original/img/2022/11/27/21/27/08/103150283_p0.jpg (Safebooru #6517847) // https://i.pximg.net/img-master/img/2019/07/09/08/27/59/75629295_p0_master1200.jpg (Safebooru #3567627) - """https?://i\.pximg\.net/img-(?:original|master)/img/\d+/\d+/\d+/\d+/\d+/\d+/(\d+)_p\d+(_master1200)?\.(png|jpg|jpeg|gif)""".toRegex() + """https?://i\.pximg\.net/img-(original|master)/img/\d+/\d+/\d+/\d+/\d+/\d+/(?\d+)_p(?\d+)(_master1200)?\.(png|jpg|jpeg|gif)""".toRegex() // 2012-2016 pixiv direct image URLs private val PIXIV_2012_TO_2016_RX = listOf( // https://i1.pixiv.net/img-original/img/2016/10/02/16/47/39/59270556_p0.jpg (Safebooru #1843535) // No source, but I'd assume an `img-master` version exists too on this old subdomain - """https?://i\d+\.pixiv\.net/img-(?:original|master)/img/\d+/\d+/\d+/\d+/\d+/\d+/(\d+)_p\d+(_master1200)?\.(png|jpg|jpeg|gif)""".toRegex(), + """https?://i\d+\.pixiv\.net/img-(original|master)/img/\d+/\d+/\d+/\d+/\d+/\d+/(?\d+)_p(?\d+)(_master1200)?\.(png|jpg|jpeg|gif)""".toRegex(), // https://i1.pixiv.net/img47/img/l3lc201/34464791.png (Safebooru #1000441) // https://i1.pixiv.net/img21/img/togainuakira/34478247_big_p8.jpg (Safebooru #1000649) - """https?://i\d+\.pixiv\.net/img\d+/img/.+/(\d+)(_big_p\d+)?\.(png|jpg|jpeg|gif)""".toRegex() + """https?://i\d+\.pixiv\.net/img\d+/img/.+/(?\d+)(_big_p(?\d+))?\.(png|jpg|jpeg|gif)""".toRegex() ) // Pre-2012 pixiv direct image URLs private val PIXIV_PRE_2012_RX = // https://img13.pixiv.net/img/tubasarei/4894590.jpg (Safebooru #166629) - """https?://img\d+\.pixiv\.net/img/.+/(\d+)\.(png|jpg|jpeg|gif)""".toRegex() + """https?://img\d+\.pixiv\.net/img/.+/(?\d+)\.(png|jpg|jpeg|gif)""".toRegex() private val PIXIV_RX = listOf(PIXIV_CURRENT_RX) + PIXIV_2012_TO_2016_RX + PIXIV_PRE_2012_RX -fun extractPixivId(url: String?): Int? { - if (url == null) return null +data class PixivArtwork(val id: Int, val index: Int) { + companion object { + fun fromUrl(url: String?): PixivArtwork? { + if (url == null) return null - for (regex in PIXIV_RX) { - val match = regex.find(url) - val id = match?.groupValues?.get(1)?.toInt()?.takeIf { it != 0 } - if (id != null) return id - } + for (regex in PIXIV_RX) { + val match = regex.find(url) ?: continue + val id = match.groups["id"]?.value?.toIntOrNull().takeIf { it != 0 } ?: continue + val index = match.groups["index"]?.value?.toIntOrNull() ?: 0 + return PixivArtwork(id, index) + } - return null + return null + } + } }