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