Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced website and add simulcast recalculation #145

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions src/main/kotlin/fr/shikkanime/controllers/admin/AdminController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ package fr.shikkanime.controllers.admin
import com.google.inject.Inject
import fr.shikkanime.converters.AbstractConverter
import fr.shikkanime.dtos.TokenDto
import fr.shikkanime.entities.Anime
import fr.shikkanime.entities.Episode
import fr.shikkanime.entities.enums.Link
import fr.shikkanime.services.AnimeService
import fr.shikkanime.services.EpisodeService
import fr.shikkanime.services.ImageService
import fr.shikkanime.services.MemberService
import fr.shikkanime.services.caches.SimulcastCacheService
import fr.shikkanime.utils.MapCache
import fr.shikkanime.utils.routes.AdminSessionAuthenticated
import fr.shikkanime.utils.routes.Controller
import fr.shikkanime.utils.routes.Path
Expand All @@ -23,6 +29,15 @@ class AdminController {
@Inject
private lateinit var memberService: MemberService

@Inject
private lateinit var simulcastCacheService: SimulcastCacheService

@Inject
private lateinit var episodeService: EpisodeService

@Inject
private lateinit var animeService: AnimeService

@Path
@Get
private fun home(@QueryParam("error") error: String?): Response {
Expand Down Expand Up @@ -89,4 +104,38 @@ class AdminController {
ImageService.invalidate()
return Response.redirect(Link.IMAGES.href)
}

@Path("/simulcasts")
@Get
@AdminSessionAuthenticated
private fun getSimulcasts(): Response {
return Response.template(
Link.SIMULCASTS,
mutableMapOf(
"simulcasts" to simulcastCacheService.findAll()
)
)
}

@Path("/simulcasts/recalculate")
@Get
@AdminSessionAuthenticated
private fun recalculateSimulcasts(): Response {
animeService.findAll().forEach { anime ->
anime.simulcasts.clear()
animeService.update(anime)
}

episodeService.findAll().forEach { episode ->
val anime = animeService.find(episode.anime!!.uuid!!)!!
episodeService.addSimulcastToAnime(anime, episodeService.getSimulcast(episode))

if (episode.anime != anime) {
animeService.update(anime)
}
}

MapCache.invalidate(Anime::class.java, Episode::class.java)
return Response.redirect(Link.SIMULCASTS.href)
}
}
88 changes: 88 additions & 0 deletions src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import fr.shikkanime.entities.enums.Link
import fr.shikkanime.services.caches.AnimeCacheService
import fr.shikkanime.services.caches.ConfigCacheService
import fr.shikkanime.services.caches.EpisodeCacheService
import fr.shikkanime.services.caches.SimulcastCacheService
import fr.shikkanime.utils.routes.Controller
import fr.shikkanime.utils.routes.Path
import fr.shikkanime.utils.routes.Response
import fr.shikkanime.utils.routes.method.Get
import fr.shikkanime.utils.routes.param.PathParam
import java.util.*

@Controller("/")
class SiteController {
Expand All @@ -24,6 +27,9 @@ class SiteController {
@Inject
private lateinit var configCacheService: ConfigCacheService

@Inject
private lateinit var simulcastCacheService: SimulcastCacheService

@Path
@Get
private fun home(): Response {
Expand Down Expand Up @@ -54,4 +60,86 @@ class SiteController {
)
)
}

@Path("catalog")
@Get
private fun catalog(): Response {
val findAll = simulcastCacheService.findAll()!!
val currentSimulcast = findAll.first()

return Response.template(
Link.CATALOG,
mutableMapOf(
"description" to configCacheService.getValueAsString(ConfigPropertyKey.SEO_DESCRIPTION),
"simulcasts" to findAll,
"currentSimulcast" to currentSimulcast,
"animes" to animeCacheService.findAllBy(
CountryCode.FR,
currentSimulcast.uuid,
listOf(SortParameter("name", SortParameter.Order.ASC)),
1,
102
)!!.data,
)
)
}

@Path("catalog/{uuid}")
@Get
private fun catalogSimulcast(@PathParam("uuid") uuid: UUID): Response {
val findAll = simulcastCacheService.findAll()!!
val currentSimulcast = findAll.first { it.uuid == uuid }

return Response.template(
Link.CATALOG,
mutableMapOf(
"description" to configCacheService.getValueAsString(ConfigPropertyKey.SEO_DESCRIPTION),
"simulcasts" to findAll,
"currentSimulcast" to currentSimulcast,
"animes" to animeCacheService.findAllBy(
CountryCode.FR,
currentSimulcast.uuid,
listOf(SortParameter("name", SortParameter.Order.ASC)),
1,
102
)!!.data,
)
)
}

@Path("404")
@Get
private fun error404(): Response {
return Response.template(
"/site/404.ftl",
"Page introuvable",
)
}

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

return Response.template(
"/site/anime.ftl",
anime.shortName,
mutableMapOf(
"description" to anime.description,
"anime" to anime,
"episodes" to episodeCacheService.findAllBy(
CountryCode.FR,
uuid,
listOf(
SortParameter("season", SortParameter.Order.ASC),
SortParameter("number", SortParameter.Order.ASC),
SortParameter("episodeType", SortParameter.Order.ASC),
SortParameter("langType", SortParameter.Order.ASC),
),
1,
12
)!!.data
)
)
}
}
32 changes: 31 additions & 1 deletion src/main/kotlin/fr/shikkanime/entities/Anime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,34 @@ data class Anime(
inverseJoinColumns = [JoinColumn(name = "simulcast_uuid")]
)
var simulcasts: MutableSet<Simulcast> = mutableSetOf(),
) : ShikkEntity(uuid)
) : ShikkEntity(uuid) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Anime

if (uuid != other.uuid) return false
if (countryCode != other.countryCode) return false
if (name != other.name) return false
if (releaseDateTime != other.releaseDateTime) return false
if (image != other.image) return false
if (banner != other.banner) return false
if (description != other.description) return false
if (simulcasts != other.simulcasts) return false

return true
}

override fun hashCode(): Int {
var result = uuid?.hashCode() ?: 0
result = 31 * result + (countryCode?.hashCode() ?: 0)
result = 31 * result + (name?.hashCode() ?: 0)
result = 31 * result + releaseDateTime.hashCode()
result = 31 * result + (image?.hashCode() ?: 0)
result = 31 * result + (banner?.hashCode() ?: 0)
result = 31 * result + (description?.hashCode() ?: 0)
result = 31 * result + simulcasts.hashCode()
return result
}
}
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 @@ -8,6 +8,7 @@ enum class Link(val href: String, val template: String, val icon: String, val la
EPISODES("/admin/episodes", "/admin/episodes/list.ftl", "bi bi-collection-play", "Episodes"),
CONFIG("/admin/config", "/admin/config/list.ftl", "bi bi-gear", "Configurations"),
IMAGES("/admin/images", "/admin/images.ftl", "bi bi-images", "Images"),
SIMULCASTS("/admin/simulcasts", "/admin/simulcasts.ftl", "bi bi-calendar-event", "Simulcasts"),

// Site
HOME("/", "/site/home.ftl", "", "Accueil"),
Expand Down
9 changes: 9 additions & 0 deletions src/main/kotlin/fr/shikkanime/plugins/HTTP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.ktor.server.plugins.compression.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.plugins.statuspages.*
import io.ktor.server.request.*
import io.ktor.server.response.*

fun Application.configureHTTP() {
Expand All @@ -34,13 +35,21 @@ fun Application.configureHTTP() {
exception<Throwable> { call, cause ->
call.respondText(text = "500: $cause", status = HttpStatusCode.InternalServerError)
}
status(HttpStatusCode.NotFound) { call, _ ->
val path = call.request.path()

if (!path.startsWith("/api") && !path.startsWith("/admin")) {
call.respondRedirect("/404")
}
}
}
install(ContentNegotiation) {
gson {
}
}
install(FreeMarker) {
templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
whitespaceStripping = true
}
install(CachingHeaders) {
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/fr/shikkanime/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ private suspend fun handleRequest(
LinkObject.list().filter { !it.href.startsWith("/admin") }

modelMap["links"] = list.map { link ->
link.active = replacedPath.startsWith(link.href)
link.active = if (link.href == "/") replacedPath == link.href else replacedPath.startsWith(link.href)
link
}

Expand Down
39 changes: 20 additions & 19 deletions src/main/kotlin/fr/shikkanime/services/EpisodeService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.shikkanime.services

import com.google.inject.Inject
import fr.shikkanime.entities.Anime
import fr.shikkanime.entities.Episode
import fr.shikkanime.entities.Simulcast
import fr.shikkanime.entities.SortParameter
Expand All @@ -10,7 +11,6 @@ import fr.shikkanime.services.caches.ConfigCacheService
import fr.shikkanime.utils.Constant
import fr.shikkanime.utils.MapCache
import io.ktor.http.*
import org.hibernate.Hibernate
import java.time.ZonedDateTime
import java.util.*

Expand Down Expand Up @@ -78,20 +78,27 @@ class EpisodeService : AbstractService<Episode, EpisodeRepository>() {
?: choosenSimulcast
}

override fun save(entity: Episode): Episode {
val banner = entity.anime?.banner
fun addSimulcastToAnime(anime: Anime, simulcast: Simulcast) {
if (anime.simulcasts.isEmpty() || anime.simulcasts.none { s -> s.uuid == simulcast.uuid }) {
if (simulcast.uuid == null) {
simulcastService.save(simulcast)
}

entity.anime = animeService.findAllByLikeName(entity.anime!!.countryCode!!, entity.anime!!.name!!).firstOrNull()
?: animeService.save(entity.anime!!)
anime.simulcasts.add(simulcast)
}
}

if (entity.anime?.banner.isNullOrBlank() && !banner.isNullOrBlank()) {
entity.anime?.banner = banner
animeService.update(entity.anime!!)
override fun save(entity: Episode): Episode {
val copy = entity.anime!!.copy()
val anime = animeService.findAllByLikeName(copy.countryCode!!, copy.name!!).firstOrNull() ?: animeService.save(copy)

if (anime.banner.isNullOrBlank() && !copy.banner.isNullOrBlank()) {
anime.banner = copy.banner
}

entity.number.takeIf { it == -1 }?.let {
entity.number = episodeRepository.getLastNumber(
entity.anime!!.uuid!!,
anime.uuid!!,
entity.platform!!,
entity.season!!,
entity.episodeType!!,
Expand All @@ -101,17 +108,11 @@ class EpisodeService : AbstractService<Episode, EpisodeRepository>() {

if (entity.langType == LangType.SUBTITLES) {
val simulcast = getSimulcast(entity)
Hibernate.initialize(entity.anime!!.simulcasts)
val animeSimulcasts = entity.anime!!.simulcasts

if (animeSimulcasts.isEmpty() || animeSimulcasts.none { s -> s.uuid == simulcast.uuid }) {
if (simulcast.uuid == null) {
simulcastService.save(simulcast)
}
addSimulcastToAnime(anime, simulcast)
}

animeSimulcasts.add(simulcast)
animeService.update(entity.anime!!)
}
if (entity.anime != anime) {
entity.anime = animeService.update(anime)
}

val savedEntity = super.save(entity)
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/fr/shikkanime/services/SimulcastService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class SimulcastService : AbstractService<Simulcast, SimulcastRepository>() {

override fun getRepository() = simulcastRepository

override fun findAll() = super.findAll().sortedWith(compareBy({ it.year }, { Constant.seasons.indexOf(it.season) }))
override fun findAll() = super.findAll().sortedWith(compareBy({ it.year }, { Constant.seasons.indexOf(it.season) })).reversed()

fun findBySeasonAndYear(season: String, year: Int) = simulcastRepository.findBySeasonAndYear(season, year)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.shikkanime.services.caches
import com.google.inject.Inject
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.entities.Anime
Expand Down Expand Up @@ -31,6 +32,10 @@ class AnimeCacheService : AbstractCacheService() {
)
}

private val findByIdCache = MapCache<UUID, AnimeDto>(classes = listOf(Anime::class.java)) {
AbstractConverter.convert(animeService.find(it), AnimeDto::class.java)
}

fun findAllBy(
countryCode: CountryCode?,
uuid: UUID?,
Expand All @@ -41,4 +46,6 @@ class AnimeCacheService : AbstractCacheService() {

fun findAllByName(name: String, countryCode: CountryCode?, page: Int, limit: Int) =
findAllByNameCache[CountryCodeNamePaginationKeyCache(countryCode, name, page, limit)]

fun find(uuid: UUID) = findByIdCache[uuid]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package fr.shikkanime.services.caches

import com.google.inject.Inject
import fr.shikkanime.converters.AbstractConverter
import fr.shikkanime.dtos.SimulcastDto
import fr.shikkanime.entities.Simulcast
import fr.shikkanime.services.SimulcastService
import fr.shikkanime.utils.MapCache

class SimulcastCacheService : AbstractCacheService() {
@Inject
private lateinit var simulcastService: SimulcastService

private val cache = MapCache<String, List<SimulcastDto>>(classes = listOf(Simulcast::class.java)) {
AbstractConverter.convert(simulcastService.findAll(), SimulcastDto::class.java)
}

fun findAll() = cache["all"]
}
Loading