Merge branch 'recloudstream:master' into master

This commit is contained in:
Thanasis Trispiotis 2022-11-01 00:05:59 +02:00 committed by GitHub
commit c0f57b1368
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 288 additions and 95 deletions

76
.github/workflows/build_to_archive.yml vendored Normal file
View file

@ -0,0 +1,76 @@
name: Archive build
on:
push:
branches: [ master ]
paths-ignore:
- '*.md'
- '*.json'
- '**/wcokey.txt'
workflow_dispatch:
concurrency:
group: "Archive-build"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/secrets"
- name: Generate access token (archive)
id: generate_archive_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/cloudstream-archive"
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Fetch keystore
id: fetch_keystore
run: |
TMP_KEYSTORE_FILE_PATH="${RUNNER_TEMP}"/keystore
mkdir -p "${TMP_KEYSTORE_FILE_PATH}"
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "${TMP_KEYSTORE_FILE_PATH}/prerelease_keystore.keystore" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore.jks"
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "keystore_password.txt" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore_password.txt"
KEY_PWD="$(cat keystore_password.txt)"
echo "::add-mask::${KEY_PWD}"
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
- name: Run Gradle
run: |
./gradlew assemblePrerelease
env:
SIGNING_KEY_ALIAS: "key0"
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
SIGNING_STORE_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
- uses: actions/checkout@v3
with:
repository: "recloudstream/cloudstream-archive"
token: ${{ steps.generate_archive_token.outputs.token }}
path: "archive"
- name: Move build
run: |
cp app/build/outputs/apk/prerelease/release/*.apk "archive/$(git rev-parse --short HEAD).apk"
- name: Push archive
run: |
cd $GITHUB_WORKSPACE/archive
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add .
git commit --amend -m "Build $GITHUB_SHA" || exit 0 # do not error if nothing to commit
git push --force

View file

@ -18,7 +18,6 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSet
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SubtitleHelper
import okhttp3.Interceptor import okhttp3.Interceptor
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -31,6 +30,12 @@ const val USER_AGENT =
val mapper = JsonMapper.builder().addModule(KotlinModule()) val mapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!! .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
/**
* Defines the constant for the all languages preference, if this is set then it is
* the equivalent of all languages being set
**/
const val AllLanguagesName = "universal"
object APIHolder { object APIHolder {
val unixTime: Long val unixTime: Long
get() = System.currentTimeMillis() / 1000L get() = System.currentTimeMillis() / 1000L
@ -159,7 +164,8 @@ object APIHolder {
val hashSet = HashSet<String>() val hashSet = HashSet<String>()
val activeLangs = getApiProviderLangSettings() val activeLangs = getApiProviderLangSettings()
hashSet.addAll(apis.filter { activeLangs.contains(it.lang) }.map { it.name }) val hasUniversal = activeLangs.contains(AllLanguagesName)
hashSet.addAll(apis.filter { hasUniversal || activeLangs.contains(it.lang) }.map { it.name })
/*val set = settingsManager.getStringSet( /*val set = settingsManager.getStringSet(
this.getString(R.string.search_providers_list_key), this.getString(R.string.search_providers_list_key),
@ -193,26 +199,17 @@ object APIHolder {
return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet() return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet()
} }
/**
* Gets all the activated provider languages
* Used to obey the preference provider_lang_key
* but it turned out too complicated and unnecessary with extensions.
**/
fun Context.getApiProviderLangSettings(): HashSet<String> { fun Context.getApiProviderLangSettings(): HashSet<String> {
val langs = apis.map { it.lang }.toSet() val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
.sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } val hashSet = hashSetOf(AllLanguagesName) // def is all languages
return langs.toHashSet()
// val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
// val hashSet = HashSet<String>()
// hashSet.add("en") // def is only en // hashSet.add("en") // def is only en
// val list = settingsManager.getStringSet( val list = settingsManager.getStringSet(
// this.getString(R.string.provider_lang_key), this.getString(R.string.provider_lang_key),
// hashSet.toMutableSet() hashSet
// ) )
//
// if (list.isNullOrEmpty()) return hashSet if (list.isNullOrEmpty()) return hashSet
// return list.toHashSet() return list.toHashSet()
} }
fun Context.getApiTypeSettings(): HashSet<TvType> { fun Context.getApiTypeSettings(): HashSet<TvType> {
@ -254,7 +251,8 @@ object APIHolder {
null null
} ?: default } ?: default
val langs = this.getApiProviderLangSettings() val langs = this.getApiProviderLangSettings()
val allApis = apis.filter { langs.contains(it.lang) } val hasUniversal = langs.contains(AllLanguagesName)
val allApis = apis.filter { hasUniversal || langs.contains(it.lang) }
.filter { api -> api.hasMainPage || !hasHomePageIsRequired } .filter { api -> api.hasMainPage || !hasHomePageIsRequired }
return if (currentPrefMedia.isEmpty()) { return if (currentPrefMedia.isEmpty()) {
allApis allApis

View file

@ -0,0 +1,72 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
open class Jeniusplay : ExtractorApi() {
override val name = "Jeniusplay"
override val mainUrl = "https://jeniusplay.com"
override val requiresReferer = true
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val document = app.get(url, referer = "$mainUrl/").document
val hash = url.split("/").last().substringAfter("data=")
val m3uLink = app.post(
url = "$mainUrl/player/index.php?data=$hash&do=getVideo",
data = mapOf("hash" to hash, "r" to "$referer"),
referer = url,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).parsed<ResponseSource>().videoSource
M3u8Helper.generateM3u8(
this.name,
m3uLink,
url,
).forEach(callback)
document.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val subData =
getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],")
tryParseJson<List<Tracks>>("[$subData]")?.map { subtitle ->
subtitleCallback.invoke(
SubtitleFile(
getLanguage(subtitle.label ?: ""),
subtitle.file
)
)
}
}
}
}
private fun getLanguage(str: String): String {
return when {
str.contains("indonesia", true) || str
.contains("bahasa", true) -> "Indonesian"
else -> str
}
}
data class ResponseSource(
@JsonProperty("hls") val hls: Boolean,
@JsonProperty("videoSource") val videoSource: String,
@JsonProperty("securedLink") val securedLink: String?,
)
data class Tracks(
@JsonProperty("kind") val kind: String?,
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String?,
)
}

View file

@ -6,7 +6,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.M3u8Helper
class Moviehab : ExtractorApi() { class MoviehabNet : Moviehab() {
override var mainUrl = "https://play.moviehab.net"
}
open class Moviehab : ExtractorApi() {
override var name = "Moviehab" override var name = "Moviehab"
override var mainUrl = "https://play.moviehab.com" override var mainUrl = "https://play.moviehab.com"
override val requiresReferer = false override val requiresReferer = false

View file

@ -70,6 +70,28 @@ object RepositoryManager {
getKey("PREBUILT_REPOSITORIES") ?: emptyArray() getKey("PREBUILT_REPOSITORIES") ?: emptyArray()
} }
suspend fun parseRepoUrl(url: String): String? {
val fixedUrl = url.trim()
return if (fixedUrl.contains("^https?://".toRegex())) {
fixedUrl
} else if (fixedUrl.contains("^(cloudstreamrepo://)|(https://cs\\.repo/\\??)".toRegex())) {
fixedUrl.replace("^(cloudstreamrepo://)|(https://cs\\.repo/\\??)".toRegex(), "").let {
return@let if (!it.contains("^https?://".toRegex()))
"https://${it}"
else fixedUrl
}
} else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) {
suspendSafeApiCall {
app.get("https://l.cloudstream.cf/${fixedUrl}").let {
return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url
else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 ->
return@let2 if (it2.isSuccessful) it2.url else null
}
}
}
} else null
}
suspend fun parseRepository(url: String): Repository? { suspend fun parseRepository(url: String): Repository? {
return suspendSafeApiCall { return suspendSafeApiCall {
// Take manifestVersion and such into account later // Take manifestVersion and such into account later

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.home package com.lagradost.cloudstream3.ui.home
import android.animation.LayoutTransition
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
@ -23,12 +24,15 @@ import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.transition.ChangeBounds
import androidx.transition.TransitionManager
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -346,7 +350,9 @@ class HomeFragment : Fragment() {
builder.setContentView(R.layout.home_select_mainpage) builder.setContentView(R.layout.home_select_mainpage)
builder.show() builder.show()
builder.let { dialog -> builder.let { dialog ->
val isMultiLang = getApiProviderLangSettings().size > 1 val isMultiLang = getApiProviderLangSettings().let { set ->
set.size > 1 || set.contains(AllLanguagesName)
}
//dialog.window?.setGravity(Gravity.BOTTOM) //dialog.window?.setGravity(Gravity.BOTTOM)
var currentApiName = selectedApiName var currentApiName = selectedApiName
@ -574,10 +580,15 @@ class HomeFragment : Fragment() {
setPageTransformer(false, HomeScrollTransformer()) setPageTransformer(false, HomeScrollTransformer())
adapter = HomeScrollAdapter { load -> adapter = HomeScrollAdapter { load ->
load.apply { load.apply {
home_preview_title_holder?.let { parent ->
TransitionManager.beginDelayedTransition(parent, ChangeBounds())
}
home_preview_tags?.text = tags?.joinToString("") ?: "" home_preview_tags?.text = tags?.joinToString("") ?: ""
home_preview_tags?.isGone = tags.isNullOrEmpty() home_preview_tags?.isGone = tags.isNullOrEmpty()
home_preview_image?.setImage(posterUrl, posterHeaders) home_preview_image?.setImage(posterUrl, posterHeaders)
home_preview_title?.text = name home_preview_title?.text = name
home_preview_play?.setOnClickListener { home_preview_play?.setOnClickListener {
activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST)
//activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST)

View file

@ -221,7 +221,9 @@ class SearchFragment : Fragment() {
builder.setContentView(R.layout.home_select_mainpage) builder.setContentView(R.layout.home_select_mainpage)
builder.show() builder.show()
builder.let { dialog -> builder.let { dialog ->
val isMultiLang = ctx.getApiProviderLangSettings().size > 1 val isMultiLang = ctx.getApiProviderLangSettings().let { set ->
set.size > 1 || set.contains(AllLanguagesName)
}
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt) val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt) val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)

View file

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.ui.settings package com.lagradost.cloudstream3.ui.settings
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
@ -9,17 +8,15 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlin.reflect.jvm.internal.impl.descriptors.deserialization.PlatformDependentDeclarationFilter.All
class SettingsProviders : PreferenceFragmentCompat() { class SettingsProviders : PreferenceFragmentCompat() {
@ -63,13 +60,17 @@ class SettingsProviders : PreferenceFragmentCompat() {
getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener { getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener {
val names = enumValues<TvType>().sorted().map { it.name } val names = enumValues<TvType>().sorted().map { it.name }
val default = enumValues<TvType>().sorted().filter { it != TvType.NSFW }.map { it.ordinal } val default =
enumValues<TvType>().sorted().filter { it != TvType.NSFW }.map { it.ordinal }
val defaultSet = default.map { it.toString() }.toSet() val defaultSet = default.map { it.toString() }.toSet()
val currentList = try { val currentList = try {
settingsManager.getStringSet(getString(R.string.prefer_media_type_key), defaultSet)?.map { settingsManager.getStringSet(getString(R.string.prefer_media_type_key), defaultSet)
?.map {
it.toInt() it.toInt()
} }
} catch (e: Throwable) { null } ?: default } catch (e: Throwable) {
null
} ?: default
activity?.showMultiDialog( activity?.showMultiDialog(
names, names,
@ -89,20 +90,23 @@ class SettingsProviders : PreferenceFragmentCompat() {
getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener { getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener {
activity?.getApiProviderLangSettings()?.let { current -> activity?.getApiProviderLangSettings()?.let { current ->
val langs = APIHolder.apis.map { it.lang }.toSet() val languages = APIHolder.apis.map { it.lang }.toSet()
.sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } .sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } + AllLanguagesName
val currentList = ArrayList<Int>() val currentList = current.map {
for (i in current) { languages.indexOf(it)
currentList.add(langs.indexOf(i))
} }
val names = langs.map { val names = languages.map {
if (it == AllLanguagesName) {
Pair(it, getString(R.string.all_languages_preference))
} else {
val emoji = SubtitleHelper.getFlagFromIso(it) val emoji = SubtitleHelper.getFlagFromIso(it)
val name = SubtitleHelper.fromTwoLettersToLanguage(it) val name = SubtitleHelper.fromTwoLettersToLanguage(it)
val fullName = "$emoji $name" val fullName = "$emoji $name"
Pair(it, fullName) Pair(it, fullName)
} }
}
activity?.showMultiDialog( activity?.showMultiDialog(
names.map { it.second }, names.map { it.second },

View file

@ -33,8 +33,6 @@ import com.lagradost.cloudstream3.widget.LinearRecycleViewLayoutManager
import kotlinx.android.synthetic.main.add_repo_input.* import kotlinx.android.synthetic.main.add_repo_input.*
import kotlinx.android.synthetic.main.fragment_extensions.* import kotlinx.android.synthetic.main.fragment_extensions.*
const val PUBLIC_REPOSITORIES_LIST = "https://recloudstream.github.io/repos/"
class ExtensionsFragment : Fragment() { class ExtensionsFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -186,15 +184,7 @@ class ExtensionsFragment : Fragment() {
(activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?)?.primaryClip?.getItemAt( (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?)?.primaryClip?.getItemAt(
0 0
)?.text?.toString()?.let { copy -> )?.text?.toString()?.let { copy ->
// Fix our own repo links and only paste the text if it's a link. dialog.repo_url_input?.setText(copy)
if (copy.startsWith("http")) {
val fixedUrl = if (copy.startsWith("https://cs.repo")) {
"https://" + copy.substringAfter("?")
} else {
copy
}
dialog.repo_url_input?.setText(fixedUrl)
}
} }
// dialog.list_repositories?.setOnClickListener { // dialog.list_repositories?.setOnClickListener {
@ -206,13 +196,12 @@ class ExtensionsFragment : Fragment() {
// dialog.text2?.text = provider.name // dialog.text2?.text = provider.name
dialog.apply_btt?.setOnClickListener secondListener@{ dialog.apply_btt?.setOnClickListener secondListener@{
val name = dialog.repo_name_input?.text?.toString() val name = dialog.repo_name_input?.text?.toString()
ioSafe {
val url = dialog.repo_url_input?.text?.toString() val url = dialog.repo_url_input?.text?.toString()
?.let { it1 -> RepositoryManager.parseRepoUrl(it1) }
if (url.isNullOrBlank()) { if (url.isNullOrBlank()) {
showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT) showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT)
return@secondListener } else {
}
ioSafe {
val fixedName = if (!name.isNullOrBlank()) name val fixedName = if (!name.isNullOrBlank()) name
else RepositoryManager.parseRepository(url)?.name ?: "No name" else RepositoryManager.parseRepository(url)?.name ?: "No name"
@ -222,6 +211,7 @@ class ExtensionsFragment : Fragment() {
extensionViewModel.loadRepositories() extensionViewModel.loadRepositories()
this@ExtensionsFragment.activity?.downloadAllPluginsDialog(url, fixedName) this@ExtensionsFragment.activity?.downloadAllPluginsDialog(url, fixedName)
} }
}
dialog.dismissSafe(activity) dialog.dismissSafe(activity)
} }
dialog.cancel_btt?.setOnClickListener { dialog.cancel_btt?.setOnClickListener {

View file

@ -7,6 +7,9 @@ import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager
@ -96,7 +99,14 @@ class SetupFragmentExtensions : Fragment() {
next_btt?.setOnClickListener { next_btt?.setOnClickListener {
// Continue setup // Continue setup
if (isSetup) if (isSetup)
if (
// If any available languages
apis.distinctBy { it.lang }.size > 1
) {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages)
} else {
findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media) findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media)
}
else else
findNavController().navigate(R.id.navigation_home) findNavController().navigate(R.id.navigation_home)
} }

View file

@ -15,7 +15,6 @@ import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.appLanguages import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -85,7 +84,7 @@ class SetupFragmentLanguage : Fragment() {
&& PluginManager.getPluginsLocal().isEmpty() && PluginManager.getPluginsLocal().isEmpty()
//&& PREBUILT_REPOSITORIES.isNotEmpty() //&& PREBUILT_REPOSITORIES.isNotEmpty()
) R.id.action_navigation_global_to_navigation_setup_extensions ) R.id.action_navigation_global_to_navigation_setup_extensions
else R.id.action_navigation_setup_language_to_navigation_setup_media else R.id.action_navigation_setup_language_to_navigation_setup_provider_languages
findNavController().navigate( findNavController().navigate(
nextDestination, nextDestination,

View file

@ -12,6 +12,7 @@ import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.APIHolder import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
@ -39,14 +40,21 @@ class SetupFragmentProviderLanguage : Fragment() {
val current = this.getApiProviderLangSettings() val current = this.getApiProviderLangSettings()
val langs = APIHolder.apis.map { it.lang }.toSet() val langs = APIHolder.apis.map { it.lang }.toSet()
.sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } .sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } + AllLanguagesName
val currentList =
current.map { langs.indexOf(it) }.filter { it != -1 } // TODO LOOK INTO
val currentList = current.map { langs.indexOf(it) }.filter { it != -1 } // TODO LOOK INTO
val languageNames = langs.map { val languageNames = langs.map {
if (it == AllLanguagesName) {
getString(R.string.all_languages_preference)
} else {
val emoji = SubtitleHelper.getFlagFromIso(it) val emoji = SubtitleHelper.getFlagFromIso(it)
val name = SubtitleHelper.fromTwoLettersToLanguage(it) val name = SubtitleHelper.fromTwoLettersToLanguage(it)
"$emoji $name" "$emoji $name"
} }
}
arrayAdapter.addAll(languageNames) arrayAdapter.addAll(languageNames)
listview1?.adapter = arrayAdapter listview1?.adapter = arrayAdapter

View file

@ -329,6 +329,8 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Vidmolyme(), Vidmolyme(),
Voe(), Voe(),
Moviehab(), Moviehab(),
MoviehabNet(),
Jeniusplay(),
Gdriveplayerapi(), Gdriveplayerapi(),
Gdriveplayerapp(), Gdriveplayerapp(),

View file

@ -363,19 +363,20 @@
--> -->
<LinearLayout <LinearLayout
android:id="@+id/home_preview_title_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/home_preview_title" android:id="@+id/home_preview_title"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:paddingStart="30dp"
android:paddingEnd="30dp"
android:paddingBottom="10dp" android:paddingBottom="10dp"
android:paddingHorizontal="30dp"
android:textColor="?attr/white" android:textColor="?attr/white"
android:textSize="17sp" android:textSize="17sp"
android:textStyle="bold" android:textStyle="bold"
@ -392,7 +393,7 @@
tools:text="5 seasons 50 episodes" />--> tools:text="5 seasons 50 episodes" />-->
<TextView <TextView
android:id="@+id/home_preview_tags" android:id="@+id/home_preview_tags"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:paddingStart="30dp" android:paddingStart="30dp"

View file

@ -522,13 +522,6 @@
app:exitAnim="@anim/exit_anim" app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim" app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" /> app:popExitAnim="@anim/exit_anim" />
<action
android:id="@+id/action_navigation_setup_language_to_navigation_setup_media"
app:destination="@id/navigation_setup_media"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
</fragment> </fragment>
<action <action

View file

@ -637,4 +637,5 @@
<string name="player_settings_play_in_web">Web Video Cast</string> <string name="player_settings_play_in_web">Web Video Cast</string>
<string name="player_settings_play_in_browser">Browser</string> <string name="player_settings_play_in_browser">Browser</string>
<string name="app_not_found_error">App not found</string> <string name="app_not_found_error">App not found</string>
<string name="all_languages_preference">All Languages</string>
</resources> </resources>

View file

@ -81,6 +81,7 @@
<item name="chipStrokeColor">@color/transparent</item> <item name="chipStrokeColor">@color/transparent</item>
<item name="textColor">@color/chip_color_text</item> <item name="textColor">@color/chip_color_text</item>
<item name="android:textColor">@color/chip_color_text</item> <item name="android:textColor">@color/chip_color_text</item>
<item name="checkedIconTint">@color/chip_color_text</item>
</style> </style>
<style name="AmoledMode"> <style name="AmoledMode">
@ -137,13 +138,13 @@
</style> </style>
<style name="OverlayPrimaryColorMonetTwo"> <style name="OverlayPrimaryColorMonetTwo">
<item name="colorPrimary">@color/material_dynamic_tertiary80</item> <item name="colorPrimary">@color/material_dynamic_secondary80</item>
<item name="android:colorPrimary">@color/material_dynamic_tertiary80</item> <item name="android:colorPrimary">@color/material_dynamic_secondary80</item>
<item name="colorPrimaryDark">@color/material_dynamic_tertiary30</item> <item name="colorPrimaryDark">@color/material_dynamic_secondary30</item>
<item name="colorAccent">@color/material_dynamic_tertiary80</item> <item name="colorAccent">@color/material_dynamic_secondary80</item>
<item name="colorOnPrimary">@color/material_dynamic_tertiary20</item> <item name="colorOnPrimary">@color/material_dynamic_secondary20</item>
<!-- Needed for leanback fuckery --> <!-- Needed for leanback fuckery -->
<item name="android:colorAccent">@color/material_dynamic_tertiary30</item> <item name="android:colorAccent">@color/material_dynamic_secondary30</item>
</style> </style>
<style name="OverlayPrimaryColorBlue"> <style name="OverlayPrimaryColorBlue">

View file

@ -2,7 +2,6 @@
<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
app:isPreferenceVisible="false"
android:icon="@drawable/ic_baseline_language_24" android:icon="@drawable/ic_baseline_language_24"
android:key="@string/provider_lang_key" android:key="@string/provider_lang_key"
android:title="@string/provider_lang_settings" /> android:title="@string/provider_lang_settings" />