mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge remote-tracking branch 'origin/master' into dialog2
This commit is contained in:
commit
7f541a89aa
29 changed files with 403 additions and 382 deletions
|
@ -164,7 +164,7 @@ dependencies {
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||||
|
|
||||||
// Android Core & Lifecycle
|
// Android Core & Lifecycle
|
||||||
implementation("androidx.core:core-ktx:1.12.0")
|
implementation("androidx.core:core-ktx:1.13.1")
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
|
||||||
|
@ -174,7 +174,7 @@ dependencies {
|
||||||
// Design & UI
|
// Design & UI
|
||||||
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
implementation("jp.wasabeef:glide-transformations:4.3.0")
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
implementation("com.google.android.material:material:1.11.0")
|
implementation("com.google.android.material:material:1.12.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ dependencies {
|
||||||
|
|
||||||
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
|
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
|
||||||
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||||
implementation("com.google.guava:guava:32.1.3-android")
|
implementation("com.google.guava:guava:33.2.0-android")
|
||||||
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
|
||||||
|
|
||||||
// Media 3 (ExoPlayer)
|
// Media 3 (ExoPlayer)
|
||||||
|
@ -202,7 +202,7 @@ dependencies {
|
||||||
// PlayBack
|
// PlayBack
|
||||||
implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker
|
implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker
|
||||||
implementation("com.github.recloudstream:media-ffmpeg:1.1.0") // Custom FF-MPEG Lib for Audio Codecs
|
implementation("com.github.recloudstream:media-ffmpeg:1.1.0") // Custom FF-MPEG Lib for Audio Codecs
|
||||||
implementation("com.github.TeamNewPipe.NewPipeExtractor:NewPipeExtractor:6dc25f7b97") /* For Trailers
|
implementation("com.github.teamnewpipe:NewPipeExtractor:fafd471") /* For Trailers
|
||||||
^ Update to Latest Commits if Trailers Misbehave, github.com/TeamNewPipe/NewPipeExtractor/commits/dev */
|
^ Update to Latest Commits if Trailers Misbehave, github.com/TeamNewPipe/NewPipeExtractor/commits/dev */
|
||||||
implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding
|
implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding
|
||||||
|
|
||||||
|
@ -219,9 +219,7 @@ dependencies {
|
||||||
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
|
||||||
|
|
||||||
// Extensions & Other Libs
|
// Extensions & Other Libs
|
||||||
implementation("org.mozilla:rhino:1.7.13") /* run JavaScript
|
implementation("org.mozilla:rhino:1.7.15") // run JavaScript
|
||||||
^ Don't Bump RhinoJS to 1.7.14,`NoClassDefFoundError` Occurs and Trailers won't play (even with Desugaring)
|
|
||||||
NewPipeExtractor Issue */
|
|
||||||
implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance
|
implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance
|
||||||
implementation("com.github.LagradOst:SafeFile:0.0.6") // To Prevent the URI File Fu*kery
|
implementation("com.github.LagradOst:SafeFile:0.0.6") // To Prevent the URI File Fu*kery
|
||||||
implementation("org.conscrypt:conscrypt-android:2.5.2") // To Fix SSL Fu*kery on Android 9
|
implementation("org.conscrypt:conscrypt-android:2.5.2") // To Fix SSL Fu*kery on Android 9
|
||||||
|
|
|
@ -651,7 +651,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
val response = CommonActivity.dispatchKeyEvent(this, event)
|
val response = CommonActivity.dispatchKeyEvent(this, event)
|
||||||
if (response != null)
|
if (response != null)
|
||||||
return response
|
return response
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
|
||||||
|
class GoodstreamExtractor : ExtractorApi() {
|
||||||
|
override var name = "Goodstream"
|
||||||
|
override val mainUrl = "https://goodstream.uno"
|
||||||
|
override val requiresReferer = false
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
app.get(url).document.select("script").map { script ->
|
||||||
|
if (script.data().contains(Regex("file|player"))) {
|
||||||
|
val urlRegex = Regex("file: \"(https:\\/\\/[a-z0-9.\\/-_?=&]+)\",")
|
||||||
|
urlRegex.find(script.data())?.groupValues?.get(1).let { link ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
link!!,
|
||||||
|
mainUrl,
|
||||||
|
Qualities.Unknown.value,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,4 +20,9 @@ class PlayRu : ContentX() {
|
||||||
class FourPlayRu : ContentX() {
|
class FourPlayRu : ContentX() {
|
||||||
override var name = "FourPlayRu"
|
override var name = "FourPlayRu"
|
||||||
override var mainUrl = "https://four.playru.net"
|
override var mainUrl = "https://four.playru.net"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FourPichive : ContentX() {
|
||||||
|
override var name = "FourPichive"
|
||||||
|
override var mainUrl = "https://four.pichive.online"
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
import com.lagradost.cloudstream3.amap
|
import com.lagradost.cloudstream3.amap
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
@ -26,12 +27,16 @@ class VidSrcTo : ExtractorApi() {
|
||||||
val res = app.get("$mainUrl/ajax/embed/episode/$mediaId/sources").parsedSafe<VidsrctoEpisodeSources>() ?: return
|
val res = app.get("$mainUrl/ajax/embed/episode/$mediaId/sources").parsedSafe<VidsrctoEpisodeSources>() ?: return
|
||||||
if (res.status != 200) return
|
if (res.status != 200) return
|
||||||
res.result?.amap { source ->
|
res.result?.amap { source ->
|
||||||
val embedRes = app.get("$mainUrl/ajax/embed/source/${source.id}").parsedSafe<VidsrctoEmbedSource>() ?: return@amap
|
try {
|
||||||
val finalUrl = DecryptUrl(embedRes.result.encUrl)
|
val embedRes = app.get("$mainUrl/ajax/embed/source/${source.id}").parsedSafe<VidsrctoEmbedSource>() ?: return@amap
|
||||||
if(finalUrl.equals(embedRes.result.encUrl)) return@amap
|
val finalUrl = DecryptUrl(embedRes.result.encUrl)
|
||||||
when (source.title) {
|
if(finalUrl.equals(embedRes.result.encUrl)) return@amap
|
||||||
"Vidplay" -> AnyVidplay(finalUrl.substringBefore("/e/")).getUrl(finalUrl, referer, subtitleCallback, callback)
|
when (source.title) {
|
||||||
"Filemoon" -> FileMoon().getUrl(finalUrl, referer, subtitleCallback, callback)
|
"Vidplay" -> AnyVidplay(finalUrl.substringBefore("/e/")).getUrl(finalUrl, referer, subtitleCallback, callback)
|
||||||
|
"Filemoon" -> FileMoon().getUrl(finalUrl, referer, subtitleCallback, callback)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,18 +166,10 @@ open class TraktProvider : MainAPI() {
|
||||||
val resSeasons = getApi("$traktApiUrl/shows/${mediaDetails?.ids?.trakt.toString()}/seasons?extended=cloud9,full,episodes")
|
val resSeasons = getApi("$traktApiUrl/shows/${mediaDetails?.ids?.trakt.toString()}/seasons?extended=cloud9,full,episodes")
|
||||||
val episodes = mutableListOf<Episode>()
|
val episodes = mutableListOf<Episode>()
|
||||||
val seasons = parseJson<List<Seasons>>(resSeasons)
|
val seasons = parseJson<List<Seasons>>(resSeasons)
|
||||||
val seasonsNames = mutableListOf<SeasonData>()
|
|
||||||
var nextAir: NextAiring? = null
|
var nextAir: NextAiring? = null
|
||||||
|
|
||||||
seasons.forEach { season ->
|
seasons.forEach { season ->
|
||||||
|
|
||||||
seasonsNames.add(
|
|
||||||
SeasonData(
|
|
||||||
season.number!!,
|
|
||||||
season.title
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
season.episodes?.map { episode ->
|
season.episodes?.map { episode ->
|
||||||
|
|
||||||
val linkData = LinkData(
|
val linkData = LinkData(
|
||||||
|
@ -250,7 +242,6 @@ open class TraktProvider : MainAPI() {
|
||||||
this.comingSoon = isUpcoming(mediaDetails.released)
|
this.comingSoon = isUpcoming(mediaDetails.released)
|
||||||
//posterHeaders
|
//posterHeaders
|
||||||
this.nextAiring = nextAir
|
this.nextAiring = nextAir
|
||||||
this.seasonNames = seasonsNames
|
|
||||||
this.backgroundPosterUrl = getOriginalWidthImageUrl(backDropUrl)
|
this.backgroundPosterUrl = getOriginalWidthImageUrl(backDropUrl)
|
||||||
this.contentRating = mediaDetails.certification
|
this.contentRating = mediaDetails.certification
|
||||||
addTrailer(mediaDetails.trailer)
|
addTrailer(mediaDetails.trailer)
|
||||||
|
|
|
@ -12,9 +12,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
val aniListApi = AniListApi(0)
|
val aniListApi = AniListApi(0)
|
||||||
val openSubtitlesApi = OpenSubtitlesApi(0)
|
val openSubtitlesApi = OpenSubtitlesApi(0)
|
||||||
val simklApi = SimklApi(0)
|
val simklApi = SimklApi(0)
|
||||||
val indexSubtitlesApi = IndexSubtitleApi()
|
|
||||||
val addic7ed = Addic7ed()
|
val addic7ed = Addic7ed()
|
||||||
val subDl = SubDL()
|
val subDlApi = SubDlApi(0)
|
||||||
val localListApi = LocalList()
|
val localListApi = LocalList()
|
||||||
|
|
||||||
// used to login via app intent
|
// used to login via app intent
|
||||||
|
@ -26,7 +25,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
// this needs init with context and can be accessed in settings
|
// this needs init with context and can be accessed in settings
|
||||||
val accountManagers
|
val accountManagers
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
malApi, aniListApi, openSubtitlesApi, simklApi //nginxApi
|
malApi, aniListApi, openSubtitlesApi, subDlApi, simklApi //nginxApi
|
||||||
)
|
)
|
||||||
|
|
||||||
// used for active syncing
|
// used for active syncing
|
||||||
|
@ -36,14 +35,16 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
|
||||||
)
|
)
|
||||||
|
|
||||||
val inAppAuths
|
val inAppAuths
|
||||||
get() = listOf(openSubtitlesApi)//, nginxApi)
|
get() = listOf<InAppAuthAPIManager>(
|
||||||
|
openSubtitlesApi,
|
||||||
|
subDlApi
|
||||||
|
)//, nginxApi)
|
||||||
|
|
||||||
val subtitleProviders
|
val subtitleProviders
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
openSubtitlesApi,
|
openSubtitlesApi,
|
||||||
indexSubtitlesApi, // they got anti scraping measures in place :(
|
|
||||||
addic7ed,
|
addic7ed,
|
||||||
subDl
|
subDlApi
|
||||||
)
|
)
|
||||||
|
|
||||||
const val appString = "cloudstreamapp"
|
const val appString = "cloudstreamapp"
|
||||||
|
|
|
@ -1,265 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.syncproviders.providers
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.lagradost.cloudstream3.TvType
|
|
||||||
import com.lagradost.cloudstream3.app
|
|
||||||
import com.lagradost.cloudstream3.imdbUrlToIdNullable
|
|
||||||
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
|
|
||||||
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
|
||||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
|
||||||
|
|
||||||
class IndexSubtitleApi : AbstractSubApi {
|
|
||||||
override val name = "IndexSubtitle"
|
|
||||||
override val idPrefix = "indexsubtitle"
|
|
||||||
override val requiresLogin = false
|
|
||||||
override val icon: Nothing? = null
|
|
||||||
override val createAccountUrl: Nothing? = null
|
|
||||||
|
|
||||||
override fun loginInfo(): Nothing? = null
|
|
||||||
|
|
||||||
override fun logOut() {}
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val host = "https://indexsubtitle.com"
|
|
||||||
const val TAG = "INDEXSUBS"
|
|
||||||
|
|
||||||
fun getOrdinal(num: Int?): String? {
|
|
||||||
return when (num) {
|
|
||||||
1 -> "First"
|
|
||||||
2 -> "Second"
|
|
||||||
3 -> "Third"
|
|
||||||
4 -> "Fourth"
|
|
||||||
5 -> "Fifth"
|
|
||||||
6 -> "Sixth"
|
|
||||||
7 -> "Seventh"
|
|
||||||
8 -> "Eighth"
|
|
||||||
9 -> "Ninth"
|
|
||||||
10 -> "Tenth"
|
|
||||||
11 -> "Eleventh"
|
|
||||||
12 -> "Twelfth"
|
|
||||||
13 -> "Thirteenth"
|
|
||||||
14 -> "Fourteenth"
|
|
||||||
15 -> "Fifteenth"
|
|
||||||
16 -> "Sixteenth"
|
|
||||||
17 -> "Seventeenth"
|
|
||||||
18 -> "Eighteenth"
|
|
||||||
19 -> "Nineteenth"
|
|
||||||
20 -> "Twentieth"
|
|
||||||
21 -> "Twenty-First"
|
|
||||||
22 -> "Twenty-Second"
|
|
||||||
23 -> "Twenty-Third"
|
|
||||||
24 -> "Twenty-Fourth"
|
|
||||||
25 -> "Twenty-Fifth"
|
|
||||||
26 -> "Twenty-Sixth"
|
|
||||||
27 -> "Twenty-Seventh"
|
|
||||||
28 -> "Twenty-Eighth"
|
|
||||||
29 -> "Twenty-Ninth"
|
|
||||||
30 -> "Thirtieth"
|
|
||||||
31 -> "Thirty-First"
|
|
||||||
32 -> "Thirty-Second"
|
|
||||||
33 -> "Thirty-Third"
|
|
||||||
34 -> "Thirty-Fourth"
|
|
||||||
35 -> "Thirty-Fifth"
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fixUrl(url: String): String {
|
|
||||||
if (url.startsWith("http")) {
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
if (url.isEmpty()) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
val startsWithNoHttp = url.startsWith("//")
|
|
||||||
if (startsWithNoHttp) {
|
|
||||||
return "https:$url"
|
|
||||||
} else {
|
|
||||||
if (url.startsWith('/')) {
|
|
||||||
return host + url
|
|
||||||
}
|
|
||||||
return "$host/$url"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?): Boolean {
|
|
||||||
val FILTER_EPS_REGEX =
|
|
||||||
Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))")
|
|
||||||
return text.contains(FILTER_EPS_REGEX)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun haveEps(text: String): Boolean {
|
|
||||||
val HAVE_EPS_REGEX =
|
|
||||||
Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))")
|
|
||||||
return text.contains(HAVE_EPS_REGEX)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
|
|
||||||
val imdbId = query.imdbId?.replace("tt", "")?.toLong() ?: 0
|
|
||||||
val lang = query.lang
|
|
||||||
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
|
|
||||||
val queryText = query.query
|
|
||||||
val epNum = query.epNumber ?: 0
|
|
||||||
val seasonNum = query.seasonNumber ?: 0
|
|
||||||
val yearNum = query.year ?: 0
|
|
||||||
|
|
||||||
val urlItems = ArrayList<String>()
|
|
||||||
|
|
||||||
fun cleanResources(
|
|
||||||
results: MutableList<AbstractSubtitleEntities.SubtitleEntity>,
|
|
||||||
name: String,
|
|
||||||
link: String
|
|
||||||
) {
|
|
||||||
results.add(
|
|
||||||
AbstractSubtitleEntities.SubtitleEntity(
|
|
||||||
idPrefix = idPrefix,
|
|
||||||
name = name,
|
|
||||||
lang = queryLang.toString(),
|
|
||||||
data = link,
|
|
||||||
source = this.name,
|
|
||||||
type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie,
|
|
||||||
epNumber = epNum,
|
|
||||||
seasonNumber = seasonNum,
|
|
||||||
year = yearNum,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val document = app.get("$host/?search=$queryText").document
|
|
||||||
|
|
||||||
document.select("div.my-3.p-3 div.media").map { block ->
|
|
||||||
if (seasonNum > 0) {
|
|
||||||
val name = block.select("strong.text-primary, strong.text-info").text().trim()
|
|
||||||
val season = getOrdinal(seasonNum)
|
|
||||||
if ((block.selectFirst("a")?.attr("href")
|
|
||||||
?.contains(
|
|
||||||
"$season",
|
|
||||||
ignoreCase = true
|
|
||||||
)!! || name.contains(
|
|
||||||
"$season",
|
|
||||||
ignoreCase = true
|
|
||||||
)) && name.contains(queryText, ignoreCase = true)
|
|
||||||
) {
|
|
||||||
block.select("div.media").mapNotNull {
|
|
||||||
urlItems.add(
|
|
||||||
fixUrl(
|
|
||||||
it.selectFirst("a")!!.attr("href")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (block.selectFirst("strong")!!.text().trim()
|
|
||||||
.matches(Regex("(?i)^$queryText\$"))
|
|
||||||
) {
|
|
||||||
if (block.select("span[title=Release]").isNullOrEmpty()) {
|
|
||||||
block.select("div.media").mapNotNull {
|
|
||||||
val urlItem = fixUrl(
|
|
||||||
it.selectFirst("a")!!.attr("href")
|
|
||||||
)
|
|
||||||
val itemDoc = app.get(urlItem).document
|
|
||||||
val id = imdbUrlToIdNullable(
|
|
||||||
itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent()
|
|
||||||
?.attr("href")
|
|
||||||
)?.toLongOrNull()
|
|
||||||
val year = itemDoc.selectFirst("div.d-flex span.badge.badge-success")
|
|
||||||
?.ownText()
|
|
||||||
?.trim().toString()
|
|
||||||
Log.i(TAG, "id => $id \nyear => $year||$yearNum")
|
|
||||||
if (imdbId > 0) {
|
|
||||||
if (id == imdbId) {
|
|
||||||
urlItems.add(urlItem)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (year.contains("$yearNum")) {
|
|
||||||
urlItems.add(urlItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (block.select("span[title=Release]").text().trim()
|
|
||||||
.contains("$yearNum")
|
|
||||||
) {
|
|
||||||
block.select("div.media").mapNotNull {
|
|
||||||
urlItems.add(
|
|
||||||
fixUrl(
|
|
||||||
it.selectFirst("a")!!.attr("href")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.i(TAG, "urlItems => $urlItems")
|
|
||||||
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
|
|
||||||
|
|
||||||
urlItems.forEach { url ->
|
|
||||||
val request = app.get(url)
|
|
||||||
if (request.isSuccessful) {
|
|
||||||
request.document.select("div.my-3.p-3 div.media").map { block ->
|
|
||||||
if (block.select("span.d-block span[data-original-title=Language]").text()
|
|
||||||
.trim()
|
|
||||||
.contains("$queryLang")
|
|
||||||
) {
|
|
||||||
var name = block.select("strong.text-primary, strong.text-info").text().trim()
|
|
||||||
val link = fixUrl(block.selectFirst("a")!!.attr("href"))
|
|
||||||
if (seasonNum > 0) {
|
|
||||||
when {
|
|
||||||
isRightEps(name, seasonNum, epNum) -> {
|
|
||||||
cleanResources(results, name, link)
|
|
||||||
}
|
|
||||||
!(haveEps(name)) -> {
|
|
||||||
name = "$name (S${seasonNum}:E${epNum})"
|
|
||||||
cleanResources(results, name, link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cleanResources(results, name, link)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun load(data: AbstractSubtitleEntities.SubtitleEntity): String? {
|
|
||||||
val seasonNum = data.seasonNumber
|
|
||||||
val epNum = data.epNumber
|
|
||||||
|
|
||||||
val req = app.get(data.data)
|
|
||||||
|
|
||||||
if (req.isSuccessful) {
|
|
||||||
val document = req.document
|
|
||||||
val link = if (document.select("div.my-3.p-3 div.media").size == 1) {
|
|
||||||
fixUrl(
|
|
||||||
document.selectFirst("div.my-3.p-3 div.media a")!!.attr("href")
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
document.select("div.my-3.p-3 div.media").firstNotNullOf { block ->
|
|
||||||
val name =
|
|
||||||
block.selectFirst("strong.d-block")?.text()?.trim().toString()
|
|
||||||
if (seasonNum!! > 0) {
|
|
||||||
if (isRightEps(name, seasonNum, epNum)) {
|
|
||||||
fixUrl(block.selectFirst("a")!!.attr("href"))
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fixUrl(block.selectFirst("a")!!.attr("href"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return link
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,21 +1,80 @@
|
||||||
package com.lagradost.cloudstream3.syncproviders.providers
|
package com.lagradost.cloudstream3.syncproviders.providers
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
|
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
import com.lagradost.cloudstream3.app
|
import com.lagradost.cloudstream3.app
|
||||||
import com.lagradost.cloudstream3.subtitles.AbstractSubProvider
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
|
||||||
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
|
||||||
import com.lagradost.cloudstream3.subtitles.SubtitleResource
|
import com.lagradost.cloudstream3.subtitles.SubtitleResource
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI.LoginInfo
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager
|
||||||
|
|
||||||
class SubDL : AbstractSubProvider {
|
class SubDlApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi {
|
||||||
//API Documentation: https://subdl.com/api-doc
|
|
||||||
val mainUrl = "https://subdl.com/"
|
|
||||||
val name = "SubDL"
|
|
||||||
override val idPrefix = "subdl"
|
override val idPrefix = "subdl"
|
||||||
|
override val name = "SubDL"
|
||||||
|
override val icon = R.drawable.subdl_logo_big
|
||||||
|
override val requiresPassword = true
|
||||||
|
override val requiresEmail = true
|
||||||
|
override val createAccountUrl = "https://subdl.com/login"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val APIKEY = "zRJl5QA-8jNA2i0pE8cxANbEukANp7IM"
|
const val APIURL = "https://api.subdl.com"
|
||||||
const val APIENDPOINT = "https://api.subdl.com/api/v1/subtitles"
|
const val APIENDPOINT = "$APIURL/api/v1/subtitles"
|
||||||
const val DOWNLOADENDPOINT = "https://dl.subdl.com"
|
const val DOWNLOADENDPOINT = "https://dl.subdl.com"
|
||||||
|
const val SUBDL_SUBTITLES_USER_KEY: String = "subdl_user"
|
||||||
|
var currentSession: SubtitleOAuthEntity? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun initialize() {
|
||||||
|
currentSession = getAuthKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun logOut() {
|
||||||
|
setAuthKey(null)
|
||||||
|
removeAccountKeys()
|
||||||
|
currentSession = getAuthKey()
|
||||||
|
}
|
||||||
|
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
|
||||||
|
val email = data.email ?: throw ErrorLoadingException("Requires Email")
|
||||||
|
val password = data.password ?: throw ErrorLoadingException("Requires Password")
|
||||||
|
switchToNewAccount()
|
||||||
|
try {
|
||||||
|
if (initLogin(email, password)) {
|
||||||
|
registerAccount()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
switchToOldAccount()
|
||||||
|
}
|
||||||
|
switchToOldAccount()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLatestLoginData(): InAppAuthAPI.LoginData? {
|
||||||
|
val current = getAuthKey() ?: return null
|
||||||
|
return InAppAuthAPI.LoginData(
|
||||||
|
email = current.userEmail,
|
||||||
|
password = current.pass
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loginInfo(): LoginInfo? {
|
||||||
|
getAuthKey()?.let { user ->
|
||||||
|
return LoginInfo(
|
||||||
|
profilePicture = null,
|
||||||
|
name = user.name ?: user.userEmail,
|
||||||
|
accountIndex = accountIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
|
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
|
||||||
|
@ -37,8 +96,8 @@ class SubDL : AbstractSubProvider {
|
||||||
|
|
||||||
val searchQueryUrl = when (idQuery) {
|
val searchQueryUrl = when (idQuery) {
|
||||||
//Use imdb/tmdb id to search if its valid
|
//Use imdb/tmdb id to search if its valid
|
||||||
null -> "$APIENDPOINT?api_key=$APIKEY&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
null -> "$APIENDPOINT?api_key=${currentSession?.apiKey}&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
||||||
else -> "$APIENDPOINT?api_key=$APIKEY$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
else -> "$APIENDPOINT?api_key=${currentSession?.apiKey}$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
|
||||||
}
|
}
|
||||||
|
|
||||||
val req = app.get(
|
val req = app.get(
|
||||||
|
@ -49,7 +108,7 @@ class SubDL : AbstractSubProvider {
|
||||||
)
|
)
|
||||||
|
|
||||||
return req.parsedSafe<ApiResponse>()?.subtitles?.map { subtitle ->
|
return req.parsedSafe<ApiResponse>()?.subtitles?.map { subtitle ->
|
||||||
val name = subtitle.releaseName
|
|
||||||
val lang = subtitle.lang.replaceFirstChar { it.uppercase() }
|
val lang = subtitle.lang.replaceFirstChar { it.uppercase() }
|
||||||
val resEpNum = subtitle.episode ?: query.epNumber
|
val resEpNum = subtitle.episode ?: query.epNumber
|
||||||
val resSeasonNum = subtitle.season ?: query.seasonNumber
|
val resSeasonNum = subtitle.season ?: query.seasonNumber
|
||||||
|
@ -57,13 +116,14 @@ class SubDL : AbstractSubProvider {
|
||||||
|
|
||||||
AbstractSubtitleEntities.SubtitleEntity(
|
AbstractSubtitleEntities.SubtitleEntity(
|
||||||
idPrefix = this.idPrefix,
|
idPrefix = this.idPrefix,
|
||||||
name = name,
|
name = subtitle.releaseName,
|
||||||
lang = lang,
|
lang = lang,
|
||||||
data = "${DOWNLOADENDPOINT}${subtitle.url}",
|
data = "${DOWNLOADENDPOINT}${subtitle.url}",
|
||||||
type = type,
|
type = type,
|
||||||
source = this.name,
|
source = this.name,
|
||||||
epNumber = resEpNum,
|
epNumber = resEpNum,
|
||||||
seasonNumber = resSeasonNum,
|
seasonNumber = resSeasonNum,
|
||||||
|
isHearingImpaired = subtitle.hearingImpaired ?: false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +134,88 @@ class SubDL : AbstractSubProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun initLogin(useremail: String, password: String): Boolean {
|
||||||
|
|
||||||
|
val tokenResponse = app.post(
|
||||||
|
url = "$APIURL/login",
|
||||||
|
data = mapOf(
|
||||||
|
"email" to useremail,
|
||||||
|
"password" to password
|
||||||
|
)
|
||||||
|
).parsedSafe<OAuthTokenResponse>()
|
||||||
|
|
||||||
|
if (tokenResponse?.token == null) return false
|
||||||
|
|
||||||
|
val apiResponse = app.get(
|
||||||
|
url = "$APIURL/user/userApi",
|
||||||
|
headers = mapOf(
|
||||||
|
"Authorization" to "Bearer ${tokenResponse.token}"
|
||||||
|
)
|
||||||
|
).parsedSafe<ApiKeyResponse>()
|
||||||
|
|
||||||
|
if (apiResponse?.ok == false) return false
|
||||||
|
|
||||||
|
setAuthKey(
|
||||||
|
SubtitleOAuthEntity(
|
||||||
|
userEmail = useremail,
|
||||||
|
pass = password,
|
||||||
|
name = tokenResponse.userData?.username ?: tokenResponse.userData?.name,
|
||||||
|
accessToken = tokenResponse.token,
|
||||||
|
apiKey = apiResponse?.apiKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAuthKey(): SubtitleOAuthEntity? {
|
||||||
|
return getKey(accountId, SUBDL_SUBTITLES_USER_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setAuthKey(data: SubtitleOAuthEntity?) {
|
||||||
|
if (data == null) removeKey(
|
||||||
|
accountId,
|
||||||
|
SUBDL_SUBTITLES_USER_KEY
|
||||||
|
)
|
||||||
|
currentSession = data
|
||||||
|
setKey(accountId, SUBDL_SUBTITLES_USER_KEY, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SubtitleOAuthEntity(
|
||||||
|
@JsonProperty("userEmail") var userEmail: String,
|
||||||
|
@JsonProperty("pass") var pass: String,
|
||||||
|
@JsonProperty("name") var name: String? = null,
|
||||||
|
@JsonProperty("accessToken") var accessToken: String? = null,
|
||||||
|
@JsonProperty("apiKey") var apiKey: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class OAuthTokenResponse(
|
||||||
|
@JsonProperty("token") val token: String? = null,
|
||||||
|
@JsonProperty("userData") val userData: UserData? = null,
|
||||||
|
@JsonProperty("status") val status: Boolean? = null,
|
||||||
|
@JsonProperty("message") val message: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class UserData(
|
||||||
|
@JsonProperty("email") val email: String,
|
||||||
|
@JsonProperty("name") val name: String,
|
||||||
|
@JsonProperty("country") val country: String,
|
||||||
|
@JsonProperty("scStepCode") val scStepCode: String,
|
||||||
|
@JsonProperty("scVerified") val scVerified: Boolean,
|
||||||
|
@JsonProperty("username") val username: String? = null,
|
||||||
|
@JsonProperty("scUsername") val scUsername: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ApiKeyResponse(
|
||||||
|
@JsonProperty("ok") val ok: Boolean? = false,
|
||||||
|
@JsonProperty("api_key") val apiKey: String? = null,
|
||||||
|
@JsonProperty("usage") val usage: Usage? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Usage(
|
||||||
|
@JsonProperty("total") val total: Long? = 0,
|
||||||
|
@JsonProperty("today") val today: Long? = 0,
|
||||||
|
)
|
||||||
|
|
||||||
data class ApiResponse(
|
data class ApiResponse(
|
||||||
@JsonProperty("status") val status: Boolean? = null,
|
@JsonProperty("status") val status: Boolean? = null,
|
||||||
@JsonProperty("results") val results: List<Result>? = null,
|
@JsonProperty("results") val results: List<Result>? = null,
|
||||||
|
@ -96,7 +238,10 @@ class SubDL : AbstractSubProvider {
|
||||||
@JsonProperty("lang") val lang: String,
|
@JsonProperty("lang") val lang: String,
|
||||||
@JsonProperty("author") val author: String? = null,
|
@JsonProperty("author") val author: String? = null,
|
||||||
@JsonProperty("url") val url: String? = null,
|
@JsonProperty("url") val url: String? = null,
|
||||||
|
@JsonProperty("subtitlePage") val subtitlePage: String? = null,
|
||||||
@JsonProperty("season") val season: Int? = null,
|
@JsonProperty("season") val season: Int? = null,
|
||||||
@JsonProperty("episode") val episode: Int? = null,
|
@JsonProperty("episode") val episode: Int? = null,
|
||||||
|
@JsonProperty("language") val language: String? = null,
|
||||||
|
@JsonProperty("hi") val hearingImpaired: Boolean? = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -17,7 +17,7 @@ import com.lagradost.safefile.SafeFile
|
||||||
const val DTAG = "PlayerActivity"
|
const val DTAG = "PlayerActivity"
|
||||||
|
|
||||||
class DownloadedPlayerActivity : AppCompatActivity() {
|
class DownloadedPlayerActivity : AppCompatActivity() {
|
||||||
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
|
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||||
CommonActivity.dispatchKeyEvent(this, event)?.let {
|
CommonActivity.dispatchKeyEvent(this, event)?.let {
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
|
@ -1728,7 +1728,7 @@ class ResultViewModel2 : ViewModel() {
|
||||||
txt(R.string.episode_action_cast_mirror)
|
txt(R.string.episode_action_cast_mirror)
|
||||||
) { (result, index) ->
|
) { (result, index) ->
|
||||||
val host = device?.host ?: return@acquireSingleLink
|
val host = device?.host ?: return@acquireSingleLink
|
||||||
val link = result.links.firstOrNull() ?: return@acquireSingleLink
|
val link = result.links.getOrNull(index) ?: return@acquireSingleLink
|
||||||
|
|
||||||
FcastSession(host).use { session ->
|
FcastSession(host).use { session ->
|
||||||
session.sendMessage(
|
session.sendMessage(
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniList
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subDlApi
|
||||||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.OAuth2API
|
import com.lagradost.cloudstream3.syncproviders.OAuth2API
|
||||||
|
@ -35,6 +36,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
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.hideOn
|
||||||
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.setToolBarScrollFlags
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
|
@ -297,10 +299,10 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
setPreferencesFromResource(R.xml.settings_account, rootKey)
|
setPreferencesFromResource(R.xml.settings_account, rootKey)
|
||||||
|
|
||||||
// hide preference on tvs and emulators
|
//Hides the security category on TV as it's only Biometric for now
|
||||||
getPref(R.string.biometric_key)?.isEnabled = isLayout(PHONE)
|
getPref(R.string.pref_category_security_key)?.hideOn(TV or EMULATOR)
|
||||||
|
|
||||||
getPref(R.string.biometric_key)?.setOnPreferenceClickListener {
|
getPref(R.string.biometric_key)?.hideOn(TV or EMULATOR)?.setOnPreferenceClickListener {
|
||||||
val ctx = context ?: return@setOnPreferenceClickListener false
|
val ctx = context ?: return@setOnPreferenceClickListener false
|
||||||
|
|
||||||
if (deviceHasPasswordPinLock(ctx)) {
|
if (deviceHasPasswordPinLock(ctx)) {
|
||||||
|
@ -324,12 +326,12 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
|
||||||
R.string.anilist_key to aniListApi,
|
R.string.anilist_key to aniListApi,
|
||||||
R.string.simkl_key to simklApi,
|
R.string.simkl_key to simklApi,
|
||||||
R.string.opensubtitles_key to openSubtitlesApi,
|
R.string.opensubtitles_key to openSubtitlesApi,
|
||||||
|
R.string.subdl_key to subDlApi,
|
||||||
)
|
)
|
||||||
|
|
||||||
for ((key, api) in syncApis) {
|
for ((key, api) in syncApis) {
|
||||||
getPref(key)?.apply {
|
getPref(key)?.apply {
|
||||||
title =
|
title = api.name
|
||||||
getString(R.string.login_format).format(api.name, getString(R.string.account))
|
|
||||||
setOnPreferenceClickListener {
|
setOnPreferenceClickListener {
|
||||||
val info = api.loginInfo()
|
val info = api.loginInfo()
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper
|
import com.lagradost.cloudstream3.utils.UIHelper
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
|
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
|
||||||
|
@ -53,6 +54,30 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide many Preferences on selected layouts.
|
||||||
|
**/
|
||||||
|
fun PreferenceFragmentCompat?.hidePrefs(ids: List<Int>, layoutFlags: Int) {
|
||||||
|
if (this == null) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
ids.forEach {
|
||||||
|
getPref(it)?.isVisible = !isLayout(layoutFlags)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the Preference on selected layouts.
|
||||||
|
**/
|
||||||
|
fun Preference?.hideOn(layoutFlags: Int): Preference? {
|
||||||
|
if (this == null) return null
|
||||||
|
this.isVisible = !isLayout(layoutFlags)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On TV you cannot properly scroll to the bottom of settings, this fixes that.
|
* On TV you cannot properly scroll to the bottom of settings, this fixes that.
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -27,10 +27,13 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.network.initClient
|
import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.beneneCount
|
import com.lagradost.cloudstream3.ui.settings.Globals.beneneCount
|
||||||
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
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.hideOn
|
||||||
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.setToolBarScrollFlags
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
|
@ -208,9 +211,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable preference on tvs and emulators
|
getPref(R.string.battery_optimisation_key)?.hideOn(TV or EMULATOR)?.setOnPreferenceClickListener {
|
||||||
getPref(R.string.battery_optimisation_key)?.isEnabled = isLayout(PHONE)
|
|
||||||
getPref(R.string.battery_optimisation_key)?.setOnPreferenceClickListener {
|
|
||||||
val ctx = context ?: return@setOnPreferenceClickListener false
|
val ctx = context ?: return@setOnPreferenceClickListener false
|
||||||
|
|
||||||
if (isAppRestricted(ctx)) {
|
if (isAppRestricted(ctx)) {
|
||||||
|
|
|
@ -7,8 +7,14 @@ import androidx.preference.PreferenceFragmentCompat
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.TV
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
|
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.getPref
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hideOn
|
||||||
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hidePrefs
|
||||||
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.setToolBarScrollFlags
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
||||||
|
@ -31,6 +37,18 @@ class SettingsPlayer : PreferenceFragmentCompat() {
|
||||||
setPreferencesFromResource(R.xml.settings_player, rootKey)
|
setPreferencesFromResource(R.xml.settings_player, rootKey)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
|
|
||||||
|
//Hide specific prefs on TV/EMULATOR
|
||||||
|
hidePrefs(
|
||||||
|
listOf(
|
||||||
|
R.string.pref_category_gestures_key,
|
||||||
|
R.string.rotate_video_key,
|
||||||
|
R.string.auto_rotate_video_key
|
||||||
|
),
|
||||||
|
TV or EMULATOR
|
||||||
|
)
|
||||||
|
|
||||||
|
getPref(R.string.pref_category_android_tv_key)?.hideOn(PHONE)
|
||||||
|
|
||||||
getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener {
|
getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener {
|
||||||
val prefNames = resources.getStringArray(R.array.video_buffer_length_names)
|
val prefNames = resources.getStringArray(R.array.video_buffer_length_names)
|
||||||
val prefValues = resources.getIntArray(R.array.video_buffer_length_values)
|
val prefValues = resources.getIntArray(R.array.video_buffer_length_values)
|
||||||
|
@ -227,6 +245,5 @@ class SettingsPlayer : PreferenceFragmentCompat() {
|
||||||
return@setOnPreferenceClickListener true
|
return@setOnPreferenceClickListener true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
|
import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider
|
||||||
import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN
|
import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.amap
|
import com.lagradost.cloudstream3.amap
|
||||||
|
@ -181,8 +182,11 @@ class PluginsViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updatePluginListPrivate(context: Context, repositoryUrl: String) {
|
private suspend fun updatePluginListPrivate(context: Context, repositoryUrl: String) {
|
||||||
|
val isAdult = settingsForProvider.enableAdult
|
||||||
val plugins = getPlugins(repositoryUrl)
|
val plugins = getPlugins(repositoryUrl)
|
||||||
val list = plugins.map { plugin ->
|
val list = plugins.filter {
|
||||||
|
return@filter !(it.second.tvTypes?.contains("NSFW") == true && !isAdult)
|
||||||
|
}.map { plugin ->
|
||||||
PluginViewData(plugin, isDownloaded(context, plugin.second.internalName, plugin.first))
|
PluginViewData(plugin, isDownloaded(context, plugin.second.internalName, plugin.first))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_T
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY
|
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY
|
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY
|
||||||
import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY
|
import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.providers.SubDlApi.Companion.SUBDL_SUBTITLES_USER_KEY
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||||
|
@ -64,6 +65,7 @@ object BackupUtils {
|
||||||
PLUGINS_KEY_LOCAL,
|
PLUGINS_KEY_LOCAL,
|
||||||
|
|
||||||
OPEN_SUBTITLES_USER_KEY,
|
OPEN_SUBTITLES_USER_KEY,
|
||||||
|
SUBDL_SUBTITLES_USER_KEY,
|
||||||
|
|
||||||
DOWNLOAD_EPISODE_CACHE,
|
DOWNLOAD_EPISODE_CACHE,
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ import com.lagradost.cloudstream3.extractors.Gdriveplayerorg
|
||||||
import com.lagradost.cloudstream3.extractors.Gdriveplayerus
|
import com.lagradost.cloudstream3.extractors.Gdriveplayerus
|
||||||
import com.lagradost.cloudstream3.extractors.Gofile
|
import com.lagradost.cloudstream3.extractors.Gofile
|
||||||
import com.lagradost.cloudstream3.extractors.GuardareStream
|
import com.lagradost.cloudstream3.extractors.GuardareStream
|
||||||
|
import com.lagradost.cloudstream3.extractors.GoodstreamExtractor
|
||||||
import com.lagradost.cloudstream3.extractors.Guccihide
|
import com.lagradost.cloudstream3.extractors.Guccihide
|
||||||
import com.lagradost.cloudstream3.extractors.Hxfile
|
import com.lagradost.cloudstream3.extractors.Hxfile
|
||||||
import com.lagradost.cloudstream3.extractors.JWPlayer
|
import com.lagradost.cloudstream3.extractors.JWPlayer
|
||||||
|
@ -110,6 +111,7 @@ import com.lagradost.cloudstream3.extractors.Hotlinger
|
||||||
import com.lagradost.cloudstream3.extractors.FourCX
|
import com.lagradost.cloudstream3.extractors.FourCX
|
||||||
import com.lagradost.cloudstream3.extractors.PlayRu
|
import com.lagradost.cloudstream3.extractors.PlayRu
|
||||||
import com.lagradost.cloudstream3.extractors.FourPlayRu
|
import com.lagradost.cloudstream3.extractors.FourPlayRu
|
||||||
|
import com.lagradost.cloudstream3.extractors.FourPichive
|
||||||
import com.lagradost.cloudstream3.extractors.HDMomPlayer
|
import com.lagradost.cloudstream3.extractors.HDMomPlayer
|
||||||
import com.lagradost.cloudstream3.extractors.HDPlayerSystem
|
import com.lagradost.cloudstream3.extractors.HDPlayerSystem
|
||||||
import com.lagradost.cloudstream3.extractors.VideoSeyred
|
import com.lagradost.cloudstream3.extractors.VideoSeyred
|
||||||
|
@ -748,6 +750,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
FourCX(),
|
FourCX(),
|
||||||
PlayRu(),
|
PlayRu(),
|
||||||
FourPlayRu(),
|
FourPlayRu(),
|
||||||
|
FourPichive(),
|
||||||
HDMomPlayer(),
|
HDMomPlayer(),
|
||||||
HDPlayerSystem(),
|
HDPlayerSystem(),
|
||||||
VideoSeyred(),
|
VideoSeyred(),
|
||||||
|
@ -877,6 +880,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
|
||||||
Gdriveplayerorg(),
|
Gdriveplayerorg(),
|
||||||
Gdriveplayerus(),
|
Gdriveplayerus(),
|
||||||
Gdriveplayerco(),
|
Gdriveplayerco(),
|
||||||
|
GoodstreamExtractor(),
|
||||||
Gdriveplayer(),
|
Gdriveplayer(),
|
||||||
DatabaseGdrive(),
|
DatabaseGdrive(),
|
||||||
DatabaseGdrive2(),
|
DatabaseGdrive2(),
|
||||||
|
|
10
app/src/main/res/drawable/subdl_logo_big.xml
Normal file
10
app/src/main/res/drawable/subdl_logo_big.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportHeight="320"
|
||||||
|
android:viewportWidth="320"
|
||||||
|
android:width="20dp">
|
||||||
|
|
||||||
|
<path android:fillColor="@color/white"
|
||||||
|
android:pathData="m107.87,39.3l-8.44,8.59l0,35.68l0,35.83l18.95,22.5c10.36,12.44 30.35,36.27 44.41,53l25.46,30.5l0,12.14l0,12.29l-24.43,-0l-24.43,-0l0,-11.84l0,-11.84l-19.99,-0l-19.99,-0l0,23.24l0,23.24l8.44,8.59l8.44,8.59l48.26,-0l48.26,-0l7.7,-7.85l7.7,-7.85l0,-36.86l-0.15,-37.01l-23.98,-28.28c-13.18,-15.54 -33.16,-39.23 -44.26,-52.55l-20.43,-24.13l0,-12.29l0,-12.29l24.43,-0l24.43,-0l0,12.58l0,12.58l19.99,-0l19.99,-0l0,-24.87l0,-24.87l-7.85,-7.7l-7.85,-7.7l-48.11,-0l-48.11,-0l-8.44,8.59z"/>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -62,14 +62,16 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/account_switch_account"
|
android:id="@+id/account_switch_account"
|
||||||
android:text="@string/switch_account"
|
android:text="@string/switch_account"
|
||||||
style="@style/SettingsItem" />
|
style="@style/SettingsItem"
|
||||||
|
android:focusable="true"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/account_logout"
|
android:id="@+id/account_logout"
|
||||||
android:text="@string/logout"
|
android:text="@string/logout"
|
||||||
style="@style/SettingsItem">
|
style="@style/SettingsItem"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="match_parent">
|
android:layout_width="match_parent"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/account_profile_picture_holder"
|
android:id="@+id/account_profile_picture_holder"
|
||||||
|
@ -15,16 +16,16 @@
|
||||||
android:layout_height="30dp">
|
android:layout_height="30dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/account_profile_picture"
|
android:id="@+id/account_profile_picture"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:ignore="ContentDescription" />
|
tools:ignore="ContentDescription" />
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:foreground="@null"
|
android:foreground="@null"
|
||||||
android:id="@+id/account_name"
|
android:id="@+id/account_name"
|
||||||
tools:text="Account 1"
|
tools:text="Account 1"
|
||||||
style="@style/SettingsItem" />
|
style="@style/SettingsItem" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,20 @@
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/account_list"
|
android:id="@+id/account_list"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
android:background="?attr/primaryBlackBackground"
|
android:background="?attr/primaryBlackBackground"
|
||||||
tools:listitem="@layout/account_single"
|
tools:listitem="@layout/account_single"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_rowWeight="1"
|
android:layout_rowWeight="1"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:focusable="true"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/account_add"
|
android:id="@+id/account_add"
|
||||||
android:text="@string/add_account"
|
android:text="@string/add_account"
|
||||||
style="@style/SettingsItem">
|
style="@style/SettingsItem"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
</TextView>
|
</TextView>
|
||||||
|
|
|
@ -107,6 +107,7 @@
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/change_providers_img_des"
|
android:contentDescription="@string/change_providers_img_des"
|
||||||
|
android:focusable="true"
|
||||||
android:nextFocusLeft="@id/year_btt"
|
android:nextFocusLeft="@id/year_btt"
|
||||||
android:nextFocusRight="@id/main_search"
|
android:nextFocusRight="@id/main_search"
|
||||||
android:nextFocusUp="@id/nav_rail_view"
|
android:nextFocusUp="@id/nav_rail_view"
|
||||||
|
|
|
@ -441,7 +441,12 @@
|
||||||
<string name="pref_category_actions">Actions</string>
|
<string name="pref_category_actions">Actions</string>
|
||||||
<string name="pref_category_cache">Cache</string>
|
<string name="pref_category_cache">Cache</string>
|
||||||
<string name="pref_category_android_tv">Android TV</string>
|
<string name="pref_category_android_tv">Android TV</string>
|
||||||
|
<string name="pref_category_android_tv_key" translatable="false" >pref_category_android_tv_key</string>
|
||||||
<string name="pref_category_gestures">Gestures</string>
|
<string name="pref_category_gestures">Gestures</string>
|
||||||
|
<string name="pref_category_gestures_key" translatable="false">pref_category_gestures_key</string>
|
||||||
|
<string name="pref_category_security">Security</string>
|
||||||
|
<string name="pref_category_security_key" translatable="false">pref_category_security_key</string>
|
||||||
|
<string name="pref_category_accounts">Accounts</string>
|
||||||
<string name="pref_category_player_features">Player features</string>
|
<string name="pref_category_player_features">Player features</string>
|
||||||
<string name="pref_category_subtitles">Subtitles</string>
|
<string name="pref_category_subtitles">Subtitles</string>
|
||||||
<string name="pref_category_player_layout">Layout</string>
|
<string name="pref_category_player_layout">Layout</string>
|
||||||
|
@ -474,6 +479,7 @@
|
||||||
<string name="simkl_key" translatable="false">simkl_key</string>
|
<string name="simkl_key" translatable="false">simkl_key</string>
|
||||||
<string name="mal_key" translatable="false">mal_key</string>
|
<string name="mal_key" translatable="false">mal_key</string>
|
||||||
<string name="opensubtitles_key" translatable="false">opensubtitles_key</string>
|
<string name="opensubtitles_key" translatable="false">opensubtitles_key</string>
|
||||||
|
<string name="subdl_key" translatable="false">subdl_key</string>
|
||||||
<string name="nginx_key" translatable="false">nginx_key</string>
|
<string name="nginx_key" translatable="false">nginx_key</string>
|
||||||
<string name="example_password">password123</string>
|
<string name="example_password">password123</string>
|
||||||
<string name="example_username">Username</string>
|
<string name="example_username">Username</string>
|
||||||
|
@ -775,4 +781,5 @@
|
||||||
<string name="biometric_prompt_description">After a few failed attempts, the prompt will close. Simply restart the app to try again.</string>
|
<string name="biometric_prompt_description">After a few failed attempts, the prompt will close. Simply restart the app to try again.</string>
|
||||||
<string name="biometric_warning">Your CloudStream data has been backed up now. Although the possibility of this is very low, all devices can behave differently. In the rare case, that you get locked out from accessing the app, clear the app data completely and restore from a backup. We are very sorry for any inconvenience arising from this.</string>
|
<string name="biometric_warning">Your CloudStream data has been backed up now. Although the possibility of this is very low, all devices can behave differently. In the rare case, that you get locked out from accessing the app, clear the app data completely and restore from a backup. We are very sorry for any inconvenience arising from this.</string>
|
||||||
<string name="reset_btn">Reset</string>
|
<string name="reset_btn">Reset</string>
|
||||||
|
<string name="cs3wiki">CloudStream Wiki</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,33 +1,49 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<Preference
|
<PreferenceCategory
|
||||||
android:icon="@drawable/mal_logo"
|
android:title="@string/pref_category_accounts">
|
||||||
android:key="@string/mal_key" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/ic_anilist_icon"
|
android:icon="@drawable/mal_logo"
|
||||||
android:key="@string/anilist_key" />
|
android:key="@string/mal_key" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/simkl_logo"
|
android:icon="@drawable/ic_anilist_icon"
|
||||||
android:key="@string/simkl_key" />
|
android:key="@string/anilist_key" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/open_subtitles_icon"
|
android:icon="@drawable/simkl_logo"
|
||||||
android:key="@string/opensubtitles_key" />
|
android:key="@string/simkl_key" />
|
||||||
|
|
||||||
<SwitchPreference
|
<Preference
|
||||||
android:defaultValue="false"
|
android:icon="@drawable/open_subtitles_icon"
|
||||||
android:icon="@drawable/ic_outline_account_circle_24"
|
android:key="@string/opensubtitles_key" />
|
||||||
android:key="@string/skip_startup_account_select_key"
|
|
||||||
android:title="@string/skip_startup_account_select_pref" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<Preference
|
||||||
android:key="@string/biometric_key"
|
android:icon="@drawable/subdl_logo_big"
|
||||||
android:defaultValue="false"
|
android:key="@string/subdl_key" />
|
||||||
android:summary="@string/biometric_setting_summary"
|
|
||||||
android:icon="@drawable/ic_fingerprint"
|
<SwitchPreference
|
||||||
android:title="@string/biometric_setting" />
|
android:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_outline_account_circle_24"
|
||||||
|
android:key="@string/skip_startup_account_select_key"
|
||||||
|
android:title="@string/skip_startup_account_select_pref" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:title="@string/pref_category_security"
|
||||||
|
app:key="@string/pref_category_security_key">
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:key="@string/biometric_key"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:summary="@string/biometric_setting_summary"
|
||||||
|
android:icon="@drawable/ic_fingerprint"
|
||||||
|
android:title="@string/biometric_setting" />
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
|
@ -86,6 +86,14 @@
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:data="https://discord.gg/5Hus6fM" />
|
android:data="https://discord.gg/5Hus6fM" />
|
||||||
</Preference>
|
</Preference>
|
||||||
|
<Preference
|
||||||
|
android:title="@string/cs3wiki"
|
||||||
|
android:icon="@drawable/baseline_description_24"
|
||||||
|
app:summary="https://cloudstream.miraheze.org/">
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:data="https://cloudstream.miraheze.org/" />
|
||||||
|
</Preference>
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
|
@ -101,7 +101,8 @@
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_category_gestures">
|
android:title="@string/pref_category_gestures"
|
||||||
|
app:key="@string/pref_category_gestures_key">
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:icon="@drawable/ic_baseline_ondemand_video_24"
|
android:icon="@drawable/ic_baseline_ondemand_video_24"
|
||||||
android:summary="@string/swipe_to_seek_settings_des"
|
android:summary="@string/swipe_to_seek_settings_des"
|
||||||
|
@ -166,7 +167,8 @@
|
||||||
android:title="@string/video_buffer_clear_settings" />
|
android:title="@string/video_buffer_clear_settings" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_category_android_tv">
|
android:title="@string/pref_category_android_tv"
|
||||||
|
android:key="@string/pref_category_android_tv_key" >
|
||||||
<SeekBarPreference
|
<SeekBarPreference
|
||||||
android:defaultValue="10"
|
android:defaultValue="10"
|
||||||
android:max="60"
|
android:max="60"
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
android:icon="@drawable/ic_baseline_bug_report_24"
|
|
||||||
android:key="acra.disable"
|
|
||||||
android:summaryOff="@string/bug_report_settings_off"
|
|
||||||
android:summaryOn="@string/bug_report_settings_on"
|
|
||||||
android:title="@string/pref_disable_acra" />
|
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_category_app_updates">
|
android:title="@string/pref_category_app_updates">
|
||||||
|
@ -80,5 +73,12 @@
|
||||||
android:icon="@drawable/ic_baseline_construction_24"
|
android:icon="@drawable/ic_baseline_construction_24"
|
||||||
android:title="@string/redo_setup_process"
|
android:title="@string/redo_setup_process"
|
||||||
app:key="@string/redo_setup_key" />
|
app:key="@string/redo_setup_key" />
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:icon="@drawable/ic_baseline_bug_report_24"
|
||||||
|
android:key="acra.disable"
|
||||||
|
android:summaryOff="@string/bug_report_settings_off"
|
||||||
|
android:summaryOn="@string/bug_report_settings_on"
|
||||||
|
android:title="@string/pref_disable_acra" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
|
@ -6,7 +6,7 @@ buildscript {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:8.2.2")
|
classpath("com.android.tools.build:gradle:8.2.2")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23")
|
||||||
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10")
|
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10")
|
||||||
// Universal build config
|
// Universal build config
|
||||||
classpath("com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:0.15.1")
|
classpath("com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:0.15.1")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue