All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.providers.YtsProvider.kt Maven / Gradle / Ivy

The newest version!
package torrentsearch.providers

import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.ResponseException
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.http.takeFrom
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import torrentsearch.models.Category
import torrentsearch.models.ProviderResult
import torrentsearch.models.SearchParam
import torrentsearch.models.TorrentDescription
import torrentsearch.models.TorrentQuery

internal class YtsProvider(
    private val httpClient: HttpClient,
    enabled: Boolean = true,
) : BaseTorrentProvider(enabled) {

    override val name: String = "yts"
    override val baseUrl: String = "https://yts.mx/api/v2/"
    override val tokenPath: String = ""
    override val searchPath: String = "list_movies.json?sort_by=date_added"
    private val imdbIdPath: String = "movie_details.json?sort_by=date_added"
    override val categories: Map = mapOf(Category.MOVIES to "")
    override val searchParams: Map = mapOf(
        SearchParam.QUERY to "query_term",
        SearchParam.LIMIT to "limit",
        SearchParam.IMDB_ID to "imdb_id",
        SearchParam.PAGE to "page",
    )

    override suspend fun search(query: TorrentQuery): ProviderResult {
        val queryString = query.content
        val imdbId = query.imdbId
        if (queryString.isNullOrBlank() && imdbId.isNullOrBlank()) {
            return ProviderResult.Error.InvalidQueryError(name, "Yts requires imdbId or a query content string.")
        }

        val response = try {
            httpClient.get {
                url {
                    takeFrom(baseUrl)
                    if (!queryString.isNullOrBlank()) {
                        takeFrom(searchPath)
                        parameter(searchParams.getValue(SearchParam.QUERY), queryString)
                    }
                    if (!imdbId.isNullOrBlank()) {
                        takeFrom(imdbIdPath)
                        parameter(searchParams.getValue(SearchParam.IMDB_ID), imdbId)
                    }
                    if (query.limit > -1) {
                        parameter(searchParams.getValue(SearchParam.LIMIT), query.limit)
                    }
                    parameter(searchParams.getValue(SearchParam.PAGE), query.page)
                }
            }
        } catch (e: ResponseException) {
            return ProviderResult.Error.RequestError(name, e.response.status, e.response.bodyAsText())
        }

        return if (response.status == HttpStatusCode.OK) {
            val ytsResponse = response.body()
            val movies = if (ytsResponse.data.movie == null) {
                ytsResponse.data.movies
            } else {
                listOf(ytsResponse.data.movie)
            }.filter { movie -> movie.id > 0 }

            val torrentDescriptions = movies.flatMap { movie ->
                movie.torrents.map { torrent ->
                    TorrentDescription(
                        provider = name,
                        magnetUrl = formatMagnet(torrent.hash, movie.title),
                        hash = torrent.hash,
                        seeds = torrent.seeds,
                        peers = torrent.peers,
                        title = "${movie.title} ${torrent.quality} ${torrent.type}",
                        size = torrent.size,
                        infoUrl = torrent.url,
                        imdbId = movie.imdbId,
                    )
                }
            }
            ProviderResult.Success(
                name,
                torrentDescriptions,
                totalTorrents = if (movies.isEmpty()) 0 else ytsResponse.data.movieCount,
                pageSize = ytsResponse.data.limit,
                page = ytsResponse.data.page,
            )
        } else {
            ProviderResult.Error.RequestError(name, response.status, response.bodyAsText())
        }
    }

    @Serializable
    internal data class YtsResponse(val data: YtsData)

    @Serializable
    internal data class YtsData(
        val limit: Int = 1,
        @SerialName("page_number")
        val page: Int = 1,
        val movies: List = emptyList(),
        val movie: YtsMovie? = null,
        @SerialName("movie_count")
        val movieCount: Int = if (movie != null) 1 else movies.size,
    )

    @Serializable
    internal data class YtsMovie(
        val id: Int,
        @SerialName("title_long")
        val title: String,
        @SerialName("imdb_code")
        val imdbId: String?,
        val torrents: List = emptyList(),
    )

    @Serializable
    internal data class YtsTorrent(
        val url: String,
        val hash: String,
        val seeds: Int,
        val peers: Int,
        val quality: String,
        val type: String,
        @SerialName("size_bytes")
        val size: Long,
    )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy