diff --git a/app/build.gradle b/app/build.gradle index 8723e33e..874ee6dc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,11 +11,11 @@ plugins { } android { - compileSdk 31 + compileSdk 32 defaultConfig { applicationId "com.kl3jvi.animity" minSdk 21 - targetSdk 31 + targetSdk 32 versionCode 5 versionName "1.0.3" archivesBaseName = "Animity-v$versionName" @@ -145,6 +145,6 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' - def paging_version = "3.1.0" - implementation "androidx.paging:paging-runtime-ktx:$paging_version" + implementation 'org.apache.commons:commons-text:1.7' + } diff --git a/app/src/main/java/com/kl3jvi/animity/data/model/EpisodeInfo.kt b/app/src/main/java/com/kl3jvi/animity/data/model/EpisodeInfo.kt index 81cfd1f8..c9c4d646 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/model/EpisodeInfo.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/model/EpisodeInfo.kt @@ -6,4 +6,6 @@ import kotlinx.parcelize.Parcelize @Parcelize data class EpisodeInfo( var vidCdnUrl: String? = null, + var nextEpisodeUrl: String? = null, + var previousEpisodeUrl: String? = null ) : Parcelable diff --git a/app/src/main/java/com/kl3jvi/animity/data/network/AnimeApiClient.kt b/app/src/main/java/com/kl3jvi/animity/data/network/AnimeApiClient.kt index 7869323d..c7d29e75 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/network/AnimeApiClient.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/network/AnimeApiClient.kt @@ -54,4 +54,7 @@ class AnimeApiClient @Inject constructor( suspend fun fetchSearchData(header: Map, keyword: String, page: Int) = animeService.fetchSearchData(header, keyword, page) + + suspend fun fetchM3u8PreProcessor(header: Map, url: String) = + animeService.fetchM3u8PreProcessor(header, url) } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/data/network/AnimeService.kt b/app/src/main/java/com/kl3jvi/animity/data/network/AnimeService.kt index 356b61df..a0225c12 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/network/AnimeService.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/network/AnimeService.kt @@ -52,6 +52,13 @@ interface AnimeService { @Url url: String ): ResponseBody + @GET + @Headers("X-Requested-With:XMLHttpRequest") + suspend fun fetchM3u8PreProcessor( + @HeaderMap header: Map, + @Url url: String + ): ResponseBody + @GET(Constants.EPISODE_LOAD_URL) suspend fun fetchEpisodeList( @HeaderMap header: Map, diff --git a/app/src/main/java/com/kl3jvi/animity/data/repository/DetailsRepositoryImpl.kt b/app/src/main/java/com/kl3jvi/animity/data/repository/DetailsRepositoryImpl.kt index 42e177a6..2542469e 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/repository/DetailsRepositoryImpl.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/repository/DetailsRepositoryImpl.kt @@ -6,12 +6,16 @@ import com.kl3jvi.animity.data.model.EpisodeReleaseModel import com.kl3jvi.animity.data.network.AnimeApiClient import com.kl3jvi.animity.domain.repositories.DetailsRepository import com.kl3jvi.animity.utils.parser.HtmlParser +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton +@Suppress("BlockingMethodInNonBlockingContext") class DetailsRepositoryImpl @Inject constructor( - private val apiClient: AnimeApiClient + private val apiClient: AnimeApiClient, + private val ioDispatcher: CoroutineDispatcher ) : DetailsRepository { override val parser: HtmlParser get() = HtmlParser @@ -19,8 +23,10 @@ class DetailsRepositoryImpl @Inject constructor( override suspend fun fetchAnimeInfo( header: Map, episodeUrl: String - ): AnimeInfoModel { - return parser.parseAnimeInfo(apiClient.fetchAnimeInfo(header, episodeUrl).string()) + ): AnimeInfoModel = withContext(ioDispatcher) { + parser.parseAnimeInfo( + apiClient.fetchAnimeInfo(header = header, episodeUrl = episodeUrl).string() + ) } override suspend fun fetchEpisodeList( @@ -28,8 +34,8 @@ class DetailsRepositoryImpl @Inject constructor( id: String, endEpisode: String, alias: String - ): ArrayList { - return parser.fetchEpisodeList( + ): List = withContext(ioDispatcher) { + parser.fetchEpisodeList( apiClient.fetchEpisodeList( header = header, id = id, @@ -40,6 +46,9 @@ class DetailsRepositoryImpl @Inject constructor( } override suspend fun fetchEpisodeTimeRelease(episodeUrl: String): EpisodeReleaseModel = - parser.fetchEpisodeReleaseTime(apiClient.fetchEpisodeTimeRelease(episodeUrl).string()) - + withContext(ioDispatcher) { + parser.fetchEpisodeReleaseTime( + apiClient.fetchEpisodeTimeRelease(episodeUrl = episodeUrl).string() + ) + } } diff --git a/app/src/main/java/com/kl3jvi/animity/data/repository/HomeRepositoryImpl.kt b/app/src/main/java/com/kl3jvi/animity/data/repository/HomeRepositoryImpl.kt index 3ae1405e..cade4322 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/repository/HomeRepositoryImpl.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/repository/HomeRepositoryImpl.kt @@ -1,18 +1,23 @@ package com.kl3jvi.animity.data.repository -import com.kl3jvi.animity.domain.repositories.HomeRepository import com.kl3jvi.animity.data.model.AnimeMetaModel import com.kl3jvi.animity.data.network.AnimeApiClient +import com.kl3jvi.animity.domain.repositories.HomeRepository import com.kl3jvi.animity.utils.Constants.Companion.TYPE_MOVIE import com.kl3jvi.animity.utils.Constants.Companion.TYPE_POPULAR_ANIME import com.kl3jvi.animity.utils.Constants.Companion.TYPE_RECENT_SUB import com.kl3jvi.animity.utils.parser.HtmlParser +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton -class HomeRepositoryImpl @Inject constructor(private val apiClient: AnimeApiClient) : - HomeRepository { +@Suppress("BlockingMethodInNonBlockingContext") +class HomeRepositoryImpl @Inject constructor( + private val apiClient: AnimeApiClient, + private val ioDispatcher: CoroutineDispatcher +) : HomeRepository { override val parser: HtmlParser get() = HtmlParser @@ -20,30 +25,37 @@ class HomeRepositoryImpl @Inject constructor(private val apiClient: AnimeApiClie header: Map, page: Int, type: Int - ): ArrayList { - return parser.parseRecentSubOrDub( - apiClient.fetchRecentSubOrDub(header, page, type).string(), TYPE_RECENT_SUB + ): List = withContext(ioDispatcher) { + parser.parseRecentSubOrDub( + apiClient.fetchRecentSubOrDub(header = header, page = page, type = type).string(), + TYPE_RECENT_SUB ) } override suspend fun fetchPopularFromAjax( header: Map, page: Int - ): ArrayList { - return parser.parsePopular(apiClient.fetchPopularFromAjax(header, page).string(), TYPE_POPULAR_ANIME) + ): List = withContext(ioDispatcher) { + parser.parsePopular( + apiClient.fetchPopularFromAjax(header = header, page = page).string(), + TYPE_POPULAR_ANIME + ) } override suspend fun fetchNewSeason( header: Map, page: Int - ): ArrayList { - return parser.parseMovie(apiClient.fetchNewSeason(header, page).string(),TYPE_MOVIE) + ): List = withContext(ioDispatcher) { + parser.parseMovie( + apiClient.fetchNewSeason(header = header, page = page).string(), + TYPE_MOVIE + ) } override suspend fun fetchMovies( header: Map, page: Int - ): ArrayList { - return parser.parseMovie(apiClient.fetchMovies(header, page).string(),TYPE_MOVIE) + ): List = withContext(ioDispatcher) { + parser.parseMovie(apiClient.fetchMovies(header = header, page = page).string(), TYPE_MOVIE) } } diff --git a/app/src/main/java/com/kl3jvi/animity/data/repository/PlayerRepositoryImpl.kt b/app/src/main/java/com/kl3jvi/animity/data/repository/PlayerRepositoryImpl.kt index 73033f45..75eadd43 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/repository/PlayerRepositoryImpl.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/repository/PlayerRepositoryImpl.kt @@ -1,15 +1,19 @@ package com.kl3jvi.animity.data.repository -import com.kl3jvi.animity.domain.repositories.PlayerRepository import com.kl3jvi.animity.data.model.EpisodeInfo import com.kl3jvi.animity.data.network.AnimeApiClient +import com.kl3jvi.animity.domain.repositories.PlayerRepository import com.kl3jvi.animity.utils.parser.HtmlParser +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton +@Suppress("BlockingMethodInNonBlockingContext") class PlayerRepositoryImpl @Inject constructor( - private val apiClient: AnimeApiClient + private val apiClient: AnimeApiClient, + private val ioDispatcher: CoroutineDispatcher ) : PlayerRepository { override val parser: HtmlParser get() = HtmlParser @@ -17,11 +21,22 @@ class PlayerRepositoryImpl @Inject constructor( override suspend fun fetchEpisodeMediaUrl( header: Map, url: String - ): EpisodeInfo { - return parser.parseMediaUrl(apiClient.fetchEpisodeMediaUrl(header, url).string()) + ): EpisodeInfo = withContext(ioDispatcher) { + parser.parseMediaUrl(apiClient.fetchEpisodeMediaUrl(header = header, url = url).string()) } - override suspend fun fetchM3u8Url(header: Map, url: String): String { - return parser.parseM3U8Url(apiClient.fetchM3u8Url(header, url).string())?:"" - } + + override suspend fun fetchM3u8Url(header: Map, url: String): String = + withContext(ioDispatcher) { + parser.parseEncryptAjax(apiClient.fetchM3u8Url(header = header, url = url).string()) ?: "" + } + + override suspend fun fetchEncryptedAjaxUrl(header: Map, url: String): String = + withContext(ioDispatcher) { + parser.parseEncryptAjax( + apiClient.fetchM3u8PreProcessor(header = header, url = url).string() + ) + } + } + diff --git a/app/src/main/java/com/kl3jvi/animity/data/repository/SearchRepositoryImpl.kt b/app/src/main/java/com/kl3jvi/animity/data/repository/SearchRepositoryImpl.kt index 6b71d453..7e72b796 100644 --- a/app/src/main/java/com/kl3jvi/animity/data/repository/SearchRepositoryImpl.kt +++ b/app/src/main/java/com/kl3jvi/animity/data/repository/SearchRepositoryImpl.kt @@ -1,16 +1,20 @@ package com.kl3jvi.animity.data.repository -import com.kl3jvi.animity.domain.repositories.SearchRepository import com.kl3jvi.animity.data.model.AnimeMetaModel import com.kl3jvi.animity.data.network.AnimeApiClient +import com.kl3jvi.animity.domain.repositories.SearchRepository import com.kl3jvi.animity.utils.Constants.Companion.TYPE_SEARCH import com.kl3jvi.animity.utils.parser.HtmlParser +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton +@Suppress("BlockingMethodInNonBlockingContext") class SearchRepositoryImpl @Inject constructor( - private val apiClient: AnimeApiClient + private val apiClient: AnimeApiClient, + private val ioDispatcher: CoroutineDispatcher ) : SearchRepository { override val parser: HtmlParser get() = HtmlParser @@ -19,9 +23,13 @@ class SearchRepositoryImpl @Inject constructor( header: Map, keyword: String, page: Int - ): ArrayList { - return parser.parseMovie( - apiClient.fetchSearchData(header, keyword, page).string(), + ): List = withContext(ioDispatcher) { + parser.parseMovie( + apiClient.fetchSearchData( + header = header, + keyword = keyword, + page = page + ).string(), TYPE_SEARCH ) } diff --git a/app/src/main/java/com/kl3jvi/animity/di/RepositoryModule.kt b/app/src/main/java/com/kl3jvi/animity/di/RepositoryModule.kt index 0d4203c9..574e0a6b 100644 --- a/app/src/main/java/com/kl3jvi/animity/di/RepositoryModule.kt +++ b/app/src/main/java/com/kl3jvi/animity/di/RepositoryModule.kt @@ -14,6 +14,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ViewModelComponent import dagger.hilt.android.scopes.ViewModelScoped +import kotlinx.coroutines.CoroutineDispatcher @Module @InstallIn(ViewModelComponent::class) @@ -21,33 +22,37 @@ object RepositoryModule { @Provides @ViewModelScoped fun provideDetailsRepository( - apiClient: AnimeApiClient + apiClient: AnimeApiClient, + ioDispatcher: CoroutineDispatcher ): DetailsRepository { - return DetailsRepositoryImpl(apiClient) + return DetailsRepositoryImpl(apiClient, ioDispatcher) } @Provides @ViewModelScoped fun provideHomeRepository( - apiClient: AnimeApiClient + apiClient: AnimeApiClient, + ioDispatcher: CoroutineDispatcher ): HomeRepository { - return HomeRepositoryImpl(apiClient) + return HomeRepositoryImpl(apiClient, ioDispatcher) } @Provides @ViewModelScoped fun provideSearchRepository( - apiClient: AnimeApiClient + apiClient: AnimeApiClient, + ioDispatcher: CoroutineDispatcher ): SearchRepository { - return SearchRepositoryImpl(apiClient) + return SearchRepositoryImpl(apiClient, ioDispatcher) } @Provides @ViewModelScoped fun providePlayerRepository( - apiClient: AnimeApiClient + apiClient: AnimeApiClient, + ioDispatcher: CoroutineDispatcher ): PlayerRepository { - return PlayerRepositoryImpl(apiClient) + return PlayerRepositoryImpl(apiClient, ioDispatcher) } } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/domain/repositories/DetailsRepository.kt b/app/src/main/java/com/kl3jvi/animity/domain/repositories/DetailsRepository.kt index 042904b9..7a6b360d 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/repositories/DetailsRepository.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/repositories/DetailsRepository.kt @@ -14,7 +14,7 @@ interface DetailsRepository { id: String, endEpisode: String, alias: String - ): ArrayList + ): List suspend fun fetchEpisodeTimeRelease(episodeUrl: String): EpisodeReleaseModel } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/domain/repositories/HomeRepository.kt b/app/src/main/java/com/kl3jvi/animity/domain/repositories/HomeRepository.kt index b27def64..300c5a4f 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/repositories/HomeRepository.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/repositories/HomeRepository.kt @@ -9,13 +9,13 @@ interface HomeRepository { header: Map, page: Int, type: Int - ): ArrayList + ): List suspend fun fetchPopularFromAjax( header: Map, page: Int - ): ArrayList + ): List - suspend fun fetchNewSeason(header: Map, page: Int): ArrayList - suspend fun fetchMovies(header: Map, page: Int): ArrayList + suspend fun fetchNewSeason(header: Map, page: Int): List + suspend fun fetchMovies(header: Map, page: Int): List } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/domain/repositories/PlayerRepository.kt b/app/src/main/java/com/kl3jvi/animity/domain/repositories/PlayerRepository.kt index da8a70ee..9f83ad30 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/repositories/PlayerRepository.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/repositories/PlayerRepository.kt @@ -7,4 +7,5 @@ interface PlayerRepository { val parser: HtmlParser suspend fun fetchEpisodeMediaUrl(header: Map, url: String): EpisodeInfo suspend fun fetchM3u8Url(header: Map, url: String):String + suspend fun fetchEncryptedAjaxUrl(header: Map, url: String):String } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/domain/repositories/SearchRepository.kt b/app/src/main/java/com/kl3jvi/animity/domain/repositories/SearchRepository.kt index 82c5e5aa..8b9d70cb 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/repositories/SearchRepository.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/repositories/SearchRepository.kt @@ -9,5 +9,5 @@ interface SearchRepository { header: Map, keyword: String, page: Int - ): ArrayList + ): List } \ No newline at end of file diff --git a/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetAnimesUseCase.kt b/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetAnimesUseCase.kt index 13862e5e..5f0dbdaf 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetAnimesUseCase.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetAnimesUseCase.kt @@ -26,7 +26,7 @@ class GetAnimesUseCase @Inject constructor( Constants.getHeader(), 1, Constants.TYPE_RECENT_DUB - ).toList() + ) emit( Resource.Success( data = response @@ -50,7 +50,7 @@ class GetAnimesUseCase @Inject constructor( fun fetchTodaySelectionAnime(): Flow>> = flow { try { emit(Resource.Loading()) - val response = homeRepository.fetchPopularFromAjax(Constants.getHeader(), 1).toList() + val response = homeRepository.fetchPopularFromAjax(Constants.getHeader(), 1) emit( Resource.Success( data = response @@ -74,7 +74,7 @@ class GetAnimesUseCase @Inject constructor( fun fetchNewSeason(): Flow>> = flow { try { emit(Resource.Loading()) - val response = homeRepository.fetchNewSeason(Constants.getHeader(), 1).toList() + val response = homeRepository.fetchNewSeason(Constants.getHeader(), 1) emit( Resource.Success( data = response @@ -98,7 +98,7 @@ class GetAnimesUseCase @Inject constructor( fun fetchMovies(): Flow>> = flow { try { emit(Resource.Loading()) - val response = homeRepository.fetchMovies(Constants.getHeader(), 1).toList() + val response = homeRepository.fetchMovies(Constants.getHeader(), 1) emit( Resource.Success( data = response diff --git a/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetEpisodeInfoUseCase.kt b/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetEpisodeInfoUseCase.kt index 07033eeb..acb66c83 100644 --- a/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetEpisodeInfoUseCase.kt +++ b/app/src/main/java/com/kl3jvi/animity/domain/use_cases/GetEpisodeInfoUseCase.kt @@ -27,7 +27,7 @@ class GetEpisodeInfoUseCase @Inject constructor( fun fetchM3U8(url: String?) = flow { emit(Resource.Loading()) try { - val response = playerRepository.fetchM3u8Url(Constants.getHeader(), url ?: "") + val response = playerRepository.fetchEncryptedAjaxUrl(Constants.getHeader(), url ?: "") emit(Resource.Success(data = response)) } catch (e: Exception) { emit(Resource.Error("Couldn't find a Stream for this Anime")) diff --git a/app/src/main/java/com/kl3jvi/animity/ui/activities/player/PlayerViewModel.kt b/app/src/main/java/com/kl3jvi/animity/ui/activities/player/PlayerViewModel.kt index ca474f15..20c8d975 100644 --- a/app/src/main/java/com/kl3jvi/animity/ui/activities/player/PlayerViewModel.kt +++ b/app/src/main/java/com/kl3jvi/animity/ui/activities/player/PlayerViewModel.kt @@ -3,9 +3,12 @@ package com.kl3jvi.animity.ui.activities.player import android.util.Log import androidx.lifecycle.* import com.google.android.exoplayer2.ExoPlayer + import com.kl3jvi.animity.data.model.Content import com.kl3jvi.animity.domain.use_cases.GetEpisodeInfoUseCase import com.kl3jvi.animity.persistence.EpisodeDao +import com.kl3jvi.animity.utils.Constants +import com.kl3jvi.animity.utils.Constants.Companion.REFERER import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,7 +36,7 @@ class PlayerViewModel @Inject constructor( @ExperimentalCoroutinesApi val videoUrlLiveData = Transformations.switchMap(_vidUrl) { url -> getEpisodeInfoUseCase(url).flatMapLatest { episodeInfo -> - getEpisodeInfoUseCase.fetchM3U8(episodeInfo.data?.vidCdnUrl) + getEpisodeInfoUseCase.fetchM3U8("${REFERER}/encrypt-ajax.php?${episodeInfo.data?.vidCdnUrl}") }.asLiveData() } diff --git a/app/src/main/java/com/kl3jvi/animity/utils/Constants.kt b/app/src/main/java/com/kl3jvi/animity/utils/Constants.kt index db8ef7b3..50f5b39f 100644 --- a/app/src/main/java/com/kl3jvi/animity/utils/Constants.kt +++ b/app/src/main/java/com/kl3jvi/animity/utils/Constants.kt @@ -41,8 +41,8 @@ class Constants { private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" - private const val ORIGIN = "https://gogoanime.film" - private const val REFERER = "https://gogoanime.film/" + private const val ORIGIN = "https://streamani.io" + const val REFERER = "https://streamani.io/" fun getHeader(): Map { return mapOf( diff --git a/app/src/main/java/com/kl3jvi/animity/utils/parser/HtmlParser.kt b/app/src/main/java/com/kl3jvi/animity/utils/parser/HtmlParser.kt index 017de6e4..a15f4ccd 100644 --- a/app/src/main/java/com/kl3jvi/animity/utils/parser/HtmlParser.kt +++ b/app/src/main/java/com/kl3jvi/animity/utils/parser/HtmlParser.kt @@ -1,12 +1,20 @@ package com.kl3jvi.animity.utils.parser +import android.os.Build import android.util.Log import com.kl3jvi.animity.data.model.* import com.kl3jvi.animity.utils.Constants -import com.kl3jvi.animity.utils.pmap +import com.kl3jvi.animity.utils.Constants.Companion.M3U8_REGEX_PATTERN +import org.apache.commons.lang3.RandomStringUtils import org.jsoup.Jsoup import org.jsoup.select.Elements +import java.net.URLDecoder +import java.util.* import java.util.regex.Pattern +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec +import kotlin.collections.ArrayList /** * This File gets response in String format and parses it @@ -206,20 +214,114 @@ object HtmlParser { ) } + private fun decryptAES(encrypted: String, key: String, iv: String): String { + val ix = IvParameterSpec(iv.toByteArray()) + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + val secretKey = SecretKeySpec(key.toByteArray(Charsets.UTF_8), "AES") + cipher.init(Cipher.DECRYPT_MODE, secretKey, ix) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + String(cipher.doFinal(Base64.getDecoder().decode(encrypted))) + } else { + String( + cipher.doFinal( + android.util.Base64.decode( + encrypted, + android.util.Base64.DEFAULT + ) + ) + ) + } + } + + private fun encryptAes(text: String, key: String, iv: String): String { + val ix = IvParameterSpec(iv.toByteArray()) + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + val secretKey = SecretKeySpec(key.toByteArray(), "AES") + cipher.init(Cipher.ENCRYPT_MODE, secretKey, ix) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + Base64.getEncoder().encodeToString(cipher.doFinal(text.toByteArray())) + } else { + android.util.Base64.encodeToString( + cipher.doFinal(text.toByteArray()), + android.util.Base64.DEFAULT + ) + } + } + + + fun parseEncryptAjax(response: String): String { + val document = Jsoup.parse(response) + val value6 = document.getElementsByAttributeValue("data-name", "ts").attr("data-value") + val value5 = document.getElementsByAttributeValue("name", "crypto").attr("content") + val value1 = decryptAES( + document.getElementsByAttributeValue("data-name", "crypto").attr("data-value"), + URLDecoder.decode(value6 + value6, Charsets.UTF_8.name()), + URLDecoder.decode(value6, Charsets.UTF_8.name()) + ) + val value4 = decryptAES( + value5, + URLDecoder.decode(value1, Charsets.UTF_8.name()), + URLDecoder.decode(value6, Charsets.UTF_8.name()) + ) + val value2 = RandomStringUtils.randomAlphanumeric(16) + val value3 = URLDecoder.decode(value4, Charsets.UTF_8.name()).toString() + val encrypted = encryptAes( + value4.removeRange(value4.indexOf("&"), value4.length), + URLDecoder.decode(value1, Charsets.UTF_8.name()), + URLDecoder.decode(value2, Charsets.UTF_8.name()) + ) + return "id=" + encrypted + "&time=" + "00" + value2 + "00" + value3.substring( + value3.indexOf( + "&" + ) + ) + } + fun parseMediaUrl(response: String): EpisodeInfo { + val mediaUrl: String? val document = Jsoup.parse(response) - val iframe = "https:" + document.selectFirst("div.play-video > iframe").attr("src") - val link = iframe.replace("streaming.php", "download") - return EpisodeInfo(vidCdnUrl = link) + val info = document?.getElementsByClass("vidcdn")?.first()?.select("a") + mediaUrl = info?.attr("data-video").toString() + val nextEpisodeUrl = + document.getElementsByClass("anime_video_body_episodes_r")?.select("a")?.first() + ?.attr("href") + val previousEpisodeUrl = + document.getElementsByClass("anime_video_body_episodes_l")?.select("a")?.first() + ?.attr("href") + + return EpisodeInfo( + nextEpisodeUrl = nextEpisodeUrl, + previousEpisodeUrl = previousEpisodeUrl, + vidCdnUrl = mediaUrl + ) + } + + fun parseMirrorLink(response: String): String? { + var m3u8Url: String? = "" + val document = Jsoup.parse(response) + val info = document?.getElementsByClass("mirror_link") + val pattern = Pattern.compile(M3U8_REGEX_PATTERN) + val matcher = pattern.matcher(info.toString()) + return try { + while (matcher.find()) { + if (matcher.group(0)!!.contains("mp4")) { + m3u8Url = matcher.group(0) + } + break + } + m3u8Url + } catch (npe: NullPointerException) { + m3u8Url + } } fun parseM3U8Url(response: String): String? { - Log.e("Response e downloadit",response) + Log.e("Response e downloadit", response) var m3u8Url: String? = "" val document = Jsoup.parse(response) val info = document?.getElementsByClass("videocontent") - val pattern = Pattern.compile(Constants.M3U8_REGEX_PATTERN) + val pattern = Pattern.compile(M3U8_REGEX_PATTERN) val matcher = pattern.matcher(info.toString()) return try { while (matcher.find()) { @@ -240,34 +342,6 @@ object HtmlParser { } } -data class ExtractorLink( - val url: String, - val isM3u8: Boolean = false, -) - -private fun extractVideos(response: String): List { - val document = Jsoup.parse(response) - - return document.select(".dowload > a").pmap { - if (it.hasAttr("download")) { - listOf( - ExtractorLink( - it.attr("href"), - it.attr("href").contains(".m3u8") - ) - ) - } else { - listOf( - ExtractorLink( - it.attr("href"), - it.attr("href").contains(".m3u8") - ) - ) - } - }.flatten() -} - - private fun getCategoryUrl(url: String): String { return try { var categoryUrl = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.')) diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml index 28d49763..88149e8c 100644 --- a/app/src/main/res/anim/slide_in_left.xml +++ b/app/src/main/res/anim/slide_in_left.xml @@ -1,6 +1,10 @@ - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_in_right.xml b/app/src/main/res/anim/slide_in_right.xml index 5d962c16..eb963fc9 100644 --- a/app/src/main/res/anim/slide_in_right.xml +++ b/app/src/main/res/anim/slide_in_right.xml @@ -1,6 +1,9 @@ - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_left.xml b/app/src/main/res/anim/slide_out_left.xml index d3943aed..a7ff4863 100644 --- a/app/src/main/res/anim/slide_out_left.xml +++ b/app/src/main/res/anim/slide_out_left.xml @@ -1,6 +1,9 @@ - \ No newline at end of file diff --git a/app/src/main/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml index 381d2eb5..a11e51e2 100644 --- a/app/src/main/res/anim/slide_out_right.xml +++ b/app/src/main/res/anim/slide_out_right.xml @@ -1,6 +1,9 @@ - \ No newline at end of file