This commit is contained in:
antonydp 2023-01-23 22:12:27 +00:00 committed by GitHub
commit c810bd2d8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 370 additions and 69 deletions

View file

@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.initAll import com.lagradost.cloudstream3.APIHolder.initAll
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.loadThemes import com.lagradost.cloudstream3.CommonActivity.loadThemes
@ -58,6 +59,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
@ -84,6 +86,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.BackupUtils.backupGithub
import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
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.getKey
@ -443,6 +447,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
//private var mCastSession: CastSession? = null //private var mCastSession: CastSession? = null
lateinit var mSessionManager: SessionManager lateinit var mSessionManager: SessionManager
private val mSessionManagerListener: SessionManagerListener<Session> by lazy { SessionManagerListenerImpl() } private val mSessionManagerListener: SessionManagerListener<Session> by lazy { SessionManagerListenerImpl() }
private val accountsLoginLock = Mutex()
private inner class SessionManagerListenerImpl : SessionManagerListener<Session> { private inner class SessionManagerListenerImpl : SessionManagerListener<Session> {
override fun onSessionStarting(session: Session) { override fun onSessionStarting(session: Session) {
@ -503,6 +508,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
} }
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
if (githubApi.getLatestLoginData() != null && settingsManager.getBoolean(getString(R.string.automatic_cloud_backups), true)) {
this@MainActivity.backupGithub()
}
} }
@ -640,36 +649,52 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
} }
lateinit var viewModel: ResultViewModel2 lateinit var viewModel: ResultViewModel2
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
viewModel = viewModel =
ViewModelProvider(this)[ResultViewModel2::class.java] ViewModelProvider(this)[ResultViewModel2::class.java]
return super.onCreateView(name, context, attrs) return super.onCreateView(name, context, attrs)
} }
private fun hidePreviewPopupDialog() { private fun hidePreviewPopupDialog() {
viewModel.clear() viewModel.clear()
bottomPreviewPopup.dismissSafe(this) bottomPreviewPopup.dismissSafe(this)
} }
var bottomPreviewPopup: BottomSheetDialog? = null var bottomPreviewPopup: BottomSheetDialog? = null
private fun showPreviewPopupDialog(): BottomSheetDialog { private fun showPreviewPopupDialog(): BottomSheetDialog {
val ret = (bottomPreviewPopup ?: run { val ret = (bottomPreviewPopup ?: run {
val builder = val builder =
BottomSheetDialog(this) BottomSheetDialog(this)
builder.setContentView(R.layout.bottom_resultview_preview) builder.setContentView(R.layout.bottom_resultview_preview)
builder.setOnDismissListener { builder.setOnDismissListener {
bottomPreviewPopup = null bottomPreviewPopup = null
viewModel.clear() viewModel.clear()
}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
})
bottomPreviewPopup = ret
return ret
}
override fun onStart() {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
super.onStart()
ioSafe {
accountsLoginLock.withLock {
if (githubApi.getLatestLoginData() != null && settingsManager.getBoolean(
getString(R.string.automatic_cloud_backups),
true
)
) {
context?.restorePromptGithub()
}
} }
builder.setCanceledOnTouchOutside(true) }
builder.show()
builder
})
bottomPreviewPopup = ret
return ret
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -845,15 +870,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// init accounts // init accounts
ioSafe { ioSafe {
for (api in accountManagers) { accountsLoginLock.withLock{
api.init() for (api in accountManagers) {
} api.init()
}
inAppAuths.amap { api -> inAppAuths.amap { api ->
try { try {
api.initialize() api.initialize()
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
}
} }
} }
} }
@ -1078,4 +1105,4 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// } // }
} }
} }

View file

@ -11,6 +11,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
val malApi = MALApi(0) val malApi = MALApi(0)
val aniListApi = AniListApi(0) val aniListApi = AniListApi(0)
val openSubtitlesApi = OpenSubtitlesApi(0) val openSubtitlesApi = OpenSubtitlesApi(0)
val githubApi = GithubApi(0)
val indexSubtitlesApi = IndexSubtitleApi() val indexSubtitlesApi = IndexSubtitleApi()
val addic7ed = Addic7ed() val addic7ed = Addic7ed()
@ -23,7 +24,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
// this needs init with context and can be accessed in settings // this needs init with context and can be accessed in settings
val accountManagers val accountManagers
get() = listOf( get() = listOf(
malApi, aniListApi, openSubtitlesApi, //nginxApi malApi, aniListApi, openSubtitlesApi, githubApi //nginxApi
) )
// used for active syncing // used for active syncing

View file

@ -0,0 +1,166 @@
package com.lagradost.cloudstream3.syncproviders.providers
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.BackupUtils.getBackup
import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.nicehttp.RequestBodyTypes
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
class GithubApi(index: Int) : InAppAuthAPIManager(index){
override val idPrefix = "Github"
override val name = "Github"
override val icon = R.drawable.ic_github_logo
override val requiresPassword = true
override val createAccountUrl = "https://github.com/settings/tokens/new?description=Cloudstream+Backup&scopes=gist"
data class GithubOAuthEntity(
var gistId: String,
var token: String,
var userName: String,
var userAvatar: String,
)
companion object {
const val GITHUB_USER_KEY: String = "github_user" // user data like profile
var currentSession: GithubOAuthEntity? = null
}
private fun getAuthKey(): GithubOAuthEntity? {
return getKey(accountId, GITHUB_USER_KEY)
}
data class GistsElements (
@JsonProperty("id") val gistId:String,
@JsonProperty("files") val files: Map<String, File>,
@JsonProperty("owner") val owner: OwnerData
)
data class OwnerData(
@JsonProperty("login") val userName: String,
@JsonProperty("avatar_url") val userAvatar : String
)
data class File (
@JsonProperty("content") val dataRaw: String?
)
data class GistRequestBody(
@JsonProperty("description") val description: String,
@JsonProperty("public") val public : Boolean,
@JsonProperty("files") val files: FilesGist?
)
data class FilesGist(
@JsonProperty("Cloudstream_Backup_data.txt") val description: ContentFilesGist?,
)
data class ContentFilesGist(
@JsonProperty("content") val description: String?,
)
private suspend fun initLogin(githubToken: String): Boolean{
val response = app.get("https://api.github.com/gists",
headers= mapOf(
Pair("Accept" , "application/vnd.github+json"),
Pair("Authorization", "token $githubToken"),
)
)
if (!response.isSuccessful) { return false }
val repo = tryParseJson<List<GistsElements>>(response.text)?.filter {
it.files.keys.first() == "Cloudstream_Backup_data.txt"
}
if (repo?.isEmpty() == true){
val backupData = context?.getBackup()
val gitResponse = app.post("https://api.github.com/gists",
headers= mapOf(
Pair("Accept" , "application/vnd.github+json"),
Pair("Authorization", "token $githubToken"),
),
requestBody = GistRequestBody("Cloudstream private backup gist", false, FilesGist(ContentFilesGist(backupData?.toJson()))).toJson().toRequestBody(
RequestBodyTypes.JSON.toMediaTypeOrNull()))
if (!gitResponse.isSuccessful) {return false}
tryParseJson<GistsElements>(gitResponse.text).let {
setKey(accountId, GITHUB_USER_KEY, GithubOAuthEntity(
token = githubToken,
gistId = it?.gistId?: run {
return false
},
userName = it.owner.userName,
userAvatar = it.owner.userAvatar
))
}
return true
}
else{
repo?.first().let {
setKey(accountId, GITHUB_USER_KEY, GithubOAuthEntity(
token = githubToken,
gistId = it?.gistId?: run {
return false
},
userName = it.owner.userName,
userAvatar = it.owner.userAvatar
))
ioSafe {
context?.restorePromptGithub()
}
return true
}
}
}
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
switchToNewAccount()
val githubToken = data.password ?: throw IllegalArgumentException ("Requires Password")
try {
if (initLogin(githubToken)) {
registerAccount()
return true
}
} catch (e: Exception) {
logError(e)
}
switchToOldAccount()
return false
}
override fun getLatestLoginData(): InAppAuthAPI.LoginData? {
val current = getAuthKey() ?: return null
return InAppAuthAPI.LoginData(server = current.gistId, password = current.token, username = current.userName)
}
override suspend fun initialize() {
currentSession = getAuthKey()
val gistId = currentSession?.gistId ?: return
val token = currentSession?.token ?: return
setKey(gistId, token)
}
override fun logOut() {
removeKey(accountId, GITHUB_USER_KEY)
removeAccountKeys()
currentSession = getAuthKey()
}
override fun loginInfo(): AuthAPI.LoginInfo? {
return getAuthKey()?.let { user ->
AuthAPI.LoginInfo(
profilePicture = user.userAvatar,
name = user.userName,
accountIndex = accountIndex,
)
}
}
}

View file

@ -126,8 +126,8 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
} }
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean { override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
val username = data.username ?: throw ErrorLoadingException("Requires Username") val username = data.username ?: throw IllegalArgumentException ("Requires Username")
val password = data.password ?: throw ErrorLoadingException("Requires Password") val password = data.password ?: throw IllegalArgumentException ("Requires Password")
switchToNewAccount() switchToNewAccount()
try { try {
if (initLogin(username, password)) { if (initLogin(username, password)) {

View file

@ -93,6 +93,7 @@ class HomeFragment : Fragment() {
val configEvent = Event<Int>() val configEvent = Event<Int>()
var currentSpan = 1 var currentSpan = 1
val listHomepageItems = mutableListOf<SearchResponse>() val listHomepageItems = mutableListOf<SearchResponse>()
val reloadStoredDataEvent = Event<Unit>()
private val errorProfilePics = listOf( private val errorProfilePics = listOf(
R.drawable.monke_benene, R.drawable.monke_benene,
@ -477,15 +478,21 @@ class HomeFragment : Fragment() {
bookmarksUpdatedEvent += ::bookmarksUpdated bookmarksUpdatedEvent += ::bookmarksUpdated
afterPluginsLoadedEvent += ::afterPluginsLoaded afterPluginsLoadedEvent += ::afterPluginsLoaded
mainPluginsLoadedEvent += ::afterMainPluginsLoaded mainPluginsLoadedEvent += ::afterMainPluginsLoaded
reloadStoredDataEvent += ::reloadStoredEvent
} }
override fun onStop() { override fun onStop() {
bookmarksUpdatedEvent -= ::bookmarksUpdated bookmarksUpdatedEvent -= ::bookmarksUpdated
afterPluginsLoadedEvent -= ::afterPluginsLoaded afterPluginsLoadedEvent -= ::afterPluginsLoaded
mainPluginsLoadedEvent -= ::afterMainPluginsLoaded mainPluginsLoadedEvent -= ::afterMainPluginsLoaded
reloadStoredDataEvent -= ::reloadStoredEvent
super.onStop() super.onStop()
} }
private fun reloadStoredEvent(input: Unit) {
reloadStored()
}
private fun reloadStored() { private fun reloadStored() {
homeViewModel.loadResumeWatching() homeViewModel.loadResumeWatching()
val list = EnumSet.noneOf(WatchType::class.java) val list = EnumSet.noneOf(WatchType::class.java)

View file

@ -20,6 +20,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi
import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.OAuth2API import com.lagradost.cloudstream3.syncproviders.OAuth2API
@ -227,6 +228,8 @@ class SettingsAccount : PreferenceFragmentCompat() {
R.string.mal_key to malApi, R.string.mal_key to malApi,
R.string.anilist_key to aniListApi, R.string.anilist_key to aniListApi,
R.string.opensubtitles_key to openSubtitlesApi, R.string.opensubtitles_key to openSubtitlesApi,
R.string.github_key to githubApi
) )
for ((key, api) in syncApis) { for ((key, api) in syncApis) {

View file

@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPadd
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.BackupUtils.backup import com.lagradost.cloudstream3.utils.BackupUtils.backup
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
import com.lagradost.cloudstream3.utils.BackupUtils.restorePromptGithub
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
@ -31,6 +32,7 @@ import java.io.BufferedReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.io.OutputStream import java.io.OutputStream
class SettingsUpdates : PreferenceFragmentCompat() { class SettingsUpdates : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)

View file

@ -14,14 +14,18 @@ import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.plugins.PLUGINS_KEY import com.lagradost.cloudstream3.plugins.PLUGINS_KEY
import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.githubApi
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_CACHED_LIST
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_SHOULD_UPDATE_LIST
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_TOKEN_KEY
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_UNIXTIME_KEY
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_USER_KEY
import com.lagradost.cloudstream3.syncproviders.providers.GithubApi
import com.lagradost.cloudstream3.syncproviders.providers.GithubApi.Companion.GITHUB_USER_KEY
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_CACHED_LIST
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_REFRESH_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_REFRESH_TOKEN_KEY
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_SHOULD_UPDATE_LIST
@ -29,6 +33,11 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_T
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY
import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY
import com.lagradost.cloudstream3.ui.home.HomeFragment
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs
import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
import com.lagradost.cloudstream3.utils.DataStore.mapper import com.lagradost.cloudstream3.utils.DataStore.mapper
@ -37,12 +46,14 @@ import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
import com.lagradost.cloudstream3.utils.VideoDownloadManager.isDownloadDir import com.lagradost.cloudstream3.utils.VideoDownloadManager.isDownloadDir
import com.lagradost.nicehttp.RequestBodyTypes
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.IOException import java.io.IOException
import java.io.PrintWriter import java.io.PrintWriter
import java.lang.System.currentTimeMillis import java.lang.System.currentTimeMillis
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
object BackupUtils { object BackupUtils {
/** /**
@ -65,14 +76,15 @@ object BackupUtils {
// The plugins themselves are not backed up // The plugins themselves are not backed up
PLUGINS_KEY, PLUGINS_KEY,
PLUGINS_KEY_LOCAL, PLUGINS_KEY_LOCAL,
GITHUB_USER_KEY,
OPEN_SUBTITLES_USER_KEY, OPEN_SUBTITLES_USER_KEY,
"nginx_user", // Nginx user key "nginx_user", // Nginx user key
) )
/** false if blacklisted key */ /** false if blacklisted key */
private fun String.isTransferable(): Boolean { private fun String.isTransferable(): Boolean {
return !nonTransferableKeys.contains(this) return !nonTransferableKeys.contains(this) and !nonTransferableKeys.any { this.endsWith(it) }
} }
private var restoreFileSelector: ActivityResultLauncher<Array<String>>? = null private var restoreFileSelector: ActivityResultLauncher<Array<String>>? = null
@ -286,4 +298,46 @@ object BackupUtils {
setKeyRaw(it.key, it.value, isEditingAppSettings) setKeyRaw(it.key, it.value, isEditingAppSettings)
} }
} }
}
fun FragmentActivity.backupGithub(){
val backup = this.getBackup().toJson()
val gistId = githubApi.getLatestLoginData()?.server ?: throw IllegalArgumentException ("Requires Username")
val token = githubApi.getLatestLoginData()?.password ?: throw IllegalArgumentException ("Requires Username")
ioSafe {
app.patch("https://api.github.com/gists/$gistId",
headers= mapOf(
Pair("Accept" , "application/vnd.github+json"),
Pair("Authorization", "token $token"),
),
requestBody = GithubApi.GistRequestBody(
"Cloudstream private backup gist",
false,
GithubApi.FilesGist(GithubApi.ContentFilesGist(backup)))
.toJson()
.toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
)
}
showToast(
this,
R.string.backup_success,
Toast.LENGTH_LONG
)
}
suspend fun Context.restorePromptGithub() {
val gistId = githubApi.getLatestLoginData()?.server ?: throw IllegalAccessException()
val jsonData = app.get("https://api.github.com/gists/$gistId").text
val dataRaw =
parseJson<GithubApi.GistsElements>(jsonData ?: "").files.values.first().dataRaw
?: throw IllegalAccessException()
val data = parseJson<BackupFile>(dataRaw)
restore(
data,
restoreSettings = true,
restoreDataStore = true
)
HomeFragment.reloadStoredDataEvent.invoke(Unit)
}
}

View file

@ -141,6 +141,8 @@
<!--<string name="episode_sync_settings">Update watch progress</string>--> <!--<string name="episode_sync_settings">Update watch progress</string>-->
<string name="episode_sync_settings_des">Sincronizza automaticamente gli episodi guardati</string> <string name="episode_sync_settings_des">Sincronizza automaticamente gli episodi guardati</string>
<string name="restore_settings">Ripristinare i dati da backup</string> <string name="restore_settings">Ripristinare i dati da backup</string>
<string name="automatic_restore_settings">Backup automatico su Github</string>
<string name="backup_settings">Backup data</string> <string name="backup_settings">Backup data</string>
<string name="restore_success">File di backup caricato</string> <string name="restore_success">File di backup caricato</string>
<string name="restore_failed_format" formatted="true">Impossibile ripristinare i dati dal file %s</string> <string name="restore_failed_format" formatted="true">Impossibile ripristinare i dati dal file %s</string>
@ -343,7 +345,7 @@
<string name="subtitle_offset_extra_hint_none_format">Nessun ritardo dei sottotitoli</string> <string name="subtitle_offset_extra_hint_none_format">Nessun ritardo dei sottotitoli</string>
<!-- <!--
Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet, Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet,
see: see:
https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300 https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
--> -->
@ -507,4 +509,4 @@
<string name="delayed_update_notice">L\'app verrà aggiornata all\'uscita</string> <string name="delayed_update_notice">L\'app verrà aggiornata all\'uscita</string>
<string name="update_started">Aggiornamento avviato</string> <string name="update_started">Aggiornamento avviato</string>
<string name="plugin_downloaded">Plugin scaricato</string> <string name="plugin_downloaded">Plugin scaricato</string>
</resources> </resources>

View file

@ -61,6 +61,8 @@
<string name="pref_filter_search_quality_key" translatable="false">pref_filter_search_quality_key</string> <string name="pref_filter_search_quality_key" translatable="false">pref_filter_search_quality_key</string>
<string name="enable_nsfw_on_providers_key" translatable="false">enable_nsfw_on_providers_key</string> <string name="enable_nsfw_on_providers_key" translatable="false">enable_nsfw_on_providers_key</string>
<string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string> <string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string>
<string name="automatic_cloud_backups" translatable="false">automatic_cloud_backups</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string> <string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %s</string> <string name="storage_size_format" formatted="true" translatable="false">%s • %s</string>
@ -222,7 +224,12 @@
<string name="episode_sync_settings">Update watch progress</string> <string name="episode_sync_settings">Update watch progress</string>
<string name="episode_sync_settings_des">Automatically sync your current episode progress</string> <string name="episode_sync_settings_des">Automatically sync your current episode progress</string>
<string name="restore_settings">Restore data from backup</string> <string name="restore_settings">Restore data from backup</string>
<string name="backup_settings">Back up data</string> <string name="automatic_restore_settings">Automatic backup from Github</string>
<string name="cloud_backups_off">Cloud Backup deactivated</string>
<string name="cloud_backups_on">Cloud Backup enabled</string>
<string name="backup_settings">Backup data</string>
<string name="restore_success">Loaded backup file</string> <string name="restore_success">Loaded backup file</string>
<string name="restore_failed_format" formatted="true">Failed to restore data from file %s</string> <string name="restore_failed_format" formatted="true">Failed to restore data from file %s</string>
<string name="backup_success">Data stored</string> <string name="backup_success">Data stored</string>
@ -428,6 +435,7 @@
<string name="anilist_key" translatable="false">anilist_key</string> <string name="anilist_key" translatable="false">anilist_key</string>
<string name="mal_key" translatable="false">mal_key</string> <string name="mal_key" translatable="false">mal_key</string>
<string name="opensubtitles_key" translatable="false">opensubtitles_key</string> <string name="opensubtitles_key" translatable="false">opensubtitles_key</string>
<string name="github_key" translatable="false">github_key</string>
<string name="nginx_key" translatable="false">nginx_key</string> <string name="nginx_key" translatable="false">nginx_key</string>
<string name="example_password">password123</string> <string name="example_password">password123</string>
<string name="example_username">MyCoolUsername</string> <string name="example_username">MyCoolUsername</string>
@ -479,7 +487,7 @@
<string name="subtitle_offset_extra_hint_none_format">No subtitle delay</string> <string name="subtitle_offset_extra_hint_none_format">No subtitle delay</string>
<!-- <!--
Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet, Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet,
see: see:
https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300 https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
--> -->

View file

@ -1,27 +1,30 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference <Preference
android:key="@string/mal_key" android:key="@string/mal_key"
android:icon="@drawable/mal_logo" /> android:icon="@drawable/mal_logo" />
<Preference <Preference
android:key="@string/anilist_key" android:key="@string/anilist_key"
android:icon="@drawable/ic_anilist_icon" /> android:icon="@drawable/ic_anilist_icon" />
<Preference <Preference
android:key="@string/opensubtitles_key" android:key="@string/opensubtitles_key"
android:icon="@drawable/open_subtitles_icon" /> android:icon="@drawable/open_subtitles_icon" />
<!-- <Preference--> <Preference
<!-- android:key="@string/nginx_key"--> android:key="@string/github_key"
<!-- android:icon="@drawable/nginx" />--> android:icon="@drawable/ic_github_logo" />
<!-- <Preference-->
<!-- android:key="@string/nginx_key"-->
<!-- android:icon="@drawable/nginx" />-->
<!-- <Preference--> <!-- <Preference-->
<!-- android:title="@string/nginx_info_title"--> <!-- android:title="@string/nginx_info_title"-->
<!-- android:icon="@drawable/nginx_question"--> <!-- android:icon="@drawable/nginx_question"-->
<!-- android:summary="@string/nginx_info_summary">--> <!-- android:summary="@string/nginx_info_summary">-->
<!-- <intent--> <!-- <intent-->
<!-- android:action="android.intent.action.VIEW"--> <!-- android:action="android.intent.action.VIEW"-->
<!-- android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />--> <!-- android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />-->
<!-- </Preference>--> <!-- </Preference>-->
</PreferenceScreen> </PreferenceScreen>

View file

@ -1,6 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:title="@string/check_for_update"
app:icon="@drawable/ic_baseline_system_update_24"
app:key="@string/manual_check_update_key"
app:summary="@string/app_version" />
<Preference
android:icon="@drawable/baseline_save_as_24"
android:key="@string/backup_key"
android:title="@string/backup_settings" />
<Preference
android:icon="@drawable/baseline_restore_page_24"
android:key="@string/restore_key"
android:title="@string/restore_settings" />
<SwitchPreference
android:defaultValue="true"
android:icon="@drawable/baseline_restore_page_24"
android:key="@string/automatic_cloud_backups"
android:summaryOff="@string/cloud_backups_off"
android:summaryOn="@string/cloud_backups_on"
android:title="@string/automatic_restore_settings" />
<Preference
android:icon="@drawable/baseline_description_24"
android:key="@string/show_logcat_key"
android:title="@string/show_log_cat" />
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:icon="@drawable/ic_baseline_bug_report_24" android:icon="@drawable/ic_baseline_bug_report_24"
@ -9,7 +37,7 @@
android:summaryOn="@string/bug_report_settings_on" android:summaryOn="@string/bug_report_settings_on"
android:title="@string/pref_disable_acra" /> android:title="@string/pref_disable_acra" />
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_category_app_updates"> android:title="@string/pref_category_app_updates">
<Preference <Preference
android:title="@string/check_for_update" android:title="@string/check_for_update"
@ -37,7 +65,7 @@
app:key="@string/auto_update_key" /> app:key="@string/auto_update_key" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_category_backup"> android:title="@string/pref_category_backup">
<Preference <Preference
android:icon="@drawable/baseline_save_as_24" android:icon="@drawable/baseline_save_as_24"
@ -50,14 +78,14 @@
android:title="@string/restore_settings" /> android:title="@string/restore_settings" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_category_extensions"> android:title="@string/pref_category_extensions">
<SwitchPreference <SwitchPreference
android:defaultValue="true" android:defaultValue="true"
android:icon="@drawable/ic_baseline_extension_24" android:icon="@drawable/ic_baseline_extension_24"
android:key="@string/auto_update_plugins_key" android:key="@string/auto_update_plugins_key"
android:title="@string/automatic_plugin_updates" /> android:title="@string/automatic_plugin_updates" />
<SwitchPreference <SwitchPreference
android:defaultValue="false" android:defaultValue="false"
android:icon="@drawable/ic_baseline_extension_24" android:icon="@drawable/ic_baseline_extension_24"
@ -66,7 +94,7 @@
android:summary="@string/automatic_plugin_download_summary" /> android:summary="@string/automatic_plugin_download_summary" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_category_actions"> android:title="@string/pref_category_actions">
<Preference <Preference
android:icon="@drawable/baseline_description_24" android:icon="@drawable/baseline_description_24"
@ -77,4 +105,4 @@
android:title="@string/redo_setup_process" android:title="@string/redo_setup_process"
app:key="@string/redo_setup_key" /> app:key="@string/redo_setup_key" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>