diff --git a/app/build.gradle b/app/build.gradle index ee125493..e08f0dcb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { targetSdkVersion 30 versionCode 47 - versionName "2.10.26" + versionName "2.10.27" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 0f484923..00eeef5e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -53,7 +53,6 @@ object APIHolder { PelisflixProvider(), SeriesflixProvider(), IHaveNoTvProvider(), // Documentaries provider - LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...) VMoveeProvider(), AllMoviesForYouProvider(), VidEmbedProvider(), @@ -472,12 +471,6 @@ fun base64Encode(array: ByteArray): String { class ErrorLoadingException(message: String? = null) : Exception(message) -fun parseRating(ratingString: String?): Int? { - if (ratingString == null) return null - val floatRating = ratingString.toFloatOrNull() ?: return null - return (floatRating * 10).toInt() -} - fun MainAPI.fixUrlNull(url: String?): String? { if (url.isNullOrEmpty()) { return null diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index c8bf72bf..8971c05f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -137,6 +137,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { R.id.navigation_settings_ui, R.id.navigation_settings_account, R.id.navigation_settings_lang, + R.id.navigation_settings_general, ).contains(destination.id) val landscape = when (resources.configuration.orientation) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LookMovieProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LookMovieProvider.kt deleted file mode 100644 index 9559f563..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LookMovieProvider.kt +++ /dev/null @@ -1,298 +0,0 @@ -package com.lagradost.cloudstream3.movieproviders - -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.module.kotlin.readValue -import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.unixTime -import com.lagradost.cloudstream3.extractors.M3u8Manifest -import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.getQualityFromName -import org.jsoup.Jsoup - -//BE AWARE THAT weboas.is is a clone of lookmovie -class LookMovieProvider : MainAPI() { - override val hasQuickSearch = true - override var name = "LookMovie" - override var mainUrl = "https://lookmovie.io" - - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - data class LookMovieSearchResult( - @JsonProperty("backdrop") val backdrop: String?, - @JsonProperty("imdb_rating") val imdb_rating: String, - @JsonProperty("poster") val poster: String?, - @JsonProperty("slug") val slug: String, - @JsonProperty("title") val title: String, - @JsonProperty("year") val year: String?, - // @JsonProperty("flag_quality") val flag_quality: Int?, - ) - - data class LookMovieTokenRoot( - @JsonProperty("data") val data: LookMovieTokenResult?, - @JsonProperty("success") val success: Boolean, - ) - - data class LookMovieTokenResult( - @JsonProperty("accessToken") val accessToken: String, - @JsonProperty("subtitles") val subtitles: List?, - ) - - data class LookMovieTokenSubtitle( - @JsonProperty("language") val language: String, - @JsonProperty("source") val source: String?, - //@JsonProperty("source_id") val source_id: String, - //@JsonProperty("kind") val kind: String, - //@JsonProperty("id") val id: String, - @JsonProperty("file") val file: String, - ) - - data class LookMovieSearchResultRoot( - // @JsonProperty("per_page") val per_page: Int?, - // @JsonProperty("total") val total: Int?, - @JsonProperty("result") val result: List?, - ) - - data class LookMovieEpisode( - @JsonProperty("title") var title: String, - @JsonProperty("index") var index: String, - @JsonProperty("episode") var episode: String, - @JsonProperty("id_episode") var idEpisode: Int, - @JsonProperty("season") var season: String, - ) - - override suspend fun quickSearch(query: String): List { - val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query" - val movieResponse = app.get(movieUrl).text - val movies = mapper.readValue(movieResponse).result - - val showsUrl = "$mainUrl/api/v1/shows/search/?q=$query" - val showsResponse = app.get(showsUrl).text - val shows = mapper.readValue(showsResponse).result - - val returnValue = ArrayList() - if (!movies.isNullOrEmpty()) { - for (m in movies) { - val url = "$mainUrl/movies/view/${m.slug}" - returnValue.add( - MovieSearchResponse( - m.title, - url, - this.name, - TvType.Movie, - m.poster ?: m.backdrop, - m.year?.toIntOrNull() - ) - ) - } - } - - if (!shows.isNullOrEmpty()) { - for (s in shows) { - val url = "$mainUrl/shows/view/${s.slug}" - returnValue.add( - MovieSearchResponse( - s.title, - url, - this.name, - TvType.TvSeries, - s.poster ?: s.backdrop, - s.year?.toIntOrNull() - ) - ) - } - } - - return returnValue - } - - override suspend fun search(query: String): List { - suspend fun search(query: String, isMovie: Boolean): List { - val url = "$mainUrl/${if (isMovie) "movies" else "shows"}/search/?q=$query" - val response = app.get(url).text - val document = Jsoup.parse(response) - - val items = document.select("div.flex-wrap-movielist > div.movie-item-style-1") - return items.map { item -> - val titleHolder = item.selectFirst("> div.mv-item-infor > h6 > a") - val href = fixUrl(titleHolder!!.attr("href")) - val name = titleHolder.text() - val posterHolder = item.selectFirst("> div.image__placeholder > a") - val poster = posterHolder!!.selectFirst("> img")?.attr("data-src") - val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull() - if (isMovie) { - MovieSearchResponse( - name, href, this.name, TvType.Movie, poster, year - ) - } else - TvSeriesSearchResponse( - name, href, this.name, TvType.TvSeries, poster, year, null - ) - } - } - - val movieList = search(query, true).toMutableList() - val seriesList = search(query, false) - movieList.addAll(seriesList) - return movieList - } - - data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean) - - private fun addSubtitles( - subs: List?, - subtitleCallback: (SubtitleFile) -> Unit - ) { - if (subs == null) return - subs.forEach { - if (it.file.endsWith(".vtt")) - subtitleCallback.invoke(SubtitleFile(it.language, fixUrl(it.file))) - } - } - - private suspend fun loadCurrentLinks(url: String, callback: (ExtractorLink) -> Unit) { - val response = app.get(url.replace("\$unixtime", unixTime.toString())).text - M3u8Manifest.extractLinks(response).forEach { - callback.invoke( - ExtractorLink( - this.name, - "${this.name} - ${it.second}", - fixUrl(it.first), - "", - getQualityFromName(it.second), - true - ) - ) - } - } - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val localData: LookMovieLinkLoad = mapper.readValue(data) - - if (localData.isMovie) { - val tokenResponse = app.get(localData.url).text - val root = mapper.readValue(tokenResponse) - val accessToken = root.data?.accessToken ?: return false - addSubtitles(root.data.subtitles, subtitleCallback) - loadCurrentLinks(localData.extraUrl.replace("\$accessToken", accessToken), callback) - return true - } else { - loadCurrentLinks(localData.url, callback) - val subResponse = app.get(localData.extraUrl).text - val subs = mapper.readValue>(subResponse) - addSubtitles(subs, subtitleCallback) - } - return true - } - - override suspend fun load(url: String): LoadResponse? { - val response = app.get(url).text - val document = Jsoup.parse(response) - val isMovie = url.contains("/movies/") - - val watchHeader = document.selectFirst("div.watch-heading") - val nameHeader = watchHeader!!.selectFirst("> h1.bd-hd") - val year = nameHeader!!.selectFirst("> span")?.text()?.toIntOrNull() - val title = nameHeader.ownText() - val rating = - parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span")!!.text()) - val imgElement = document.selectFirst("div.movie-img > p.movie__poster") - val img = imgElement?.attr("style") - var poster = if (img.isNullOrEmpty()) null else "url\\((.*?)\\)".toRegex() - .find(img)?.groupValues?.get(1) - if (poster.isNullOrEmpty()) poster = imgElement?.attr("data-background-image") - val descript = document.selectFirst("p.description-short")!!.text() - val id = "${if (isMovie) "id_movie" else "id_show"}:(.*?),".toRegex() - .find(response)?.groupValues?.get(1) - ?.replace(" ", "") - ?: return null - val realSlug = url.replace("$mainUrl/${if (isMovie) "movies" else "shows"}/view/", "") - val realUrl = - "$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1" - - if (isMovie) { - val localData = - LookMovieLinkLoad( - realUrl, - "$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8", - true - ).toJson() - - return MovieLoadResponse( - title, - url, - this.name, - TvType.Movie, - localData, - poster, - year, - descript, - rating - ) - } else { - val tokenResponse = app.get(realUrl).text - val root = mapper.readValue(tokenResponse) - val accessToken = root.data?.accessToken ?: return null - - val window = - "window\\['show_storage'] =((.|\\n)*?<)".toRegex().find(response)?.groupValues?.get( - 1 - ) - ?: return null - // val id = "id_show:(.*?),".toRegex().find(response.text)?.groupValues?.get(1) ?: return null - val season = "seasons:.*\\[((.|\\n)*?)]".toRegex().find(window)?.groupValues?.get(1) - ?: return null - - fun String.fixSeasonJson(replace: String): String { - return this.replace("$replace:", "\"$replace\":") - } - - val json = season - .replace("\'", "\"") - .fixSeasonJson("title") - .fixSeasonJson("id_episode") - .fixSeasonJson("episode") - .fixSeasonJson("index") - .fixSeasonJson("season") - val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]" - - val episodes = mapper.readValue>(realJson).map { - val localData = - LookMovieLinkLoad( - "$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8", - "https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=${it.idEpisode}", - false - ).toJson() - - - Episode( - localData, - it.title, - it.season.toIntOrNull(), - it.episode.toIntOrNull(), - ) - }.toList() - - return TvSeriesLoadResponse( - title, - url, - this.name, - TvType.TvSeries, - ArrayList(episodes), - poster, - year, - descript, - null, - rating - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 247d4b6f..8b1d9f6e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -1786,7 +1786,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio } result_description.setOnClickListener { val builder: AlertDialog.Builder = - AlertDialog.Builder(requireContext()) + AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) builder.setMessage(d.plot) .setTitle(if (d.type == TvType.Torrent) R.string.torrent_plot else R.string.result_plot) .show() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index c31c9e0e..b0319b26 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -11,7 +11,6 @@ import androidx.appcompat.app.AlertDialog import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R @@ -24,7 +23,6 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSub import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.OAuth2API -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.beneneCount import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -38,7 +36,7 @@ import kotlinx.android.synthetic.main.add_account_input.* class SettingsAccount : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setUpToolbar(R.string.category_credits) + setUpToolbar(R.string.category_account) } private fun showLoginInfo(api: AccountManager, info: AuthAPI.LoginInfo) { @@ -179,16 +177,7 @@ class SettingsAccount : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { hideKeyboard() - setPreferencesFromResource(R.xml.settings_credits_account, rootKey) - val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) - - getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener { - val builder: AlertDialog.Builder = AlertDialog.Builder(it.context) - builder.setTitle(R.string.legal_notice) - builder.setMessage(R.string.legal_notice_text) - builder.show() - return@setOnPreferenceClickListener true - } + setPreferencesFromResource(R.xml.settings_account, rootKey) val syncApis = listOf( @@ -213,33 +202,5 @@ class SettingsAccount : PreferenceFragmentCompat() { } } } - - try { - beneneCount = settingsManager.getInt(getString(R.string.benene_count), 0) - getPref(R.string.benene_count)?.let { pref -> - pref.summary = - if (beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( - R.string.benene_count_text - ).format( - beneneCount - ) - - pref.setOnPreferenceClickListener { - try { - beneneCount++ - settingsManager.edit().putInt(getString(R.string.benene_count), beneneCount) - .apply() - it.summary = getString(R.string.benene_count_text).format(beneneCount) - } catch (e: Exception) { - logError(e) - } - - return@setOnPreferenceClickListener true - } - } - } catch (e: Exception) { - e.printStackTrace() - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 884feaba..f86419bc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -40,7 +40,7 @@ class SettingsFragment : Fragment() { } } - fun PreferenceFragmentCompat?.setUpToolbar(@StringRes title : Int) { + fun PreferenceFragmentCompat?.setUpToolbar(@StringRes title: Int) { if (this == null) return settings_toolbar?.apply { setTitle(title) @@ -131,6 +131,7 @@ class SettingsFragment : Fragment() { } listOf( + Pair(settings_general, R.id.action_navigation_settings_to_navigation_settings_general), Pair(settings_player, R.id.action_navigation_settings_to_navigation_settings_player), Pair(settings_credits, R.id.action_navigation_settings_to_navigation_settings_account), Pair(settings_ui, R.id.action_navigation_settings_to_navigation_settings_ui), diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt new file mode 100644 index 00000000..22ca83a3 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -0,0 +1,182 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.view.View +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.hippo.unifile.UniFile +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.VideoDownloadManager +import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath +import java.io.File + +class SettingsGeneral : PreferenceFragmentCompat() { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setUpToolbar(R.string.category_general) + } + + // Open file picker + private val pathPicker = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + // It lies, it can be null if file manager quits. + if (uri == null) return@registerForActivityResult + val context = context ?: AcraApplication.context ?: return@registerForActivityResult + // RW perms for the path + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + + context.contentResolver.takePersistableUriPermission(uri, flags) + + val file = UniFile.fromUri(context, uri) + println("Selected URI path: $uri - Full path: ${file.filePath}") + + // Stores the real URI using download_path_key + // Important that the URI is stored instead of filepath due to permissions. + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(getString(R.string.download_path_key), uri.toString()).apply() + + // From URI -> File path + // File path here is purely for cosmetic purposes in settings + (file.filePath ?: uri.toString()).let { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(getString(R.string.download_path_pref), it).apply() + } + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settins_general, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener { + val builder: AlertDialog.Builder = + AlertDialog.Builder(it.context, R.style.AlertDialogCustom) + builder.setTitle(R.string.legal_notice) + builder.setMessage(R.string.legal_notice_text) + builder.show() + return@setOnPreferenceClickListener true + } + + getPref(R.string.dns_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.dns_pref) + val prefValues = resources.getIntArray(R.array.dns_pref_values) + + val currentDns = + settingsManager.getInt(getString(R.string.dns_pref), 0) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentDns), + getString(R.string.dns_pref), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply() + (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } + } + return@setOnPreferenceClickListener true + } + fun getDownloadDirs(): List { + return normalSafeApiCall { + val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath + + // app_name_download_path = Cloudstream and does not change depending on release. + // DOES NOT WORK ON SCOPED STORAGE. + val secondaryDir = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + + File.separator + resources.getString(R.string.app_name_download_path) + val first = listOf(defaultDir, secondaryDir) + (try { + val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } + + (first + + requireContext().getExternalFilesDirs("").mapNotNull { it.path } + + currentDir) + } catch (e: Exception) { + first + }).filterNotNull().distinct() + } ?: emptyList() + } + + getPref(R.string.download_path_key)?.setOnPreferenceClickListener { + val dirs = getDownloadDirs() + + val currentDir = + settingsManager.getString(getString(R.string.download_path_pref), null) + ?: VideoDownloadManager.getDownloadDir().toString() + + activity?.showBottomDialog( + dirs + listOf("Custom"), + dirs.indexOf(currentDir), + getString(R.string.download_path_pref), + true, + {}) { + // Last = custom + if (it == dirs.size) { + try { + pathPicker.launch(Uri.EMPTY) + } catch (e: Exception) { + logError(e) + } + } else { + // Sets both visual and actual paths. + // key = used path + // pref = visual path + settingsManager.edit() + .putString(getString(R.string.download_path_key), dirs[it]).apply() + settingsManager.edit() + .putString(getString(R.string.download_path_pref), dirs[it]).apply() + } + } + return@setOnPreferenceClickListener true + } + + try { + SettingsFragment.beneneCount = + settingsManager.getInt(getString(R.string.benene_count), 0) + getPref(R.string.benene_count)?.let { pref -> + pref.summary = + if (SettingsFragment.beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( + R.string.benene_count_text + ).format( + SettingsFragment.beneneCount + ) + + pref.setOnPreferenceClickListener { + try { + SettingsFragment.beneneCount++ + settingsManager.edit().putInt( + getString(R.string.benene_count), + SettingsFragment.beneneCount + ) + .apply() + it.summary = + getString(R.string.benene_count_text).format(SettingsFragment.beneneCount) + } catch (e: Exception) { + logError(e) + } + + return@setOnPreferenceClickListener true + } + } + } catch (e: Exception) { + e.printStackTrace() + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt index 3911eae9..72ed9ad9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt @@ -1,21 +1,11 @@ package com.lagradost.cloudstream3.ui.settings -import android.content.Intent -import android.net.Uri -import android.os.Build import android.os.Bundle -import android.os.Environment import android.view.View -import androidx.activity.result.contract.ActivityResultContracts import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager -import com.hippo.unifile.UniFile -import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar @@ -25,43 +15,12 @@ import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard -import com.lagradost.cloudstream3.utils.VideoDownloadManager -import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath -import java.io.File class SettingsPlayer : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setUpToolbar(R.string.category_player) } - // Open file picker - private val pathPicker = - registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> - // It lies, it can be null if file manager quits. - if (uri == null) return@registerForActivityResult - val context = context ?: AcraApplication.context ?: return@registerForActivityResult - // RW perms for the path - val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION - - context.contentResolver.takePersistableUriPermission(uri, flags) - - val file = UniFile.fromUri(context, uri) - println("Selected URI path: $uri - Full path: ${file.filePath}") - - // Stores the real URI using download_path_key - // Important that the URI is stored instead of filepath due to permissions. - PreferenceManager.getDefaultSharedPreferences(context) - .edit().putString(getString(R.string.download_path_key), uri.toString()).apply() - - // From URI -> File path - // File path here is purely for cosmetic purposes in settings - (file.filePath ?: uri.toString()).let { - PreferenceManager.getDefaultSharedPreferences(context) - .edit().putString(getString(R.string.download_path_pref), it).apply() - } - } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { hideKeyboard() setPreferencesFromResource(R.xml.settings_player, rootKey) @@ -86,24 +45,6 @@ class SettingsPlayer : PreferenceFragmentCompat() { } return@setOnPreferenceClickListener true } - getPref(R.string.dns_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.dns_pref) - val prefValues = resources.getIntArray(R.array.dns_pref_values) - - val currentDns = - settingsManager.getInt(getString(R.string.dns_pref), 0) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentDns), - getString(R.string.dns_pref), - true, - {}) { - settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply() - (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } - } - return@setOnPreferenceClickListener true - } getPref(R.string.prefer_limit_title_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.limit_title_pref_names) @@ -242,60 +183,6 @@ class SettingsPlayer : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } } - fun getDownloadDirs(): List { - return normalSafeApiCall { - val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath - - // app_name_download_path = Cloudstream and does not change depending on release. - // DOES NOT WORK ON SCOPED STORAGE. - val secondaryDir = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + - File.separator + resources.getString(R.string.app_name_download_path) - val first = listOf(defaultDir, secondaryDir) - (try { - val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } - - (first + - requireContext().getExternalFilesDirs("").mapNotNull { it.path } + - currentDir) - } catch (e: Exception) { - first - }).filterNotNull().distinct() - } ?: emptyList() - } - - getPref(R.string.download_path_key)?.setOnPreferenceClickListener { - val dirs = getDownloadDirs() - - val currentDir = - settingsManager.getString(getString(R.string.download_path_pref), null) - ?: VideoDownloadManager.getDownloadDir().toString() - - activity?.showBottomDialog( - dirs + listOf("Custom"), - dirs.indexOf(currentDir), - getString(R.string.download_path_pref), - true, - {}) { - // Last = custom - if (it == dirs.size) { - try { - pathPicker.launch(Uri.EMPTY) - } catch (e: Exception) { - logError(e) - } - } else { - // Sets both visual and actual paths. - // key = used path - // pref = visual path - settingsManager.edit() - .putString(getString(R.string.download_path_key), dirs[it]).apply() - settingsManager.edit() - .putString(getString(R.string.download_path_pref), dirs[it]).apply() - } - } - return@setOnPreferenceClickListener true - } } } \ No newline at end of file diff --git a/app/src/main/res/drawable/baseline_grid_view_24.xml b/app/src/main/res/drawable/baseline_grid_view_24.xml index 55ffb0c9..079a2320 100644 --- a/app/src/main/res/drawable/baseline_grid_view_24.xml +++ b/app/src/main/res/drawable/baseline_grid_view_24.xml @@ -1,12 +1,11 @@ - + android:tint="?attr/white"> + diff --git a/app/src/main/res/layout/add_account_input.xml b/app/src/main/res/layout/add_account_input.xml index e954f3b1..1471af9c 100644 --- a/app/src/main/res/layout/add_account_input.xml +++ b/app/src/main/res/layout/add_account_input.xml @@ -44,6 +44,7 @@ android:layout_height="wrap_content"> - + + + android:text="@string/category_account" /> - + android:background="?attr/primaryBlackBackground"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/result_sync.xml b/app/src/main/res/layout/result_sync.xml index 02851942..9cde195c 100644 --- a/app/src/main/res/layout/result_sync.xml +++ b/app/src/main/res/layout/result_sync.xml @@ -44,6 +44,7 @@ app:tint="?attr/textColor" /> - + + - - - - - - - - - - - - + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/search_poster_img_des" /> + style="@style/SearchBox" + android:background="@drawable/type_bg_color" /> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index a3785b2f..606ff34d 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -119,6 +119,15 @@ app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + + امتداد تكبير - عام + عام لغات الموفر واجهة التطبيق النوع المفضل من المشاهدة diff --git a/app/src/main/res/values-es/strings-es.xml b/app/src/main/res/values-es/strings-es.xml index 36e80ac6..0f7cbde8 100644 --- a/app/src/main/res/values-es/strings-es.xml +++ b/app/src/main/res/values-es/strings-es.xml @@ -238,7 +238,7 @@ Búsqueda Nginx - Créditos y cuenta + Créditos y cuenta Actualizaciones y copia de seguridad Credencial Nginx Tienes que usar el siguiente formato minombredeusuariogenial:micontraseñasegura123 @@ -408,7 +408,7 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - General + General Botón aleatorio Mostrar botón aleatorio en la página de inicio Idiomas del proveedor diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 7e00e6e2..3c40d690 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -193,7 +193,7 @@ Thème de l\'application Vitesse (%.2fx) Utiliser la luminosité du système - Général + Général DNS avec HTTPS Afficher les animés en Anglais (Dub) / sous-titrés Disposition en mode téléphone diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index d287ce54..a5691e35 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -188,7 +188,7 @@ Cari Nginx - Kredit dan akun + Kredit dan akun Update dan cadangan Kredensial Nginx Kamu harus menggunakan format berikut namaku:passwordku123 @@ -359,7 +359,7 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - Umum + Umum Tombol Acak Tampilkan tombol acak di Beranda Bahasa provider diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 26945d1b..ec81dd84 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -181,7 +181,7 @@ Cerca Nginx - Accounts e Crediti + Accounts e Crediti Aggiornamenti e Backup Credenziali Nginx È necessario utilizzare il seguente formato mycoolusername:mysecurepassword123 @@ -328,7 +328,7 @@ Zoom Disclaimer - Generale + Generale Random Mostra pulsante Random nella homepage Lingua provider diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 55ca401f..229a1a82 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -220,7 +220,7 @@ Зумирај Disclaimer - Генерално + Генерално Јазици на провајдерите Распоред на апликацијата Претпочитани медиуми diff --git a/app/src/main/res/values-mo/string.xml b/app/src/main/res/values-mo/string.xml index e1d944c5..bf0f0dca 100644 --- a/app/src/main/res/values-mo/string.xml +++ b/app/src/main/res/values-mo/string.xml @@ -188,7 +188,7 @@ uhuoh o a ohahuhohoa hah ua hu ouo o aoau hah ah ah huu oouhhau aoaoaaohoo ha - a ahu uoo uoahuo uo + a ahu uoo uoahuo uo uo u ohouao uuoouhh hhuhuuh ouhoaao hau aouo uha uh huo uooaah u diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 73575ba5..1f3ebcc2 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -221,7 +221,7 @@ Tilpass til skjermen Tøye ut Zoome - Grunnleggende + Grunnleggende Leverandør Språk App Oppsett Automatisk diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index aaf0cb13..8ac78d74 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -270,7 +270,7 @@ Zastrzeżenie Języki dostawców - Ogólne + Ogólne Układ aplikacji Preferowane media diff --git a/app/src/main/res/values-pt/strings-pt.xml b/app/src/main/res/values-pt/strings-pt.xml index edc21ff8..038bf25b 100644 --- a/app/src/main/res/values-pt/strings-pt.xml +++ b/app/src/main/res/values-pt/strings-pt.xml @@ -258,7 +258,7 @@ responsabilidade do usuário de evitar qualquer ação que possa violar as leis que regem sua localidade. Utilizar CloudStream 3 por sua própria conta e risco. - Geral + Geral Idiomas dos Origems Layout do App Mídia preferida diff --git a/app/src/main/res/values-ro/strings-ro.xml b/app/src/main/res/values-ro/strings-ro.xml index 37d558c0..427f354a 100644 --- a/app/src/main/res/values-ro/strings-ro.xml +++ b/app/src/main/res/values-ro/strings-ro.xml @@ -259,7 +259,7 @@ responsabilitatea utilizatorului de a evita orice acțiune care ar putea încălca legile care guvernează localitatea sa. Utilizați CloudStream 3 pe propriul risc. - Generală + Generală Limbile Furnizorului Aranjament-ul Aplicației Media Preferată diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 47cdf0d6..605a91ce 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -199,7 +199,7 @@ Återuppta Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter Föredragen videokvalitet - Allmänna Inställningar + Allmänna Inställningar Textstorlek Använd system ljusstyrka Använder systemets ljusstyrka instället för en svart överlaga diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 3a493947..49b892ec 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -231,7 +231,7 @@ Punan ang buong screen Zoom - Pangkalahatan + Pangkalahatan Lenggwahe ng Provider App Layout Awtomatik diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e95f6efb..296534cf 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -344,7 +344,7 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - Genel + Genel Sağlayıcı Dilleri Uygulama Düzeni Tercih Edilen Medya diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index e518f420..8f788302 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -243,7 +243,7 @@ Kéo dãn Phóng to - Tổng quan + Tổng quan Ngôn ngữ nhà cung cấp Giao diện App Thể loại ưu tiên diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 08928366..64c0bbe0 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -306,7 +306,7 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - 全局 + 全局 内容提供者语言 应用布局 首选媒体 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1169dd4..36802938 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,9 +35,6 @@ provider_lang_key dns_key download_path_key - nginx_url_key - nginx_credentials - nginx_info Cloudstream app_layout_key primary_color_key @@ -241,11 +238,8 @@ Error backing up %s Search - Nginx - Credits and account + Accounts Updates and backup - Nginx Credential - You have to use the following format mycoolusername:mysecurepassword123 What is Nginx ? Nginx is a software that can be used to display files from a server that you own. Click to see a Nginx setup guide @@ -414,15 +408,15 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - General + General Random Button Show random button on Homepage Provider languages App Layout Preferred media Subtitle encoding - Preferred media and language - User interface + Language + Layout Auto TV layout @@ -535,4 +529,5 @@ Invalid id Remove closed captions from subtitles Remove bloat from subtitles + Extras diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml new file mode 100644 index 00000000..652b011a --- /dev/null +++ b/app/src/main/res/xml/settings_account.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml index 25c1e741..d98f506f 100644 --- a/app/src/main/res/xml/settings_player.xml +++ b/app/src/main/res/xml/settings_player.xml @@ -12,16 +12,6 @@ android:icon="@drawable/ic_outline_subtitles_24" app:summary="@string/chromecast_subtitles_settings_des" /> - - - - - - + android:key="@string/dns_key" + android:title="@string/dns_pref" + android:summary="@string/dns_pref_summary" + android:icon="@drawable/ic_baseline_dns_24" /> - - + android:key="@string/download_path_key" + android:title="@string/download_path_pref" + android:icon="@drawable/netflix_download" />