diff --git a/src/main/kotlin/fr/shikkanime/entities/Anime.kt b/src/main/kotlin/fr/shikkanime/entities/Anime.kt index 407d0429..e794ede7 100644 --- a/src/main/kotlin/fr/shikkanime/entities/Anime.kt +++ b/src/main/kotlin/fr/shikkanime/entities/Anime.kt @@ -14,6 +14,7 @@ import java.util.* name = "anime", indexes = [ Index(name = "idx_anime_country_code", columnList = "country_code"), + Index(name = "idx_anime_slug", columnList = "slug"), ] ) @Indexed @@ -44,7 +45,7 @@ class Anime( ) @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) var simulcasts: MutableSet = mutableSetOf(), - @Column(nullable = true) + @Column(nullable = false, unique = true) var slug: String? = null, @Column(nullable = false, name = "last_release_date_time") var lastReleaseDateTime: ZonedDateTime = releaseDateTime, diff --git a/src/main/kotlin/fr/shikkanime/repositories/AbstractRepository.kt b/src/main/kotlin/fr/shikkanime/repositories/AbstractRepository.kt index 6809168a..6711c623 100644 --- a/src/main/kotlin/fr/shikkanime/repositories/AbstractRepository.kt +++ b/src/main/kotlin/fr/shikkanime/repositories/AbstractRepository.kt @@ -6,6 +6,7 @@ import fr.shikkanime.entities.ShikkEntity import fr.shikkanime.utils.Database import jakarta.persistence.EntityManager import jakarta.persistence.TypedQuery +import jakarta.persistence.criteria.CriteriaQuery import org.hibernate.ScrollMode import org.hibernate.jpa.AvailableHints import org.hibernate.query.Query @@ -41,6 +42,11 @@ abstract class AbstractRepository { .setHint(AvailableHints.HINT_READ_ONLY, true) } + fun createReadOnlyQuery(entityManager: EntityManager, criteriaQuery: CriteriaQuery): TypedQuery { + return entityManager.createQuery(criteriaQuery) + .setHint(AvailableHints.HINT_READ_ONLY, true) + } + fun buildPageableQuery(query: TypedQuery, page: Int, limit: Int): Pageable { val scrollableResults = query.unwrap(Query::class.java) .setReadOnly(true) diff --git a/src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt b/src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt index fb894bf6..e5a9516d 100644 --- a/src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt +++ b/src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt @@ -1,6 +1,7 @@ package fr.shikkanime.repositories import fr.shikkanime.entities.Anime +import fr.shikkanime.entities.Anime_ import fr.shikkanime.entities.Pageable import fr.shikkanime.entities.SortParameter import fr.shikkanime.entities.enums.CountryCode @@ -139,9 +140,15 @@ class AnimeRepository : AbstractRepository() { } fun findBySlug(slug: String): Anime? { - return inTransaction { - createReadOnlyQuery(it, "FROM Anime WHERE slug = :slug", getEntityClass()) - .setParameter("slug", slug) + return inTransaction { entityManager -> + val cb = entityManager.criteriaBuilder + val query = cb.createQuery(getEntityClass()) + val root = query.from(getEntityClass()) + + query.select(root) + .where(cb.equal(root[Anime_.slug], slug)) + + createReadOnlyQuery(entityManager, query) .resultList .firstOrNull() ?.initialize() diff --git a/src/main/kotlin/fr/shikkanime/repositories/EpisodeRepository.kt b/src/main/kotlin/fr/shikkanime/repositories/EpisodeRepository.kt index 516de0b1..319a39d2 100644 --- a/src/main/kotlin/fr/shikkanime/repositories/EpisodeRepository.kt +++ b/src/main/kotlin/fr/shikkanime/repositories/EpisodeRepository.kt @@ -163,15 +163,15 @@ class EpisodeRepository : AbstractRepository() { ): List { return inTransaction { entityManager -> val cb = entityManager.criteriaBuilder - val query = cb.createQuery(Episode::class.java) - val root = query.from(Episode::class.java) + val query = cb.createQuery(getEntityClass()) + val root = query.from(getEntityClass()) val countryPredicate = cb.equal(root[Episode_.anime][Anime_.countryCode], countryCode) val datePredicate = cb.between(root[Episode_.releaseDateTime], start, end) query.select(root).where(cb.and(countryPredicate, datePredicate)) - entityManager.createQuery(query) + createReadOnlyQuery(entityManager, query) .resultList } } diff --git a/src/main/kotlin/fr/shikkanime/utils/StringUtils.kt b/src/main/kotlin/fr/shikkanime/utils/StringUtils.kt index 81207ccb..d4a36ee3 100644 --- a/src/main/kotlin/fr/shikkanime/utils/StringUtils.kt +++ b/src/main/kotlin/fr/shikkanime/utils/StringUtils.kt @@ -11,7 +11,7 @@ object StringUtils { private val NONLATIN: Pattern = Pattern.compile("[^\\w-]") private val WHITESPACE: Pattern = Pattern.compile("\\s") private val regex = "([-|!].*[-|!])|(Saison \\d*)|\\(\\d*\\)".toRegex() - private val separators = listOf(":", ",", "!", " so ") + private val separators = listOf(":", ",", "!", "–", " so ") fun getShortName(fullName: String): String { var shortName = regex.replace(fullName, "") diff --git a/src/main/resources/db/changelog/2024/04/01-changelog.xml b/src/main/resources/db/changelog/2024/04/01-changelog.xml new file mode 100644 index 00000000..b1c88f80 --- /dev/null +++ b/src/main/resources/db/changelog/2024/04/01-changelog.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.xml b/src/main/resources/db/changelog/db.changelog-master.xml index 7ec47419..75c2b01b 100644 --- a/src/main/resources/db/changelog/db.changelog-master.xml +++ b/src/main/resources/db/changelog/db.changelog-master.xml @@ -38,4 +38,6 @@ + + \ No newline at end of file diff --git a/src/test/kotlin/fr/shikkanime/services/EpisodeServiceTest.kt b/src/test/kotlin/fr/shikkanime/services/EpisodeServiceTest.kt index a06df5b2..71c34458 100644 --- a/src/test/kotlin/fr/shikkanime/services/EpisodeServiceTest.kt +++ b/src/test/kotlin/fr/shikkanime/services/EpisodeServiceTest.kt @@ -56,6 +56,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -90,6 +91,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = ZonedDateTime.parse("2023-12-20T00:00:00Z"), + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -111,6 +113,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = ZonedDateTime.parse("2023-12-20T00:00:00Z"), + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -140,6 +143,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -171,6 +175,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -195,6 +200,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -219,6 +225,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -243,6 +250,7 @@ class EpisodeServiceTest { image = "https://www.shikkanime.com/image.png", banner = "https://www.shikkanime.com/image.png", releaseDateTime = releaseDateTime, + slug = "test", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -274,6 +282,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -296,6 +305,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -317,6 +327,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -342,6 +353,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -367,6 +379,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -395,6 +408,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -417,6 +431,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, @@ -438,6 +453,7 @@ class EpisodeServiceTest { image = Constant.DEFAULT_IMAGE_PREVIEW, banner = Constant.DEFAULT_IMAGE_PREVIEW, releaseDateTime = ZonedDateTime.parse("2023-07-10T15:30:00Z"), + slug = "synduality-noir", ), episodeType = EpisodeType.EPISODE, langType = LangType.SUBTITLES, diff --git a/src/test/kotlin/fr/shikkanime/utils/StringUtilsTest.kt b/src/test/kotlin/fr/shikkanime/utils/StringUtilsTest.kt index 4d4dfb08..d11172da 100644 --- a/src/test/kotlin/fr/shikkanime/utils/StringUtilsTest.kt +++ b/src/test/kotlin/fr/shikkanime/utils/StringUtilsTest.kt @@ -37,6 +37,7 @@ class StringUtilsTest { "Studio Apartment" to "Studio Apartment, Good Lighting, Angel Included", "I Was Reincarnated as the 7th Prince" to "I Was Reincarnated as the 7th Prince so I Can Take My Time Perfecting My Magical Ability", "Mushoku Tensei: Jobless Reincarnation" to "Mushoku Tensei: Jobless Reincarnation", + "Yuru Camp" to "Yuru Camp – Au grand air", ) list.forEach { (expected, input) -> diff --git a/src/test/resources/animes/dragon-ball.json b/src/test/resources/animes/dragon-ball.json index a3a17544..73793c61 100644 --- a/src/test/resources/animes/dragon-ball.json +++ b/src/test/resources/animes/dragon-ball.json @@ -10,5 +10,6 @@ "season": "WINTER", "year": 2024 } - ] + ], + "slug": "dragon-ball-z" } \ No newline at end of file diff --git a/src/test/resources/animes/naruto.json b/src/test/resources/animes/naruto.json index ba8bd541..047f6855 100644 --- a/src/test/resources/animes/naruto.json +++ b/src/test/resources/animes/naruto.json @@ -10,5 +10,6 @@ "season": "WINTER", "year": 2024 } - ] + ], + "slug": "naruto" } \ No newline at end of file diff --git a/src/test/resources/animes/one-piece.json b/src/test/resources/animes/one-piece.json index 0f4d8d45..894106db 100644 --- a/src/test/resources/animes/one-piece.json +++ b/src/test/resources/animes/one-piece.json @@ -10,5 +10,6 @@ "season": "WINTER", "year": 2024 } - ] + ], + "slug": "one-piece" } \ No newline at end of file diff --git a/src/test/resources/animes/two-piece.json b/src/test/resources/animes/two-piece.json index 5fa47700..74df5bdf 100644 --- a/src/test/resources/animes/two-piece.json +++ b/src/test/resources/animes/two-piece.json @@ -10,5 +10,6 @@ "season": "WINTER", "year": 2024 } - ] + ], + "slug": "two-piece" } \ No newline at end of file