Skip to content

Commit

Permalink
Merge pull request #155 from Shikkanime/dev
Browse files Browse the repository at this point in the history
Add anime slug
  • Loading branch information
Ziedelth authored Feb 8, 2024
2 parents 33a1207 + a5d9b95 commit 0a659cc
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 13 deletions.
14 changes: 13 additions & 1 deletion src/main/kotlin/fr/shikkanime/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import fr.shikkanime.services.MemberService
import fr.shikkanime.utils.Constant
import fr.shikkanime.utils.JobManager
import fr.shikkanime.utils.LoggerFactory
import fr.shikkanime.utils.StringUtils
import io.ktor.server.application.*
import io.ktor.server.cio.*
import io.ktor.server.engine.*
Expand All @@ -28,7 +29,18 @@ fun main() {
logger.info("Admin user already exists")
}

Constant.injector.getInstance(AnimeService::class.java).preIndex()
val animeService = Constant.injector.getInstance(AnimeService::class.java)
animeService.preIndex()

animeService.findAll()
.filter { it.slug.isNullOrBlank() }
.forEach {
val name = StringUtils.getShortName(it.name!!)
val slug = StringUtils.toSlug(name)
it.slug = slug
animeService.update(it)
}

ImageService.addAll()

logger.info("Starting jobs...")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ class SiteController {
)
}

@Path("animes/{uuid}")
@Path("animes/{slug}")
@Get
private fun animeDetail(@PathParam("uuid") uuid: UUID): Response {
val anime = animeCacheService.find(uuid) ?: return Response.redirect("/404")
private fun animeDetail(@PathParam("slug") slug: String): Response {
val anime = animeCacheService.findBySlug(slug) ?: return Response.redirect("/404")

return Response.template(
"/site/anime.ftl",
Expand All @@ -131,7 +131,7 @@ class SiteController {
"anime" to anime,
"episodes" to episodeCacheService.findAllBy(
CountryCode.FR,
uuid,
anime.uuid,
listOf(
SortParameter("season", SortParameter.Order.ASC),
SortParameter("number", SortParameter.Order.ASC),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class AnimeDtoToAnimeConverter : AbstractConverter<AnimeDto, Anime>() {
image = from.image,
banner = from.banner,
description = from.description,
simulcasts = convert(from.simulcasts ?: emptyList(), Simulcast::class.java).toMutableSet()
simulcasts = convert(from.simulcasts ?: emptyList(), Simulcast::class.java).toMutableSet(),
slug = from.slug,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class AnimeToAnimeDtoConverter : AbstractConverter<Anime, AnimeDto>() {
SimulcastDto::class.java
) else null,
status = status,
slug = from.slug,
)
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/dtos/AnimeDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ data class AnimeDto(
val description: String?,
val simulcasts: List<SimulcastDto>?,
val status: Status? = null,
val slug: String? = null,
) : Serializable
4 changes: 4 additions & 0 deletions src/main/kotlin/fr/shikkanime/entities/Anime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ data class Anime(
inverseJoinColumns = [JoinColumn(name = "simulcast_uuid")]
)
var simulcasts: MutableSet<Simulcast> = mutableSetOf(),
@Column(nullable = true)
var slug: String? = null,
) : ShikkEntity(uuid) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand All @@ -49,6 +51,7 @@ data class Anime(
if (banner != other.banner) return false
if (description != other.description) return false
if (simulcasts != other.simulcasts) return false
if (slug != other.slug) return false

return true
}
Expand All @@ -62,6 +65,7 @@ data class Anime(
result = 31 * result + (banner?.hashCode() ?: 0)
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + simulcasts.hashCode()
result = 31 * result + (slug?.hashCode() ?: 0)
return result
}
}
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ private fun handlePathParam(kParameter: KParameter, parameters: Map<String, List
return when (kParameter.type.javaType) {
UUID::class.java -> UUID.fromString(pathParamValue)
Platform::class.java -> Platform.valueOf(pathParamValue!!)
String::class.java -> pathParamValue
else -> throw Exception("Unknown type ${kParameter.type}")
}
}
10 changes: 10 additions & 0 deletions src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ class AnimeRepository : AbstractRepository<Anime>() {
return bool
}

fun findBySlug(slug: String): Anime? {
return inTransaction {
createReadOnlyQuery(it, "FROM Anime WHERE slug = :slug", getEntityClass())
.setParameter("slug", slug)
.resultList
.firstOrNull()
?.initialize()
}
}

override fun find(uuid: UUID): Anime? {
return inTransaction {
createReadOnlyQuery(it, "FROM Anime WHERE uuid = :uuid", getEntityClass())
Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/fr/shikkanime/services/AnimeService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class AnimeService : AbstractService<Anime, AnimeRepository>() {
fun findAllByName(name: String, countryCode: CountryCode?, page: Int, limit: Int) =
animeRepository.findAllByName(name, countryCode, page, limit)

fun findBySlug(slug: String) = animeRepository.findBySlug(slug)

fun findAllUUIDAndImage() = animeRepository.findAllUUIDAndImage()

fun addImage(uuid: UUID, image: String) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class AnimeCacheService : AbstractCacheService() {
)
}

private val findBySlugCache = MapCache<String, AnimeDto>(classes = listOf(Anime::class.java)) {
AbstractConverter.convert(animeService.findBySlug(it), AnimeDto::class.java)
}

private val findByIdCache = MapCache<UUID, AnimeDto>(classes = listOf(Anime::class.java)) {
AbstractConverter.convert(animeService.find(it), AnimeDto::class.java)
}
Expand All @@ -47,5 +51,7 @@ class AnimeCacheService : AbstractCacheService() {
fun findAllByName(name: String, countryCode: CountryCode?, page: Int, limit: Int) =
findAllByNameCache[CountryCodeNamePaginationKeyCache(countryCode, name, page, limit)]

fun findBySlug(slug: String) = findBySlugCache[slug]

fun find(uuid: UUID) = findByIdCache[uuid]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import java.time.ZonedDateTime
import java.util.logging.Level
import javax.imageio.ImageIO

private const val BASE_URL = "https://www.shikkanime.fr/"

class DiscordSocialNetwork : AbstractSocialNetwork() {
private val logger = LoggerFactory.getLogger(DiscordSocialNetwork::class.java)
private var isInitialized = false
Expand All @@ -29,7 +31,7 @@ class DiscordSocialNetwork : AbstractSocialNetwork() {

try {
val builder = JDABuilder.createDefault(configCacheService.getValueAsString(ConfigPropertyKey.DISCORD_TOKEN))
builder.setActivity(Activity.playing("https://www.shikkanime.fr/"))
builder.setActivity(Activity.playing(BASE_URL))
jda = builder.build()
jda?.awaitReady()
isInitialized = true
Expand Down Expand Up @@ -67,13 +69,13 @@ class DiscordSocialNetwork : AbstractSocialNetwork() {
embedMessage.setAuthor(
episodeDto.platform.platformName,
episodeDto.platform.url,
"https://www.shikkanime.fr/assets/img/platforms/${episodeDto.platform.image}"
"${BASE_URL}assets/img/platforms/${episodeDto.platform.image}"
)
embedMessage.setTitle(episodeDto.anime.shortName, episodeDto.url)
embedMessage.setTitle(episodeDto.anime.shortName, "${BASE_URL}animes/${episodeDto.anime.slug}")
embedMessage.setThumbnail(episodeDto.anime.image)
embedMessage.setDescription("**${episodeDto.title ?: "Untitled"}**\n${StringUtils.toEpisodeString(episodeDto)}")
embedMessage.setImage(episodeDto.image)
embedMessage.setFooter(Constant.NAME, "https://www.shikkanime.fr/assets/img/favicons/favicon-64x64.png")
embedMessage.setFooter(Constant.NAME, "${BASE_URL}assets/img/favicons/favicon-64x64.png")
embedMessage.setTimestamp(ZonedDateTime.parse(episodeDto.releaseDateTime).toInstant())
val embed = embedMessage.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,13 @@ class TwitterSocialNetwork : AbstractSocialNetwork() {
if (!isInitialized) return
if (twitter == null) return

val url = "https://www.shikkanime.fr/animes/${episodeDto.anime.slug}"
val uncensored = if (episodeDto.uncensored) " non censuré" else ""
val isVoice = if (episodeDto.langType == LangType.VOICE) " en VF " else " "
val message =
"\uD83D\uDEA8 ${information(episodeDto)}${uncensored} de #${StringUtils.getHashtag(episodeDto.anime.shortName)} est maintenant disponible${isVoice}sur ${
platformAccount(episodeDto.platform)
}\n\nBon visionnage. \uD83C\uDF7F\n\n\uD83D\uDD36 Lien de l'épisode : ${episodeDto.url}"
}\n\nBon visionnage. \uD83C\uDF7F\n\n\uD83D\uDD36 Lien de l'épisode : $url"

val byteArrayOutputStream = ByteArrayOutputStream()
ImageIO.write(ImageService.toEpisodeImage(episodeDto), "png", byteArrayOutputStream)
Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/fr/shikkanime/utils/StringUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ package fr.shikkanime.utils
import fr.shikkanime.dtos.EpisodeDto
import fr.shikkanime.entities.enums.EpisodeType
import fr.shikkanime.entities.enums.LangType
import java.text.Normalizer
import java.util.*
import java.util.regex.Pattern

object StringUtils {
private val NONLATIN: Pattern = Pattern.compile("[^\\w-]")
private val WHITESPACE: Pattern = Pattern.compile("\\s")

fun getShortName(fullName: String): String {
val regexs = listOf("-.*-".toRegex(), "Saison \\d*".toRegex(), "\\(\\d*\\)".toRegex())
val separators = listOf(":", ",", "!")
Expand Down Expand Up @@ -59,4 +64,11 @@ object StringUtils {

return "Saison ${episode.season}$etName ${episode.number} $ltName"
}

fun toSlug(input: String): String {
val nowhitespace: String = WHITESPACE.matcher(input).replaceAll("-")
val normalized: String = Normalizer.normalize(nowhitespace, Normalizer.Form.NFD)
val slug: String = NONLATIN.matcher(normalized).replaceAll("")
return slug.lowercase()
}
}
21 changes: 21 additions & 0 deletions src/main/resources/db/changelog/2024/02/03-changelog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.26.xsd" objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS">
<property global="false" name="id" value="1707393112645"/>
<property global="false" name="author" value="Ziedelth"/>

<changeSet id="${id}-1" author="${author}">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="anime" columnName="slug"/>
</not>
</preConditions>

<addColumn tableName="anime">
<column name="slug" type="VARCHAR(255)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/db.changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@
<!-- February 2024 -->
<include file="/db/changelog/2024/02/01-changelog.xml"/>
<include file="/db/changelog/2024/02/02-changelog.xml"/>
<include file="/db/changelog/2024/02/03-changelog.xml"/>
</databaseChangeLog>
2 changes: 1 addition & 1 deletion src/main/resources/templates/admin/episodes/edit.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<div class="col-md-6">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description"
rows="6">${episode.description}</textarea>
rows="6">${episode.description!""}</textarea>
</div>
<div class="col-md-6">
<label for="url" class="form-label">URL</label>
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/templates/site/components/anime.ftl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<#macro display anime>
<div class="col-md-2 col-6 mt-0">
<article>
<a href="/animes/${anime.uuid}" class="text-decoration-none text-white">
<a href="/animes/${anime.slug}" class="text-decoration-none text-white">
<div class="hover-card position-relative">
<img loading="lazy" src="https://api.shikkanime.fr/v1/attachments?uuid=${anime.uuid}&type=image"
alt="${anime.shortName?replace("\"", "'")} anime image" class="w-100">
Expand Down
22 changes: 22 additions & 0 deletions src/test/kotlin/fr/shikkanime/AnimeSlug.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package fr.shikkanime

import java.text.Normalizer
import java.text.Normalizer.Form
import java.util.regex.Pattern

private val NONLATIN: Pattern = Pattern.compile("[^\\w-]")
private val WHITESPACE: Pattern = Pattern.compile("\\s")

fun toSlug(input: String): String {
val nowhitespace: String = WHITESPACE.matcher(input).replaceAll("-")
val normalized: String = Normalizer.normalize(nowhitespace, Form.NFD)
val slug: String = NONLATIN.matcher(normalized).replaceAll("")
return slug.lowercase()
}

fun main() {
println(toSlug("Metallic Rouge"))
println(toSlug("BANISHED FROM THE HERO'S PARTY"))
println(toSlug("'TIS TIME FOR \"TORTURE,\" PRINCESS"))
println(toSlug("HOKKAIDO GALS ARE SUPER ADORABLE!"))
}

0 comments on commit 0a659cc

Please sign in to comment.