2022-10-16 17:44:52 +00:00
package com.lagradost
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
class Ask4MovieProvider : MainAPI ( ) {
override var mainUrl = " https://ask4movie.mx "
override var name = " Ask4Movie "
override val supportedTypes = setOf ( TvType . TvSeries , TvType . Movie , TvType . AnimeMovie )
override val hasMainPage = true
private fun Element . toSearchResponse ( ) : MovieSearchResponse {
// style="background-image: url(https://ask4movie.me/wp-content/uploads/2022/08/Your-Name.-2016-cover.jpg)"
val posterRegex = Regex ( """ url\((.*?)\) """ )
val poster = posterRegex . find ( this . attr ( " style " ) ) ?. groupValues ?. get ( 1 )
val a = this . select ( " a " )
val href = a . attr ( " href " )
val title = a . text ( ) . trim ( )
// Title (2022) -> 2022
val year =
Regex ( """ \((\d{4})\) $ """ ) . find ( title ) ?. groupValues ?. getOrNull ( 1 ) ?. toIntOrNull ( )
return MovieSearchResponse (
title ,
href ,
this @Ask4MovieProvider . name ,
TvType . Movie ,
poster ,
year ,
null ,
null ,
null
)
}
// Used in movies/single seasons to get recommendations
private fun Element . articleToSearchResponse ( ) : MovieSearchResponse {
val poster = this . select ( " img " ) . attr ( " src " )
val a = this . select ( " a " )
val href = a . attr ( " href " )
val title = a . attr ( " title " )
// Title (2022) -> 2022
val year =
Regex ( """ \((\d{4})\) $ """ ) . find ( title ) ?. groupValues ?. getOrNull ( 1 ) ?. toIntOrNull ( )
return MovieSearchResponse (
title ,
href ,
this @Ask4MovieProvider . name ,
TvType . Movie ,
poster ,
year ,
null ,
null ,
null
)
}
override suspend fun search ( query : String ) : List < SearchResponse > {
val url = " $mainUrl /?s= $query "
val doc = app . post (
url ,
// data = mapOf("np_asl_data" to "customset[]=post&customset[]=ct_channel&customset[]=post&customset[]=post&customset[]=post&customset[]=post&asl_gen[]=title&asl_gen[]=exact&qtranslate_lang=0&filters_initial=1&filters_changed=0")
) . document
return doc . select ( " div.item " ) . map {
it . toSearchResponse ( )
}
}
private fun getIframe ( html : String ) : String ? {
2023-01-29 22:52:44 +00:00
return Jsoup . parse ( html ) . select ( " iframe " ) . attr ( " data-src " )
2022-10-16 17:44:52 +00:00
}
private suspend fun getEpisodes ( iframe : String ) : List < Episode > {
val playlistDoc = app . get ( iframe ) . document
// S04┋E01
val episodeRegex = Regex ( """ S(\d+).E(\d+) """ )
return playlistDoc . select ( " span.episode " ) . mapNotNull { episode ->
val partialUrl = episode . attr ( " data-url " )
val fullUrl = " https:// ${URI(iframe).rawAuthority} $partialUrl "
val info = episodeRegex . find ( episode . text ( ) )
val seasonIndex = info ?. groupValues ?. getOrNull ( 1 ) ?. toIntOrNull ( )
val episodeIndex = info ?. groupValues ?. getOrNull ( 2 ) ?. toIntOrNull ( )
Episode (
fullUrl ,
episode = episodeIndex ,
season = seasonIndex
)
}
}
override suspend fun getMainPage ( page : Int , request : MainPageRequest ) : HomePageResponse ? {
val document = app . get ( mainUrl ) . document
val rows = document . select ( " div.row " )
val posterRegex = Regex ( """ url\((.*?)\) """ )
val mappedRows = rows . mapNotNull {
2022-10-16 18:45:17 +00:00
var isHorizontal = true
2022-10-16 17:44:52 +00:00
val items = it . select ( " div.slide-item " ) . map { element ->
val thumb = element . select ( " div.item-thumb " )
val poster = posterRegex . find ( thumb . attr ( " style " ) ) ?. groupValues ?. get ( 1 )
val href = thumb . select ( " a " ) . attr ( " href " )
val title = element . select ( " div.video-short-intro a " ) . text ( )
val year =
Regex ( """ \((\d{4})\) $ """ ) . find ( title ) ?. groupValues ?. getOrNull ( 1 ) ?. toIntOrNull ( )
MovieSearchResponse ( title , href , this . name , TvType . Movie , poster , year )
} . ifEmpty {
2022-10-16 18:45:17 +00:00
isHorizontal = false
2022-10-16 17:44:52 +00:00
it . select ( " div.channel-content.clearfix " ) . map { searchElement ->
searchElement . articleToSearchResponse ( )
}
}
val title = it . select ( " div.title " ) . text ( )
if ( title . contains ( " porn " , true ) && ! settingsForProvider . enableAdult ) return @mapNotNull null
2022-10-16 18:45:17 +00:00
HomePageList ( title , items , isHorizontal )
2022-10-16 17:44:52 +00:00
}
return HomePageResponse ( mappedRows )
}
override suspend fun load ( url : String ) : LoadResponse {
val response = app . get ( url )
val document = response . document
val seasons = document . select ( " div.item " )
val isSingleVideo = ( seasons . isNullOrEmpty ( ) )
val yearRegex = Regex ( """ \((\d{4})\) $ """ )
if ( isSingleVideo ) {
val description = document . select ( " div.custom.video-the-content " ) . text ( ) . trim ( )
val ( title , year ) = document . select ( " a.video-title " ) . text ( ) . let {
it . replace ( yearRegex , " " ) to yearRegex . find ( it ) ?. groupValues ?. get ( 1 ) ?. toIntOrNull ( )
}
val genres =
document . selectFirst ( " div.categories.cactus-info " ) ?. select ( " a " ) ?. map { it . text ( ) }
// This is actually a json with all the data, but I opted to just scrape the html
// Try the json in the future if html turns out bad
val posterRegex = Regex ( """ contentUrl['"].*?(http[^"']*) """ )
val poster = posterRegex . find ( response . text ) ?. groupValues ?. get ( 1 )
val recommendations = document . select ( " div.cactus-sub-wrap article " ) . mapNotNull {
it . articleToSearchResponse ( )
}
val iframe = getIframe ( response . text )
// It can be a season as a single video iframe!
return if ( iframe ?. contains ( " /p/ " ) == true ) {
val episodes = getEpisodes ( iframe )
TvSeriesLoadResponse (
title ,
url ,
this . name ,
TvType . TvSeries ,
episodes ,
poster ,
year ,
recommendations = recommendations ,
tags = genres ,
plot = description
)
} else {
newMovieLoadResponse ( title , url , TvType . Movie , iframe ) {
this . posterUrl = poster
this . tags = genres
this . plot = description
this . year = year
this . recommendations = recommendations
}
}
} else {
val recommendations = document . select ( " div.channel.clearfix " ) . mapNotNull {
it . articleToSearchResponse ( )
}
val descriptionDiv = document . select ( " div.channel-description " )
val description = descriptionDiv . select ( " p " ) . firstOrNull ( ) ?. text ( ) ?. trim ( )
val genres = descriptionDiv . select ( " span.genres " ) . let {
if ( it . isNotEmpty ( ) ) it . text ( ) . split ( " , " )
else descriptionDiv . select ( " p " )
. firstOrNull { element -> element . text ( ) . contains ( " Genre: " ) }
?. text ( ) ?. substringAfter ( " Genre: " ) ?. split ( " , " )
}
val ( title , year ) = document . select ( " h3.channel-name " ) . text ( ) . let {
it . replace ( yearRegex , " " ) to yearRegex . find ( it ) ?. groupValues ?. get ( 1 ) ?. toIntOrNull ( )
}
val poster = document . select ( " div.channel-pic > img " ) . attr ( " src " )
val mappedSeasons = seasons . apmap {
val href = it . select ( " div.top-item > a " ) . attr ( " href " )
val text = app . get ( href ) . text
val iframe = getIframe ( text ) ?: return @apmap emptyList ( )
getEpisodes ( iframe )
} . flatten ( )
return TvSeriesLoadResponse (
title ,
url ,
this . name ,
TvType . TvSeries ,
mappedSeasons ,
poster ,
year ,
recommendations = recommendations ,
tags = genres ,
plot = description
)
}
}
override suspend fun loadLinks (
data : String ,
isCasting : Boolean ,
subtitleCallback : ( SubtitleFile ) -> Unit ,
callback : ( ExtractorLink ) -> Unit
) : Boolean {
loadExtractor ( data , this . mainUrl , subtitleCallback , callback )
return true
}
}