Skip to content

Commit

Permalink
Merge pull request #147 from Shikkanime/dev
Browse files Browse the repository at this point in the history
Improve SEO
  • Loading branch information
Ziedelth committed Feb 7, 2024
2 parents d974f04 + dbee116 commit 2905a66
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 110 deletions.
4 changes: 2 additions & 2 deletions src/main/kotlin/fr/shikkanime/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import io.ktor.server.application.*
import io.ktor.server.cio.*
import io.ktor.server.engine.*

private val logger = LoggerFactory.getLogger("Shikkanime")
private val logger = LoggerFactory.getLogger(Constant.NAME)

fun main() {
logger.info("Starting ShikkAnime...")
logger.info("Starting ${Constant.NAME}...")
ImageService.loadCache()

val memberService = Constant.injector.getInstance(MemberService::class.java)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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.LangType
import fr.shikkanime.entities.enums.Link
import fr.shikkanime.services.AnimeService
import fr.shikkanime.services.EpisodeService
Expand Down Expand Up @@ -126,14 +127,16 @@ class AdminController {
animeService.update(anime)
}

episodeService.findAll().forEach { episode ->
val anime = animeService.find(episode.anime!!.uuid!!)!!
episodeService.addSimulcastToAnime(anime, episodeService.getSimulcast(episode))
episodeService.findAll()
.filter { it.langType == LangType.SUBTITLES }
.forEach { episode ->
val anime = animeService.find(episode.anime!!.uuid!!)!!
episodeService.addSimulcastToAnime(anime, episodeService.getSimulcast(episode))

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

MapCache.invalidate(Anime::class.java, Episode::class.java)
return Response.redirect(Link.SIMULCASTS.href)
Expand Down
40 changes: 23 additions & 17 deletions src/main/kotlin/fr/shikkanime/controllers/site/SiteController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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 io.ktor.http.*
import java.util.*

@Controller("/")
Expand Down Expand Up @@ -61,27 +62,32 @@ class SiteController {
)
}

@Path("robots.txt")
@Get
private fun robots(): Response {
return Response.template(
"/site/seo/robots.ftl",
null,
contentType = ContentType.Text.Plain
)
}

@Path("sitemap.xml")
@Get
private fun sitemap(): Response {
return Response.template(
"/site/seo/sitemap.ftl",
null,
contentType = ContentType.Text.Xml
)
}

@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,
)
)
return Response.redirect("/catalog/${currentSimulcast.uuid}")
}

@Path("catalog/{uuid}")
Expand Down Expand Up @@ -125,7 +131,7 @@ class SiteController {
"/site/anime.ftl",
anime.shortName,
mutableMapOf(
"description" to anime.description,
"description" to configCacheService.getValueAsString(ConfigPropertyKey.SEO_DESCRIPTION),
"anime" to anime,
"episodes" to episodeCacheService.findAllBy(
CountryCode.FR,
Expand Down
6 changes: 4 additions & 2 deletions src/main/kotlin/fr/shikkanime/entities/enums/Link.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package fr.shikkanime.entities.enums

enum class Link(val href: String, val template: String, val icon: String, val label: String) {
import fr.shikkanime.utils.Constant

enum class Link(val href: String, val template: String, val icon: String, val label: String, val title: String = label) {
// Admin
DASHBOARD("/admin/dashboard", "/admin/dashboard.ftl", "bi bi-pc-display", "Dashboard"),
PLATFORMS("/admin/platforms", "/admin/platforms/list.ftl", "bi bi-display", "Platforms"),
Expand All @@ -11,7 +13,7 @@ enum class Link(val href: String, val template: String, val icon: String, val la
SIMULCASTS("/admin/simulcasts", "/admin/simulcasts.ftl", "bi bi-calendar-event", "Simulcasts"),

// Site
HOME("/", "/site/home.ftl", "", "Accueil"),
HOME("/", "/site/home.ftl", "", "Accueil", "${Constant.NAME} : Ne manquez plus jamais un épisode d'animé !"),
CATALOG("/catalog", "/site/catalog.ftl", "", "Catalogue"),
;
}
6 changes: 5 additions & 1 deletion src/main/kotlin/fr/shikkanime/plugins/HTTP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ fun Application.configureHTTP() {
status(HttpStatusCode.NotFound) { call, _ ->
val path = call.request.path()

if (path.contains(".")) {
return@status
}

if (!path.startsWith("/api") && !path.startsWith("/admin")) {
call.respondRedirect("/404")
}
Expand All @@ -60,7 +64,7 @@ fun Application.configureHTTP() {
forwardRoot = false
}
info {
title = "Shikkanime API"
title = "${Constant.NAME} API"
version = "1.0"
description = "API for testing and demonstration purposes"
}
Expand Down
75 changes: 29 additions & 46 deletions src/main/kotlin/fr/shikkanime/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -216,60 +216,43 @@ private suspend fun handleRequest(
try {
val response = callMethodWithParameters(method, controller, call, parameters)

if (method.hasAnnotation<Cached>()) {
val cached = method.findAnnotation<Cached>()!!.maxAgeSeconds
call.caching = CachingOptions(CacheControl.MaxAge(maxAgeSeconds = cached))
}

if (response.session != null) {
call.sessions.set(response.session)
}
response.session?.let { call.sessions.set(it) }

when (response.type) {
ResponseType.MULTIPART -> {
val map = response.data as Map<String, Any>

call.respondBytes(
map["image"] as ByteArray,
map["contentType"] as ContentType,
)
}

ResponseType.TEMPLATE -> {
val map = response.data as Map<String, Any>
val modelMap = map["model"] as MutableMap<String, Any>

val list = if (controller.javaClass.simpleName.startsWith("Admin"))
LinkObject.list().filter { it.href.startsWith("/admin") }
else
LinkObject.list().filter { !it.href.startsWith("/admin") }
ResponseType.MULTIPART -> handleMultipartResponse(call, response)
ResponseType.TEMPLATE -> handleTemplateResponse(call, controller, replacedPath, response)
ResponseType.REDIRECT -> call.respondRedirect(response.data as String)
else -> call.respond(response.status, response.data ?: "")
}
} catch (e: Exception) {
logger.log(Level.SEVERE, "Error while calling method $method", e)
call.respond(HttpStatusCode.BadRequest)
}
}

modelMap["links"] = list.map { link ->
link.active = if (link.href == "/") replacedPath == link.href else replacedPath.startsWith(link.href)
link
}
private suspend fun handleMultipartResponse(call: ApplicationCall, response: Response) {
val map = response.data as Map<String, Any>
call.respondBytes(map["image"] as ByteArray, map["contentType"] as ContentType)
}

val title = map["title"] as String?
modelMap["title"] = (if (!title.isNullOrBlank()) "$title - " else "") + "Shikkanime"
private suspend fun handleTemplateResponse(call: ApplicationCall, controller: Any, replacedPath: String, response: Response) {
val map = response.data as Map<String, Any>
val modelMap = (map["model"] as Map<String, Any>).toMutableMap()

call.respond(FreeMarkerContent(map["template"] as String, modelMap, ""))
}
val list = if (controller.javaClass.simpleName.startsWith("Admin"))
LinkObject.list().filter { it.href.startsWith("/admin") }
else
LinkObject.list().filter { !it.href.startsWith("/admin") }

ResponseType.REDIRECT -> {
call.respondRedirect(response.data as String)
}
modelMap["links"] = list.map { link ->
link.active = if (link.href == "/") replacedPath == link.href else replacedPath.startsWith(link.href)
link
}

else -> {
call.respond(response.status, response.data ?: "")
}
}
} catch (e: Exception) {
if (e.message?.contains("Broken pipe") != true && e.message?.contains("Relais brisé (pipe)") != true) {
logger.log(Level.SEVERE, "Error while calling method $method", e)
}
val title = map["title"] as String?
modelMap["title"] = if (title?.contains(Constant.NAME) == true) title else (if (!title.isNullOrBlank()) "$title - " else "") + Constant.NAME

call.respond(HttpStatusCode.BadRequest)
}
call.respond(FreeMarkerContent(map["template"] as String, modelMap, "", response.contentType))
}

private fun replacePathWithParameters(path: String, parameters: Map<String, List<String>>): String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fr.shikkanime.socialnetworks
import fr.shikkanime.dtos.EpisodeDto
import fr.shikkanime.entities.enums.ConfigPropertyKey
import fr.shikkanime.services.ImageService
import fr.shikkanime.utils.Constant
import fr.shikkanime.utils.LoggerFactory
import fr.shikkanime.utils.StringUtils
import net.dv8tion.jda.api.EmbedBuilder
Expand Down Expand Up @@ -72,7 +73,7 @@ class DiscordSocialNetwork : AbstractSocialNetwork() {
embedMessage.setThumbnail(episodeDto.anime.image)
embedMessage.setDescription("**${episodeDto.title ?: "Untitled"}**\n${StringUtils.toEpisodeString(episodeDto)}")
embedMessage.setImage(episodeDto.image)
embedMessage.setFooter("Shikkanime", "https://www.shikkanime.fr/assets/img/favicons/favicon-64x64.png")
embedMessage.setFooter(Constant.NAME, "https://www.shikkanime.fr/assets/img/favicons/favicon-64x64.png")
embedMessage.setTimestamp(ZonedDateTime.parse(episodeDto.releaseDateTime).toInstant())
val embed = embedMessage.build()

Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/fr/shikkanime/utils/Constant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import java.io.File
import java.time.ZoneId

object Constant {
const val NAME = "Shikkanime"
val reflections = Reflections("fr.shikkanime")
val injector: Injector = Guice.createInjector(DefaultModule())
val abstractPlatforms = reflections.getSubTypesOf(AbstractPlatform::class.java).map { injector.getInstance(it) }
Expand Down
18 changes: 12 additions & 6 deletions src/main/kotlin/fr/shikkanime/utils/routes/Response.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ open class Response(
val type: ResponseType = ResponseType.JSON,
val session: TokenDto? = null,
val data: Any? = null,
val contentType: ContentType = ContentType.Application.Json,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
Expand All @@ -24,6 +25,7 @@ open class Response(
if (status != other.status) return false
if (data != other.data) return false
if (session != other.session) return false
if (contentType != other.contentType) return false

return true
}
Expand All @@ -32,6 +34,7 @@ open class Response(
var result = status.hashCode()
result = 31 * result + (data?.hashCode() ?: 0)
result = 31 * result + (session?.hashCode() ?: 0)
result = 31 * result + contentType.hashCode()
return result
}

Expand Down Expand Up @@ -60,20 +63,23 @@ open class Response(
fun template(
template: String,
title: String? = null,
model: MutableMap<String, Any?> = mutableMapOf(),
session: TokenDto? = null
model: Map<String, Any?> = mapOf(),
session: TokenDto? = null,
contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8),
): Response = Response(
HttpStatusCode.OK,
type = ResponseType.TEMPLATE,
data = mapOf("template" to template, "title" to title, "model" to model),
session = session
session = session,
contentType = contentType
)

fun template(
link: Link,
model: MutableMap<String, Any?> = mutableMapOf(),
session: TokenDto? = null
): Response = template(link.template, link.label, model, session)
model: Map<String, Any?> = mapOf(),
session: TokenDto? = null,
contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8),
): Response = template(link.template, link.title, model, session, contentType)

fun redirect(path: String, session: TokenDto? = null): Response =
Response(HttpStatusCode.Found, type = ResponseType.REDIRECT, data = path, session = session)
Expand Down
5 changes: 2 additions & 3 deletions src/main/resources/templates/site/_layout.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@
</style>
</head>
<body>
<main>
<#nested 0>
</main>

<#nested 0>

<script src="/assets/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
</body>
Expand Down
Loading

0 comments on commit 2905a66

Please sign in to comment.