Skip to content

Commit

Permalink
Add calendar on website
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziedelth committed Mar 22, 2024
1 parent 633d80b commit 4019901
Show file tree
Hide file tree
Showing 22 changed files with 355 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package fr.shikkanime.caches

import fr.shikkanime.entities.enums.CountryCode
import java.time.LocalDate

data class CountryCodeLocalDateKeyCache(
val countryCode: CountryCode,
val localDate: LocalDate
)
49 changes: 49 additions & 0 deletions src/main/kotlin/fr/shikkanime/controllers/api/AnimeController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.shikkanime.controllers.api
import com.google.inject.Inject
import fr.shikkanime.dtos.MessageDto
import fr.shikkanime.dtos.PageableDto
import fr.shikkanime.dtos.WeeklyAnimesDto
import fr.shikkanime.entities.enums.CountryCode
import fr.shikkanime.services.caches.AnimeCacheService
import fr.shikkanime.utils.routes.Controller
Expand All @@ -13,6 +14,8 @@ import fr.shikkanime.utils.routes.method.Get
import fr.shikkanime.utils.routes.openapi.OpenAPI
import fr.shikkanime.utils.routes.openapi.OpenAPIResponse
import fr.shikkanime.utils.routes.param.QueryParam
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.*

@Controller("/api/v1/animes")
Expand Down Expand Up @@ -74,4 +77,50 @@ class AnimeController : HasPageableRoute() {
}
)
}

@Path("/weekly")
@Get
@OpenAPI(
"Get weekly anime",
[
OpenAPIResponse(
200,
"Weekly anime found",
Array<WeeklyAnimesDto>::class,
),
OpenAPIResponse(
400,
"Invalid week format",
MessageDto::class
),
]
)
private fun getWeekly(
@QueryParam("country", description = "By default: FR", type = CountryCode::class) countryParam: String?,
@QueryParam("date", description = "By default: today", type = String::class) dateParam: String?,
): Response {
val parsedDate = if (dateParam != null) {
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")

try {
LocalDate.parse(dateParam, formatter)
} catch (e: Exception) {
return Response.badRequest(
MessageDto(
MessageDto.Type.ERROR,
"Invalid week format",
)
)
}
} else {
LocalDate.now()
}

return Response.ok(
animeCacheService.getWeeklyAnimes(
parsedDate!!.minusDays(parsedDate.dayOfWeek.value.toLong() - 1),
CountryCode.fromNullable(countryParam) ?: CountryCode.FR
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class EpisodeController : HasPageableRoute() {
@QueryParam("desc") descParam: String?,
): Response {
val (page, limit, sortParameters) = pageableRoute(pageParam, limitParam, sortParam, descParam)

return Response.ok(
episodeCacheService.findAllBy(
CountryCode.fromNullable(countryParam) ?: CountryCode.FR,
Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import fr.shikkanime.utils.routes.method.Get
import fr.shikkanime.utils.routes.param.PathParam
import fr.shikkanime.utils.routes.param.QueryParam
import io.ktor.http.*
import java.time.ZonedDateTime

@Controller("/")
class SiteController {
Expand Down Expand Up @@ -152,6 +153,19 @@ class SiteController {
)
}

@Path("calendar")
@Get
private fun calendar(): Response {
val now = ZonedDateTime.now().toLocalDate()

return Response.template(
Link.CALENDAR,
mutableMapOf(
"weeklyAnimes" to animeCacheService.getWeeklyAnimes(now.minusDays(now.dayOfWeek.value.toLong() - 1), CountryCode.FR),
)
)
}

@Path("presentation")
@Get
private fun presentation(): Response {
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/fr/shikkanime/dtos/WeeklyAnimeDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package fr.shikkanime.dtos

data class WeeklyAnimeDto(
val anime: AnimeDto,
val releaseDateTime: String,
val platforms: List<PlatformDto>
)
6 changes: 6 additions & 0 deletions src/main/kotlin/fr/shikkanime/dtos/WeeklyAnimesDto.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package fr.shikkanime.dtos

data class WeeklyAnimesDto(
val dayOfWeek: String,
val releases: List<WeeklyAnimeDto>
)
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/entities/enums/Link.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum class Link(
// Site
HOME("/", "/site/home.ftl", "", "Accueil", "${Constant.NAME} : Ne manquez plus jamais un épisode d'animé !"),
CATALOG("/catalog/{currentSimulcast}", "/site/catalog.ftl", "", "Catalogue"),
CALENDAR("/calendar", "/site/calendar.ftl", "", "Calendrier"),
SEARCH("/search", "/site/search.ftl", "", "Recherche"),
PRESENTATION("/presentation", "/site/presentation.ftl", "", "Présentation", footer = true),
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class FetchDeprecatedEpisodeJob : AbstractJob {
requireNotNull(
biggestImage.asJsonObject.getAsString("source")?.takeIf { it.isNotBlank() }) { IMAGE_NULL_ERROR }
}

Platform.PRIM -> content.getAsString("image")!!
else -> content.getAsString("image2x")!!
}
Expand Down
25 changes: 25 additions & 0 deletions src/main/kotlin/fr/shikkanime/repositories/EpisodeRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,29 @@ class EpisodeRepository : AbstractRepository<Episode>() {
.initialize()
}
}

fun findAllByDateRange(
countryCode: CountryCode,
start: ZonedDateTime,
end: ZonedDateTime,
blacklisted: List<UUID> = emptyList(),
): List<Episode> {
return inTransaction { entityManager ->
createReadOnlyQuery(
entityManager,
"""
FROM Episode e
WHERE e.anime.countryCode = :countryCode AND e.releaseDateTime BETWEEN :start AND :end AND e.anime.uuid NOT IN :blacklisted
ORDER BY e.releaseDateTime ASC
""".trimIndent(),
getEntityClass()
)
.setParameter("countryCode", countryCode)
.setParameter("start", start)
.setParameter("end", end)
.setParameter("blacklisted", blacklisted)
.resultList
.initialize()
}
}
}
42 changes: 42 additions & 0 deletions src/main/kotlin/fr/shikkanime/services/AnimeService.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package fr.shikkanime.services

import com.google.inject.Inject
import fr.shikkanime.converters.AbstractConverter
import fr.shikkanime.dtos.EpisodeDto
import fr.shikkanime.dtos.PlatformDto
import fr.shikkanime.dtos.WeeklyAnimeDto
import fr.shikkanime.dtos.WeeklyAnimesDto
import fr.shikkanime.entities.Anime
import fr.shikkanime.entities.SortParameter
import fr.shikkanime.entities.enums.CountryCode
import fr.shikkanime.repositories.AnimeRepository
import fr.shikkanime.utils.MapCache
import fr.shikkanime.utils.StringUtils.capitalizeWords
import io.ktor.http.*
import java.time.LocalDate
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.util.*

class AnimeService : AbstractService<Anime, AnimeRepository>() {
Expand Down Expand Up @@ -42,6 +50,40 @@ class AnimeService : AbstractService<Anime, AnimeRepository>() {

fun findAllUUIDAndImage() = animeRepository.findAllUUIDAndImage()

fun getWeeklyAnimes(startOfWeekDay: LocalDate, countryCode: CountryCode): List<WeeklyAnimesDto> {
return startOfWeekDay.datesUntil(startOfWeekDay.plusDays(7)).map {
val start = ZonedDateTime.parse("${it}T00:00:00Z")
val end = ZonedDateTime.parse("${it}T23:59:59Z")
val dateTitle =
it.format(DateTimeFormatter.ofPattern("EEEE", Locale.of(countryCode.locale.split("-")[0], countryCode.locale.split("-")[1]))).capitalizeWords()
val list = episodeService.findAllByDateRange(countryCode, start, end).toMutableList()

list.addAll(
episodeService.findAllByDateRange(
countryCode,
start.minusDays(7),
end.minusDays(7),
list.mapNotNull { anime -> anime.uuid })
)

WeeklyAnimesDto(
dateTitle,
AbstractConverter.convert(
list.distinctBy { episode -> episode.anime?.uuid },
EpisodeDto::class.java
).map { episodeDto ->
WeeklyAnimeDto(
episodeDto.anime,
episodeDto.releaseDateTime,
AbstractConverter.convert(list.filter { episode -> episode.anime?.uuid == episodeDto.anime.uuid }
.map { episode -> episode.platform!! }
.distinct(), PlatformDto::class.java)
)
}
)
}.toList()
}

fun addImage(uuid: UUID, image: String) {
ImageService.add(uuid, ImageService.Type.IMAGE, image, 480, 720)
}
Expand Down
10 changes: 9 additions & 1 deletion src/main/kotlin/fr/shikkanime/services/EpisodeService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ class EpisodeService : AbstractService<Episode, EpisodeRepository>() {
) =
episodeRepository.findAllByPlatformDeprecatedEpisodes(platform, lastUpdateDateTime, notLike)

fun findAllByDateRange(
countryCode: CountryCode,
start: ZonedDateTime,
end: ZonedDateTime,
blacklisted: List<UUID> = emptyList()
) = episodeRepository.findAllByDateRange(countryCode, start, end, blacklisted)

fun addImage(uuid: UUID, image: String) {
ImageService.add(uuid, ImageService.Type.IMAGE, image, 640, 360)
}
Expand All @@ -71,7 +78,7 @@ class EpisodeService : AbstractService<Episode, EpisodeRepository>() {
val nextSimulcast = simulcasts[2]

val isAnimeReleaseDateTimeBeforeMinusXDays = anime.releaseDateTime.isBefore(adjustedDates[0])
val animeEpisodes = anime.uuid?.let { episodeRepository.findAllByAnime(it).sortedBy { it.releaseDateTime } }
val animeEpisodes = anime.uuid?.let { episodeRepository.findAllByAnime(it).sortedBy { episode -> episode.releaseDateTime } }
val previousEpisode =
animeEpisodes?.lastOrNull { it.releaseDateTime.isBefore(entity.releaseDateTime) && it.episodeType == entity.episodeType && it.langType == entity.langType }
val diff = previousEpisode?.releaseDateTime?.until(entity.releaseDateTime, ChronoUnit.MONTHS) ?: -1
Expand All @@ -83,6 +90,7 @@ class EpisodeService : AbstractService<Episode, EpisodeRepository>() {
ConfigPropertyKey.SIMULCAST_RANGE_DELAY,
3
)) -> nextSimulcast

entity.number!! > 1 && isAnimeReleaseDateTimeBeforeMinusXDays && currentSimulcast != previousSimulcast -> previousSimulcast
else -> currentSimulcast
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/fr/shikkanime/services/ImageService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,9 @@ object ImageService {
ClassLoader.getSystemClassLoader()
.getResource("media-image")?.file?.let { File(it).takeIf { file -> file.exists() } }
?: File(
Constant.dataFolder,
"media-image"
)
Constant.dataFolder,
"media-image"
)
require(mediaImageFolder.exists()) { "Media image folder not found" }
val backgroundsFolder = File(mediaImageFolder, "backgrounds")
require(backgroundsFolder.exists()) { "Background folder not found" }
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/fr/shikkanime/services/caches/AnimeCacheService.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package fr.shikkanime.services.caches

import com.google.inject.Inject
import fr.shikkanime.caches.CountryCodeLocalDateKeyCache
import fr.shikkanime.caches.CountryCodeNamePaginationKeyCache
import fr.shikkanime.caches.CountryCodeUUIDSortPaginationKeyCache
import fr.shikkanime.converters.AbstractConverter
import fr.shikkanime.dtos.AnimeDto
import fr.shikkanime.dtos.PageableDto
import fr.shikkanime.dtos.WeeklyAnimesDto
import fr.shikkanime.entities.Anime
import fr.shikkanime.entities.Episode
import fr.shikkanime.entities.SortParameter
import fr.shikkanime.entities.enums.CountryCode
import fr.shikkanime.services.AnimeService
import fr.shikkanime.utils.MapCache
import java.time.LocalDate
import java.util.*

class AnimeCacheService : AbstractCacheService {
Expand All @@ -37,6 +41,10 @@ class AnimeCacheService : AbstractCacheService {
AbstractConverter.convert(animeService.findBySlug(it), AnimeDto::class.java)
}

private val weeklyCache = MapCache<CountryCodeLocalDateKeyCache, List<WeeklyAnimesDto>>(classes = listOf(Episode::class.java)) {
animeService.getWeeklyAnimes(it.localDate, it.countryCode)
}

fun findAllBy(
countryCode: CountryCode?,
uuid: UUID?,
Expand All @@ -49,4 +57,7 @@ class AnimeCacheService : AbstractCacheService {
findAllByNameCache[CountryCodeNamePaginationKeyCache(countryCode, name, page, limit)]

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

fun getWeeklyAnimes(startOfWeekDay: LocalDate, countryCode: CountryCode) =
weeklyCache[CountryCodeLocalDateKeyCache(countryCode, startOfWeekDay)]
}
2 changes: 1 addition & 1 deletion src/main/resources/assets/css/purged/bootstrap.min.css

Large diffs are not rendered by default.

Binary file modified src/main/resources/assets/css/purged/main.min.css
Binary file not shown.
1 change: 1 addition & 0 deletions src/main/resources/templates/_freemarker_implicit.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
[#-- @ftlvariable name="seoLinks" type="kotlin.collections.AbstractList<fr.shikkanime.entities.enums.Link>" --]
[#-- @ftlvariable name="query" type="java.lang.String" --]
[#-- @ftlvariable name="su" type="fr.shikkanime.utils.StringUtils" --]
[#-- @ftlvariable name="weeklyAnimes" type="kotlin.collections.AbstractList<fr.shikkanime.dtos.WeeklyAnimesDto>" --]

[#-- @ftlvariable name="analyticsDomain" type="java.lang.String" --]
[#-- @ftlvariable name="analyticsApi" type="java.lang.String" --]
Expand Down
9 changes: 0 additions & 9 deletions src/main/resources/templates/site/_navigation.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,6 @@
</svg>
</a>
</li>
<li class="me-3">
<a class="text-muted" href="https://www.instagram.com/shikkanime/" target="_blank"
aria-label="Instagram">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-instagram" viewBox="0 0 16 16">
<path d="M8 0C5.829 0 5.556.01 4.703.048 3.85.088 3.269.222 2.76.42a3.9 3.9 0 0 0-1.417.923A3.9 3.9 0 0 0 .42 2.76C.222 3.268.087 3.85.048 4.7.01 5.555 0 5.827 0 8.001c0 2.172.01 2.444.048 3.297.04.852.174 1.433.372 1.942.205.526.478.972.923 1.417.444.445.89.719 1.416.923.51.198 1.09.333 1.942.372C5.555 15.99 5.827 16 8 16s2.444-.01 3.298-.048c.851-.04 1.434-.174 1.943-.372a3.9 3.9 0 0 0 1.416-.923c.445-.445.718-.891.923-1.417.197-.509.332-1.09.372-1.942C15.99 10.445 16 10.173 16 8s-.01-2.445-.048-3.299c-.04-.851-.175-1.433-.372-1.941a3.9 3.9 0 0 0-.923-1.417A3.9 3.9 0 0 0 13.24.42c-.51-.198-1.092-.333-1.943-.372C10.443.01 10.172 0 7.998 0zm-.717 1.442h.718c2.136 0 2.389.007 3.232.046.78.035 1.204.166 1.486.275.373.145.64.319.92.599s.453.546.598.92c.11.281.24.705.275 1.485.039.843.047 1.096.047 3.231s-.008 2.389-.047 3.232c-.035.78-.166 1.203-.275 1.485a2.5 2.5 0 0 1-.599.919c-.28.28-.546.453-.92.598-.28.11-.704.24-1.485.276-.843.038-1.096.047-3.232.047s-2.39-.009-3.233-.047c-.78-.036-1.203-.166-1.485-.276a2.5 2.5 0 0 1-.92-.598 2.5 2.5 0 0 1-.6-.92c-.109-.281-.24-.705-.275-1.485-.038-.843-.046-1.096-.046-3.233s.008-2.388.046-3.231c.036-.78.166-1.204.276-1.486.145-.373.319-.64.599-.92s.546-.453.92-.598c.282-.11.705-.24 1.485-.276.738-.034 1.024-.044 2.515-.045zm4.988 1.328a.96.96 0 1 0 0 1.92.96.96 0 0 0 0-1.92m-4.27 1.122a4.109 4.109 0 1 0 0 8.217 4.109 4.109 0 0 0 0-8.217m0 1.441a2.667 2.667 0 1 1 0 5.334 2.667 2.667 0 0 1 0-5.334"/>
</svg>
</a>
</li>
<li class="me-3">
<a class="text-muted" href="https://www.threads.net/@shikkanime" target="_blank"
aria-label="Threads">
Expand Down
Loading

0 comments on commit 4019901

Please sign in to comment.