From a96b39630759e2624089cac87a3a54f9e87c68b2 Mon Sep 17 00:00:00 2001 From: LagradOst Date: Fri, 17 Dec 2021 00:45:20 +0100 Subject: [PATCH] removed context (no user change) + small bug fixes --- app/build.gradle | 2 +- .../lagradost/cloudstream3/AcraApplication.kt | 50 ++++++ .../com/lagradost/cloudstream3/MainAPI.kt | 35 +++- .../lagradost/cloudstream3/MainActivity.kt | 4 +- .../syncproviders/AccountManager.kt | 47 +++--- .../cloudstream3/syncproviders/OAuth2API.kt | 9 +- .../cloudstream3/syncproviders/SyncAPI.kt | 9 +- .../syncproviders/providers/AniListApi.kt | 69 ++++---- .../syncproviders/providers/DropboxApi.kt | 9 +- .../syncproviders/providers/MALApi.kt | 87 +++++----- .../ui/download/DownloadButtonSetup.kt | 2 +- .../ui/download/DownloadChildAdapter.kt | 2 +- .../cloudstream3/ui/home/HomeFragment.kt | 41 ++--- .../cloudstream3/ui/home/HomeViewModel.kt | 54 +++--- .../cloudstream3/ui/player/PlayerFragment.kt | 11 +- .../ui/quicksearch/QuickSearchFragment.kt | 9 +- .../cloudstream3/ui/result/ResultFragment.kt | 48 +++--- .../cloudstream3/ui/result/ResultViewModel.kt | 155 +++++++++--------- .../cloudstream3/ui/search/SearchFragment.kt | 10 +- .../cloudstream3/ui/search/SearchViewModel.kt | 87 +++++----- .../ui/settings/SettingsFragment.kt | 49 +++--- .../ui/subtitles/SubtitlesFragment.kt | 29 ++-- .../lagradost/cloudstream3/utils/AppUtils.kt | 44 ++--- .../cloudstream3/utils/CastHelper.kt | 16 +- .../cloudstream3/utils/DataStoreHelper.kt | 55 ++++--- .../utils/DownloadFileWorkManager.kt | 6 +- .../utils/VideoDownloadManager.kt | 53 +++--- .../lagradost/cloudstream3/ProviderTests.kt | 1 - 28 files changed, 525 insertions(+), 468 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b0f62af9..5d7d2d23 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { applicationId "com.lagradost.cloudstream3" minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 30 versionCode 37 versionName "2.4.5" diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index 0280c5a2..faa08045 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -5,7 +5,13 @@ import android.content.Context import android.widget.Toast import com.google.auto.service.AutoService import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread +import com.lagradost.cloudstream3.utils.DataStore.getKey +import com.lagradost.cloudstream3.utils.DataStore.getKeys +import com.lagradost.cloudstream3.utils.DataStore.removeKey +import com.lagradost.cloudstream3.utils.DataStore.removeKeys +import com.lagradost.cloudstream3.utils.DataStore.setKey import org.acra.ReportField import org.acra.config.CoreConfiguration import org.acra.data.CrashReportData @@ -84,5 +90,49 @@ class AcraApplication : Application() { private set(value) { _context = WeakReference(value) } + + fun removeKeys(folder: String): Int? { + return context?.removeKeys(folder) + } + + fun setKey(path: String, value: T) { + context?.setKey(path, value) + } + + fun setKey(folder: String, path: String, value: T) { + context?.setKey(folder, path, value) + } + + inline fun getKey(path: String, defVal: T?): T? { + return context?.getKey(path, defVal) + } + + inline fun getKey(path: String): T? { + return context?.getKey(path) + } + + inline fun getKey(folder: String, path: String): T? { + return context?.getKey(folder, path) + } + + inline fun getKey(folder: String, path: String, defVal: T?): T? { + return context?.getKey(folder, path, defVal) + } + + fun getKeys(folder: String): List? { + return context?.getKeys(folder) + } + + fun removeKey(folder: String, path: String) { + context?.removeKey(folder, path) + } + + fun removeKey(path: String) { + context?.removeKey(path) + } + + fun openBrowser(url: String) { + context?.openBrowser(url) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index b56e0ef7..24859c50 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -164,6 +164,25 @@ object APIHolder { return realSet } + + fun Context.filterProviderByPreferredMedia(): List { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + val currentPrefMedia = settingsManager.getInt(this.getString(R.string.prefer_media_type_key), 0) + val langs = this.getApiProviderLangSettings() + val allApis = apis.filter { langs.contains(it.lang) }.filter { api -> api.hasMainPage } + return if (currentPrefMedia < 1) { + allApis + } else { + // Filter API depending on preferred media type + val listEnumAnime = listOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA) + val listEnumMovieTv = listOf(TvType.Movie, TvType.TvSeries, TvType.Cartoon) + val mediaTypeList = if (currentPrefMedia == 1) listEnumMovieTv else listEnumAnime + + val filteredAPI = + allApis.filter { api -> api.supportedTypes.any { it in mediaTypeList } } + filteredAPI + } + } } /**Every provider will **not** have try catch built in, so handle exceptions when calling these functions*/ @@ -525,25 +544,27 @@ fun MainAPI.newMovieLoadResponse( name: String, url: String, type: TvType, - dataUrl : String, + dataUrl: String, initializer: MovieLoadResponse.() -> Unit = { } ): MovieLoadResponse { - val builder = MovieLoadResponse(name = name, url = url, apiName = this.name, type = type,dataUrl = dataUrl) + val builder = MovieLoadResponse(name = name, url = url, apiName = this.name, type = type, dataUrl = dataUrl) builder.initializer() return builder } -fun LoadResponse.setDuration(input : String?) { +fun LoadResponse.setDuration(input: String?) { if (input == null) return Regex("([0-9]*)h.*?([0-9]*)m").matchEntire(input)?.groupValues?.let { values -> - if(values.size == 3) { + if (values.size == 3) { val hours = values[1].toIntOrNull() val minutes = values[2].toIntOrNull() - this.duration = if(minutes != null && hours != null) { hours * 60 + minutes } else null + this.duration = if (minutes != null && hours != null) { + hours * 60 + minutes + } else null } } Regex("([0-9]*)m").matchEntire(input)?.groupValues?.let { values -> - if(values.size == 2) { + if (values.size == 2) { this.duration = values[1].toIntOrNull() } } @@ -584,7 +605,7 @@ fun MainAPI.newTvSeriesLoadResponse( name: String, url: String, type: TvType, - episodes : List, + episodes: List, initializer: TvSeriesLoadResponse.() -> Unit = { } ): TvSeriesLoadResponse { val builder = TvSeriesLoadResponse(name = name, url = url, apiName = this.name, type = type, episodes = episodes) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 914bbe16..65d8a5d7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -490,7 +490,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { if (str.contains(appString)) { for (api in OAuth2Apis) { if (str.contains("/${api.redirectUrl}")) { - api.handleRedirect(this, str) + api.handleRedirect(str) } } } else { @@ -511,7 +511,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { override fun onCreate(savedInstanceState: Bundle?) { // init accounts for (api in OAuth2accountApis) { - api.init(this) + api.init() } val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index ca81485e..bfae9cf4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -1,9 +1,8 @@ package com.lagradost.cloudstream3.syncproviders -import android.content.Context -import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.removeKeys -import com.lagradost.cloudstream3.utils.DataStore.setKey +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey abstract class AccountManager(private val defIndex: Int) : OAuth2API { var accountIndex = defIndex @@ -13,44 +12,44 @@ abstract class AccountManager(private val defIndex: Int) : OAuth2API { // int array of all accounts indexes private val accountsKey get() = "${idPrefix}_accounts" - protected fun Context.removeAccountKeys() { - this.removeKeys(accountId) - val accounts = getAccounts(this).toMutableList() + protected fun removeAccountKeys() { + removeKeys(accountId) + val accounts = getAccounts()?.toMutableList() ?: mutableListOf() accounts.remove(accountIndex) - this.setKey(accountsKey, accounts.toIntArray()) + setKey(accountsKey, accounts.toIntArray()) - init(this) + init() } - fun getAccounts(context: Context): IntArray { - return context.getKey(accountsKey, intArrayOf())!! + fun getAccounts(): IntArray? { + return getKey(accountsKey, intArrayOf()) } - fun init(context: Context) { - accountIndex = context.getKey(accountActiveKey, defIndex)!! - val accounts = getAccounts(context) - if (accounts.isNotEmpty() && this.loginInfo(context) == null) { + fun init() { + accountIndex = getKey(accountActiveKey, defIndex)!! + val accounts = getAccounts() + if (accounts?.isNotEmpty() == true && this.loginInfo() == null) { accountIndex = accounts.first() } } - protected fun Context.switchToNewAccount() { - val accounts = getAccounts(this) - accountIndex = (accounts.maxOrNull() ?: 0) + 1 + protected fun switchToNewAccount() { + val accounts = getAccounts() + accountIndex = (accounts?.maxOrNull() ?: 0) + 1 } - protected fun Context.registerAccount() { - this.setKey(accountActiveKey, accountIndex) - val accounts = getAccounts(this).toMutableList() + protected fun registerAccount() { + setKey(accountActiveKey, accountIndex) + val accounts = getAccounts()?.toMutableList() ?: mutableListOf() if (!accounts.contains(accountIndex)) { accounts.add(accountIndex) } - this.setKey(accountsKey, accounts.toIntArray()) + setKey(accountsKey, accounts.toIntArray()) } - fun changeAccount(context: Context, index: Int) { + fun changeAccount(index: Int) { accountIndex = index - context.setKey(accountActiveKey, index) + setKey(accountActiveKey, index) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/OAuth2API.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/OAuth2API.kt index 504b07f1..1fef3849 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/OAuth2API.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/OAuth2API.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.syncproviders -import android.content.Context import com.lagradost.cloudstream3.syncproviders.providers.AniListApi import com.lagradost.cloudstream3.syncproviders.providers.MALApi import java.util.concurrent.TimeUnit @@ -13,11 +12,11 @@ interface OAuth2API { // don't change this as all keys depend on it val idPrefix : String - fun handleRedirect(context: Context, url: String) - fun authenticate(context: Context) + fun handleRedirect(url: String) + fun authenticate() - fun loginInfo(context: Context): LoginInfo? - fun logOut(context: Context) + fun loginInfo(): LoginInfo? + fun logOut() class LoginInfo( val profilePicture: String?, diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index bacd982e..f0fde233 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.syncproviders -import android.content.Context import com.lagradost.cloudstream3.ShowStatus interface SyncAPI : OAuth2API { @@ -67,7 +66,7 @@ interface SyncAPI : OAuth2API { val icon: Int val mainUrl: String - fun search(context: Context, name: String): List? + fun search(name: String): List? /** -1 -> None @@ -78,9 +77,9 @@ interface SyncAPI : OAuth2API { 4 -> PlanToWatch 5 -> ReWatching */ - fun score(context: Context, id: String, status: SyncStatus): Boolean + fun score(id: String, status: SyncStatus): Boolean - fun getStatus(context: Context, id: String): SyncStatus? + fun getStatus(id: String): SyncStatus? - fun getResult(context: Context, id: String): SyncResult? + fun getResult(id: String): SyncResult? } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index 91a38d06..44e3e9f9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -1,11 +1,14 @@ package com.lagradost.cloudstream3.syncproviders.providers -import android.content.Context import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys +import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -15,12 +18,8 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.appString import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.maxStale import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.unixTime import com.lagradost.cloudstream3.syncproviders.SyncAPI -import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.getKeys -import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject import java.net.URL import java.util.* @@ -33,9 +32,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { override val mainUrl = "https://anilist.co" override val icon = R.drawable.ic_anilist_icon - override fun loginInfo(context: Context): OAuth2API.LoginInfo? { + override fun loginInfo(): OAuth2API.LoginInfo? { // context.getUser(true)?. - context.getKey(accountId, ANILIST_USER_KEY)?.let { user -> + getKey(accountId, ANILIST_USER_KEY)?.let { user -> return OAuth2API.LoginInfo( profilePicture = user.picture, name = user.name, @@ -45,16 +44,16 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return null } - override fun logOut(context: Context) { - context.removeAccountKeys() + override fun logOut() { + removeAccountKeys() } - override fun authenticate(context: Context) { + override fun authenticate() { val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token" - context.openBrowser(request) + openBrowser(request) } - override fun handleRedirect(context: Context, url: String) { + override fun handleRedirect(url: String) { try { val sanitizer = splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR @@ -63,19 +62,19 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { val endTime = unixTime + expiresIn.toLong() - context.switchToNewAccount() - context.setKey(accountId, ANILIST_UNIXTIME_KEY, endTime) - context.setKey(accountId, ANILIST_TOKEN_KEY, token) - context.setKey(ANILIST_SHOULD_UPDATE_LIST, true) + switchToNewAccount() + setKey(accountId, ANILIST_UNIXTIME_KEY, endTime) + setKey(accountId, ANILIST_TOKEN_KEY, token) + setKey(ANILIST_SHOULD_UPDATE_LIST, true) ioSafe { - context.getUser() + getUser() } } catch (e: Exception) { e.printStackTrace() } } - override fun search(context: Context, name: String): List? { + override fun search(name: String): List? { val data = searchShows(name) ?: return null return data.data.Page.media.map { SyncAPI.SyncSearchResult( @@ -88,7 +87,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - override fun getResult(context: Context, id: String): SyncAPI.SyncResult? { + override fun getResult(id: String): SyncAPI.SyncResult? { val internalId = id.toIntOrNull() ?: return null val season = getSeason(internalId)?.data?.Media ?: return null @@ -104,9 +103,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ) } - override fun getStatus(context: Context, id: String): SyncAPI.SyncStatus? { + override fun getStatus(id: String): SyncAPI.SyncStatus? { val internalId = id.toIntOrNull() ?: return null - val data = context.getDataAboutId(internalId) ?: return null + val data = getDataAboutId(internalId) ?: return null return SyncAPI.SyncStatus( score = data.score, @@ -116,13 +115,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ) } - override fun score(context: Context, id: String, status: SyncAPI.SyncStatus): Boolean { - return context.postDataAboutId( + override fun score(id: String, status: SyncAPI.SyncStatus): Boolean { + return postDataAboutId( id.toIntOrNull() ?: return false, fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) + ) ?: return false } companion object { @@ -331,21 +330,21 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun Context.initGetUser() { + fun initGetUser() { if (getKey(accountId, ANILIST_TOKEN_KEY, null) == null) return ioSafe { getUser() } } - private fun Context.checkToken(): Boolean { + private fun checkToken(): Boolean { return unixTime > getKey( accountId, ANILIST_UNIXTIME_KEY, 0L )!! } - fun Context.getDataAboutId(id: Int): AniListTitleHolder? { + fun getDataAboutId(id: Int): AniListTitleHolder? { val q = """query (${'$'}id: Int = $id) { # Define which variables will be used in the query (id) Media (id: ${'$'}id, type: ANIME) { # Insert our variables into the query arguments (id) (type: ANIME is hard-coded in the query) @@ -404,7 +403,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.postApi(url: String, q: String, cache: Boolean = false): String { + private fun postApi(url: String, q: String, cache: Boolean = false): String { return try { if (!checkToken()) { // println("VARS_ " + vars) @@ -504,11 +503,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("MediaListCollection") val MediaListCollection: MediaListCollection ) - fun Context.getAnilistListCached(): Array? { + fun getAnilistListCached(): Array? { return getKey(ANILIST_CACHED_LIST) as? Array } - fun Context.getAnilistAnimeListSmart(): Array? { + fun getAnilistAnimeListSmart(): Array? { if (getKey( accountId, ANILIST_TOKEN_KEY, @@ -529,11 +528,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.getFullAnilistList(): FullAnilistList? { + private fun getFullAnilistList(): FullAnilistList? { try { var userID: Int? = null /** WARNING ASSUMES ONE USER! **/ - getKeys(ANILIST_USER_KEY).forEach { key -> + getKeys(ANILIST_USER_KEY)?.forEach { key -> getKey(key, null)?.let { userID = it.id } @@ -591,7 +590,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun Context.toggleLike(id: Int): Boolean { + fun toggleLike(id: Int): Boolean { val q = """mutation (${'$'}animeId: Int = $id) { ToggleFavourite (animeId: ${'$'}animeId) { anime { @@ -608,7 +607,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return data != "" } - private fun Context.postDataAboutId(id: Int, type: AniListStatusType, score: Int?, progress: Int?): Boolean { + private fun postDataAboutId(id: Int, type: AniListStatusType, score: Int?, progress: Int?): Boolean { try { val q = """mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ @@ -632,7 +631,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.getUser(setSettings: Boolean = true): AniListUser? { + private fun getUser(setSettings: Boolean = true): AniListUser? { val q = """ { Viewer { diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/DropboxApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/DropboxApi.kt index c38bbf78..344c12aa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/DropboxApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/DropboxApi.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.syncproviders.providers -import android.content.Context import com.lagradost.cloudstream3.syncproviders.OAuth2API //TODO dropbox sync @@ -10,19 +9,19 @@ class Dropbox : OAuth2API { override val key = "zlqsamadlwydvb2" override val redirectUrl = "dropboxlogin" - override fun authenticate(context: Context) { + override fun authenticate() { TODO("Not yet implemented") } - override fun handleRedirect(context: Context,url: String) { + override fun handleRedirect(url: String) { TODO("Not yet implemented") } - override fun logOut(context: Context) { + override fun logOut() { TODO("Not yet implemented") } - override fun loginInfo(context: Context): OAuth2API.LoginInfo? { + override fun loginInfo(): OAuth2API.LoginInfo? { TODO("Not yet implemented") } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index b9914f81..09833da1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -1,12 +1,14 @@ package com.lagradost.cloudstream3.syncproviders.providers -import android.content.Context import android.util.Base64 import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -16,11 +18,8 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.appString import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.secondsToReadable import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.unixTime import com.lagradost.cloudstream3.syncproviders.SyncAPI -import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject import java.net.URL import java.security.SecureRandom @@ -40,26 +39,27 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { override val icon: Int get() = R.drawable.mal_logo - override fun logOut(context: Context) { - context.removeAccountKeys() + override fun logOut() { + removeAccountKeys() } - override fun loginInfo(context: Context): OAuth2API.LoginInfo? { - //context.getMalUser(true)? - context.getKey(accountId, MAL_USER_KEY)?.let { user -> + override fun loginInfo(): OAuth2API.LoginInfo? { + //getMalUser(true)? + getKey(accountId, MAL_USER_KEY)?.let { user -> return OAuth2API.LoginInfo(profilePicture = user.picture, name = user.name, accountIndex = accountIndex) } return null } - override fun search(context: Context, name: String): List { + override fun search(name: String): List { val url = "https://api.myanimelist.net/v2/anime?q=$name&limit=$MAL_MAX_SEARCH_LIMIT" - var res = app.get( + val auth = getKey( + accountId, + MAL_TOKEN_KEY + ) ?: return emptyList() + val res = app.get( url, headers = mapOf( - "Authorization" to "Bearer " + context.getKey( - accountId, - MAL_TOKEN_KEY - )!!, + "Authorization" to "Bearer " + auth, ), cacheTime = 0 ).text return mapper.readValue(res).data.map { @@ -74,8 +74,8 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - override fun score(context: Context, id: String, status : SyncAPI.SyncStatus): Boolean { - return context.setScoreRequest( + override fun score(id: String, status : SyncAPI.SyncStatus): Boolean { + return setScoreRequest( id.toIntOrNull() ?: return false, fromIntToAnimeStatus(status.status), status.score, @@ -83,15 +83,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { ) } - override fun getResult(context: Context, id: String): SyncAPI.SyncResult? { + override fun getResult(id: String): SyncAPI.SyncResult? { val internalId = id.toIntOrNull() ?: return null TODO("Not yet implemented") } - override fun getStatus(context: Context, id: String): SyncAPI.SyncStatus? { + override fun getStatus(id: String): SyncAPI.SyncStatus? { val internalId = id.toIntOrNull() ?: return null - val data = context.getDataAboutMalId(internalId)?.my_list_status ?: return null + val data = getDataAboutMalId(internalId)?.my_list_status ?: return null return SyncAPI.SyncStatus( score = data.score, status = malStatusAsString.indexOf(data.status), @@ -111,7 +111,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api } - override fun handleRedirect(context: Context, url: String) { + override fun handleRedirect(url: String) { try { val sanitizer = splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR @@ -136,10 +136,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } if (res != "") { - context.switchToNewAccount() - context.storeToken(res) - context.getMalUser() - context.setKey(MAL_SHOULD_UPDATE_LIST, true) + switchToNewAccount() + storeToken(res) + getMalUser() + setKey(MAL_SHOULD_UPDATE_LIST, true) } } } @@ -148,7 +148,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - override fun authenticate(context: Context) { + override fun authenticate() { // It is recommended to use a URL-safe string as code_verifier. // See section 4 of RFC 7636 for more details. @@ -161,7 +161,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { val codeChallenge = codeVerifier val request = "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=$key&code_challenge=$codeChallenge&state=RequestID$requestId" - context.openBrowser(request) + openBrowser(request) } private val mapper = JsonMapper.builder().addModule(KotlinModule()) @@ -170,7 +170,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { private var requestId = 0 private var codeVerifier = "" - private fun Context.storeToken(response: String) { + private fun storeToken(response: String) { try { if (response != "") { val token = mapper.readValue(response) @@ -183,7 +183,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.refreshToken() { + private fun refreshToken() { try { val res = app.post( "https://myanimelist.net/v1/oauth2/token", @@ -278,11 +278,11 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("start_time") val start_time: String? ) - fun Context.getMalAnimeListCached(): Array? { + fun getMalAnimeListCached(): Array? { return getKey(MAL_CACHED_LIST) as? Array } - fun Context.getMalAnimeListSmart(): Array? { + fun getMalAnimeListSmart(): Array? { if (getKey( accountId, MAL_TOKEN_KEY @@ -300,7 +300,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.getMalAnimeList(): Array? { + private fun getMalAnimeList(): Array? { return try { checkMalToken() var offset = 0 @@ -322,8 +322,12 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) } - private fun Context.getMalAnimeListSlice(offset: Int = 0): MalList? { + private fun getMalAnimeListSlice(offset: Int = 0): MalList? { val user = "@me" + val auth = getKey( + accountId, + MAL_TOKEN_KEY + ) ?: return null return try { // Very lackluster docs // https://myanimelist.net/apiconfig/references/api/v2#operation/users_user_id_animelist_get @@ -331,10 +335,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { "https://api.myanimelist.net/v2/users/$user/animelist?fields=list_status,num_episodes,media_type,status,start_date,end_date,synopsis,alternative_titles,mean,genres,rank,num_list_users,nsfw,average_episode_duration,num_favorites,popularity,num_scoring_users,start_season,favorites_info,broadcast,created_at,updated_at&nsfw=1&limit=100&offset=$offset" val res = app.get( url, headers = mapOf( - "Authorization" to "Bearer " + getKey( - accountId, - MAL_TOKEN_KEY - )!!, + "Authorization" to "Bearer $auth", ), cacheTime = 0 ).text res.toKotlinObject() @@ -344,7 +345,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.getDataAboutMalId(id: Int): MalAnime? { + private fun getDataAboutMalId(id: Int): MalAnime? { return try { // https://myanimelist.net/apiconfig/references/api/v2#operation/anime_anime_id_get val url = "https://api.myanimelist.net/v2/anime/$id?fields=id,title,num_episodes,my_list_status" @@ -362,7 +363,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - fun Context.setAllMalData() { + fun setAllMalData() { val user = "@me" var isDone = false var index = 0 @@ -426,7 +427,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return null } - private fun Context.checkMalToken() { + private fun checkMalToken() { if (unixTime > getKey( accountId, MAL_UNIXTIME_KEY @@ -436,7 +437,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.getMalUser(setSettings: Boolean = true): MalUser? { + private fun getMalUser(setSettings: Boolean = true): MalUser? { checkMalToken() return try { val res = app.get( @@ -483,7 +484,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - fun Context.setScoreRequest( + fun setScoreRequest( id: Int, status: MalStatusType? = null, score: Int? = null, @@ -514,7 +515,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } - private fun Context.setScoreRequest( + private fun setScoreRequest( id: Int, status: String? = null, score: Int? = null, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt index 72d1cfc8..78c6a407 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt @@ -113,7 +113,7 @@ object DownloadButtonSetup { if (click.data.episode <= 0) null else click.data.episode, click.data.season ), - act.getViewPos(click.data.id)?.position ?: 0 + getViewPos(click.data.id)?.position ?: 0 ) ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt index 0c71c66b..a541171b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildAdapter.kt @@ -106,7 +106,7 @@ class DownloadChildAdapter( localCard = card val d = card.data - val posDur = itemView.context.getViewPos(d.id) + val posDur = getViewPos(d.id) if (posDur != null) { val visualPos = posDur.fixVisual() progressBar.max = (visualPos.duration / 1000).toInt() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index ce475f14..230afa39 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -14,7 +14,6 @@ import android.widget.TextView import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper @@ -22,7 +21,10 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.bottomsheet.BottomSheetDialog import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.apis +import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe @@ -36,7 +38,6 @@ import com.lagradost.cloudstream3.ui.search.* import com.lagradost.cloudstream3.ui.search.SearchFragment.Companion.filterSearchResponse import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings -import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey @@ -130,14 +131,12 @@ class HomeFragment : Fragment() { } private val apiChangeClickListener = View.OnClickListener { view -> - val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) - val currentPrefMedia = settingsManager.getInt(getString(R.string.preferred_media_settings), 0) - val validAPIs = AppUtils.filterProviderByPreferredMedia(apis, currentPrefMedia).toMutableList() + val validAPIs = view.context?.filterProviderByPreferredMedia()?.toMutableList() ?: mutableListOf() validAPIs.add(0, randomApi) validAPIs.add(0, noneApi) view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api -> Pair(index, api.name) }) { - homeViewModel.loadAndCancel(validAPIs[itemId].name, currentPrefMedia) + homeViewModel.loadAndCancel(validAPIs[itemId].name) } } @@ -157,14 +156,12 @@ class HomeFragment : Fragment() { }*/ private fun reloadStored() { - context?.let { ctx -> - homeViewModel.loadResumeWatching(ctx) - val list = EnumSet.noneOf(WatchType::class.java) - ctx.getKey(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let { - list.addAll(it) - } - homeViewModel.loadStoredData(ctx, list) + homeViewModel.loadResumeWatching() + val list = EnumSet.noneOf(WatchType::class.java) + getKey(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let { + list.addAll(it) } + homeViewModel.loadStoredData(list) } /*private fun handleBack(poppedFragment: Boolean) { @@ -182,7 +179,7 @@ class HomeFragment : Fragment() { home_change_api_loading.setOnClickListener(apiChangeClickListener) observe(homeViewModel.apiName) { apiName -> - context?.setKey(HOMEPAGE_API, apiName) + setKey(HOMEPAGE_API, apiName) home_provider_name?.text = apiName home_provider_meta_info?.isVisible = false @@ -326,11 +323,11 @@ class HomeFragment : Fragment() { } else { list.add(watch) } - homeViewModel.loadStoredData(itemView.context, list) + homeViewModel.loadStoredData(list) } - item.first?.setOnLongClickListener { itemView -> - homeViewModel.loadStoredData(itemView.context, EnumSet.of(watch)) + item.first?.setOnLongClickListener { + homeViewModel.loadStoredData(EnumSet.of(watch)) return@setOnLongClickListener true } } @@ -404,7 +401,7 @@ class HomeFragment : Fragment() { if (id != null) { callback.view.popupMenuNoIcons(listOf(Pair(0, R.string.action_remove_from_bookmarks))) { if (itemId == 0) { - activity?.setResultWatchState(id, WatchType.NONE.internalId) + setResultWatchState(id, WatchType.NONE.internalId) reloadStored() } } @@ -438,7 +435,7 @@ class HomeFragment : Fragment() { if (itemId == 0) { val card = callback.card if (card is DataStoreHelper.ResumeWatchingResult) { - context?.removeLastWatched(card.parentId) + removeLastWatched(card.parentId) reloadStored() } } @@ -476,11 +473,9 @@ class HomeFragment : Fragment() { reloadStored() val apiName = context?.getKey(HOMEPAGE_API) - val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) - val currentPrefMedia = settingsManager.getInt(getString(R.string.preferred_media_settings), 0) if (homeViewModel.apiName.value != apiName || apiName == null) { //println("Caught home: " + homeViewModel.apiName.value + " at " + apiName) - homeViewModel.loadAndCancel(apiName, currentPrefMedia) + homeViewModel.loadAndCancel(apiName) } // nice profile pic on homepage @@ -497,7 +492,7 @@ class HomeFragment : Fragment() { } for (syncApi in OAuth2API.OAuth2Apis) { - val login = syncApi.loginInfo(ctx) + val login = syncApi.loginInfo() val pic = login?.profilePicture if (pic != null) { home_profile_picture.setImage(pic) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index ff8a63db..a72a8581 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -1,13 +1,15 @@ package com.lagradost.cloudstream3.ui.home -import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.APIHolder.apis +import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.AcraApplication.Companion.context +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.HomePageResponse import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.SearchResponse @@ -16,15 +18,16 @@ import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.WatchType -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.setKey +import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE +import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData import com.lagradost.cloudstream3.utils.DataStoreHelper.getLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos +import com.lagradost.cloudstream3.utils.HOMEPAGE_API +import com.lagradost.cloudstream3.utils.VideoDownloadHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -55,22 +58,22 @@ class HomeViewModel : ViewModel() { private val _resumeWatching = MutableLiveData>() val resumeWatching: LiveData> = _resumeWatching - fun loadResumeWatching(context: Context) = viewModelScope.launch { + fun loadResumeWatching() = viewModelScope.launch { val resumeWatching = withContext(Dispatchers.IO) { - context.getAllResumeStateIds().mapNotNull { id -> - context.getLastWatched(id) - }.sortedBy { -it.updateTime } + getAllResumeStateIds()?.mapNotNull { id -> + getLastWatched(id) + }?.sortedBy { -it.updateTime } } // val resumeWatchingResult = ArrayList() val resumeWatchingResult = withContext(Dispatchers.IO) { - resumeWatching.map { resume -> - val data = context.getKey( + resumeWatching?.map { resume -> + val data = getKey( DOWNLOAD_HEADER_CACHE, resume.parentId.toString() ) ?: return@map null - val watchPos = context.getViewPos(resume.episodeId) + val watchPos = getViewPos(resume.episodeId) DataStoreHelper.ResumeWatchingResult( data.name, data.url, @@ -84,18 +87,19 @@ class HomeViewModel : ViewModel() { resume.season, resume.isFromDownload ) - }.filterNotNull() + }?.filterNotNull() } _resumeWatching.postValue(resumeWatchingResult) } - fun loadStoredData(context: Context, preferredWatchStatus: EnumSet?) = viewModelScope.launch { + fun loadStoredData(preferredWatchStatus: EnumSet?) = viewModelScope.launch { val watchStatusIds = withContext(Dispatchers.IO) { - context.getAllWatchStateIds().map { id -> - Pair(id, context.getResultWatchState(id)) + getAllWatchStateIds()?.map { id -> + Pair(id, getResultWatchState(id)) } - }.distinctBy { it.first } + }?.distinctBy { it.first } ?: return@launch + val length = WatchType.values().size val currentWatchTypes = EnumSet.noneOf(WatchType::class.java) @@ -125,10 +129,10 @@ class HomeViewModel : ViewModel() { val list = withContext(Dispatchers.IO) { watchStatusIds.filter { watchPrefNotNull.contains(it.second) } - .mapNotNull { context.getBookmarkedData(it.first) } + .mapNotNull { getBookmarkedData(it.first) } .sortedBy { -it.latestUpdatedTime } } - _bookmarks.postValue(Pair(true,list)) + _bookmarks.postValue(Pair(true, list)) } private var onGoingLoad: Job? = null @@ -176,15 +180,19 @@ class HomeViewModel : ViewModel() { } } - fun loadAndCancel(preferredApiName: String?, currentPrefMedia: Int) = viewModelScope.launch { + fun loadAndCancel(preferredApiName: String?) = viewModelScope.launch { val api = getApiFromNameNull(preferredApiName) if (preferredApiName == noneApi.name) loadAndCancel(noneApi) else if (preferredApiName == randomApi.name || api == null) { - val validAPIs = AppUtils.filterProviderByPreferredMedia(apis, currentPrefMedia) - val apiRandom = validAPIs.random() - loadAndCancel(apiRandom) - context?.setKey(HOMEPAGE_API, apiRandom.name) + val validAPIs = context?.filterProviderByPreferredMedia() + if(validAPIs.isNullOrEmpty()) { + loadAndCancel(noneApi) + } else { + val apiRandom = validAPIs.random() + loadAndCancel(apiRandom) + setKey(HOMEPAGE_API, apiRandom.name) + } } else { loadAndCancel(api) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt index 723f7e0a..5cbe4854 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt @@ -818,7 +818,6 @@ class PlayerFragment : Fragment() { context?.let { ctx -> //if (this::viewModel.isInitialized) { viewModel.setViewPos( - ctx, if (isDownloadedFile) uriData.id else getEpisode()?.id, exoPlayer.currentPosition, exoPlayer.duration @@ -832,7 +831,7 @@ class PlayerFragment : Fragment() { }*/ if (isDownloadedFile) { - ctx.setLastWatched( + setLastWatched( uriData.parentId, uriData.id, uriData.episode, @@ -840,7 +839,7 @@ class PlayerFragment : Fragment() { true ) } else - viewModel.reloadEpisodes(ctx) + viewModel.reloadEpisodes() } } } @@ -1390,9 +1389,7 @@ class PlayerFragment : Fragment() { handlePlayerEvent(PlayerEventType.Play) } - context?.let { ctx -> - setPreferredSubLanguage(ctx.getAutoSelectLanguageISO639_1()) - } + setPreferredSubLanguage(getAutoSelectLanguageISO639_1()) subView = player_view?.findViewById(R.id.exo_subtitles) subView?.let { sView -> @@ -1400,7 +1397,7 @@ class PlayerFragment : Fragment() { subtitle_holder.addView(sView) } - subStyle = context?.getCurrentSavedStyle()!! + subStyle = getCurrentSavedStyle() onSubStyleChanged(subStyle) SubtitlesFragment.applyStyleEvent += ::onSubStyleChanged diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index 03ebec87..30364374 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -38,7 +38,7 @@ class QuickSearchFragment(var isMainApis: Boolean = false) : Fragment() { }) } - fun pushSync(activity: Activity?, autoSearch: String? = null, callback : (SearchClickCallback) -> Unit) { + fun pushSync(activity: Activity?, autoSearch: String? = null, callback: (SearchClickCallback) -> Unit) { clickCallback = callback activity.navigate(R.id.global_to_navigation_quick_search, Bundle().apply { putBoolean("mainapi", false) @@ -46,7 +46,7 @@ class QuickSearchFragment(var isMainApis: Boolean = false) : Fragment() { }) } - var clickCallback : ((SearchClickCallback) -> Unit)? = null + var clickCallback: ((SearchClickCallback) -> Unit)? = null } private val searchViewModel: SearchViewModel by activityViewModels() @@ -124,10 +124,7 @@ class QuickSearchFragment(var isMainApis: Boolean = false) : Fragment() { searchMagIcon.scaleY = 0.65f quick_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - context?.let { ctx -> - searchViewModel.searchAndCancel(query = query, context = ctx, isMainApis = isMainApis, ignoreSettings = true) - } - + searchViewModel.searchAndCancel(query = query, isMainApis = isMainApis, ignoreSettings = true) quick_search?.let { UIHelper.hideKeyboard(it) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index a48a8bdd..a9abfde2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.ui.result import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager -import android.content.Context import android.content.Context.CLIPBOARD_SERVICE import android.content.Intent import android.content.Intent.* @@ -60,6 +59,7 @@ import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.isAppInstalled import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.isConnectedToChromecast +import com.lagradost.cloudstream3.utils.AppUtils.openBrowser import com.lagradost.cloudstream3.utils.CastHelper.startCast import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getFolderName @@ -128,7 +128,7 @@ fun ResultEpisode.getDisplayPosition(): Long { return position } -fun Context.buildResultEpisode( +fun buildResultEpisode( name: String?, poster: String?, episode: Int, @@ -269,7 +269,7 @@ class ResultFragment : Fragment() { private var startValue: Int? = null private fun updateSync(id: Int) { - val syncList = context?.getSync(id, SyncApis.map { it.idPrefix }) ?: return + val syncList = getSync(id, SyncApis.map { it.idPrefix }) ?: return val list = ArrayList>() for (i in 0 until SyncApis.count()) { val res = syncList[i] ?: continue @@ -596,7 +596,7 @@ class ResultFragment : Fragment() { // 1. Checks if the lang should be downloaded // 2. Makes it into the download format // 3. Downloads it as a .vtt file - val downloadList = ctx.getDownloadSubsLanguageISO639_1() + val downloadList = getDownloadSubsLanguageISO639_1() main { subs?.let { subsList -> subsList.filter { @@ -857,7 +857,7 @@ class ResultFragment : Fragment() { //.map { watchType -> Triple(watchType.internalId, watchType.iconRes, watchType.stringRes) }, ) { context?.let { localContext -> - viewModel.updateWatchStatus(localContext, WatchType.fromInternalId(this.itemId)) + viewModel.updateWatchStatus(WatchType.fromInternalId(this.itemId)) } } } @@ -883,7 +883,7 @@ class ResultFragment : Fragment() { fab.context.getString(R.string.action_add_to_bookmarks), showApply = false, {}) { - viewModel.updateWatchStatus(fab.context, WatchType.values()[it]) + viewModel.updateWatchStatus(WatchType.values()[it]) } } } @@ -938,9 +938,8 @@ class ResultFragment : Fragment() { .map { Pair(it ?: -2, fromIndexToSeasonText(it)) }, ) { val id = this.itemId - context?.let { - viewModel.changeSeason(it, if (id == -2) null else id) - } + + viewModel.changeSeason(if (id == -2) null else id) } } } @@ -981,7 +980,7 @@ class ResultFragment : Fragment() { if (ranges != null) { it.popupMenuNoIconsAndNoStringRes(ranges.map { status -> Pair(status.ordinal, status.toString()) } .toList()) { - viewModel.changeDubStatus(requireContext(), DubStatus.values()[itemId]) + viewModel.changeDubStatus(DubStatus.values()[itemId]) } } } @@ -999,7 +998,7 @@ class ResultFragment : Fragment() { val ranges = episodeRanges if (ranges != null) { it.popupMenuNoIconsAndNoStringRes(ranges.mapIndexed { index, s -> Pair(index, s) }.toList()) { - viewModel.changeRange(requireContext(), itemId) + viewModel.changeRange(itemId) } } } @@ -1087,13 +1086,14 @@ class ResultFragment : Fragment() { updateSync(d.getId()) result_add_sync?.setOnClickListener { QuickSearchFragment.pushSync(activity, d.name) { click -> - context?.addSync(d.getId(), click.card.apiName, click.card.url)?.let { - showToast( - activity, - context?.getString(R.string.added_sync_format)?.format(click.card.name), - Toast.LENGTH_SHORT - ) - } + addSync(d.getId(), click.card.apiName, click.card.url) + + showToast( + activity, + context?.getString(R.string.added_sync_format)?.format(click.card.name), + Toast.LENGTH_SHORT + ) + updateSync(d.getId()) } } @@ -1290,7 +1290,7 @@ class ResultFragment : Fragment() { val tempUrl = url if (tempUrl != null) { result_reload_connectionerror.setOnClickListener { - viewModel.load(it.context, tempUrl, apiName, showFillers) + viewModel.load(tempUrl, apiName, showFillers) } result_reload_connection_open_in_browser?.setOnClickListener { @@ -1304,18 +1304,12 @@ class ResultFragment : Fragment() { } result_meta_site?.setOnClickListener { - val i = Intent(ACTION_VIEW) - i.data = Uri.parse(tempUrl) - try { - startActivity(i) - } catch (e: Exception) { - e.printStackTrace() - } + it.context?.openBrowser(tempUrl) } if (restart || viewModel.resultResponse.value == null) { viewModel.clear() - viewModel.load(ctx, tempUrl, apiName, showFillers) + viewModel.load(tempUrl, apiName, showFillers) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt index 7fa13561..d65a0ce2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt @@ -8,23 +8,24 @@ import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getId +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData +import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultSeason import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.setBookmarkedData +import com.lagradost.cloudstream3.utils.DataStoreHelper.setDub import com.lagradost.cloudstream3.utils.DataStoreHelper.setLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState -import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos import com.lagradost.cloudstream3.utils.FillerEpisodeCheck.getFillerEpisodes import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -49,6 +50,7 @@ class ResultViewModel : ViewModel() { id.value = null selectedSeason.value = -2 _dubSubEpisodes.value = null + _sync.value = null } private var repo: APIRepository? = null @@ -89,17 +91,17 @@ class ResultViewModel : ViewModel() { private val _sync: MutableLiveData>> = MutableLiveData() val sync: LiveData>> get() = _sync - fun updateWatchStatus(context: Context, status: WatchType) = viewModelScope.launch { + fun updateWatchStatus(status: WatchType) = viewModelScope.launch { val currentId = id.value ?: return@launch _watchStatus.postValue(status) val resultPage = page.value withContext(Dispatchers.IO) { - context.setResultWatchState(currentId, status.internalId) + setResultWatchState(currentId, status.internalId) if (resultPage != null) { - val current = context.getBookmarkedData(currentId) + val current = getBookmarkedData(currentId) val currentTime = System.currentTimeMillis() - context.setBookmarkedData( + setBookmarkedData( currentId, DataStoreHelper.BookmarkedData( currentId, @@ -117,13 +119,13 @@ class ResultViewModel : ViewModel() { } } - private fun loadWatchStatus(context: Context, localId: Int? = null) { + private fun loadWatchStatus(localId: Int? = null) { val currentId = localId ?: id.value ?: return - val currentWatch = context.getResultWatchState(currentId) + val currentWatch = getResultWatchState(currentId) _watchStatus.postValue(currentWatch) } - private fun filterEpisodes(context: Context, list: List?, selection: Int?, range: Int?) { + private fun filterEpisodes(list: List?, selection: Int?, range: Int?) { if (list == null) return val seasonTypes = HashMap() for (i in list) { @@ -141,7 +143,7 @@ class ResultViewModel : ViewModel() { val realSelection = if (!seasonTypes.containsKey(selection)) seasons.first() else selection val internalId = id.value - if (internalId != null) context.setResultSeason(internalId, realSelection) + if (internalId != null) setResultSeason(internalId, realSelection) selectedSeason.postValue(realSelection ?: -2) @@ -187,33 +189,38 @@ class ResultViewModel : ViewModel() { _publicEpisodes.postValue(Resource.Success(currentList)) } - fun changeSeason(context: Context, selection: Int?) { - filterEpisodes(context, _episodes.value, selection, null) + fun changeSeason(selection: Int?) { + filterEpisodes(_episodes.value, selection, null) } - fun changeRange(context: Context, range: Int?) { - filterEpisodes(context, _episodes.value, null, range) + fun changeRange(range: Int?) { + filterEpisodes(_episodes.value, null, range) } - fun changeDubStatus(context: Context, status: DubStatus?) { + fun changeDubStatus(status: DubStatus?) { dubSubEpisodes.value?.get(status)?.let { episodes -> + id.value?.let { + if (status != null) { + setDub(it, status) + } + } _dubStatus.postValue(status) - updateEpisodes(context, null, episodes, null) + updateEpisodes(null, episodes, null) } } fun updateSync(context: Context?, sync: List>) = viewModelScope.launch { - if(context == null) return@launch + if (context == null) return@launch val list = ArrayList>() for (s in sync) { - val result = safeApiCall { s.first.getResult(context, s.second) } + val result = safeApiCall { s.first.getResult(s.second) } list.add(result) _sync.postValue(list) } } - private fun updateEpisodes(context: Context, localId: Int?, list: List, selection: Int?) { + private fun updateEpisodes(localId: Int?, list: List, selection: Int?) { _episodes.postValue(list) val set = HashMap() @@ -221,25 +228,23 @@ class ResultViewModel : ViewModel() { episodeById.postValue(set) filterEpisodes( - context, list, - if (selection == -1) context.getResultSeason(localId ?: id.value ?: return) else selection, null + if (selection == -1) getResultSeason(localId ?: id.value ?: return) else selection, null ) } - fun reloadEpisodes(context: Context) { + fun reloadEpisodes() { val current = _episodes.value ?: return val copy = current.map { - val posDur = context.getViewPos(it.id) + val posDur = getViewPos(it.id) it.copy(position = posDur?.position ?: 0, duration = posDur?.duration ?: 0) } - updateEpisodes(context, null, copy, selectedSeason.value) + updateEpisodes(null, copy, selectedSeason.value) } - fun setViewPos(context: Context?, episodeId: Int?, pos: Long, dur: Long) { + fun setViewPos(episodeId: Int?, pos: Long, dur: Long) { try { - if (context == null || episodeId == null) return - context.setViewPos(episodeId, pos, dur) + DataStoreHelper.setViewPos(episodeId, pos, dur) var index = episodeById.value?.get(episodeId) ?: return var startPos = pos @@ -251,7 +256,7 @@ class ResultViewModel : ViewModel() { if (startDur > 0L && (startPos * 100 / startDur) > 95) { index++ if (episodeList.size <= index) { // last episode - context.removeLastWatched(parentId) + removeLastWatched(parentId) return } episode = episodeList[index] @@ -261,7 +266,7 @@ class ResultViewModel : ViewModel() { continue } else { - context.setLastWatched(parentId, episode.id, episode.episode, episode.season) + setLastWatched(parentId, episode.id, episode.episode, episode.season) return } } @@ -279,7 +284,7 @@ class ResultViewModel : ViewModel() { return name } - fun load(context: Context, url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch { + fun load(url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch { _resultResponse.postValue(Resource.Loading(url)) _publicEpisodes.postValue(Resource.Loading()) @@ -301,9 +306,9 @@ class ResultViewModel : ViewModel() { page.postValue(d) val mainId = d.getId() id.postValue(mainId) - loadWatchStatus(context, mainId) + loadWatchStatus(mainId) - context.setKey( + setKey( DOWNLOAD_HEADER_CACHE, mainId.toString(), VideoDownloadHelper.DownloadHeaderCached( @@ -319,11 +324,14 @@ class ResultViewModel : ViewModel() { when (d) { is AnimeLoadResponse -> { - //TODO context.getKey<>() isdub + if (d.episodes.isEmpty()) { + _dubSubEpisodes.postValue(emptyMap()) + return@launch + } - val isDub = - d.episodes.containsKey(DubStatus.Dubbed) && !d.episodes[DubStatus.Dubbed].isNullOrEmpty() - val dubStatus = if (isDub) DubStatus.Dubbed else DubStatus.Subbed + val status = getDub(mainId) + val statuses = d.episodes.map { it.key } + val dubStatus = if (statuses.contains(status)) status else statuses.first() _dubStatus.postValue(dubStatus) _dubSubSelections.postValue(d.episodes.keys) @@ -335,23 +343,21 @@ class ResultViewModel : ViewModel() { for ((index, i) in ep.value.withIndex()) { val episode = i.episode ?: (index + 1) - episodes.add( - context.buildResultEpisode( - filterName(i.name), - i.posterUrl, - episode, - null, // TODO FIX SEASON - i.url, - apiName, - mainId + index + 1 + idIndex * 100000, - index, - i.rating, - i.description, - if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let { - it.contains(episode) && it[episode] == true - } ?: false else false, - ) - ) + episodes.add(buildResultEpisode( + filterName(i.name), + i.posterUrl, + episode, + null, // TODO FIX SEASON + i.url, + apiName, + mainId + index + 1 + idIndex * 100000, + index, + i.rating, + i.description, + if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let { + it.contains(episode) && it[episode] == true + } ?: false else false, + )) } idIndex++ @@ -360,7 +366,7 @@ class ResultViewModel : ViewModel() { _dubSubEpisodes.postValue(res) res[dubStatus]?.let { episodes -> - updateEpisodes(context, mainId, episodes, -1) + updateEpisodes(mainId, episodes, -1) } } @@ -368,7 +374,7 @@ class ResultViewModel : ViewModel() { val episodes = ArrayList() for ((index, i) in d.episodes.withIndex()) { episodes.add( - context.buildResultEpisode( + buildResultEpisode( filterName(i.name), i.posterUrl, i.episode ?: (index + 1), @@ -382,32 +388,31 @@ class ResultViewModel : ViewModel() { null, ) ) + } - updateEpisodes(context, mainId, episodes, -1) + updateEpisodes(mainId, episodes, -1) } is MovieLoadResponse -> { - updateEpisodes( - context, mainId, arrayListOf( - context.buildResultEpisode( - d.name, - null, - 0, - null, - d.dataUrl, - d.apiName, - (mainId), // HAS SAME ID - 0, - null, - null, - null, - ) - ), -1 - ) + buildResultEpisode( + d.name, + null, + 0, + null, + d.dataUrl, + d.apiName, + (mainId), // HAS SAME ID + 0, + null, + null, + null, + ).let { + updateEpisodes(mainId, listOf(it), -1) + } } is TorrentLoadResponse -> { updateEpisodes( - context, mainId, arrayListOf( - context.buildResultEpisode( + mainId, listOf( + buildResultEpisode( d.name, null, 0, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 29fb6d7c..c09fd9c5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -48,7 +48,9 @@ class SearchFragment : Fragment() { fun List.filterSearchResponse(): List { return this.filter { response -> if (response is AnimeSearchResponse) { - (response.dubStatus.isNullOrEmpty()) || (response.dubStatus.any { APIRepository.dubStatusActive.contains(it) }) + (response.dubStatus.isNullOrEmpty()) || (response.dubStatus.any { + APIRepository.dubStatusActive.contains(it) + }) } else { true } @@ -291,16 +293,14 @@ class SearchFragment : Fragment() { } } - if(context?.isTvSettings() == true) { + if (context?.isTvSettings() == true) { search_filter.isFocusable = true search_filter.isFocusableInTouchMode = true } main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String): Boolean { - context?.let { ctx -> - searchViewModel.searchAndCancel(query = query, context = ctx) - } + searchViewModel.searchAndCancel(query = query) main_search?.let { hideKeyboard(it) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt index 889b3a92..df4b19cd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.ui.search -import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -41,9 +40,9 @@ class SearchViewModel : ViewModel() { } var onGoingSearch: Job? = null - fun searchAndCancel(query: String, isMainApis : Boolean = true, ignoreSettings : Boolean = false, context: Context) { + fun searchAndCancel(query: String, isMainApis: Boolean = true, ignoreSettings: Boolean = false) { onGoingSearch?.cancel() - onGoingSearch = search(query, isMainApis, ignoreSettings, context) + onGoingSearch = search(query, isMainApis, ignoreSettings) } data class SyncSearchResultSearchResponse( @@ -66,57 +65,59 @@ class SearchViewModel : ViewModel() { ) } - private fun search(query: String, isMainApis : Boolean = true, ignoreSettings : Boolean = false, context: Context) = viewModelScope.launch { - if (query.length <= 1) { - clearSearch() - return@launch - } + private fun search(query: String, isMainApis: Boolean = true, ignoreSettings: Boolean = false) = + viewModelScope.launch { + if (query.length <= 1) { + clearSearch() + return@launch + } - _searchResponse.postValue(Resource.Loading()) + _searchResponse.postValue(Resource.Loading()) - val currentList = ArrayList() + val currentList = ArrayList() - _currentSearch.postValue(ArrayList()) + _currentSearch.postValue(ArrayList()) - withContext(Dispatchers.IO) { // This interrupts UI otherwise - if (isMainApis) { - repos.filter { a -> - ignoreSettings || (providersActive.size == 0 || providersActive.contains(a.name)) - }.apmap { a -> // Parallel - val search = a.search(query) - currentList.add(OnGoingSearch(a.name, search)) - _currentSearch.postValue(currentList) - } - } else { - syncApis.apmap { a -> - val search = safeApiCall { - a.search(context, query)?.map { it.toSearchResponse() } ?: throw ErrorLoadingException() + withContext(Dispatchers.IO) { // This interrupts UI otherwise + if (isMainApis) { + repos.filter { a -> + ignoreSettings || (providersActive.size == 0 || providersActive.contains(a.name)) + }.apmap { a -> // Parallel + val search = a.search(query) + currentList.add(OnGoingSearch(a.name, search)) + _currentSearch.postValue(currentList) } + } else { + syncApis.apmap { a -> + val search = safeApiCall { + a.search(query)?.map { it.toSearchResponse() } + ?: throw ErrorLoadingException() + } - currentList.add(OnGoingSearch(a.name, search)) + currentList.add(OnGoingSearch(a.name, search)) + } } } - } - _currentSearch.postValue(currentList) + _currentSearch.postValue(currentList) - val list = ArrayList() - val nestedList = - currentList.map { it.data }.filterIsInstance>>().map { it.value } + val list = ArrayList() + val nestedList = + currentList.map { it.data }.filterIsInstance>>().map { it.value } - // I do it this way to move the relevant search results to the top - var index = 0 - while (true) { - var added = 0 - for (sublist in nestedList) { - if (sublist.size > index) { - list.add(sublist[index]) - added++ + // I do it this way to move the relevant search results to the top + var index = 0 + while (true) { + var added = 0 + for (sublist in nestedList) { + if (sublist.size > index) { + list.add(sublist[index]) + added++ + } } + if (added == 0) break + index++ } - if (added == 0) break - index++ - } - _searchResponse.postValue(Resource.Success(list)) - } + _searchResponse.postValue(Resource.Success(list)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 90c9d592..21412a35 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import android.content.res.Configuration import android.net.Uri +import android.os.Build import android.os.Bundle import android.os.Environment import android.widget.ImageView @@ -24,6 +25,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiSettings import com.lagradost.cloudstream3.APIHolder.restrictedApis import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.MainActivity.Companion.setLocale import com.lagradost.cloudstream3.MainActivity.Companion.showToast @@ -36,8 +38,6 @@ import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment -import com.lagradost.cloudstream3.utils.AppUtils -import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.HOMEPAGE_API import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.Qualities @@ -50,7 +50,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadDir -import com.lagradost.cloudstream3.utils.VideoDownloadManager.isScopedStorage import java.io.File import kotlin.concurrent.thread @@ -126,13 +125,14 @@ class SettingsFragment : PreferenceFragmentCompat() { ).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top private fun showAccountSwitch(context: Context, api: AccountManager) { + val accounts = api.getAccounts() ?: return + val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_switch) val dialog = builder.show() - val accounts = api.getAccounts(context) dialog.findViewById(R.id.account_add)?.setOnClickListener { - api.authenticate(it.context) + api.authenticate() } val ogIndex = api.accountIndex @@ -141,7 +141,7 @@ class SettingsFragment : PreferenceFragmentCompat() { for (index in accounts) { api.accountIndex = index - val accountInfo = api.loginInfo(context) + val accountInfo = api.loginInfo() if (accountInfo != null) { items.add(accountInfo) } @@ -149,26 +149,26 @@ class SettingsFragment : PreferenceFragmentCompat() { api.accountIndex = ogIndex val adapter = AccountAdapter(items, R.layout.account_single) { dialog?.dismissSafe(activity) - api.changeAccount(it.view.context, it.card.accountIndex) + api.changeAccount(it.card.accountIndex) } val list = dialog.findViewById(R.id.account_list) list?.adapter = adapter } - private fun showLoginInfo(context: Context, api: AccountManager, info: OAuth2API.LoginInfo) { + private fun showLoginInfo(api: AccountManager, info: OAuth2API.LoginInfo) { val builder = - AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_managment) + AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom).setView(R.layout.account_managment) val dialog = builder.show() dialog.findViewById(R.id.account_profile_picture)?.setImage(info.profilePicture) dialog.findViewById(R.id.account_logout)?.setOnClickListener { - it.context?.let { ctx -> - api.logOut(ctx) - dialog.dismissSafe(activity) - } + api.logOut() + dialog.dismissSafe(activity) } - dialog.findViewById(R.id.account_name)?.text = info.name ?: context.getString(R.string.no_data) + (info.name ?: context?.getString(R.string.no_data))?.let { + dialog.findViewById(R.id.account_name)?.text = it + } dialog.findViewById(R.id.account_site)?.text = api.name dialog.findViewById(R.id.account_switch_account)?.setOnClickListener { dialog.dismissSafe(activity) @@ -208,11 +208,11 @@ class SettingsFragment : PreferenceFragmentCompat() { title = getString(R.string.login_format).format(api.name, getString(R.string.account)) setOnPreferenceClickListener { pref -> pref.context?.let { ctx -> - val info = api.loginInfo(ctx) + val info = api.loginInfo() if (info != null) { - showLoginInfo(ctx, api, info) + showLoginInfo(api, info) } else { - api.authenticate(ctx) + api.authenticate() } } @@ -305,7 +305,7 @@ class SettingsFragment : PreferenceFragmentCompat() { // app_name_download_path = Cloudstream and does not change depending on release. // DOES NOT WORK ON SCOPED STORAGE. - val secondaryDir = if (isScopedStorage) null else Environment.getExternalStorageDirectory().absolutePath + + val secondaryDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + File.separator + resources.getString(R.string.app_name_download_path) val first = listOf(defaultDir, secondaryDir) return (try { @@ -352,7 +352,7 @@ class SettingsFragment : PreferenceFragmentCompat() { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val currentPrefMedia = - settingsManager.getInt(getString(R.string.preferred_media_settings), 0) + settingsManager.getInt(getString(R.string.prefer_media_type_key), 0) activity?.showBottomDialog( prefNames.toList(), @@ -361,15 +361,10 @@ class SettingsFragment : PreferenceFragmentCompat() { true, {}) { settingsManager.edit() - .putInt(getString(R.string.preferred_media_settings), prefValues[it]) + .putInt(getString(R.string.prefer_media_type_key), prefValues[it]) .apply() - val apilist = AppUtils.filterProviderByPreferredMedia(apis, prefValues[it]) - val apiRandom = if (apilist.size > 0) { - apilist.random().name - } else { - "" - } - context?.setKey(HOMEPAGE_API, apiRandom) + + removeKey(HOMEPAGE_API) (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } } return@setOnPreferenceClickListener true diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index 5051d1b5..430590f9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -19,10 +19,11 @@ import androidx.fragment.app.Fragment import com.google.android.exoplayer2.text.Cue import com.google.android.exoplayer2.ui.CaptionStyleCompat import com.jaredrummler.android.colorpicker.ColorPickerDialog +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.MainActivity.Companion.showToast import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.Event import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog @@ -87,8 +88,8 @@ class SubtitlesFragment : Fragment() { this.setKey(SUBTITLE_KEY, style) } - fun Context.getCurrentSavedStyle(): SaveCaptionStyle { - return this.getKey(SUBTITLE_KEY) ?: SaveCaptionStyle( + fun getCurrentSavedStyle(): SaveCaptionStyle { + return getKey(SUBTITLE_KEY) ?: SaveCaptionStyle( getDefColor(0), getDefColor(2), getDefColor(3), @@ -109,11 +110,11 @@ class SubtitlesFragment : Fragment() { return TypedValue.applyDimension(unit, size, metrics).toInt() } - fun Context.getDownloadSubsLanguageISO639_1(): List { + fun getDownloadSubsLanguageISO639_1(): List { return getKey(SUBTITLE_DOWNLOAD_KEY) ?: listOf("en") } - fun Context.getAutoSelectLanguageISO639_1(): String { + fun getAutoSelectLanguageISO639_1(): String { return getKey(SUBTITLE_AUTO_SELECT_KEY) ?: "en" } } @@ -184,7 +185,7 @@ class SubtitlesFragment : Fragment() { context?.fixPaddingStatusbar(subs_root) - state = requireContext().getCurrentSavedStyle() + state = getCurrentSavedStyle() context?.updateState() fun View.setup(id: Int) { @@ -381,17 +382,17 @@ class SubtitlesFragment : Fragment() { val lang639_1 = langMap.map { it.ISO_639_1 } activity?.showDialog( langMap.map { it.languageName }, - lang639_1.indexOf(textView.context.getAutoSelectLanguageISO639_1()), + lang639_1.indexOf(getAutoSelectLanguageISO639_1()), (textView as TextView).text.toString(), true, dismissCallback ) { index -> - textView.context.setKey(SUBTITLE_AUTO_SELECT_KEY, lang639_1[index]) + setKey(SUBTITLE_AUTO_SELECT_KEY, lang639_1[index]) } } - subs_auto_select_language.setOnLongClickListener { textView -> - textView.context.setKey(SUBTITLE_AUTO_SELECT_KEY, "en") + subs_auto_select_language.setOnLongClickListener { + setKey(SUBTITLE_AUTO_SELECT_KEY, "en") showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT) return@setOnLongClickListener true } @@ -399,7 +400,7 @@ class SubtitlesFragment : Fragment() { subs_download_languages.setOnClickListener { textView -> val langMap = SubtitleHelper.languages val lang639_1 = langMap.map { it.ISO_639_1 } - val keys = textView.context.getDownloadSubsLanguageISO639_1() + val keys = getDownloadSubsLanguageISO639_1() val keyMap = keys.map { lang639_1.indexOf(it) }.filter { it >= 0 } activity?.showMultiDialog( @@ -408,12 +409,12 @@ class SubtitlesFragment : Fragment() { (textView as TextView).text.toString(), dismissCallback ) { indexList -> - textView.context.setKey(SUBTITLE_DOWNLOAD_KEY, indexList.map { lang639_1[it] }.toList()) + setKey(SUBTITLE_DOWNLOAD_KEY, indexList.map { lang639_1[it] }.toList()) } } - subs_download_languages.setOnLongClickListener { textView -> - textView.context.setKey(SUBTITLE_DOWNLOAD_KEY, listOf("en")) + subs_download_languages.setOnLongClickListener { + setKey(SUBTITLE_DOWNLOAD_KEY, listOf("en")) showToast(activity, R.string.subs_default_reset_toast, Toast.LENGTH_SHORT) return@setOnLongClickListener true diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 9b88abbf..1d0b270f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -21,6 +21,7 @@ import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.wrappers.Wrappers import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.result.ResultFragment import com.lagradost.cloudstream3.utils.UIHelper.navigate import java.net.URL @@ -46,17 +47,21 @@ object AppUtils { } fun Context.openBrowser(url: String) { - val components = arrayOf(ComponentName(applicationContext, MainActivity::class.java)) - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse(url) + try { + val components = arrayOf(ComponentName(applicationContext, MainActivity::class.java)) + val intent = Intent(Intent.ACTION_VIEW) + intent.data = Uri.parse(url) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - startActivity( - Intent.createChooser(intent, null) - .putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, components) - ) - else - startActivity(intent) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + startActivity( + Intent.createChooser(intent, null) + .putExtra(Intent.EXTRA_EXCLUDE_COMPONENTS, components) + ) + else + startActivity(intent) + } catch (e : Exception) { + logError(e) + } } fun splitQuery(url: URL): Map { @@ -233,23 +238,4 @@ object AppUtils { } return currentAudioFocusRequest } - - fun filterProviderByPreferredMedia( - apis: ArrayList, - currentPrefMedia: Int - ): List { - val allApis = apis.filter { api -> api.hasMainPage } - return if (currentPrefMedia < 1) { - allApis - } else { - // Filter API depending on preferred media type - val listEnumAnime = listOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA) - val listEnumMovieTv = listOf(TvType.Movie, TvType.TvSeries, TvType.Cartoon) - val mediaTypeList = if (currentPrefMedia == 1) listEnumMovieTv else listEnumAnime - - val filteredAPI = - allApis.filter { api -> api.supportedTypes.any { it in mediaTypeList } } - filteredAPI - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt index f5af6b97..edca0760 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastHelper.kt @@ -40,7 +40,9 @@ object CastHelper { (epData.name ?: "Episode ${epData.episode}") + " - ${link.name}" ) - movieMetadata.putString(MediaMetadata.KEY_TITLE, holder.title) + holder.title?.let { + movieMetadata.putString(MediaMetadata.KEY_TITLE, it) + } val srcPoster = epData.poster ?: holder.poster if (srcPoster != null) { @@ -58,7 +60,7 @@ object CastHelper { val builder = MediaInfo.Builder(link.url) .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED) - .setContentType(MimeTypes.VIDEO_UNKNOWN) + .setContentType(if (link.isM3u8) MimeTypes.APPLICATION_M3U8 else MimeTypes.VIDEO_MP4) .setMetadata(movieMetadata) .setMediaTracks(tracks) data?.let { @@ -95,8 +97,8 @@ object CastHelper { subtitles: List, startIndex: Int? = null, startTime: Long? = null, - ) : Boolean { - if(this == null) return false + ): Boolean { + if (this == null) return false if (episodes.isEmpty()) return false if (currentLinks.size <= currentEpisodeIndex) return false @@ -105,13 +107,15 @@ object CastHelper { val holder = MetadataHolder(apiName, isMovie, title, poster, currentEpisodeIndex, episodes, currentLinks, subtitles) - val index = if(startIndex == null || startIndex < 0) 0 else startIndex + val index = if (startIndex == null || startIndex < 0) 0 else startIndex val mediaItem = getMediaInfo(epData, holder, index, JSONObject(mapper.writeValueAsString(holder)), subtitles) awaitLinks( - this.remoteMediaClient?.load(MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() ) + this.remoteMediaClient?.load( + MediaLoadRequestData.Builder().setMediaInfo(mediaItem).setCurrentTime(startTime ?: 0L).build() + ) ) { if (currentLinks.size > index + 1) startCast( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index e6e62850..9850b21e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -1,19 +1,20 @@ package com.lagradost.cloudstream3.utils -import android.content.Context +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.ui.WatchType -import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.getKeys -import com.lagradost.cloudstream3.utils.DataStore.removeKey -import com.lagradost.cloudstream3.utils.DataStore.setKey const val VIDEO_POS_DUR = "video_pos_dur" const val RESULT_WATCH_STATE = "result_watch_state" const val RESULT_WATCH_STATE_DATA = "result_watch_state_data" const val RESULT_RESUME_WATCHING = "result_resume_watching" const val RESULT_SEASON = "result_season" +const val RESULT_DUB = "result_dub" object DataStoreHelper { data class PosDur(val position: Long, val duration: Long) @@ -57,21 +58,21 @@ object DataStoreHelper { var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION - fun Context.getAllWatchStateIds(): List { + fun getAllWatchStateIds(): List? { val folder = "$currentAccount/$RESULT_WATCH_STATE" - return getKeys(folder).mapNotNull { + return getKeys(folder)?.mapNotNull { it.removePrefix("$folder/").toIntOrNull() } } - fun Context.getAllResumeStateIds(): List { + fun getAllResumeStateIds(): List? { val folder = "$currentAccount/$RESULT_RESUME_WATCHING" - return getKeys(folder).mapNotNull { + return getKeys(folder)?.mapNotNull { it.removePrefix("$folder/").toIntOrNull() } } - fun Context.setLastWatched( + fun setLastWatched( parentId: Int?, episodeId: Int?, episode: Int?, @@ -93,12 +94,12 @@ object DataStoreHelper { ) } - fun Context.removeLastWatched(parentId: Int?) { + fun removeLastWatched(parentId: Int?) { if (parentId == null) return removeKey("$currentAccount/$RESULT_RESUME_WATCHING", parentId.toString()) } - fun Context.getLastWatched(id: Int?): VideoDownloadHelper.ResumeWatching? { + fun getLastWatched(id: Int?): VideoDownloadHelper.ResumeWatching? { if (id == null) return null return getKey( "$currentAccount/$RESULT_RESUME_WATCHING", @@ -106,27 +107,35 @@ object DataStoreHelper { ) } - fun Context.setBookmarkedData(id: Int?, data: BookmarkedData) { + fun setBookmarkedData(id: Int?, data: BookmarkedData) { if (id == null) return setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data) } - fun Context.getBookmarkedData(id: Int?): BookmarkedData? { + fun getBookmarkedData(id: Int?): BookmarkedData? { if (id == null) return null return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString()) } - fun Context.setViewPos(id: Int?, pos: Long, dur: Long) { + fun setViewPos(id: Int?, pos: Long, dur: Long) { if (id == null) return if (dur < 10_000) return // too short setKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), PosDur(pos, dur)) } - fun Context.getViewPos(id: Int): PosDur? { + fun getViewPos(id: Int): PosDur? { return getKey("$currentAccount/$VIDEO_POS_DUR", id.toString(), null) } - fun Context.setResultWatchState(id: Int?, status: Int) { + fun getDub(id: Int): DubStatus { + return DubStatus.values()[getKey("$currentAccount/$RESULT_DUB", id.toString()) ?: 0] + } + + fun setDub(id: Int, status: DubStatus) { + setKey("$currentAccount/$RESULT_DUB", id.toString(), status.ordinal) + } + + fun setResultWatchState(id: Int?, status: Int) { if (id == null) return val folder = "$currentAccount/$RESULT_WATCH_STATE" if (status == WatchType.NONE.internalId) { @@ -137,23 +146,23 @@ object DataStoreHelper { } } - fun Context.getResultWatchState(id: Int): WatchType { + fun getResultWatchState(id: Int): WatchType { return WatchType.fromInternalId(getKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), null)) } - fun Context.getResultSeason(id: Int): Int { - return getKey("$currentAccount/$RESULT_SEASON", id.toString(), -1)!! + fun getResultSeason(id: Int): Int { + return getKey("$currentAccount/$RESULT_SEASON", id.toString()) ?: -1 } - fun Context.setResultSeason(id: Int, value: Int?) { + fun setResultSeason(id: Int, value: Int?) { setKey("$currentAccount/$RESULT_SEASON", id.toString(), value) } - fun Context.addSync(id: Int, idPrefix: String, url: String) { + fun addSync(id: Int, idPrefix: String, url: String) { setKey("${idPrefix}_sync", id.toString(), url) } - fun Context.getSync(id : Int, idPrefixes : List) : List { + fun getSync(id: Int, idPrefixes: List): List { return idPrefixes.map { idPrefix -> getKey("${idPrefix}_sync", id.toString()) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadFileWorkManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadFileWorkManager.kt index 16936f49..09a91c5e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadFileWorkManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DownloadFileWorkManager.kt @@ -5,10 +5,10 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey -import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.VideoDownloadManager.WORK_KEY_INFO import com.lagradost.cloudstream3.utils.VideoDownloadManager.WORK_KEY_PACKAGE import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadCheck @@ -62,8 +62,8 @@ class DownloadFileWorkManager(val context: Context, private val workerParams: Wo } private fun removeKeys(key: String) { - applicationContext.removeKey(WORK_KEY_INFO, key) - applicationContext.removeKey(WORK_KEY_PACKAGE, key) + removeKey(WORK_KEY_INFO, key) + removeKey(WORK_KEY_PACKAGE, key) } private suspend fun awaitDownload(id: Int) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt index 0e2d88f0..f00587e6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -22,6 +22,8 @@ import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager import com.fasterxml.jackson.annotation.JsonProperty import com.hippo.unifile.UniFile +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError @@ -30,7 +32,6 @@ import com.lagradost.cloudstream3.services.VideoDownloadService import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.removeKey -import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -150,16 +151,16 @@ object VideoDownloadManager { private const val SUCCESS_DOWNLOAD_DONE = 1 private const val SUCCESS_STREAM = 3 private const val SUCCESS_STOPPED = 2 - private const val ERROR_DELETING_FILE = - 3 // will not download the next one, but is still classified as an error + // will not download the next one, but is still classified as an error + private const val ERROR_DELETING_FILE = 3 private const val ERROR_CREATE_FILE = -2 private const val ERROR_UNKNOWN = -10 - private const val ERROR_OPEN_FILE = -3 + //private const val ERROR_OPEN_FILE = -3 private const val ERROR_TOO_SMALL_CONNECTION = -4 - private const val ERROR_WRONG_CONTENT = -5 + //private const val ERROR_WRONG_CONTENT = -5 private const val ERROR_CONNECTION_ERROR = -6 - private const val ERROR_MEDIA_STORE_URI_CANT_BE_CREATED = -7 - private const val ERROR_CONTENT_RESOLVER_CANT_OPEN_STREAM = -8 + //private const val ERROR_MEDIA_STORE_URI_CANT_BE_CREATED = -7 + //private const val ERROR_CONTENT_RESOLVER_CANT_OPEN_STREAM = -8 private const val ERROR_CONTENT_RESOLVER_NOT_FOUND = -9 private const val KEY_RESUME_PACKAGES = "download_resume" @@ -460,7 +461,7 @@ object VideoDownloadManager { val base = basePathToFile(context, basePath) val folder = base?.gotoDir(relativePath, false) - if (isScopedStorage && base.isDownloadDir()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && base.isDownloadDir()) { return context.contentResolver?.getExistingFolderStartName(relativePath) } else { // val normalPath = @@ -529,8 +530,6 @@ object VideoDownloadManager { } } - val isScopedStorage = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q - data class CreateNotificationMetadata( val type: DownloadType, val bytesDownloaded: Long, @@ -561,7 +560,7 @@ object VideoDownloadManager { var resume = tryResume val baseFile = context.getBasePath() - if (isScopedStorage && baseFile.first?.isDownloadDir() == true) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && baseFile.first?.isDownloadDir() == true) { val cr = context.contentResolver ?: return StreamData(ERROR_CONTENT_RESOLVER_NOT_FOUND) val currentExistingFile = @@ -658,7 +657,7 @@ object VideoDownloadManager { val displayName = getDisplayName(name, extension) val relativePath = - if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && basePath.first.isDownloadDir()) getRelativePath(folder) else folder fun deleteFile(): Int { return delete(context, name, relativePath, extension, parentId, basePath.first) @@ -720,7 +719,7 @@ object VideoDownloadManager { if (extension == "mp4" && bytesTotal < 5000000) return ERROR_TOO_SMALL_CONNECTION // DATA IS LESS THAN 5MB, SOMETHING IS WRONG parentId?.let { - context.setKey( + setKey( KEY_DOWNLOAD_INFO, it.toString(), DownloadedFileInfo( @@ -798,8 +797,8 @@ object VideoDownloadManager { } DownloadActionType.Stop -> { isStopped = true; updateNotification() - context.removeKey(KEY_RESUME_PACKAGES, event.first.toString()) - saveQueue(context) + removeKey(KEY_RESUME_PACKAGES, event.first.toString()) + saveQueue() } DownloadActionType.Resume -> { isPaused = false; updateNotification() @@ -1025,7 +1024,7 @@ object VideoDownloadManager { val displayName = getDisplayName(name, extension) // If scoped storage and using download dir (not accessible with UniFile) - if (isScopedStorage && basePath.isDownloadDir()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && basePath.isDownloadDir()) { val relativePath = getRelativePath(folder) val lastContent = context.contentResolver.getExistingDownloadUriOrNullQ(relativePath, displayName) @@ -1081,7 +1080,7 @@ object VideoDownloadManager { val basePath = context.getBasePath() val relativePath = - if (isScopedStorage && basePath.first.isDownloadDir()) getRelativePath(folder) else folder + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && basePath.first.isDownloadDir()) getRelativePath(folder) else folder val stream = setupStream(context, name, relativePath, extension, realIndex > 0) if (stream.errorCode != SUCCESS_STREAM) return stream.errorCode @@ -1119,7 +1118,7 @@ object VideoDownloadManager { fun updateInfo() { parentId?.let { - context.setKey( + setKey( KEY_DOWNLOAD_INFO, it.toString(), DownloadedFileInfo( @@ -1364,7 +1363,7 @@ object VideoDownloadManager { val link = item.links[index] val resume = pkg.linkIndex == index - context.setKey( + setKey( KEY_RESUME_PACKAGES, id.toString(), DownloadResumePackage(item, index) @@ -1383,7 +1382,7 @@ object VideoDownloadManager { } } if (connectionResult != null && connectionResult > 0) { // SUCCESS - context.removeKey(KEY_RESUME_PACKAGES, id.toString()) + removeKey(KEY_RESUME_PACKAGES, id.toString()) break } } @@ -1410,7 +1409,7 @@ object VideoDownloadManager { context.getKey(KEY_DOWNLOAD_INFO, id.toString()) ?: return null val base = basePathToFile(context, info.basePath) - if (isScopedStorage && base.isDownloadDir()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && base.isDownloadDir()) { val cr = context.contentResolver ?: return null val fileUri = cr.getExistingDownloadUriOrNullQ(info.relativePath, info.displayName) ?: return null @@ -1457,7 +1456,7 @@ object VideoDownloadManager { downloadStatusEvent.invoke(Pair(id, DownloadType.IsStopped)) downloadDeleteEvent.invoke(id) val base = basePathToFile(context, info.basePath) - if (isScopedStorage && base.isDownloadDir()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && base.isDownloadDir()) { val cr = context.contentResolver ?: return false val fileUri = cr.getExistingDownloadUriOrNullQ(info.relativePath, info.displayName) @@ -1505,7 +1504,7 @@ object VideoDownloadManager { // } downloadQueue.addLast(pkg) downloadCheck(context, notificationCallback) - if (setKey) saveQueue(context) + if (setKey) saveQueue() } else { downloadEvent.invoke( Pair(pkg.item.ep.id, DownloadActionType.Resume) @@ -1513,12 +1512,12 @@ object VideoDownloadManager { } } - private fun saveQueue(context: Context) { + private fun saveQueue() { val dQueue = downloadQueue.toList() .mapIndexed { index, any -> DownloadQueueResumePackage(index, any) } .toTypedArray() - context.setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue) + setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue) } /*fun isMyServiceRunning(context: Context, serviceClass: Class<*>): Boolean { @@ -1576,7 +1575,7 @@ object VideoDownloadManager { pkg: DownloadResumePackage, ) { val key = pkg.item.ep.id.toString() - context.setKey(WORK_KEY_PACKAGE, key, pkg) + setKey(WORK_KEY_PACKAGE, key, pkg) startWork(context, key) } @@ -1596,7 +1595,7 @@ object VideoDownloadManager { ) val key = info.ep.id.toString() - context.setKey(WORK_KEY_INFO, key, info) + setKey(WORK_KEY_INFO, key, info) startWork(context, key) } diff --git a/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt b/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt index cfdabef3..910268a5 100644 --- a/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt +++ b/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3 -import com.lagradost.cloudstream3.movieproviders.ThenosProvider import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SubtitleHelper