Skip to content

Commit

Permalink
handle invalid URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
DeDiamondPro committed Nov 2, 2024
1 parent 133d0fa commit 94fc229
Show file tree
Hide file tree
Showing 18 changed files with 81 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import dev.dediamondpro.resourcify.services.IService
import dev.dediamondpro.resourcify.services.ProjectType
import dev.dediamondpro.resourcify.util.localize
import dev.dediamondpro.resourcify.util.ofResourceCustom
import dev.dediamondpro.resourcify.util.ofURL
import dev.dediamondpro.resourcify.util.ofURLCustom
import gg.essential.elementa.components.*
import gg.essential.elementa.constraints.CenterConstraint
import gg.essential.elementa.constraints.ChildBasedSizeConstraint
Expand Down Expand Up @@ -60,7 +60,7 @@ class ResourceCard(service: IService, project: IProject, type: ProjectType, down
} effect ScissorEffect() childOf this

project.getBannerUrl()?.let {
UIImage.ofURL(it, false)
UIImage.ofURLCustom(it, false)
.constrain {
x = CenterConstraint()
y = CenterConstraint()
Expand All @@ -78,10 +78,10 @@ class ResourceCard(service: IService, project: IProject, type: ProjectType, down
} childOf this

val iconUrl = project.getIconUrl()
if (iconUrl.isNullOrBlank()) {
if (iconUrl == null) {
UIImage.ofResourceCustom("/assets/resourcify/pack.png")
} else {
UIImage.ofURL(iconUrl)
UIImage.ofURLCustom(iconUrl)
}.constrain {
width = ImageFillConstraint(ImageFillConstraint.FillType.CROP)
height = ImageFillConstraint(ImageFillConstraint.FillType.CROP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class ProjectScreen(
val version = versions.firstOrNull {
it.getMinecraftVersions().contains(Platform.getMcVersion())
} ?: return@thenAccept
val url = version.getDownloadUrl().toURL()
val url = version.getDownloadUrl() ?: return@thenAccept
var installed = packHashes.get().contains(version.getSha1())
val buttonText = if (installed) "${ChatColor.BOLD}${localize("resourcify.version.installed")}"
else version.getVersionNumber()?.let {
Expand Down Expand Up @@ -221,15 +221,15 @@ class ProjectScreen(
height = ChildBasedSizeConstraint() + 4.pixels()
} childOf sideContainer
val bannerUrl = project.getBannerUrl()
if (bannerUrl != null) UIImage.ofURL(bannerUrl, false).constrain {
if (bannerUrl != null) UIImage.ofURLCustom(bannerUrl, false).constrain {
x = 0.pixels()
y = 0.pixels()
width = 100.percent()
height = ImageAspectConstraint()
} childOf sideBox
val iconUrl = project.getIconUrl()
(if (iconUrl.isNullOrBlank()) UIImage.ofResourceCustom("/assets/resourcify/pack.png")
else UIImage.ofURL(iconUrl))
(if (iconUrl == null) UIImage.ofResourceCustom("/assets/resourcify/pack.png")
else UIImage.ofURLCustom(iconUrl))
.constrain {
x = 4.pixels()
y = SiblingConstraint(padding = 4f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ class VersionsPage(private val screen: ProjectScreen) : UIContainer() {
y = 8.pixels()
} childOf changeLogHolder
VersionCard.createDownloadButton(version, screen.packHashes.get(), screen.downloadFolder, screen.type)
.constrain {
x = 4.pixels(true)
y = 4.pixels()
} childOf changeLogHolder
?.let {
it.constrain {
x = 4.pixels(true)
y = 4.pixels()
} childOf changeLogHolder
}
version.getChangeLog().thenApply {
Window.enqueueRenderOperation {
var changelog = it
Expand Down Expand Up @@ -125,10 +127,10 @@ class VersionsPage(private val screen: ProjectScreen) : UIContainer() {
UScreen.displayScreen(ConfirmLinkScreen(project.getBrowserUrl(), screen))
} childOf changeLogHolder
val iconUrl = project.getIconUrl()
if (iconUrl.isNullOrBlank()) {
if (iconUrl == null) {
UIImage.ofResourceCustom("/assets/resourcify/pack.png")
} else {
UIImage.ofURL(
UIImage.ofURLCustom(
iconUrl,
width = 24f,
height = 24f,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ package dev.dediamondpro.resourcify.gui.projectpage.components
import dev.dediamondpro.resourcify.constraints.ChildLocationSizeConstraint
import dev.dediamondpro.resourcify.constraints.ImageFillConstraint
import dev.dediamondpro.resourcify.services.IGalleryImage
import dev.dediamondpro.resourcify.util.ofURL
import dev.dediamondpro.resourcify.util.ofURLCustom
import dev.dediamondpro.resourcify.util.toURL
import gg.essential.elementa.components.UIBlock
import gg.essential.elementa.components.UIImage
import gg.essential.elementa.components.UIWrappedText
Expand Down Expand Up @@ -57,19 +58,23 @@ class GalleryCard(gallery: IGalleryImage) : UIBlock(color = Color(0, 0, 0, 100))
} childOf Window.of(this)
background.setFloating(true)
background.grabWindowFocus()
UIImage.ofURL(gallery.url, true).constrain {
x = CenterConstraint()
y = CenterConstraint()
width = ImageFillConstraint()
height = ImageFillConstraint()
} childOf background
gallery.url.toURL()?.let { image ->
UIImage.ofURLCustom(image, true).constrain {
x = CenterConstraint()
y = CenterConstraint()
width = ImageFillConstraint()
height = ImageFillConstraint()
} childOf background
}
}
gallery.getThumbnailUrlIfEnabled()?.let { image ->
UIImage.ofURLCustom(image, false).constrain {
x = 0.pixels()
y = 0.pixels()
width = 100.percent()
height = ImageAspectConstraint()
} childOf this
}
UIImage.ofURL(gallery.getThumbnailUrlIfEnabled(), false).constrain {
x = 0.pixels()
y = 0.pixels()
width = 100.percent()
height = ImageAspectConstraint()
} childOf this
if (!gallery.title.isNullOrBlank()) UIWrappedText(gallery.title ?: "").constrain {
x = 4.pixels()
y = SiblingConstraint(padding = 4f)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* This file is part of Resourcify
* Copyright (C) 2023 DeDiamondPro
* Copyright (C) 2023-2024 DeDiamondPro
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand All @@ -19,7 +19,8 @@ package dev.dediamondpro.resourcify.gui.projectpage.components

import dev.dediamondpro.resourcify.services.IMember
import dev.dediamondpro.resourcify.util.capitalizeAll
import dev.dediamondpro.resourcify.util.ofURL
import dev.dediamondpro.resourcify.util.ofURLCustom
import dev.dediamondpro.resourcify.util.toURL
import gg.essential.elementa.components.UIContainer
import gg.essential.elementa.components.UIImage
import gg.essential.elementa.components.UIText
Expand All @@ -42,8 +43,8 @@ class MemberCard(member: IMember) : UIContainer() {
if (it.mouseButton != 0) return@onMouseClick
UDesktop.browse(URI(member.url))
}
member.avatarUrl?.let {
UIImage.ofURL(it).constrain {
member.avatarUrl?.toURL()?.let {
UIImage.ofURLCustom(it).constrain {
x = 0.pixels()
y = 0.pixels()
width = 32.pixels()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,13 @@ class VersionCard(
color = Color.LIGHT_GRAY.toConstraint()
} childOf statsContainer

val button = createDownloadButton(version, hashes, downloadFolder, type) childOf this
onMouseClick {
if (button.isPointInside(it.absoluteX, it.absoluteY)) return@onMouseClick
parent.showChangelog(version)
val button = createDownloadButton(version, hashes, downloadFolder, type)
if (button != null) {
button childOf this
onMouseClick {
if (button.isPointInside(it.absoluteX, it.absoluteY)) return@onMouseClick
parent.showChangelog(version)
}
}
}

Expand Down Expand Up @@ -177,8 +180,8 @@ class VersionCard(
}

companion object {
fun createDownloadButton(version: IVersion, hashes: List<String>, downloadFolder: File, type: ProjectType): UIComponent {
val url = version.getDownloadUrl().toURL()
fun createDownloadButton(version: IVersion, hashes: List<String>, downloadFolder: File, type: ProjectType): UIComponent? {
val url = version.getDownloadUrl() ?: return null
var installed = hashes.contains(version.getSha1())
val buttonText =
"${ChatColor.BOLD}${if (installed) "resourcify.version.installed".localize() else "resourcify.version.install".localize()}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class UpdateGui(val type: ProjectType, private val folder: File) : PaginatedScre
y = CenterConstraint()
} childOf topBar

cards.addAll(projects.map { (project, newVersion) ->
cards.addAll(projects.filter { it.value.getDownloadUrl() != null }.map { (project, newVersion) ->
UpdateCard(project, newVersion, hashes.get()[updates.get()[newVersion]]!!, this).constrain {
y = SiblingConstraint(padding = 2f)
width = 100.percent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class UpdateCard(
val file: File,
private val gui: UpdateGui
) : UIBlock(color = Color(0, 0, 0, 100)) {
private val updateUrl = newVersion.getDownloadUrl().toURL()
private val updateUrl = newVersion.getDownloadUrl()!!
private var progressBox: UIBlock? = null
private var text: UIText? = null

Expand All @@ -58,10 +58,10 @@ class UpdateCard(
}

val iconUrl = project.getIconUrl()
if (iconUrl.isNullOrBlank()) {
if (iconUrl == null) {
UIImage.ofResourceCustom("/assets/resourcify/pack.png")
} else {
UIImage.ofURL(iconUrl)
UIImage.ofURLCustom(iconUrl)
}.constrain {
x = 4.pixels()
y = 4.pixels()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@
package dev.dediamondpro.resourcify.services

import dev.dediamondpro.resourcify.config.Config
import dev.dediamondpro.resourcify.util.toURL
import java.net.URL

interface IGalleryImage {
val url: String
val thumbnailUrl: String?
val title: String?
val description: String?

fun getThumbnailUrlIfEnabled(): String {
if (Config.instance.fullResThumbnail) return url
return thumbnailUrl ?: url
fun getThumbnailUrlIfEnabled(): URL? {
if (Config.instance.fullResThumbnail) return url.toURL()
return thumbnailUrl?.toURL() ?: url.toURL()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
package dev.dediamondpro.resourcify.services

import java.awt.Color
import java.net.URL
import java.util.concurrent.CompletableFuture

interface IProject {
fun getName(): String
fun getId(): String
fun getSummary(): String
fun getAuthor(): String
fun getIconUrl(): String? = null
fun getBannerUrl(): String? = null
fun getIconUrl(): URL? = null
fun getBannerUrl(): URL? = null
fun getBannerColor(): Color? = null
fun getBrowserUrl(): String
fun getDescription(): CompletableFuture<String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@

package dev.dediamondpro.resourcify.services

import java.net.URL
import java.util.concurrent.CompletableFuture

interface IVersion {
fun getName(): String
fun getVersionNumber(): String?
fun getProjectId(): String
fun getDownloadUrl(): String
fun getDownloadUrl(): URL?
fun getFileName(): String
fun getSha1(): String
fun getChangeLog(): CompletableFuture<String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ data class CurseForgeProject(
override fun getId(): String = id.toString()
override fun getSummary(): String = summary
override fun getAuthor(): String = authors.firstOrNull()?.name ?: ""
override fun getIconUrl(): String? = logo?.let { it.thumbnailUrl ?: it.url }
override fun getBannerUrl(): String? = screenshots.firstOrNull()?.getThumbnailUrlIfEnabled()
override fun getIconUrl(): URL? = logo?.let { it.thumbnailUrl?.toURL() ?: it.url.toURL() }
override fun getBannerUrl(): URL? = screenshots.firstOrNull()?.getThumbnailUrlIfEnabled()

override fun getDescription(): CompletableFuture<String> {
return descriptionRequest ?: supplyAsync {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ data class CurseForgeVersion(
override fun getVersionNumber(): String? = null
override fun getProjectId(): String = modId.toString()
fun hasDownloadUrl(): Boolean = downloadUrl != null
override fun getDownloadUrl(): String = downloadUrl ?: error("No download URL.")
override fun getDownloadUrl(): URL? = downloadUrl?.toURL()
override fun getFileName(): String = fileName
override fun getSha1(): String = hashes.firstOrNull { it.algo == 1 }?.value ?: ""

Expand Down Expand Up @@ -84,7 +84,7 @@ data class CurseForgeVersion(
return (dependenciesRequest ?: supplyAsync {
val deps = dependencies.filter { DependencyType.fromCurseForgeId(it.relationType) != null }
val projects: ModsResponse = "${CurseForgeService.API}/mods".toURL()
.postAndGetJson(
?.postAndGetJson(
GetByIdProperty(deps.map { it.modId }),
headers = mapOf("x-api-key" to CurseForgeService.API_KEY)
) ?: error("Failed to fetch dependencies.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ data class FullModrinthProject(
override fun getBrowserUrl(): String = "https://modrinth.com/$projectType/$slug"

override fun getDescription(): CompletableFuture<String> = supply { body }
override fun getIconUrl(): String? = iconUrl
override fun getIconUrl(): URL? = iconUrl?.toURL()

override fun getBannerUrl(): String? = gallery.minByOrNull { it.ordering }?.getThumbnailUrlIfEnabled()
override fun getBannerUrl(): URL? = gallery.minByOrNull { it.ordering }?.getThumbnailUrlIfEnabled()

override fun getBannerColor(): Color? = color?.let { Color(it) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import dev.dediamondpro.resourcify.services.*
import dev.dediamondpro.resourcify.util.getJson
import dev.dediamondpro.resourcify.util.supply
import dev.dediamondpro.resourcify.util.supplyAsync
import dev.dediamondpro.resourcify.util.toURL
import org.apache.http.client.utils.URIBuilder
import java.net.URL
import java.util.concurrent.CompletableFuture

data class ModrinthVersion(
Expand All @@ -46,7 +48,7 @@ data class ModrinthVersion(
override fun getProjectId(): String = projectId
fun hasFile() = files.isNotEmpty()
private fun getPrimaryFile() = files.firstOrNull { it.primary } ?: files.first()
override fun getDownloadUrl(): String = getPrimaryFile().url
override fun getDownloadUrl(): URL? = getPrimaryFile().url.toURL()
override fun getFileName(): String = getPrimaryFile().filename
override fun getSha1(): String = getPrimaryFile().hashes.sha1
override fun getChangeLog(): CompletableFuture<String> = supply { changelog }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ data class PartialModrinthProject(
override fun getId(): String = id
override fun getSummary(): String = summary
override fun getAuthor(): String = author
override fun getIconUrl(): String? = iconUrl
override fun getBannerUrl(): String? = featuredGallery ?: gallery.firstOrNull()
override fun getIconUrl(): URL? = iconUrl?.toURL()
override fun getBannerUrl(): URL? = featuredGallery?.toURL() ?: gallery.firstOrNull()?.toURL()
override fun getBannerColor(): Color? = color?.let { Color(it) }
override fun getDescription(): CompletableFuture<String> =
fetchProject().thenApply { it?.getDescription()?.getNow(null) ?: error("Failed to fetch description.") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension
import org.commonmark.ext.gfm.tables.TablesExtension
import java.awt.Color
import java.awt.image.BufferedImage
import java.util.concurrent.CompletableFuture
import java.net.URL
import javax.imageio.ImageIO
import kotlin.math.min

fun UIImage.Companion.ofURL(
source: String,
fun UIImage.Companion.ofURLCustom(
url: URL,
loadingImage: Boolean = true,
width: Float? = null,
height: Float? = null,
Expand All @@ -51,7 +51,6 @@ fun UIImage.Companion.ofURL(
minFilter: UIImage.TextureScalingMode = UIImage.TextureScalingMode.LINEAR,
magFilter: UIImage.TextureScalingMode = UIImage.TextureScalingMode.LINEAR,
): UIImage {
val url = source.toURL()
val image = UIImage(
url.getImageAsync(
useCache = useCache,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,8 @@ fun String.toURI(): URI = try {
URI(this.encodeUrl())
}

fun String.toURL(): URL = this.toURI().toURL()
fun String.toURL(): URL? = try {
this.toURI().toURL()
} catch (_: Exception) {
null
}

0 comments on commit 94fc229

Please sign in to comment.