mirror of https://github.com/aruppi/aruppi-api.git
Include new endpoints, Anitakume for Spanish Podcasts and Gallery
parent
6f56611f18
commit
c54a28967b
@ -0,0 +1,35 @@
|
||||
package com.jeluchu.features.anitakume.mappers
|
||||
|
||||
import com.jeluchu.core.extensions.getStringSafe
|
||||
import com.jeluchu.core.extensions.parseRssDate
|
||||
import com.jeluchu.features.anitakume.models.AnitakumeEntity
|
||||
import com.prof18.rssparser.model.RssChannel
|
||||
import org.bson.Document
|
||||
|
||||
fun RssChannel.toPodcast(
|
||||
list: MutableList<AnitakumeEntity>
|
||||
) = list.apply{
|
||||
items.forEach { item ->
|
||||
add(
|
||||
AnitakumeEntity(
|
||||
image = item.itunesItemData?.image.orEmpty(),
|
||||
title = item.title?.substringAfter(" · ").orEmpty(),
|
||||
description = item.description.orEmpty(),
|
||||
date = item.pubDate?.parseRssDate().orEmpty(),
|
||||
link = item.link.orEmpty(),
|
||||
audio = item.audio.orEmpty(),
|
||||
duration = item.itunesItemData?.duration.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun documentToAnitakumeEntity(doc: Document) = AnitakumeEntity(
|
||||
image = doc.getStringSafe("image"),
|
||||
title = doc.getStringSafe("title"),
|
||||
description = doc.getStringSafe("description"),
|
||||
date = doc.getStringSafe("date"),
|
||||
link = doc.getStringSafe("link"),
|
||||
audio = doc.getStringSafe("audio"),
|
||||
duration = doc.getStringSafe("duration")
|
||||
)
|
@ -0,0 +1,14 @@
|
||||
package com.jeluchu.features.anitakume.models
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class AnitakumeEntity(
|
||||
val image: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val date: String,
|
||||
val link: String,
|
||||
val audio: String,
|
||||
val duration: String
|
||||
)
|
@ -0,0 +1,15 @@
|
||||
package com.jeluchu.features.anitakume.routes
|
||||
|
||||
import com.jeluchu.core.extensions.getToJson
|
||||
import com.jeluchu.core.utils.Routes
|
||||
import com.jeluchu.features.anitakume.services.AnitakumeService
|
||||
import com.jeluchu.features.news.services.NewsService
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Route.anitakumeEndpoints(
|
||||
mongoDatabase: MongoDatabase,
|
||||
service: AnitakumeService = AnitakumeService(mongoDatabase)
|
||||
) = route(Routes.ANITAKUME) {
|
||||
getToJson { service.getEpisodes(call) }
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.jeluchu.features.anitakume.services
|
||||
|
||||
import com.jeluchu.core.enums.TimeUnit
|
||||
import com.jeluchu.core.extensions.needsUpdate
|
||||
import com.jeluchu.core.extensions.update
|
||||
import com.jeluchu.core.utils.Collections
|
||||
import com.jeluchu.core.utils.RssSources
|
||||
import com.jeluchu.core.utils.RssUrls
|
||||
import com.jeluchu.core.utils.parseDataToDocuments
|
||||
import com.jeluchu.features.anitakume.mappers.documentToAnitakumeEntity
|
||||
import com.jeluchu.features.anitakume.mappers.toPodcast
|
||||
import com.jeluchu.features.anitakume.models.AnitakumeEntity
|
||||
import com.jeluchu.features.news.mappers.documentToNewsEntity
|
||||
import com.jeluchu.features.news.mappers.toNews
|
||||
import com.jeluchu.features.news.models.NewEntity
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import com.prof18.rssparser.RssParser
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.bson.Document
|
||||
|
||||
class AnitakumeService(
|
||||
database: MongoDatabase
|
||||
) {
|
||||
private val timers = database.getCollection(Collections.TIMERS)
|
||||
private val anitakume = database.getCollection(Collections.ANITAKUME)
|
||||
|
||||
suspend fun getEpisodes(call: RoutingCall) {
|
||||
val needsUpdate = timers.needsUpdate(
|
||||
amount = 1,
|
||||
key = Collections.ANITAKUME,
|
||||
unit = TimeUnit.DAY
|
||||
)
|
||||
|
||||
if (needsUpdate) {
|
||||
anitakume.deleteMany(Document())
|
||||
|
||||
val response = mutableListOf<AnitakumeEntity>().apply {
|
||||
RssParser().getRssChannel(RssUrls.ANITAKUME).toPodcast(this@apply)
|
||||
}
|
||||
|
||||
val documentsToInsert = parseDataToDocuments(response, AnitakumeEntity.serializer())
|
||||
if (documentsToInsert.isNotEmpty()) anitakume.insertMany(documentsToInsert)
|
||||
timers.update(Collections.ANITAKUME)
|
||||
|
||||
val elements = documentsToInsert.map { documentToAnitakumeEntity(it) }
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(elements))
|
||||
} else {
|
||||
val animes = anitakume
|
||||
.find()
|
||||
.toList()
|
||||
|
||||
val elements = animes.map { documentToNewsEntity(it) }
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(elements))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.jeluchu.features.gallery.mappers
|
||||
|
||||
import com.jeluchu.core.extensions.getBooleanSafe
|
||||
import com.jeluchu.core.extensions.getDocumentSafe
|
||||
import com.jeluchu.core.extensions.getFloatSafe
|
||||
import com.jeluchu.core.extensions.getIntSafe
|
||||
import com.jeluchu.core.extensions.getListSafe
|
||||
import com.jeluchu.core.extensions.getStringSafe
|
||||
import com.jeluchu.core.extensions.parseRssDate
|
||||
import com.jeluchu.features.anime.mappers.documentToVideoPromo
|
||||
import com.jeluchu.features.anime.models.anime.VideoPromo
|
||||
import com.jeluchu.features.gallery.models.PostsResponse
|
||||
import com.jeluchu.features.gallery.models.ProcessedPost
|
||||
import com.jeluchu.features.news.models.NewEntity
|
||||
import com.prof18.rssparser.model.RssChannel
|
||||
import org.bson.Document
|
||||
|
||||
fun PostsResponse.Post.toProcessedPost(page: Int): ProcessedPost {
|
||||
val imageUrl = "https://oimages.anime-pictures.net/${md5.take(3)}/$md5$ext"
|
||||
|
||||
return ProcessedPost(
|
||||
id = id,
|
||||
image = imageUrl,
|
||||
width = width,
|
||||
height = height,
|
||||
pubtime = pubtime,
|
||||
size = size,
|
||||
erotics = erotics,
|
||||
spoiler = spoiler,
|
||||
haveAlpha = haveAlpha,
|
||||
page = page
|
||||
)
|
||||
}
|
||||
|
||||
fun PostsResponse.Post.toProcessedPostQuery(page: Int, query: String): ProcessedPost {
|
||||
val imageUrl = "https://oimages.anime-pictures.net/${md5.take(3)}/$md5$ext"
|
||||
|
||||
return ProcessedPost(
|
||||
id = id,
|
||||
image = imageUrl,
|
||||
width = width,
|
||||
height = height,
|
||||
pubtime = pubtime,
|
||||
size = size,
|
||||
erotics = erotics,
|
||||
spoiler = spoiler,
|
||||
haveAlpha = haveAlpha,
|
||||
page = page,
|
||||
query = query
|
||||
)
|
||||
}
|
||||
|
||||
fun documentToProcessedPost(doc: Document) = ProcessedPost(
|
||||
id = doc.getIntSafe("id"),
|
||||
image = doc.getStringSafe("image"),
|
||||
width = doc.getIntSafe("width"),
|
||||
height = doc.getIntSafe("height"),
|
||||
pubtime = doc.getStringSafe("pubtime"),
|
||||
size = doc.getIntSafe("size"),
|
||||
erotics = doc.getIntSafe("erotics"),
|
||||
spoiler = doc.getBooleanSafe("spoiler"),
|
||||
haveAlpha = doc.getBooleanSafe("haveAlpha")
|
||||
)
|
@ -0,0 +1,97 @@
|
||||
package com.jeluchu.features.gallery.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class PostsResponse(
|
||||
@SerialName("exclusive_tag")
|
||||
val exclusiveTag: String? = null,
|
||||
|
||||
@SerialName("posts_per_page")
|
||||
val postsPerPage: Int = 0,
|
||||
|
||||
@SerialName("response_posts_count")
|
||||
val responsePostsCount: Int = 0,
|
||||
|
||||
@SerialName("page_number")
|
||||
val pageNumber: Int = 0,
|
||||
|
||||
@SerialName("posts")
|
||||
val posts: List<Post> = emptyList(),
|
||||
|
||||
@SerialName("posts_count")
|
||||
val postsCount: Int = 0,
|
||||
|
||||
@SerialName("max_pages")
|
||||
val maxPages: Int = 0
|
||||
) {
|
||||
@Serializable
|
||||
data class Post(
|
||||
@SerialName("id")
|
||||
val id: Int = 0,
|
||||
|
||||
@SerialName("md5")
|
||||
val md5: String = "",
|
||||
|
||||
@SerialName("md5_pixels")
|
||||
val md5Pixels: String = "",
|
||||
|
||||
@SerialName("width")
|
||||
val width: Int = 0,
|
||||
|
||||
@SerialName("height")
|
||||
val height: Int = 0,
|
||||
|
||||
@SerialName("pubtime")
|
||||
val pubtime: String = "",
|
||||
|
||||
@SerialName("datetime")
|
||||
val datetime: String = "",
|
||||
|
||||
@SerialName("score")
|
||||
val score: Int = 0,
|
||||
|
||||
@SerialName("score_number")
|
||||
val scoreNumber: Int = 0,
|
||||
|
||||
@SerialName("size")
|
||||
val size: Int = 0,
|
||||
|
||||
@SerialName("download_count")
|
||||
val downloadCount: Int = 0,
|
||||
|
||||
@SerialName("erotics")
|
||||
val erotics: Int = 0,
|
||||
|
||||
@SerialName("color")
|
||||
val color: List<Int> = emptyList(),
|
||||
|
||||
@SerialName("ext")
|
||||
val ext: String = "",
|
||||
|
||||
@SerialName("status")
|
||||
val status: Int = 0,
|
||||
|
||||
@SerialName("status_type")
|
||||
val statusType: Int = 0,
|
||||
|
||||
@SerialName("redirect_id")
|
||||
val redirectId: String? = null,
|
||||
|
||||
@SerialName("spoiler")
|
||||
val spoiler: Boolean = false,
|
||||
|
||||
@SerialName("have_alpha")
|
||||
val haveAlpha: Boolean = false,
|
||||
|
||||
@SerialName("tags_count")
|
||||
val tagsCount: Int = 0,
|
||||
|
||||
@SerialName("artefacts_degree")
|
||||
val artefactsDegree: Double = 0.0,
|
||||
|
||||
@SerialName("smooth_degree")
|
||||
val smoothDegree: Double = 0.0
|
||||
)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.jeluchu.features.gallery.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ProcessedPost(
|
||||
@SerialName("id")
|
||||
val id: Int,
|
||||
|
||||
@SerialName("image")
|
||||
val image: String,
|
||||
|
||||
@SerialName("width")
|
||||
val width: Int,
|
||||
|
||||
@SerialName("height")
|
||||
val height: Int,
|
||||
|
||||
@SerialName("pubtime")
|
||||
val pubtime: String,
|
||||
|
||||
@SerialName("size")
|
||||
val size: Int,
|
||||
|
||||
@SerialName("erotics")
|
||||
val erotics: Int,
|
||||
|
||||
@SerialName("spoiler")
|
||||
val spoiler: Boolean,
|
||||
|
||||
@SerialName("have_alpha")
|
||||
val haveAlpha: Boolean,
|
||||
|
||||
@SerialName("page")
|
||||
val page: Int = 0,
|
||||
|
||||
@SerialName("query")
|
||||
val query: String = "0"
|
||||
)
|
@ -0,0 +1,19 @@
|
||||
package com.jeluchu.features.gallery.routes
|
||||
|
||||
import com.jeluchu.core.extensions.getToJson
|
||||
import com.jeluchu.core.utils.Routes
|
||||
import com.jeluchu.features.gallery.services.GalleryService
|
||||
import com.jeluchu.features.news.services.NewsService
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import io.ktor.server.routing.*
|
||||
|
||||
fun Route.galleryEndpoints(
|
||||
mongoDatabase: MongoDatabase,
|
||||
service: GalleryService = GalleryService(mongoDatabase)
|
||||
) = route(Routes.GALLERY) {
|
||||
route(Routes.LAST_POST) {
|
||||
getToJson { service.getLastPosts(call) }
|
||||
}
|
||||
|
||||
getToJson { service.getQueryImages(call) }
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
package com.jeluchu.features.gallery.services
|
||||
|
||||
import com.jeluchu.core.connection.RestClient
|
||||
import com.jeluchu.core.enums.TimeUnit
|
||||
import com.jeluchu.core.extensions.needsUpdate
|
||||
import com.jeluchu.core.extensions.update
|
||||
import com.jeluchu.core.messages.ErrorMessages
|
||||
import com.jeluchu.core.models.ErrorResponse
|
||||
import com.jeluchu.core.models.PaginationResponse
|
||||
import com.jeluchu.core.utils.BaseUrls
|
||||
import com.jeluchu.core.utils.Collections
|
||||
import com.jeluchu.core.utils.Endpoints
|
||||
import com.jeluchu.core.utils.parseDataToDocuments
|
||||
import com.jeluchu.features.gallery.mappers.documentToProcessedPost
|
||||
import com.jeluchu.features.gallery.mappers.toProcessedPost
|
||||
import com.jeluchu.features.gallery.mappers.toProcessedPostQuery
|
||||
import com.jeluchu.features.gallery.models.PostsResponse
|
||||
import com.jeluchu.features.gallery.models.ProcessedPost
|
||||
import com.mongodb.client.MongoDatabase
|
||||
import com.mongodb.client.model.Filters
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okio.IOException
|
||||
import java.net.URLEncoder
|
||||
|
||||
class GalleryService(
|
||||
database: MongoDatabase
|
||||
) {
|
||||
private val timers = database.getCollection(Collections.TIMERS)
|
||||
private val queryPosts = database.getCollection(Collections.ANIME_PICTURES_QUERY)
|
||||
private val recentPosts = database.getCollection(Collections.ANIME_PICTURES_RECENT)
|
||||
|
||||
suspend fun getLastPosts(call: RoutingCall) {
|
||||
val size = 80
|
||||
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
|
||||
val timerKey = "${Collections.ANIME_PICTURES_RECENT}_${page}"
|
||||
val skipCount = (page - 1) * size
|
||||
|
||||
val needsUpdate = timers.needsUpdate(
|
||||
amount = 30,
|
||||
key = timerKey,
|
||||
unit = TimeUnit.DAY
|
||||
)
|
||||
|
||||
if (needsUpdate) {
|
||||
recentPosts.deleteMany(Filters.and(Filters.eq("page", page)))
|
||||
val response = RestClient.request(
|
||||
BaseUrls.ANIME_PICTURES + Endpoints.POSTS + "?page=$page",
|
||||
PostsResponse.serializer()
|
||||
).posts.map { it.toProcessedPost(page) }
|
||||
|
||||
val documentsToInsert = parseDataToDocuments(response, ProcessedPost.serializer())
|
||||
if (documentsToInsert.isNotEmpty()) recentPosts.insertMany(documentsToInsert)
|
||||
timers.update(timerKey)
|
||||
|
||||
val elements = documentsToInsert.map { documentToProcessedPost(it) }
|
||||
|
||||
val paginationResponse = PaginationResponse(
|
||||
page = page,
|
||||
size = size,
|
||||
data = elements
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
|
||||
} else {
|
||||
val posts = recentPosts
|
||||
.find(Filters.and(Filters.eq("page", page)))
|
||||
.skip(skipCount)
|
||||
.limit(size)
|
||||
.toList()
|
||||
|
||||
val elements = posts.map { documentToProcessedPost(it) }
|
||||
val response = PaginationResponse(
|
||||
page = page,
|
||||
size = size,
|
||||
data = elements
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getQueryImages(call: RoutingCall) {
|
||||
val size = 80
|
||||
val query = call.request.queryParameters["query"].orEmpty()
|
||||
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 0
|
||||
val timerKey = "${Collections.ANIME_PICTURES_QUERY}_${query}_${page}"
|
||||
val skipCount = (page - 1) * size
|
||||
val fixedPage = page + 1
|
||||
|
||||
val needsUpdate = timers.needsUpdate(
|
||||
amount = 30,
|
||||
key = timerKey,
|
||||
unit = TimeUnit.DAY
|
||||
)
|
||||
|
||||
if (needsUpdate) {
|
||||
queryPosts.deleteMany(Filters.and(Filters.eq("page", fixedPage)))
|
||||
|
||||
try {
|
||||
val response = RestClient.request(
|
||||
BaseUrls.ANIME_PICTURES + Endpoints.POSTS + "?search_tag=${URLEncoder.encode(query, "UTF-8")}" + "&lang=en&type=json_v3" + "&page=$page",
|
||||
PostsResponse.serializer()
|
||||
).posts.map { it.toProcessedPostQuery(fixedPage, query) }
|
||||
|
||||
val documentsToInsert = parseDataToDocuments(response, ProcessedPost.serializer())
|
||||
if (documentsToInsert.isNotEmpty()) queryPosts.insertMany(documentsToInsert)
|
||||
timers.update(timerKey)
|
||||
|
||||
val elements = documentsToInsert.map { documentToProcessedPost(it) }
|
||||
|
||||
val paginationResponse = PaginationResponse(
|
||||
page = fixedPage,
|
||||
size = size,
|
||||
data = elements
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
|
||||
} catch (e: IOException) {
|
||||
val paginationResponse = PaginationResponse(
|
||||
page = fixedPage,
|
||||
size = size,
|
||||
data = emptyList<ProcessedPost>()
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(paginationResponse))
|
||||
}
|
||||
} else {
|
||||
val posts = queryPosts
|
||||
.find(Filters.and(Filters.eq("page", fixedPage)))
|
||||
.skip(skipCount)
|
||||
.limit(size)
|
||||
.toList()
|
||||
|
||||
val elements = posts.map { documentToProcessedPost(it) }
|
||||
val response = PaginationResponse(
|
||||
page = fixedPage,
|
||||
size = size,
|
||||
data = elements
|
||||
)
|
||||
|
||||
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue