Skip to content

Commit

Permalink
Merge pull request #117 from Shikkanime/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Ziedelth authored Feb 1, 2024
2 parents 3928649 + 68fd4dd commit 6ac2545
Show file tree
Hide file tree
Showing 24 changed files with 5,292 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/global_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
run: gradle clean test jacocoTestReport

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/fr/shikkanime/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import fr.shikkanime.utils.LoggerFactory
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import java.util.UUID
import java.util.*

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ enum class ConfigPropertyKey(val key: String) {
TWITTER_ACCESS_TOKEN_SECRET("twitter_access_token_secret"),
USE_CRUNCHYROLL_API("use_crunchyroll_api"),
SEO_DESCRIPTION("seo_description"),
SOCIAL_NETWORK_EPISODES_SIZE_LIMIT("social_network_episodes_size_limit"),
}
26 changes: 19 additions & 7 deletions src/main/kotlin/fr/shikkanime/jobs/FetchEpisodesJob.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package fr.shikkanime.jobs
import fr.shikkanime.converters.AbstractConverter
import fr.shikkanime.dtos.EpisodeDto
import fr.shikkanime.entities.Episode
import fr.shikkanime.entities.enums.ConfigPropertyKey
import fr.shikkanime.services.EpisodeService
import fr.shikkanime.services.caches.ConfigCacheService
import fr.shikkanime.utils.Constant
import fr.shikkanime.utils.LoggerFactory
import fr.shikkanime.utils.isEqualOrAfter
Expand All @@ -24,6 +26,9 @@ class FetchEpisodesJob : AbstractJob() {
@Inject
private lateinit var episodeService: EpisodeService

@Inject
private lateinit var configCacheService: ConfigCacheService

override fun run() {
if (isRunning) {
if (++lock > maxLock) {
Expand Down Expand Up @@ -59,22 +64,29 @@ class FetchEpisodesJob : AbstractJob() {
}
}

episodes
val savedEpisodes = episodes
.filter { (zonedDateTime.isEqualOrAfter(it.releaseDateTime)) && !set.contains(it.hash) }
.forEach {
.mapNotNull {
try {
val savedEpisode = episodeService.save(it)
savedEpisode.hash?.let { hash -> set.add(hash) }

Thread {
val dto = AbstractConverter.convert(savedEpisode, EpisodeDto::class.java)
sendToSocialNetworks(dto)
}.start()
savedEpisode
} catch (e: Exception) {
logger.log(Level.SEVERE, "Error while saving episode ${it.hash} (${it.anime?.name})", e)
null
}
}

if (savedEpisodes.isNotEmpty() && savedEpisodes.size < configCacheService.getValueAsInt(ConfigPropertyKey.SOCIAL_NETWORK_EPISODES_SIZE_LIMIT)) {
val dtos = AbstractConverter.convert(savedEpisodes, EpisodeDto::class.java)

dtos.forEach {
Thread {
sendToSocialNetworks(it)
}.start()
}
}

isRunning = false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,21 @@ import fr.shikkanime.entities.enums.Platform
import fr.shikkanime.exceptions.AnimeException
import fr.shikkanime.exceptions.AnimeNotSimulcastedException
import fr.shikkanime.platforms.configuration.AnimationDigitalNetworkConfiguration
import fr.shikkanime.utils.HttpRequest
import fr.shikkanime.utils.ObjectParser
import fr.shikkanime.utils.ObjectParser.getAsBoolean
import fr.shikkanime.utils.ObjectParser.getAsInt
import fr.shikkanime.utils.ObjectParser.getAsLong
import fr.shikkanime.utils.ObjectParser.getAsString
import io.ktor.client.statement.*
import io.ktor.http.*
import fr.shikkanime.wrappers.AnimationDigitalNetworkWrapper
import java.io.File
import java.time.ZonedDateTime
import java.util.logging.Level

class AnimationDigitalNetworkPlatform :
AbstractPlatform<AnimationDigitalNetworkConfiguration, CountryCode, JsonArray>() {
AbstractPlatform<AnimationDigitalNetworkConfiguration, CountryCode, List<JsonObject>>() {
override fun getPlatform(): Platform = Platform.ANIM

override suspend fun fetchApiContent(key: CountryCode, zonedDateTime: ZonedDateTime): JsonArray {
val toDateString = zonedDateTime.toLocalDate().toString()
val url = "https://gw.api.animationdigitalnetwork.${key.name.lowercase()}/video/calendar?date=$toDateString"
val response = HttpRequest().get(url)

if (response.status != HttpStatusCode.OK) {
return JsonArray()
}

return ObjectParser.fromJson(response.bodyAsText(), JsonObject::class.java).getAsJsonArray("videos")!!
override suspend fun fetchApiContent(key: CountryCode, zonedDateTime: ZonedDateTime): List<JsonObject> {
return AnimationDigitalNetworkWrapper.getLatestVideos(zonedDateTime.toLocalDate())
}

override fun fetchEpisodes(zonedDateTime: ZonedDateTime, bypassFileContent: File?): List<Episode> {
Expand All @@ -47,7 +36,7 @@ class AnimationDigitalNetworkPlatform :

api.forEach {
try {
list.addAll(convertEpisode(countryCode, it.asJsonObject, zonedDateTime))
list.addAll(convertEpisode(countryCode, it, zonedDateTime))
} catch (_: AnimeException) {
// Ignore
} catch (e: Exception) {
Expand All @@ -67,7 +56,8 @@ class AnimationDigitalNetworkPlatform :
val show = jsonObject.getAsJsonObject("show") ?: throw Exception("Show is null")
val season = jsonObject.getAsString("season")?.toIntOrNull() ?: 1

var animeName = show.getAsString("shortTitle") ?: show.getAsString("title") ?: throw Exception("Anime name is null")
var animeName =
show.getAsString("shortTitle") ?: show.getAsString("title") ?: throw Exception("Anime name is null")
animeName = animeName.replace(Regex("Saison \\d"), "").trim()
animeName = animeName.replace(season.toString(), "").trim()
// Replace "Edens Zero -" to get "Edens Zero"
Expand Down
28 changes: 21 additions & 7 deletions src/main/kotlin/fr/shikkanime/platforms/CrunchyrollPlatform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo
)
val previousSimulcastAnimes =
previousSimulcastContent.select(simulcastAnimesSelector).map { a -> a.text().lowercase() }.toSet()
logger.info("Found ${previousSimulcastAnimes.size} animes for the previous simulcast")
logger.info("Found ${previousSimulcastAnimes.size} animes for the sprevious simulcast")

val combinedSimulcasts = (currentSimulcastAnimes + previousSimulcastAnimes).toSet()
simulcasts.addAll(combinedSimulcasts)
Expand All @@ -119,11 +119,24 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo

if (configCacheService.getValueAsBoolean(ConfigPropertyKey.USE_CRUNCHYROLL_API)) {
val (token, cms) = identifiers[it.countryCode]!!
val `object` = runBlocking { CrunchyrollWrapper.getObject(token, cms, it.animeId) }[0].asJsonObject
val `object` = runBlocking {
CrunchyrollWrapper.getObject(
it.countryCode.locale,
token,
cms,
it.animeId
)
}[0].asJsonObject
val postersTall = `object`.getAsJsonObject("images").getAsJsonArray("poster_tall")[0].asJsonArray
val postersWide = `object`.getAsJsonObject("images").getAsJsonArray("poster_wide")[0].asJsonArray
image = postersTall?.maxByOrNull { poster -> poster.asJsonObject.getAsInt("width")!! }?.asJsonObject?.getAsString("source")
banner = postersWide?.maxByOrNull { poster -> poster.asJsonObject.getAsInt("width")!! }?.asJsonObject?.getAsString("source")
image =
postersTall?.maxByOrNull { poster -> poster.asJsonObject.getAsInt("width")!! }?.asJsonObject?.getAsString(
"source"
)
banner =
postersWide?.maxByOrNull { poster -> poster.asJsonObject.getAsInt("width")!! }?.asJsonObject?.getAsString(
"source"
)
description = `object`.getAsString("description")
} else {
HttpRequest().use { httpRequest ->
Expand All @@ -135,8 +148,9 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo
image =
content.selectXpath("//*[@id=\"content\"]/div/div[2]/div/div[1]/div[2]/div/div/div[2]/div[2]/figure/picture/img")
.attr("src")
banner = content.selectXpath("//*[@id=\"content\"]/div/div[2]/div/div[1]/div[2]/div/div/div[2]/div[1]/figure/picture/img")
.attr("src")
banner =
content.selectXpath("//*[@id=\"content\"]/div/div[2]/div/div[1]/div[2]/div/div/div[2]/div[1]/figure/picture/img")
.attr("src")
description =
content.selectXpath("//*[@id=\"content\"]/div/div[2]/div/div[2]/div[1]/div[1]/div[5]/div/div/div/p")
.text()
Expand Down Expand Up @@ -172,7 +186,7 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo

override suspend fun fetchApiContent(key: CountryCode, zonedDateTime: ZonedDateTime): List<JsonObject> {
return if (configCacheService.getValueAsBoolean(ConfigPropertyKey.USE_CRUNCHYROLL_API)) {
CrunchyrollWrapper.getBrowse(identifiers[key]!!.first)
CrunchyrollWrapper.getBrowse(key.locale, identifiers[key]!!.first)
} else {
val url = "https://www.crunchyroll.com/rss/anime?lang=${key.locale.replace("-", "")}"
val response = HttpRequest().get(url)
Expand Down
4 changes: 3 additions & 1 deletion src/main/kotlin/fr/shikkanime/platforms/NetflixPlatform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class NetflixPlatform : AbstractPlatform<NetflixConfiguration, CountryCodeNetfli
val document =
HttpRequest().use { it.getBrowser("https://www.netflix.com/${key.countryCode.name.lowercase()}/title/$id") }
val animeName = document.selectFirst(".title-title")?.text() ?: return emptySet()
val animeBanner = document.selectXpath("//*[@id=\"section-hero\"]/div[1]/div[2]/picture/source[2]").attr("srcset").substringBefore("?")
val animeBanner =
document.selectXpath("//*[@id=\"section-hero\"]/div[1]/div[2]/picture/source[2]").attr("srcset")
.substringBefore("?")
val animeDescription = document.selectFirst(".title-info-synopsis")?.text()
val episodes = document.selectFirst("ol.episodes-container")?.select("li.episode") ?: emptySet()

Expand Down
11 changes: 9 additions & 2 deletions src/main/kotlin/fr/shikkanime/repositories/AbstractRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fr.shikkanime.entities.ShikkEntity
import fr.shikkanime.utils.Database
import jakarta.persistence.EntityManager
import jakarta.persistence.TypedQuery
import org.hibernate.jpa.AvailableHints
import org.hibernate.query.Query
import java.lang.reflect.ParameterizedType
import java.util.*
Expand Down Expand Up @@ -54,13 +55,19 @@ abstract class AbstractRepository<E : ShikkEntity> {

open fun findAll(): List<E> {
return inTransaction {
it.createQuery("FROM ${getEntityClass().simpleName}", getEntityClass()).resultList
it.createQuery("FROM ${getEntityClass().simpleName}", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
}
}

open fun find(uuid: UUID): E? {
return inTransaction {
it.find(getEntityClass(), uuid)
it.createQuery("FROM ${getEntityClass().simpleName} WHERE uuid = :uuid", getEntityClass())
.setParameter("uuid", uuid)
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
.firstOrNull()
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/fr/shikkanime/repositories/AnimeRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fr.shikkanime.entities.SortParameter
import fr.shikkanime.entities.enums.CountryCode
import jakarta.persistence.Tuple
import org.hibernate.Hibernate
import org.hibernate.jpa.AvailableHints
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory
import org.hibernate.search.engine.search.query.SearchResult
Expand Down Expand Up @@ -59,6 +60,7 @@ class AnimeRepository : AbstractRepository<Anime>() {
override fun findAll(): List<Anime> {
return inTransaction {
it.createQuery("FROM Anime", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
.initialize()
}
Expand Down Expand Up @@ -88,6 +90,7 @@ class AnimeRepository : AbstractRepository<Anime>() {
buildSortQuery(sort, queryBuilder)

val query = it.createQuery(queryBuilder.toString(), getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
countryCode?.let { query.setParameter("countryCode", countryCode) }
simulcast?.let { query.setParameter("uuid", simulcast) }
buildPageableQuery(query, page, limit).initialize()
Expand All @@ -97,6 +100,7 @@ class AnimeRepository : AbstractRepository<Anime>() {
fun findAllByLikeName(countryCode: CountryCode, name: String?): List<Anime> {
return inTransaction {
it.createQuery("FROM Anime WHERE countryCode = :countryCode AND LOWER(name) LIKE :name", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("countryCode", countryCode)
.setParameter("name", "%${name?.lowercase()}%")
.resultList
Expand Down Expand Up @@ -131,6 +135,7 @@ class AnimeRepository : AbstractRepository<Anime>() {
override fun find(uuid: UUID): Anime? {
return inTransaction {
it.createQuery("FROM Anime WHERE uuid = :uuid", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("uuid", uuid)
.resultList
.firstOrNull()
Expand All @@ -141,6 +146,7 @@ class AnimeRepository : AbstractRepository<Anime>() {
fun findAllUUIDAndImage(): List<Tuple> {
return inTransaction {
it.createQuery("SELECT uuid, image FROM Anime", Tuple::class.java)
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package fr.shikkanime.repositories

import fr.shikkanime.entities.Config
import org.hibernate.jpa.AvailableHints

class ConfigRepository : AbstractRepository<Config>() {
fun findAllByName(name: String): List<Config> {
return inTransaction {
it.createQuery("FROM Config c WHERE LOWER(c.propertyKey) LIKE :name", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("name", "%${name.lowercase()}%")
.resultList
}
Expand All @@ -14,6 +16,7 @@ class ConfigRepository : AbstractRepository<Config>() {
fun findByName(name: String): Config? {
return inTransaction {
it.createQuery("FROM Config c WHERE c.propertyKey = :name", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("name", name)
.resultList
.firstOrNull()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fr.shikkanime.entities.enums.LangType
import fr.shikkanime.entities.enums.Platform
import jakarta.persistence.Tuple
import org.hibernate.Hibernate
import org.hibernate.jpa.AvailableHints
import java.util.*

class EpisodeRepository : AbstractRepository<Episode>() {
Expand Down Expand Up @@ -51,6 +52,7 @@ class EpisodeRepository : AbstractRepository<Episode>() {
override fun findAll(): List<Episode> {
return inTransaction {
it.createQuery("FROM Episode", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
.initialize()
}
Expand All @@ -77,6 +79,7 @@ class EpisodeRepository : AbstractRepository<Episode>() {
buildSortQuery(sort, queryBuilder)

val query = it.createQuery(queryBuilder.toString(), getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
countryCode?.let { query.setParameter("countryCode", countryCode) }
anime?.let { query.setParameter("uuid", anime) }
buildPageableQuery(query, page, limit).initialize()
Expand All @@ -86,13 +89,15 @@ class EpisodeRepository : AbstractRepository<Episode>() {
fun findAllHashes(): List<String> {
return inTransaction {
it.createQuery("SELECT hash FROM Episode", String::class.java)
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
}
}

fun findAllByAnime(uuid: UUID): List<Episode> {
return inTransaction {
it.createQuery("FROM Episode WHERE anime.uuid = :uuid", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("uuid", uuid)
.resultList
}
Expand All @@ -101,6 +106,7 @@ class EpisodeRepository : AbstractRepository<Episode>() {
fun findByHash(hash: String?): Episode? {
return inTransaction {
it.createQuery("FROM Episode WHERE LOWER(hash) LIKE :hash", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("hash", "%${hash?.lowercase()}%")
.resultList
.firstOrNull()
Expand All @@ -112,7 +118,7 @@ class EpisodeRepository : AbstractRepository<Episode>() {
val query = it.createQuery(
"SELECT number FROM Episode WHERE anime.uuid = :uuid AND platform = :platform AND season = :season AND episodeType = :episodeType AND langType = :langType ORDER BY number DESC",
Int::class.java
)
).setHint(AvailableHints.HINT_READ_ONLY, true)
query.maxResults = 1
query.setParameter("uuid", anime)
query.setParameter("platform", platform)
Expand All @@ -126,6 +132,7 @@ class EpisodeRepository : AbstractRepository<Episode>() {
fun findAllUUIDAndImage(): List<Tuple> {
return inTransaction {
it.createQuery("SELECT uuid, image FROM Episode", Tuple::class.java)
.setHint(AvailableHints.HINT_READ_ONLY, true)
.resultList
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package fr.shikkanime.repositories

import fr.shikkanime.entities.Member
import fr.shikkanime.entities.enums.Role
import org.hibernate.jpa.AvailableHints

class MemberRepository : AbstractRepository<Member>() {
fun findAllByRole(role: Role): List<Member> {
return inTransaction {
it.createQuery("FROM Member WHERE role = :role", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("role", role)
.resultList
}
Expand All @@ -15,6 +17,7 @@ class MemberRepository : AbstractRepository<Member>() {
fun findByUsernameAndPassword(username: String, password: ByteArray): Member? {
return inTransaction {
it.createQuery("FROM Member WHERE username = :username AND encryptedPassword = :password", getEntityClass())
.setHint(AvailableHints.HINT_READ_ONLY, true)
.setParameter("username", username)
.setParameter("password", password)
.resultList
Expand Down
Loading

0 comments on commit 6ac2545

Please sign in to comment.