@ -2,9 +2,11 @@ package eu.kanade.tachiyomi.animeextension.ar.mycima
import android.app.Application
import android.app.Application
import android.content.SharedPreferences
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.AppInfo
import eu.kanade.tachiyomi.animeextension.ar.mycima.extractors.GoVadExtractor
import eu.kanade.tachiyomi.animeextension.ar.mycima.extractors.UQLoadExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
@ -14,7 +16,10 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Headers.Companion.toHead ers
import kotlinx.coroutines.Dispatch ers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Request
import okhttp3.Response
import okhttp3.Response
@ -28,10 +33,6 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val name = " MY Cima "
override val name = " MY Cima "
private val defaultBaseUrl = " https://wecima.co "
private val baseUrlPref = " overrideBaseUrl_v ${AppInfo.getVersionName()} "
override val baseUrl by lazy { getPrefBaseUrl ( ) }
override val baseUrl by lazy { getPrefBaseUrl ( ) }
override val lang = " ar "
override val lang = " ar "
@ -123,13 +124,35 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun videoListParse ( response : Response ) : List < Video > {
override fun videoListParse ( response : Response ) : List < Video > {
val document = response . asJsoup ( )
val document = response . asJsoup ( )
val iframe = document . selectFirst ( " iframe " ) !! . attr ( " data-lazy-src " )
return document . select ( " ul.WatchServersList li btn " ) . parallelMap {
val referer = response . request . url . encodedPath
val frameURL = it . attr ( " data-url " )
val newHeaderList = mutableMapOf ( Pair ( " referer " , baseUrl + referer ) )
runCatching {
headers . forEach { newHeaderList [ it . first ] = it . second }
if ( it . parent ( ) ?. hasClass ( " MyCimaServer " ) = = true )
val iframeResponse = client . newCall ( GET ( iframe , newHeaderList . toHeaders ( ) ) )
{
. execute ( ) . asJsoup ( )
val referer = response . request . url . encodedPath
return videosFromElement ( iframeResponse . selectFirst ( videoListSelector ( ) ) !! )
val newHeader = headers . newBuilder ( ) . add ( " referer " , baseUrl + referer ) . build ( )
val iframeResponse = client . newCall ( GET ( frameURL , newHeader ) ) . execute ( ) . asJsoup ( )
videosFromElement ( iframeResponse . selectFirst ( videoListSelector ( ) ) !! )
} else {
extractVideos ( frameURL )
}
} . getOrElse { emptyList ( ) }
} . flatten ( )
}
private fun extractVideos ( url : String ) : List < Video > {
return when {
GOVAD_REGEX . containsMatchIn ( url ) -> {
val finalUrl = GOVAD_REGEX . find ( url ) !! . groupValues [ 0 ]
val urlHost = GOVAD_REGEX . find ( url ) !! . groupValues [ 1 ]
GoVadExtractor ( client ) . videosFromUrl ( " https://www. $finalUrl .html " , urlHost )
}
UQLOAD_REGEX . containsMatchIn ( url ) -> {
val finalUrl = UQLOAD_REGEX . find ( url ) !! . groupValues [ 0 ]
UQLoadExtractor ( client ) . videosFromUrl ( " https://www. $finalUrl .html " )
}
else -> null
} ?: emptyList ( )
}
}
override fun videoListSelector ( ) = " body "
override fun videoListSelector ( ) = " body "
@ -139,7 +162,6 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val script = element . select ( " script " )
val script = element . select ( " script " )
. firstOrNull { it . data ( ) . contains ( " player.qualityselector({ " ) }
. firstOrNull { it . data ( ) . contains ( " player.qualityselector({ " ) }
if ( script != null ) {
if ( script != null ) {
val scriptV = element . select ( " script:containsData(source) " )
val data = element . data ( ) . substringAfter ( " sources: [ " ) . substringBefore ( " ], " )
val data = element . data ( ) . substringAfter ( " sources: [ " ) . substringBefore ( " ], " )
val sources = data . split ( " format: ' " ) . drop ( 1 )
val sources = data . split ( " format: ' " ) . drop ( 1 )
for ( source in sources ) {
for ( source in sources ) {
@ -155,21 +177,10 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
override fun List < Video > . sort ( ) : List < Video > {
override fun List < Video > . sort ( ) : List < Video > {
val quality = preferences . getString ( " preferred_quality " , null )
val quality = preferences . getString ( PREF _QUALITY _KEY , PREF _QUALITY _DEFAULT ) !!
if ( quality != null ) {
return sortedWith (
val newList = mutableListOf < Video > ( )
compareBy { it . quality . contains ( quality ) } ,
var p ref err ed = 0
) . rev ers ed( )
for ( video in this ) {
if ( video . quality . contains ( quality ) ) {
newList . add ( preferred , video )
preferred ++
} else {
newList . add ( video )
}
}
return newList
}
return this
}
}
override fun videoFromElement ( element : Element ) = throw Exception ( " not used " )
override fun videoFromElement ( element : Element ) = throw Exception ( " not used " )
@ -196,7 +207,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
when ( filter ) {
when ( filter ) {
is SearchCategoryList -> {
is SearchCategoryList -> {
val catQ = getSearchCategoryList ( ) [ filter . state ] . query
val catQ = getSearchCategoryList ( ) [ filter . state ] . query
val catUrl = " $baseUrl /search/ $query / $catQ $page "
val catUrl = " $baseUrl /search/ $query / " + if ( catQ == " page/ " && page == 1 ) " " else " $catQ $page "
return GET ( catUrl , headers )
return GET ( catUrl , headers )
}
}
else -> { }
else -> { }
@ -208,7 +219,7 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
is CategoryList -> {
is CategoryList -> {
if ( filter . state > 0 ) {
if ( filter . state > 0 ) {
val catQ = getCategoryList ( ) [ filter . state ] . query
val catQ = getCategoryList ( ) [ filter . state ] . query
val catUrl = " $baseUrl /category/ $catQ /page/ $page "
val catUrl = " $baseUrl / $catQ /page/ $page / "
return GET ( catUrl , headers )
return GET ( catUrl , headers )
}
}
}
}
@ -224,12 +235,19 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun animeDetailsParse ( document : Document ) : SAnime {
override fun animeDetailsParse ( document : Document ) : SAnime {
val anime = SAnime . create ( )
val anime = SAnime . create ( )
anime . title = document . select ( " div.Title--Content--Single-begin > h1 " ) . text ( )
anime . title = when {
document . selectFirst ( " li:contains(المسلسل) p " ) != null -> {
document . select ( " li:contains(المسلسل) p " ) . text ( )
}
else -> {
document . select ( " div.Title--Content--Single-begin > h1 " ) . text ( ) . substringBefore ( " ( " )
}
}
anime . genre = document . select ( " li:contains(التصنيف) > p > a, li:contains(النوع) > p > a " ) . joinToString ( " , " ) { it . text ( ) }
anime . genre = document . select ( " li:contains(التصنيف) > p > a, li:contains(النوع) > p > a " ) . joinToString ( " , " ) { it . text ( ) }
anime . description = document . select ( " div.AsideContext > div.StoryMovieContent, div.PostItemContent " ) . text ( )
anime . description = document . select ( " div.AsideContext > div.StoryMovieContent " ) . text ( )
anime . author = document . select ( " li:contains(شركات الإنتاج) > p > a " ) . joinToString ( " , " ) { it . text ( ) }
anime . author = document . select ( " li:contains(شركات الإنتاج) > p > a " ) . joinToString ( " , " ) { it . text ( ) }
// add alternative name to anime description
// add alternative name to anime description
document . select ( " li:contains( بالعربي) > p, li:contains(معروف) > p " ) . text ( ) ? .let {
document . select ( " li:contains( بالعربي) > p, li:contains(معروف) > p " ) . text ( ) .let {
if ( it . isEmpty ( ) . not ( ) ) {
if ( it . isEmpty ( ) . not ( ) ) {
anime . description += when {
anime . description += when {
anime . description !! . isEmpty ( ) -> " Alternative Name: $it "
anime . description !! . isEmpty ( ) -> " Alternative Name: $it "
@ -277,45 +295,51 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
} . toTypedArray ( )
} . toTypedArray ( )
private fun getSearchCategoryList ( ) = listOf (
private fun getSearchCategoryList ( ) = listOf (
CatUnit ( " فيلم " , " / page/" ) ,
CatUnit ( " فيلم " , " page/ " ) ,
CatUnit ( " مسلسل " , " list/series/?page_number= " ) ,
CatUnit ( " مسلسل " , " list/series/?page_number= " ) ,
CatUnit ( " انمى " , " list/anime/?page_number= " ) ,
CatUnit ( " انمى " , " list/anime/?page_number= " ) ,
CatUnit ( " برنامج " , " list/tv/?page_number= " ) ,
CatUnit ( " برنامج " , " list/tv/?page_number= " ) ,
)
)
private fun getCategoryList ( ) = listOf (
private fun getCategoryList ( ) = listOf (
CatUnit ( " اختر " , " " ) ,
CatUnit ( " اختر " , " " ) ,
CatUnit ( " جميع الافلام " , " ا فلام" ) ,
CatUnit ( " جميع الافلام " , " category/أ فلام/ " ) ,
CatUnit ( " افلام اجنبى " , " ا فلام/10-movies-english-افلام-اجنبي/ " ) ,
CatUnit ( " افلام اجنبى " , " category/أ فلام/10-movies-english-افلام-اجنبي" ) ,
CatUnit ( " افلام عربى " , " افلام/6 -arabic-movies-افلام-عربي/ " ) ,
CatUnit ( " افلام عربى " , " category/أفلام/افلام-عربي -arabic-movies" ) ,
CatUnit ( " افلام هندى " , " ا فلام/افلام-هندي-indian-movies/ " ) ,
CatUnit ( " افلام هندى " , " category/أ فلام/افلام-هندي-indian-movies" ) ,
CatUnit ( " افلام تركى " , " ا فلام/افلام-تركى-turkish-films/ " ) ,
CatUnit ( " افلام تركى " , " category/أ فلام/افلام-تركى-turkish-films" ) ,
CatUnit ( " افلام وثائقية " , " ا فلام/افلام-وثائقية-documentary-films/ " ) ,
CatUnit ( " افلام وثائقية " , " category/أ فلام/افلام-وثائقية-documentary-films" ) ,
CatUnit ( " افلام انمي " , " افلام-كرتون/ " ) ,
CatUnit ( " افلام انمي " , " category/ افلام-كرتون" ) ,
CatUnit ( " سلاسل افلام " , " ا فلام/10-movies-english-افلام-اجنبي/سلاسل-الافلام-الكاملة-full-pack/ " ) ,
CatUnit ( " سلاسل افلام " , " category/أ فلام/10-movies-english-افلام-اجنبي/سلاسل-الافلام-الكاملة-full-pack" ) ,
CatUnit ( " مسلسلات " , " مسلسلات " ) ,
CatUnit ( " مسلسلات " , " category/ مسلسلات" ) ,
CatUnit ( " مسلسلات اجنبى " , " مسلسلات/5-series-english-مسلسلات-اجنبي/ " ) ,
CatUnit ( " مسلسلات اجنبى " , " category/ مسلسلات/5-series-english-مسلسلات-اجنبي" ) ,
CatUnit ( " مسلسلات عربى " , " مسلسلات/13-مسلسلات-عربيه-arabic-series/ " ) ,
CatUnit ( " مسلسلات عربى " , " category/مسلسلات/5-series-english-مسلسلات-اجنبي " ) ,
CatUnit ( " مسلسلات هندى " , " مسلسلات/9-series-indian-مسلسلات-هندية/ " ) ,
CatUnit ( " مسلسلات هندى " , " category/ مسلسلات/9-series-indian-مسلسلات-هندية" ) ,
CatUnit ( " مسلسلات اسيوى " , " مسلسلات/مسلسلات-اسيوية/ " ) ,
CatUnit ( " مسلسلات اسيوى " , " category/ مسلسلات/مسلسلات-اسيوية" ) ,
CatUnit ( " مسلسلات تركى " , " مسلسلات/8-مسلسلات-تركية-turkish-series/ " ) ,
CatUnit ( " مسلسلات تركى " , " category/ مسلسلات/8-مسلسلات-تركية-turkish-series" ) ,
CatUnit ( " مسلسلات وثائقية " , " مسلسلات/مسلسلات-وثائقية-documentary-series/ " ) ,
CatUnit ( " مسلسلات وثائقية " , " category/ مسلسلات/مسلسلات-وثائقية-documentary-series" ) ,
CatUnit ( " مسلسلات انمي " , " مسلسلات-كرتون/ " ) ,
CatUnit ( " مسلسلات انمي " , " category/ مسلسلات-كرتون" ) ,
CatUnit ( " NETFLIX " , " production/netflix " ) ,
CatUnit ( " WARNER BROS " , " production/warner-bros " ) ,
CatUnit ( " LIONSGATE " , " production/lionsgate " ) ,
CatUnit ( " DISNEY " , " production/walt-disney-pictures " ) ,
CatUnit ( " COLUMBIA " , " production/columbia-pictures " ) ,
)
)
// preferred quality settings
// preferred quality settings
override fun setupPreferenceScreen ( screen : PreferenceScreen ) {
override fun setupPreferenceScreen ( screen : PreferenceScreen ) {
val baseUrlPref = androidx . preference . EditTextPreference ( screen . context ) . apply {
val baseUrlPref = androidx . preference . EditTextPreference ( screen . context ) . apply {
key = BASE _URL _PREF _TITLE
key = PREF _ BASE_URL _KEY
title = BASE _URL _PREF _ TITLE
title = PREF _ BASE_URL _TITLE
summary = BASE _URL _PREF _SUMMARY
summary = getPrefBaseUrl ( )
this . setDefaultValue ( defaultBaseUrl )
this . setDefaultValue ( PREF _BASE _URL _DEFAULT )
dialogTitle = BASE _URL _PREF _TITLE
dialogTitle = PREF _ BASE_URL _DIALOG _TITLE
dialogMessage = " Default: $defaultBaseUrl "
dialogMessage = PREF _BASE _URL _DIALOG _MESSAGE
setOnPreferenceChangeListener { _ , newValue ->
setOnPreferenceChangeListener { _ , newValue ->
try {
try {
val res = preferences . edit ( ) . putString ( baseUrlPref , newValue as String ) . commit ( )
val res = preferences . edit ( ) . putString ( PREF _BASE _URL _KEY , newValue as String ) . commit ( )
Toast . makeText ( screen . context , " Restart Aniyomi to apply changes " , Toast . LENGTH _LONG ) . show ( )
res
res
} catch ( e : Exception ) {
} catch ( e : Exception ) {
e . printStackTrace ( )
e . printStackTrace ( )
@ -324,13 +348,12 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}
}
}
val videoQualityPref = ListPreference ( screen . context ) . apply {
val videoQualityPref = ListPreference ( screen . context ) . apply {
key = " preferred_quality "
key = PREF _QUALITY _KEY
title = " Preferred quality "
title = PREF _QUALITY _TITLE
entries = arrayOf ( " 1080p " , " 720p " , " 480p " , " 360p " , " 240p " )
entries = PREF _QUALITY _ENTRIES
entryValues = arrayOf ( " 1080 " , " 720 " , " 480 ", " 360 " , " 240 " )
entryValues = PREF_QUALITY_ENTRIES . map { it . replace ( " p " , " " ) } . toTypedArray ( )
setDefaultValue ( " 1080 " )
setDefaultValue ( PREF _QUALITY _DEFAULT )
summary = " %s "
summary = " %s "
setOnPreferenceChangeListener { _ , newValue ->
setOnPreferenceChangeListener { _ , newValue ->
val selected = newValue as String
val selected = newValue as String
val index = findIndexOfValue ( selected )
val index = findIndexOfValue ( selected )
@ -342,10 +365,26 @@ class MyCima : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
screen . addPreference ( videoQualityPref )
screen . addPreference ( videoQualityPref )
}
}
private fun getPrefBaseUrl ( ) : String = preferences . getString ( baseUrlPref , defaultBaseUrl ) !!
private fun getPrefBaseUrl ( ) : String = preferences . getString ( PREF _BASE _URL _KEY , PREF _BASE _URL _DEFAULT ) !!
// ============================= Utilities ===================================
private inline fun < A , B > Iterable < A > . parallelMap ( crossinline f : suspend ( A ) -> B ) : List < B > =
runBlocking {
map { async ( Dispatchers . Default ) { f ( it ) } } . awaitAll ( )
}
companion object {
companion object {
private const val BASE _URL _PREF _TITLE = " Override BaseUrl "
private const val PREF _QUALITY _KEY = " preferred_quality "
private const val BASE _URL _PREF _SUMMARY = " Override default domain with a different one "
private const val PREF _QUALITY _TITLE = " Preferred quality "
private const val PREF _QUALITY _DEFAULT = " 1080 "
private val PREF _QUALITY _ENTRIES = arrayOf ( " 1080p " , " 720p " , " 480p " , " 360p " , " 240p " )
private const val PREF _BASE _URL _DEFAULT = " https://cdn3.wecima.watch "
private const val PREF _BASE _URL _KEY = " default_domain "
private const val PREF _BASE _URL _TITLE = " Enter default domain "
private const val PREF _BASE _URL _DIALOG _TITLE = " Default domain "
private const val PREF _BASE _URL _DIALOG _MESSAGE = " You can change the site domain from here "
private val GOVAD _REGEX = Regex ( " (v[aie]d[bp][aoe]?m|myvii?d|govad|segavid|v[aei]{1,2}dshar[er]?) \\ .(?:com|net|org|xyz)(?:: \\ d+)?/(?:embed[/-])?([A-Za-z0-9]+) " )
private val UQLOAD _REGEX = Regex ( " (uqload \\ .[ic]om?)/(?:embed-)?([0-9a-zA-Z]+) " )
}
}
}
}