settings change + flashbang mode fixes

This commit is contained in:
LagradOst 2022-06-09 15:50:55 +02:00
parent db04dd2781
commit 162ce53a82
37 changed files with 357 additions and 604 deletions

View file

@ -36,7 +36,7 @@ android {
targetSdkVersion 30
versionCode 47
versionName "2.10.26"
versionName "2.10.27"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"

View file

@ -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

View file

@ -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) {

View file

@ -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<LookMovieTokenSubtitle>?,
)
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<LookMovieSearchResult>?,
)
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<SearchResponse> {
val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query"
val movieResponse = app.get(movieUrl).text
val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse).result
val showsUrl = "$mainUrl/api/v1/shows/search/?q=$query"
val showsResponse = app.get(showsUrl).text
val shows = mapper.readValue<LookMovieSearchResultRoot>(showsResponse).result
val returnValue = ArrayList<SearchResponse>()
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<SearchResponse> {
suspend fun search(query: String, isMovie: Boolean): List<SearchResponse> {
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<LookMovieTokenSubtitle>?,
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<LookMovieTokenRoot>(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<List<LookMovieTokenSubtitle>>(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<LookMovieTokenRoot>(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<List<LookMovieEpisode>>(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
)
}
}
}

View file

@ -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()

View file

@ -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()
}
}
}

View file

@ -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),

View file

@ -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<String> {
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()
}
}
}

View file

@ -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<String> {
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
}
}
}

View file

@ -1,12 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
app:tint="?attr/white">
android:tint="?attr/white">
<path
android:fillColor="@android:color/white"
android:pathData="M3,3v8h8L11,3L3,3zM9,9L5,9L5,5h4v4zM3,13v8h8v-8L3,13zM9,19L5,19v-4h4v4zM13,3v8h8L21,3h-8zM19,9h-4L15,5h4v4zM13,13v8h8v-8h-8zM19,19h-4v-4h4v4z"
android:fillType="evenOdd"/>
android:fillType="evenOdd" />
</vector>

View file

@ -44,6 +44,7 @@
android:layout_height="wrap_content">
<EditText
android:textColorHint="?attr/grayTextColor"
android:hint="@string/example_username"
android:autofillHints="username"
android:id="@+id/login_username_input"
@ -57,6 +58,7 @@
tools:ignore="LabelFor" />
<EditText
android:textColorHint="?attr/grayTextColor"
android:autofillHints="emailAddress"
android:hint="@string/example_email"
android:id="@+id/login_email_input"
@ -72,6 +74,7 @@
tools:ignore="LabelFor" />
<EditText
android:textColorHint="?attr/grayTextColor"
android:hint="@string/example_ip"
android:id="@+id/login_server_input"
android:nextFocusRight="@id/cancel_btt"
@ -85,6 +88,7 @@
tools:ignore="LabelFor" />
<EditText
android:textColorHint="?attr/grayTextColor"
android:hint="@string/example_password"
android:id="@+id/login_password_input"
android:nextFocusRight="@id/cancel_btt"

View file

@ -117,7 +117,7 @@
android:layout_gravity="bottom"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:textColor="@color/textColor"
android:textColor="?attr/textColor"
android:id="@+id/imageText"
android:minLines="2"
android:maxLines="2"

View file

@ -52,8 +52,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:nextFocusDown="@id/settings_player"
android:id="@+id/settings_general"
style="@style/SettingsItem"
android:text="@string/category_general" />
<TextView
android:nextFocusUp="@id/settings_general"
android:nextFocusDown="@id/settings_lang"
android:id="@+id/settings_player"
@ -89,7 +96,7 @@
android:id="@+id/settings_credits"
style="@style/SettingsItem"
android:text="@string/category_credits" />
android:text="@string/category_account" />
<TextView
android:padding="10dp"

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/primaryBlackBackground" >
android:background="?attr/primaryBlackBackground">
<ImageView
android:id="@+id/imgPoster"
android:layout_width="match_parent"
@ -13,6 +13,5 @@
android:adjustViewBounds="true"
android:src="@drawable/default_cover"
android:background="#fffff0"
android:contentDescription="@string/poster_image"
/>
android:contentDescription="@string/poster_image" />
</LinearLayout>

View file

@ -44,6 +44,7 @@
app:tint="?attr/textColor" />
<EditText
android:textColorHint="?attr/grayTextColor"
android:id="@+id/result_sync_current_episodes"
style="@style/AppEditStyle"
tools:hint="20"

View file

@ -70,7 +70,7 @@
android:layout_gravity="bottom"
android:paddingBottom="5dp"
android:paddingTop="5dp"
android:textColor="@color/textColor"
android:textColor="?attr/textColor"
android:id="@+id/imageText"
android:minLines="2"
android:maxLines="2"

View file

@ -119,6 +119,15 @@
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
<fragment
android:id="@+id/navigation_settings_general"
android:label="@string/title_settings"
android:name="com.lagradost.cloudstream3.ui.settings.SettingsGeneral"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
<fragment
android:id="@+id/navigation_settings_lang"
android:label="@string/title_settings"
@ -290,6 +299,13 @@
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
<action
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
android:id="@+id/action_navigation_settings_to_navigation_settings_general"
app:destination="@id/navigation_settings_general" />
</fragment>
<fragment

View file

@ -246,7 +246,7 @@
<string name="resize_fill">امتداد</string>
<string name="resize_zoom">تكبير</string>
<string name="general">عام</string>
<string name="category_general">عام</string>
<string name="provider_lang_settings">لغات الموفر</string>
<string name="app_layout">واجهة التطبيق</string>
<string name="preferred_media_settings">النوع المفضل من المشاهدة</string>

View file

@ -238,7 +238,7 @@
<string name="search">Búsqueda</string>
<string name="category_nginx">Nginx</string>
<string name="category_credits">Créditos y cuenta</string>
<string name="category_account">Créditos y cuenta</string>
<string name="category_updates">Actualizaciones y copia de seguridad</string>
<string name="nginx_credentials_title">Credencial Nginx</string>
<string name="nginx_credentials_summary">Tienes que usar el siguiente formato minombredeusuariogenial:micontraseñasegura123</string>
@ -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.
</string>
<string name="general">General</string>
<string name="category_general">General</string>
<string name="random_button_settings">Botón aleatorio</string>
<string name="random_button_settings_desc">Mostrar botón aleatorio en la página de inicio</string>
<string name="provider_lang_settings">Idiomas del proveedor</string>

View file

@ -193,7 +193,7 @@
<string name="app_theme_settings">Thème de l\'application</string>
<string name="player_speed_text_format" formatted="true">Vitesse (%.2fx)</string>
<string name="use_system_brightness_settings">Utiliser la luminosité du système</string>
<string name="general">Général</string>
<string name="category_general">Général</string>
<string name="dns_pref">DNS avec HTTPS</string>
<string name="display_subbed_dubbed_settings">Afficher les animés en Anglais (Dub) / sous-titrés</string>
<string name="phone_layout">Disposition en mode téléphone</string>

View file

@ -188,7 +188,7 @@
<string name="search">Cari</string>
<string name="category_nginx">Nginx</string>
<string name="category_credits">Kredit dan akun</string>
<string name="category_account">Kredit dan akun</string>
<string name="category_updates">Update dan cadangan</string>
<string name="nginx_credentials_title">Kredensial Nginx</string>
<string name="nginx_credentials_summary">Kamu harus menggunakan format berikut namaku:passwordku123</string>
@ -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.
</string>
<string name="general">Umum</string>
<string name="category_general">Umum</string>
<string name="random_button_settings">Tombol Acak</string>
<string name="random_button_settings_desc">Tampilkan tombol acak di Beranda</string>
<string name="provider_lang_settings">Bahasa provider</string>

View file

@ -181,7 +181,7 @@
<string name="search">Cerca</string>
<string name="category_nginx">Nginx</string>
<string name="category_credits">Accounts e Crediti</string>
<string name="category_account">Accounts e Crediti</string>
<string name="category_updates">Aggiornamenti e Backup</string>
<string name="nginx_credentials_title">Credenziali Nginx</string>
<string name="nginx_credentials_summary">È necessario utilizzare il seguente formato mycoolusername:mysecurepassword123</string>
@ -328,7 +328,7 @@
<string name="resize_zoom">Zoom</string>
<string name="legal_notice">Disclaimer</string>
<string name="general">Generale</string>
<string name="category_general">Generale</string>
<string name="random_button_settings">Random</string>
<string name="random_button_settings_desc">Mostra pulsante Random nella homepage</string>
<string name="provider_lang_settings">Lingua provider</string>

View file

@ -220,7 +220,7 @@
<string name="resize_zoom">Зумирај</string>
<string name="legal_notice">Disclaimer</string>
<string name="general">Генерално</string>
<string name="category_general">Генерално</string>
<string name="provider_lang_settings">Јазици на провајдерите</string>
<string name="app_layout">Распоред на апликацијата</string>
<string name="preferred_media_settings">Претпочитани медиуми</string>

View file

@ -188,7 +188,7 @@
<string name="resize_zoom">uhuoh o a ohahuhohoa hah</string>
<string name="provider_lang_settings">ua hu ouo o aoau hah ah</string>
<string name="legal_notice">ah huu oouhhau aoaoaaohoo ha</string>
<string name="general">a ahu uoo uoahuo uo</string>
<string name="category_general">a ahu uoo uoahuo uo</string>
<string name="app_layout">uo u ohouao</string>
<string name="automatic">uuoouhh hhuhuuh ouhoaao hau aouo</string>
<string name="tv_layout">uha uh huo uooaah u</string>

View file

@ -221,7 +221,7 @@
<string name="resize_fit">Tilpass til skjermen</string>
<string name="resize_fill">Tøye ut</string>
<string name="resize_zoom">Zoome</string>
<string name="general">Grunnleggende</string>
<string name="category_general">Grunnleggende</string>
<string name="provider_lang_settings">Leverandør Språk</string>
<string name="app_layout">App Oppsett</string>
<string name="automatic">Automatisk</string>

View file

@ -270,7 +270,7 @@
<string name="legal_notice">Zastrzeżenie</string>
<string name="provider_lang_settings">Języki dostawców</string>
<string name="general">Ogólne</string>
<string name="category_general">Ogólne</string>
<string name="app_layout">Układ aplikacji</string>
<string name="preferred_media_settings">Preferowane media</string>

View file

@ -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.
</string>
<string name="general">Geral</string>
<string name="category_general">Geral</string>
<string name="provider_lang_settings">Idiomas dos Origems</string>
<string name="app_layout">Layout do App</string>
<string name="preferred_media_settings">Mídia preferida</string>

View file

@ -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.
</string>
<string name="general">Generală</string>
<string name="category_general">Generală</string>
<string name="provider_lang_settings">Limbile Furnizorului</string>
<string name="app_layout">Aranjament-ul Aplicației</string>
<string name="preferred_media_settings">Media Preferată</string>

View file

@ -199,7 +199,7 @@
<string name="resume">Återuppta</string>
<string name="storage_error">Ett nerladdningsfel uppstod, kolla om appen har lagringsbehörigheter</string>
<string name="watch_quality_pref">Föredragen videokvalitet</string>
<string name="general">Allmänna Inställningar</string>
<string name="category_general">Allmänna Inställningar</string>
<string name="subs_font_size">Textstorlek</string>
<string name="use_system_brightness_settings">Använd system ljusstyrka</string>
<string name="use_system_brightness_settings_des">Använder systemets ljusstyrka instället för en svart överlaga</string>

View file

@ -231,7 +231,7 @@
<string name="resize_fill">Punan ang buong screen</string>
<string name="resize_zoom">Zoom</string>
<string name="general">Pangkalahatan</string>
<string name="category_general">Pangkalahatan</string>
<string name="provider_lang_settings">Lenggwahe ng Provider</string>
<string name="app_layout">App Layout</string>
<string name="automatic">Awtomatik</string>

View file

@ -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.
</string>
<string name="general">Genel</string>
<string name="category_general">Genel</string>
<string name="provider_lang_settings">Sağlayıcı Dilleri</string>
<string name="app_layout">Uygulama Düzeni</string>
<string name="preferred_media_settings">Tercih Edilen Medya</string>

View file

@ -243,7 +243,7 @@
<string name="resize_fill">Kéo dãn</string>
<string name="resize_zoom">Phóng to</string>
<string name="general">Tổng quan</string>
<string name="category_general">Tổng quan</string>
<string name="provider_lang_settings">Ngôn ngữ nhà cung cấp</string>
<string name="app_layout">Giao diện App</string>
<string name="preferred_media_settings">Thể loại ưu tiên</string>

View file

@ -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.
</string>
<string name="general">全局</string>
<string name="category_general">全局</string>
<string name="provider_lang_settings">内容提供者语言</string>
<string name="app_layout">应用布局</string>
<string name="preferred_media_settings">首选媒体</string>

View file

@ -35,9 +35,6 @@
<string name="provider_lang_key" translatable="false">provider_lang_key</string>
<string name="dns_key" translatable="false">dns_key</string>
<string name="download_path_key" translatable="false">download_path_key</string>
<string name="nginx_url_key" translatable="false">nginx_url_key</string>
<string name="nginx_credentials" translatable="false">nginx_credentials</string>
<string name="nginx_info" translatable="false">nginx_info</string>
<string name="app_name_download_path" translatable="false">Cloudstream</string>
<string name="app_layout_key" translatable="false">app_layout_key</string>
<string name="primary_color_key" translatable="false">primary_color_key</string>
@ -241,11 +238,8 @@
<string name="backup_failed_error_format">Error backing up %s</string>
<string name="search">Search</string>
<string name="category_nginx">Nginx</string>
<string name="category_credits">Credits and account</string>
<string name="category_account">Accounts</string>
<string name="category_updates">Updates and backup</string>
<string name="nginx_credentials_title">Nginx Credential</string>
<string name="nginx_credentials_summary">You have to use the following format mycoolusername:mysecurepassword123</string>
<string name="nginx_info_title">What is Nginx ?</string>
<string name="nginx_info_summary">Nginx is a software that can be used to display files from a server that you own. Click to see a Nginx setup guide</string>
@ -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.
</string>
<string name="general">General</string>
<string name="category_general">General</string>
<string name="random_button_settings">Random Button</string>
<string name="random_button_settings_desc">Show random button on Homepage</string>
<string name="provider_lang_settings">Provider languages</string>
<string name="app_layout">App Layout</string>
<string name="preferred_media_settings">Preferred media</string>
<string name="subtitles_encoding">Subtitle encoding</string>
<string name="category_preferred_media_and_lang">Preferred media and language</string>
<string name="category_ui">User interface</string>
<string name="category_preferred_media_and_lang">Language</string>
<string name="category_ui">Layout</string>
<string name="automatic">Auto</string>
<string name="tv_layout">TV layout</string>
@ -535,4 +529,5 @@
<string name="error_invalid_id">Invalid id</string>
<string name="subtitles_remove_captions">Remove closed captions from subtitles</string>
<string name="subtitles_remove_bloat">Remove bloat from subtitles</string>
<string name="extras">Extras</string>
</resources>

View file

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

View file

@ -12,16 +12,6 @@
android:icon="@drawable/ic_outline_subtitles_24"
app:summary="@string/chromecast_subtitles_settings_des" />
<Preference
android:key="@string/dns_key"
android:title="@string/dns_pref"
android:summary="@string/dns_pref_summary"
android:icon="@drawable/ic_baseline_dns_24" />
<Preference
android:key="@string/download_path_key"
android:title="@string/download_path_pref"
android:icon="@drawable/netflix_download" />
<Preference
android:key="@string/quality_pref_key"

View file

@ -1,29 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:key="@string/mal_key"
android:icon="@drawable/mal_logo" />
<Preference
android:key="@string/anilist_key"
android:icon="@drawable/ic_anilist_icon" />
<Preference
android:key="@string/opensubtitles_key"
android:icon="@drawable/open_subtitles_icon" />
<Preference
android:key="@string/nginx_key"
android:icon="@drawable/nginx" />
android:key="@string/dns_key"
android:title="@string/dns_pref"
android:summary="@string/dns_pref_summary"
android:icon="@drawable/ic_baseline_dns_24" />
<Preference
android:key="@string/nginx_info"
android:title="@string/nginx_info_title"
android:icon="@drawable/nginx_question"
android:summary="@string/nginx_info_summary">
<intent
android:action="android.intent.action.VIEW"
android:data="https://www.sarlays.com/use-nginx-with-cloudstream/" />
</Preference>
android:key="@string/download_path_key"
android:title="@string/download_path_pref"
android:icon="@drawable/netflix_download" />
<Preference
android:key="@string/legal_notice_key"