Merge remote-tracking branch 'origin/master'

# Conflicts:
#	app/build.gradle
This commit is contained in:
KillerDogeEmpire 2022-10-27 19:49:27 -07:00
commit 62d08653de
40 changed files with 1905 additions and 1422 deletions

View File

@ -18,7 +18,7 @@ jobs:
uses: actions-cool/issues-similarity-analysis@v1
with:
token: ${{ steps.generate_token.outputs.token }}
filter-threshold: 0.5
filter-threshold: 0.60
title-excludes: ''
comment-title: |
### Your issue looks similar to these issues:
@ -41,7 +41,7 @@ jobs:
wget --output-document check_issue.py "https://raw.githubusercontent.com/recloudstream/.github/master/.github/check_issue.py"
pip3 install httpx
RES="$(python3 ./check_issue.py)"
echo "::set-output name=name::${RES}"
echo "name=${RES}" >> $GITHUB_OUTPUT
- name: Comment if issue mentions a provider
if: steps.provider_check.outputs.name != 'none'
uses: actions-cool/issues-helper@v3

View File

@ -33,7 +33,7 @@ jobs:
curl -H "Authorization: token ${{ secrets.PAT }}" -o "keystore_password.txt" "https://raw.githubusercontent.com/KillerDogeEmpire/secrets/master/keystore_password.txt"
KEY_PWD="$(cat keystore_password.txt)"
echo "::add-mask::${KEY_PWD}"
echo "::set-output name=key_pwd::$KEY_PWD"
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
- name: Run Gradle
run: |
./gradlew assemblePrerelease makeJar androidSourcesJar
@ -49,6 +49,6 @@ jobs:
prerelease: true
title: "Pre-release Build"
files: |
app/build/outputs/apk/prerelease/*.apk
app/build/outputs/apk/prerelease/release/*.apk
app/build/libs/app-sources.jar
app/build/classes.jar

View File

@ -15,9 +15,9 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run Gradle
run: ./gradlew assembleDebug
run: ./gradlew assemblePrereleaseDebug
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: pull-request-build
path: "app/build/outputs/apk/debug/*.apk"
path: "app/build/outputs/apk/prerelease/debug/*.apk"

View File

@ -31,5 +31,10 @@
<option name="name" value="maven2" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

View File

@ -19,6 +19,7 @@
***The list of supported languages:***
* 🇱🇧 Arabic
* 🇧🇬 Bulgarian
* 🇭🇷 Croatian
* 🇨🇿 Czech
* 🇳🇱 Dutch

View File

@ -1,233 +0,0 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-android-extensions'
id 'org.jetbrains.dokka'
}
def tmpFilePath = System.getProperty("user.home") + "/work/_temp/keystore/"
def allFilesFromDir = new File(tmpFilePath).listFiles()
def prereleaseStoreFile = null
if (allFilesFromDir != null) {
prereleaseStoreFile = allFilesFromDir.first()
}
android {
testOptions {
unitTests.returnDefaultValues = true
}
signingConfigs {
prerelease {
if (prereleaseStoreFile != null) {
storeFile = file(prereleaseStoreFile)
storePassword System.getenv("SIGNING_STORE_PASSWORD")
keyAlias System.getenv("SIGNING_KEY_ALIAS")
keyPassword System.getenv("SIGNING_KEY_PASSWORD")
}
}
}
compileSdkVersion 31
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.killerdogeempire.aquastream"
minSdkVersion 21
targetSdkVersion 30
versionCode 52
versionName "1.0.1"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
resValue "string", "commit_hash",
("git rev-parse --short HEAD".execute().text.trim() ?: "")
resValue "bool", "is_prerelease", "false"
buildConfigField("String", "BUILDDATE", "new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm\").format(new java.util.Date(" + System.currentTimeMillis() + "L));")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
kapt {
includeCompileClasspath = true
}
}
buildTypes {
release {
debuggable false
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "bool", "is_prerelease", "false"
}
prerelease {
applicationIdSuffix ".prerelease"
buildConfigField("boolean", "BETA", "true")
signingConfig signingConfigs.prerelease
versionNameSuffix '-PRE'
debuggable false
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "bool", "is_prerelease", "true"
}
debug {
debuggable true
applicationIdSuffix ".debug"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "bool", "is_prerelease", "true"
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs = ['-Xjvm-default=compatibility']
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.google.android.mediahome:video:1.0.0'
implementation 'androidx.test.ext:junit-ktx:1.1.3'
testImplementation 'org.json:json:20180813'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2' // need target 32 for 1.5.0
// dont change this to 1.6.0 it looks ugly af
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//implementation "io.karn:khttp-android:0.1.2" //okhttp instead
// implementation 'org.jsoup:jsoup:1.13.1'
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation 'com.github.bumptech.glide:glide:4.13.1'
kapt 'com.github.bumptech.glide:compiler:4.13.1'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.13.0'
implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
// implementation "androidx.leanback:leanback-paging:1.1.0-alpha09"
// Exoplayer
implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
implementation 'com.google.android.exoplayer:extension-cast:2.16.1'
implementation "com.google.android.exoplayer:extension-mediasession:2.16.1"
implementation 'com.google.android.exoplayer:extension-okhttp:2.16.1'
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
// Bug reports
implementation "ch.acra:acra-core:5.8.4"
implementation "ch.acra:acra-toast:5.8.4"
compileOnly "com.google.auto.service:auto-service-annotations:1.0"
//either for java sources:
annotationProcessor "com.google.auto.service:auto-service:1.0"
//or for kotlin sources (requires kapt gradle plugin):
kapt "com.google.auto.service:auto-service:1.0"
// subtitle color picker
implementation 'com.jaredrummler:colorpicker:1.1.0'
//run JS
implementation 'org.mozilla:rhino:1.7.14'
// TorrentStream
//implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0'
// Downloading
implementation "androidx.work:work-runtime:2.7.1"
implementation "androidx.work:work-runtime-ktx:2.7.1"
// Networking
// implementation "com.squareup.okhttp3:okhttp:4.9.2"
// implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1"
implementation 'com.github.Blatzar:NiceHttp:0.3.3'
// Util to skip the URI file fuckery 🙏
implementation "com.github.tachiyomiorg:unifile:17bec43"
// API because cba maintaining it myself
implementation "com.uwetrottmann.tmdb2:tmdb-java:2.6.0"
implementation 'com.github.discord:OverlappingPanels:0.1.3'
// debugImplementation because LeakCanary should only run in debug builds.
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
// for shimmer when loading
implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation "androidx.tvprovider:tvprovider:1.0.0"
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
// slow af yt
//implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'
// newpipe yt
implementation 'com.github.TeamNewPipe:NewPipeExtractor:dev-SNAPSHOT'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Library/extensions searching with Levenshtein distance
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
}
task androidSourcesJar(type: Jar) {
getArchiveClassifier().set('sources')
from android.sourceSets.main.java.srcDirs//full sources
}
// this is used by the gradlew plugin
task makeJar(type: Copy) {
from('build/intermediates/compile_app_classes_jar/debug')
into('build')
include('classes.jar')
dependsOn('build')
}
dokkaHtml {
moduleName.set("Cloudstream")
dokkaSourceSets {
main {
sourceLink {
// Unix based directory relative path to the root of the project (where you execute gradle respectively).
localDirectory.set(file("src/main/java"))
// URL showing where the source code can be accessed through the web browser
remoteUrl.set(new URL(
"https://github.com/recloudstream/cloudstream/tree/master/app/src/main/java"))
// Suffix which is used to append the line number to the URL. Use #L for GitHub
remoteLineSuffix.set("#L")
}
}
}
}

250
app/build.gradle.kts Normal file
View File

@ -0,0 +1,250 @@
import com.android.build.gradle.api.BaseVariantOutput
import org.jetbrains.dokka.gradle.DokkaTask
import java.io.ByteArrayOutputStream
import java.net.URL
plugins {
id("com.android.application")
id("kotlin-android")
id("kotlin-kapt")
id("kotlin-android-extensions")
id("org.jetbrains.dokka")
}
val tmpFilePath = System.getProperty("user.home") + "/work/_temp/keystore/"
val prereleaseStoreFile: File? = File(tmpFilePath).listFiles()?.first()
fun String.execute() = ByteArrayOutputStream().use { baot ->
if (project.exec {
workingDir = projectDir
commandLine = this@execute.split(Regex("\\s"))
standardOutput = baot
}.exitValue == 0)
String(baot.toByteArray()).trim()
else null
}
android {
testOptions {
unitTests.isReturnDefaultValues = true
}
signingConfigs {
create("prerelease") {
if (prereleaseStoreFile != null) {
storeFile = file(prereleaseStoreFile)
storePassword = System.getenv("SIGNING_STORE_PASSWORD")
keyAlias = System.getenv("SIGNING_KEY_ALIAS")
keyPassword = System.getenv("SIGNING_KEY_PASSWORD")
}
}
}
compileSdk = 31
buildToolsVersion = "30.0.3"
defaultConfig {
applicationId = "com.lagradost.cloudstream3"
minSdk = 21
targetSdk = 30
versionCode = 52
versionName = "3.1.6"
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
resValue("bool", "is_prerelease", "false")
buildConfigField(
"String",
"BUILDDATE",
"new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm\").format(new java.util.Date(" + System.currentTimeMillis() + "L));"
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
kapt {
includeCompileClasspath = true
}
}
buildTypes {
release {
isDebuggable = false
isMinifyEnabled = false
isShrinkResources = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
debug {
isDebuggable = true
applicationIdSuffix = ".debug"
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
flavorDimensions.add("state")
productFlavors {
create("stable") {
dimension = "state"
resValue("bool", "is_prerelease", "false")
}
create("prerelease") {
dimension = "state"
resValue("bool", "is_prerelease", "true")
buildConfigField("boolean", "BETA", "true")
applicationIdSuffix = ".prerelease"
signingConfig = signingConfigs.getByName("prerelease")
versionNameSuffix = "-PRE"
versionCode = (System.currentTimeMillis() / 60000).toInt()
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = listOf("-Xjvm-default=compatibility")
}
lint {
abortOnError = false
checkReleaseBuilds = false
}
namespace = "com.lagradost.cloudstream3"
}
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("com.google.android.mediahome:video:1.0.0")
implementation("androidx.test.ext:junit-ktx:1.1.3")
testImplementation("org.json:json:20180813")
implementation("androidx.core:core-ktx:1.8.0")
implementation("androidx.appcompat:appcompat:1.4.2") // need target 32 for 1.5.0
// dont change this to 1.6.0 it looks ugly af
implementation("com.google.android.material:material:1.5.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.1")
implementation("androidx.navigation:navigation-ui-ktx:2.5.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
//implementation("io.karn:khttp-android:0.1.2") //okhttp instead
// implementation("org.jsoup:jsoup:1.13.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("com.github.bumptech.glide:glide:4.13.1")
kapt("com.github.bumptech.glide:compiler:4.13.1")
implementation("com.github.bumptech.glide:okhttp3-integration:4.13.0")
implementation("jp.wasabeef:glide-transformations:4.3.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
// Exoplayer
implementation("com.google.android.exoplayer:exoplayer:2.16.1")
implementation("com.google.android.exoplayer:extension-cast:2.16.1")
implementation("com.google.android.exoplayer:extension-mediasession:2.16.1")
implementation("com.google.android.exoplayer:extension-okhttp:2.16.1")
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
// Bug reports
implementation("ch.acra:acra-core:5.8.4")
implementation("ch.acra:acra-toast:5.8.4")
compileOnly("com.google.auto.service:auto-service-annotations:1.0")
//either for java sources:
annotationProcessor("com.google.auto.service:auto-service:1.0")
//or for kotlin sources (requires kapt gradle plugin):
kapt("com.google.auto.service:auto-service:1.0")
// subtitle color picker
implementation("com.jaredrummler:colorpicker:1.1.0")
//run JS
implementation("org.mozilla:rhino:1.7.14")
// TorrentStream
//implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0")
// Downloading
implementation("androidx.work:work-runtime:2.7.1")
implementation("androidx.work:work-runtime-ktx:2.7.1")
// Networking
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
implementation("com.github.Blatzar:NiceHttp:0.3.3")
// Util to skip the URI file fuckery 🙏
implementation("com.github.tachiyomiorg:unifile:17bec43")
// API because cba maintaining it myself
implementation("com.uwetrottmann.tmdb2:tmdb-java:2.6.0")
implementation("com.github.discord:OverlappingPanels:0.1.3")
// debugImplementation because LeakCanary should only run in debug builds.
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
// for shimmer when loading
implementation("com.facebook.shimmer:shimmer:0.5.0")
implementation("androidx.tvprovider:tvprovider:1.0.0")
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation("com.github.albfernandez:juniversalchardet:2.4.0")
// slow af yt
//implementation("com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT")
// newpipe yt
implementation("com.github.TeamNewPipe:NewPipeExtractor:dev-SNAPSHOT")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
// Library/extensions searching with Levenshtein distance
implementation("me.xdrop:fuzzywuzzy:1.4.0")
}
tasks.register("androidSourcesJar", Jar::class) {
archiveClassifier.set("sources")
from(android.sourceSets.getByName("main").java.srcDirs) //full sources
}
// this is used by the gradlew plugin
tasks.register("makeJar", Copy::class) {
from("build/intermediates/compile_app_classes_jar/prereleaseDebug")
into("build")
include("classes.jar")
dependsOn("build")
}
tasks.withType<DokkaTask>().configureEach {
moduleName.set("Cloudstream")
dokkaSourceSets {
named("main") {
sourceLink {
// Unix based directory relative path to the root of the project (where you execute gradle respectively).
localDirectory.set(file("src/main/java"))
// URL showing where the source code can be accessed through the web browser
remoteUrl.set(URL("https://github.com/recloudstream/cloudstream/tree/master/app/src/main/java"))
// Suffix which is used to append the line number to the URL. Use #L for GitHub
remoteLineSuffix.set("#L")
}
}
}
}

View File

@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.lagradost.cloudstream3">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- I dont remember, probs has to do with downloads -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- unless you only use cs3 as a player for downloaded stuff, you need this -->
@ -27,6 +26,7 @@
<package android:name="is.xyz.mpv" />
</queries>
<!-- Without the large heap Exoplayer buffering gets reset due to OOM. -->
<!--TODO https://stackoverflow.com/questions/41799732/chromecast-button-not-visible-in-android-->
<application
android:name=".AcraApplication"
@ -36,6 +36,7 @@
android:fullBackupContent="@xml/backup_descriptor"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"

View File

@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import org.jsoup.nodes.Element
import java.security.DigestException
import java.security.MessageDigest
@ -10,43 +11,47 @@ import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
class DatabaseGdrive2 : Gdriveplayer() {
override var mainUrl = "https://databasegdriveplayer.co"
}
class DatabaseGdrive : Gdriveplayer() {
override var mainUrl = "https://series.databasegdriveplayer.co"
}
class Gdriveplayerapi: Gdriveplayer() {
class Gdriveplayerapi : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayerapi.com"
}
class Gdriveplayerapp: Gdriveplayer() {
class Gdriveplayerapp : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.app"
}
class Gdriveplayerfun: Gdriveplayer() {
class Gdriveplayerfun : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.fun"
}
class Gdriveplayerio: Gdriveplayer() {
class Gdriveplayerio : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.io"
}
class Gdriveplayerme: Gdriveplayer() {
class Gdriveplayerme : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.me"
}
class Gdriveplayerbiz: Gdriveplayer() {
class Gdriveplayerbiz : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.biz"
}
class Gdriveplayerorg: Gdriveplayer() {
class Gdriveplayerorg : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.org"
}
class Gdriveplayerus: Gdriveplayer() {
class Gdriveplayerus : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.us"
}
class Gdriveplayerco: Gdriveplayer() {
class Gdriveplayerco : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.co"
}
@ -136,6 +141,10 @@ open class Gdriveplayer : ExtractorApi() {
return find(str)?.groupValues?.getOrNull(1)
}
private fun String.addMarks(str: String): String {
return this.replace(Regex("\"?$str\"?"), "\"$str\"")
}
override suspend fun getUrl(
url: String,
referer: String?,
@ -145,18 +154,19 @@ open class Gdriveplayer : ExtractorApi() {
val document = app.get(url).document
val eval = unpackJs(document)?.replace("\\", "") ?: return
val data = AppUtils.tryParseJson<AesData>(Regex("data='(\\S+?)'").first(eval)) ?: return
val data = tryParseJson<AesData>(Regex("data='(\\S+?)'").first(eval)) ?: return
val password = Regex("null,['|\"](\\w+)['|\"]").first(eval)
?.split(Regex("\\D+"))
?.joinToString("") {
Char(it.toInt()).toString()
}.let { Regex("var pass = \"(\\S+?)\"").first(it ?: return)?.toByteArray() }
?: throw ErrorLoadingException("can't find password")
val decryptedData =
cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "")
?.substringAfter("sources:[")?.substringBefore("],")
val decryptedData = cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "")
Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(decryptedData ?: return).map {
val sourceData = decryptedData?.substringAfter("sources:[")?.substringBefore("],")
val subData = decryptedData?.substringAfter("tracks:[")?.substringBefore("],")
Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(sourceData ?: return).map {
it.groupValues[1] to it.groupValues[2]
}.toList().distinctBy { it.second }.map { (link, quality) ->
callback.invoke(
@ -171,6 +181,17 @@ open class Gdriveplayer : ExtractorApi() {
)
}
subData?.addMarks("file")?.addMarks("kind")?.addMarks("label").let { dataSub ->
tryParseJson<List<Tracks>>("[$dataSub]")?.map { sub ->
subtitleCallback.invoke(
SubtitleFile(
sub.label,
httpsify(sub.file)
)
)
}
}
}
data class AesData(
@ -179,4 +200,10 @@ open class Gdriveplayer : ExtractorApi() {
@JsonProperty("s") val s: String
)
data class Tracks(
@JsonProperty("file") val file: String,
@JsonProperty("kind") val kind: String,
@JsonProperty("label") val label: String
)
}

View File

@ -0,0 +1,40 @@
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.M3u8Helper
class Moviehab : ExtractorApi() {
override var name = "Moviehab"
override var mainUrl = "https://play.moviehab.com"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url)
res.document.select("video#player").let {
//should redirect first for making it works
val link = app.get("$mainUrl/${it.select("source").attr("src")}", referer = url).url
M3u8Helper.generateM3u8(
this.name,
link,
url
).forEach(callback)
Regex("src[\"|'],\\s[\"|'](\\S+)[\"|']\\)").find(res.text)?.groupValues?.get(1).let {sub ->
subtitleCallback.invoke(
SubtitleFile(
it.select("track").attr("label"),
"$mainUrl/$sub"
)
)
}
}
}
}

View File

@ -7,7 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class SpeedoStream : ExtractorApi() {
class SpeedoStream1 : SpeedoStream() {
override val mainUrl = "https://speedostream.nl"
}
open class SpeedoStream : ExtractorApi() {
override val name = "SpeedoStream"
override val mainUrl = "https://speedostream.com"
override val requiresReferer = true

View File

@ -7,6 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class Sbspeed : StreamSB() {
override var name = "Sbspeed"
override var mainUrl = "https://sbspeed.com"
}
class Streamsss : StreamSB() {
override var mainUrl = "https://streamsss.net"
}
@ -93,15 +98,15 @@ open class StreamSB : ExtractorApi() {
}
data class Subs (
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
)
data class StreamData (
@JsonProperty("file") val file: String,
@JsonProperty("cdn_img") val cdnImg: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("subs") val subs: List<Subs>?,
@JsonProperty("subs") val subs: ArrayList<Subs>? = arrayListOf(),
@JsonProperty("length") val length: String,
@JsonProperty("id") val id: String,
@JsonProperty("title") val title: String,
@ -141,5 +146,14 @@ open class StreamSB : ExtractorApi() {
url,
headers = headers
).forEach(callback)
mapped.streamData.subs?.map {sub ->
subtitleCallback.invoke(
SubtitleFile(
sub.label.toString(),
sub.file ?: return@map null,
)
)
}
}
}

View File

@ -85,18 +85,12 @@ open class VidSrcExtractor : ExtractorApi() {
this.name,
this.name,
srcm3u8,
this.mainUrl,
"https://vidsrc.stream/",
Qualities.Unknown.value,
extractorData = pass,
isM3u8 = true
)
)
// M3u8Helper.generateM3u8(
// name,
// srcm3u8,
// absoluteUrl
// ).forEach(callback)
} else {
loadExtractor(linkfixed, url, subtitleCallback, callback)
}

View File

@ -1,12 +1,23 @@
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.AppUtils
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class Cdnplayer: XStreamCdn() {
override val name: String = "Cdnplayer"
override val mainUrl: String = "https://cdnplayer.online"
}
class Kotakajair: XStreamCdn() {
override val name: String = "Kotakajair"
override val mainUrl: String = "https://kotakajair.xyz"
}
class FEnet: XStreamCdn() {
override val name: String = "FEnet"
override val mainUrl: String = "https://fembed.net"
@ -59,44 +70,67 @@ open class XStreamCdn : ExtractorApi() {
//val type: String // Mp4
)
private data class Player(
@JsonProperty("poster_file") val poster_file: String? = null,
)
private data class ResponseJson(
@JsonProperty("success") val success: Boolean,
@JsonProperty("data") val data: List<ResponseData>?
@JsonProperty("player") val player: Player? = null,
@JsonProperty("data") val data: List<ResponseData>?,
@JsonProperty("captions") val captions: List<Captions?>?,
)
private data class Captions(
@JsonProperty("id") val id: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("language") val language: String,
@JsonProperty("extension") val extension: String
)
override fun getExtractorUrl(id: String): String {
return "$domainUrl/api/source/$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val headers = mapOf(
"Referer" to url,
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
)
val id = url.trimEnd('/').split("/").last()
val newUrl = "https://${domainUrl}/api/source/${id}"
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(app.post(newUrl, headers = headers)) {
if (this.code != 200) return listOf()
val text = this.text
if (text.isEmpty()) return listOf()
if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
AppUtils.parseJson<ResponseJson?>(text)?.let {
app.post(newUrl, headers = headers).let { res ->
val sources = tryParseJson<ResponseJson?>(res.text)
sources?.let {
if (it.success && it.data != null) {
it.data.forEach { data ->
extractedLinksList.add(
it.data.map { source ->
callback.invoke(
ExtractorLink(
name,
name = name,
data.file,
source.file,
url,
getQualityFromName(data.label),
getQualityFromName(source.label),
)
)
}
}
}
val userData = sources?.player?.poster_file?.split("/")?.get(2)
sources?.captions?.map {
subtitleCallback.invoke(
SubtitleFile(
it?.language.toString(),
"$mainUrl/asset/userdata/$userData/caption/${it?.hash}/${it?.id}.${it?.extension}"
)
)
}
}
return extractedLinksList
}
}

View File

@ -26,6 +26,8 @@ class APIRepository(val api: MainAPI) {
fun isInvalidData(data: String): Boolean {
return data.isEmpty() || data == "[]" || data == "about:blank"
}
private val cacheHash: HashMap<Pair<String,String>, LoadResponse> = hashMapOf()
}
val hasMainPage = api.hasMainPage
@ -39,7 +41,13 @@ class APIRepository(val api: MainAPI) {
suspend fun load(url: String): Resource<LoadResponse> {
return safeApiCall {
if (isInvalidData(url)) throw ErrorLoadingException()
api.load(api.fixUrl(url)) ?: throw ErrorLoadingException()
val fixedUrl = api.fixUrl(url)
val key = Pair(api.name,url)
cacheHash[key] ?: api.load(fixedUrl)?.also {
// we cache 20 responses because ppl often go back to the same shit + 20 because I dont want to cause too much memory leak
if (cacheHash.size > 20) cacheHash.remove(cacheHash.keys.random())
cacheHash[key] = it
} ?: throw ErrorLoadingException()
}
}
@ -75,7 +83,12 @@ class APIRepository(val api: MainAPI) {
api.lastHomepageRequest = unixTimeMS
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages)))
listOf(
api.getMainPage(
page,
MainPageRequest(data.name, data.data, data.horizontalImages)
)
)
} ?: run {
if (api.sequentialMainPage) {
var first = true

View File

@ -5,13 +5,12 @@ import androidx.annotation.StringRes
import com.lagradost.cloudstream3.R
enum class WatchType(val internalId: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) {
// FIX ICONS
WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_remove_red_eye_24),
COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_check_24),
ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_pause_24),
DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_close_24),
PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_close_24),
NONE(5, R.string.type_none, R.drawable.ic_baseline_remove_red_eye_24);
WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_bookmark_24),
COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_bookmark_24),
ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_bookmark_24),
DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_bookmark_24),
PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_bookmark_24),
NONE(5, R.string.type_none, R.drawable.ic_baseline_add_24);
companion object {
fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE

View File

@ -14,6 +14,8 @@ import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getDrawable
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
@ -26,11 +28,14 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
@ -43,6 +48,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.ResultViewModel2.Companion.updateWatchStatus
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.search.*
@ -50,6 +56,7 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallba
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@ -61,11 +68,11 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.UIHelper.setImage
@ -82,14 +89,12 @@ import kotlinx.android.synthetic.main.fragment_home.home_loading
import kotlinx.android.synthetic.main.fragment_home.home_loading_error
import kotlinx.android.synthetic.main.fragment_home.home_loading_shimmer
import kotlinx.android.synthetic.main.fragment_home.home_loading_statusbar
import kotlinx.android.synthetic.main.fragment_home.home_main_poster_recyclerview
import kotlinx.android.synthetic.main.fragment_home.home_master_recycler
import kotlinx.android.synthetic.main.fragment_home.home_plan_to_watch_btt
import kotlinx.android.synthetic.main.fragment_home.home_provider_meta_info
import kotlinx.android.synthetic.main.fragment_home.home_provider_name
import kotlinx.android.synthetic.main.fragment_home.home_reload_connection_open_in_browser
import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror
import kotlinx.android.synthetic.main.fragment_home.home_statusbar
import kotlinx.android.synthetic.main.fragment_home.home_type_completed_btt
import kotlinx.android.synthetic.main.fragment_home.home_type_dropped_btt
import kotlinx.android.synthetic.main.fragment_home.home_type_on_hold_btt
@ -100,6 +105,8 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title
import kotlinx.android.synthetic.main.fragment_home.result_error_text
import kotlinx.android.synthetic.main.fragment_home_tv.*
import kotlinx.android.synthetic.main.home_episodes_expanded.*
import kotlinx.android.synthetic.main.tvtypes_chips.*
import kotlinx.android.synthetic.main.tvtypes_chips.view.*
import java.util.*
const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list"
@ -247,16 +254,16 @@ class HomeFragment : Fragment() {
}
fun getPairList(
anime: MaterialButton?,
cartoons: MaterialButton?,
tvs: MaterialButton?,
docs: MaterialButton?,
movies: MaterialButton?,
asian: MaterialButton?,
livestream: MaterialButton?,
nsfw: MaterialButton?,
others: MaterialButton?,
): List<Pair<MaterialButton?, List<TvType>>> {
anime: Chip?,
cartoons: Chip?,
tvs: Chip?,
docs: Chip?,
movies: Chip?,
asian: Chip?,
livestream: Chip?,
nsfw: Chip?,
others: Chip?,
): List<Pair<Chip?, List<TvType>>> {
// This list should be same order as home screen to aid navigation
return listOf(
Pair(movies, listOf(TvType.Movie, TvType.Torrent)),
@ -271,6 +278,59 @@ class HomeFragment : Fragment() {
)
}
private fun getPairList(header: ChipGroup) = getPairList(
header.home_select_anime,
header.home_select_cartoons,
header.home_select_tv_series,
header.home_select_documentaries,
header.home_select_movies,
header.home_select_asian,
header.home_select_livestreams,
header.home_select_nsfw,
header.home_select_others
)
fun validateChips(header: ChipGroup?, validTypes: List<TvType>) {
if (header == null) return
val pairList = getPairList(header)
for ((button, types) in pairList) {
val isValid = validTypes.any { types.contains(it) }
button?.isVisible = isValid
}
}
fun updateChips(header: ChipGroup?, selectedTypes: List<TvType>) {
if (header == null) return
val pairList = getPairList(header)
for ((button, types) in pairList) {
button?.isChecked =
button?.isVisible == true && selectedTypes.any { types.contains(it) }
}
}
fun bindChips(
header: ChipGroup?,
selectedTypes: List<TvType>,
validTypes: List<TvType>,
callback: (List<TvType>) -> Unit
) {
if (header == null) return
val pairList = getPairList(header)
for ((button, types) in pairList) {
val isValid = validTypes.any { types.contains(it) }
button?.isVisible = isValid
button?.isChecked = isValid && selectedTypes.any { types.contains(it) }
button?.setOnCheckedChangeListener { _, _ ->
val list = ArrayList<TvType>()
for ((sbutton, vvalidTypes) in pairList) {
if (sbutton?.isChecked == true)
list.addAll(vvalidTypes)
}
callback(list)
}
}
}
fun Context.selectHomepage(selectedApiName: String?, callback: (String) -> Unit) {
val validAPIs = filterProviderByPreferredMedia().toMutableList()
@ -296,21 +356,9 @@ class HomeFragment : Fragment() {
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
val anime = dialog.findViewById<MaterialButton>(R.id.home_select_anime)
val cartoons = dialog.findViewById<MaterialButton>(R.id.home_select_cartoons)
val tvs = dialog.findViewById<MaterialButton>(R.id.home_select_tv_series)
val docs = dialog.findViewById<MaterialButton>(R.id.home_select_documentaries)
val movies = dialog.findViewById<MaterialButton>(R.id.home_select_movies)
val asian = dialog.findViewById<MaterialButton>(R.id.home_select_asian)
val livestream = dialog.findViewById<MaterialButton>(R.id.home_select_livestreams)
val nsfw = dialog.findViewById<MaterialButton>(R.id.home_select_nsfw)
val others = dialog.findViewById<MaterialButton>(R.id.home_select_others)
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
val pairList =
getPairList(anime, cartoons, tvs, docs, movies, asian, livestream, nsfw, others)
cancelBtt?.setOnClickListener {
dialog.dismissSafe()
}
@ -355,52 +403,14 @@ class HomeFragment : Fragment() {
arrayAdapter.notifyDataSetChanged()
}
/**
* Since fire tv is fucked we need to manually define the focus layout.
* Since visible buttons are only known in runtime this is required.
**/
var lastButton: MaterialButton? = null
for ((button, validTypes) in pairList) {
val isValid =
validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
button?.isVisible = isValid
if (isValid) {
// Set focus navigation
button?.let { currentButton ->
lastButton?.nextFocusRightId = currentButton.id
lastButton?.id?.let { currentButton.nextFocusLeftId = it }
lastButton = currentButton
}
fun buttonContains(): Boolean {
return preSelectedTypes.any { validTypes.contains(it) }
}
button?.isSelected = buttonContains()
button?.setOnClickListener {
preSelectedTypes.clear()
preSelectedTypes.addAll(validTypes)
for ((otherButton, _) in pairList) {
otherButton?.isSelected = false
}
button.isSelected = true
updateList()
}
button?.setOnLongClickListener {
if (!buttonContains()) {
button.isSelected = true
preSelectedTypes.addAll(validTypes)
} else {
button.isSelected = false
preSelectedTypes.removeAll(validTypes)
}
updateList()
return@setOnLongClickListener true
}
}
bindChips(
dialog.home_select_group,
preSelectedTypes,
validAPIs.flatMap { it.supportedTypes }.distinct()
) { list ->
preSelectedTypes.clear()
preSelectedTypes.addAll(list)
updateList()
}
updateList()
}
@ -422,7 +432,6 @@ class HomeFragment : Fragment() {
}
private fun toggleMainVisibility(visible: Boolean) {
home_main_holder?.isVisible = visible
home_main_poster_recyclerview?.isVisible = visible
}
@ -531,6 +540,51 @@ class HomeFragment : Fragment() {
home_random?.visibility = View.GONE
}
observe(homeViewModel.preview) { preview ->
when (preview) {
is Resource.Success -> {
home_preview?.isVisible = true
preview.value.apply {
home_preview_tags?.text = tags?.joinToString("") ?: ""
home_preview_tags?.isGone = tags.isNullOrEmpty()
home_preview_image?.setImage(posterUrl, posterHeaders)
home_preview_title?.text = name
home_preview_play?.setOnClickListener {
activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST)
//activity.loadSearchResult(url, START_ACTION_RESUME_LATEST)
}
home_preview_info?.setOnClickListener {
activity?.loadResult(url, apiName)
//activity.loadSearchResult(random)
}
// very ugly code, but I dont care
val watchType = DataStoreHelper.getResultWatchState(preview.value.getId())
home_preview_bookmark?.setText(watchType.stringRes)
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, watchType.iconRes),null,null)
home_preview_bookmark?.setOnClickListener { fab ->
activity?.showBottomDialog(
WatchType.values().map { fab.context.getString(it.stringRes) }
.toList(),
DataStoreHelper.getResultWatchState(preview.value.getId()).ordinal,
fab.context.getString(R.string.action_add_to_bookmarks),
showApply = false,
{}) {
val newValue = WatchType.values()[it]
home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, newValue.iconRes),null,null)
home_preview_bookmark?.setText(newValue.stringRes)
updateWatchStatus(preview.value, newValue)
reloadStored()
}
}
}
}
else -> {
home_preview?.isVisible = false
}
}
}
observe(homeViewModel.apiName) { apiName ->
currentApiName = apiName
// setKey(USER_SELECTED_HOMEPAGE_API, apiName)
@ -563,17 +617,17 @@ class HomeFragment : Fragment() {
HomeChildItemAdapter(
mutableListOf(),
R.layout.home_result_big_grid,
nextFocusUp = home_main_poster_recyclerview.nextFocusUpId,
nextFocusDown = home_main_poster_recyclerview.nextFocusDownId
nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId,
nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId
) { callback ->
homeHandleSearch(callback)
}
home_main_poster_recyclerview.setLinearListLayout()
home_main_poster_recyclerview?.setLinearListLayout()
observe(homeViewModel.randomItems) { items ->
if (items.isNullOrEmpty()) {
toggleMainVisibility(false)
} else {
val tempAdapter = home_main_poster_recyclerview.adapter as HomeChildItemAdapter?
val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter?
// no need to reload if it has the same data
if (tempAdapter != null && tempAdapter.cardList == items) {
toggleMainVisibility(true)
@ -938,7 +992,7 @@ class HomeFragment : Fragment() {
}
}
context?.fixPaddingStatusbarView(home_statusbar)
//context?.fixPaddingStatusbarView(home_statusbar)
context?.fixPaddingStatusbar(home_loading_statusbar)
home_master_recycler.adapter =
@ -959,33 +1013,6 @@ class HomeFragment : Fragment() {
}
} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() }
if (!isTvSettings()) {
LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap
val centerLayoutManager =
CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
centerLayoutManager.setOnSizeListener { index ->
(home_main_poster_recyclerview?.adapter as HomeChildItemAdapter?)?.cardList?.get(
index
)?.let { random ->
home_main_play?.setOnClickListener {
activity.loadSearchResult(random, START_ACTION_RESUME_LATEST)
}
home_main_info?.setOnClickListener {
activity.loadSearchResult(random)
}
home_main_text?.text =
random.name + if (random is AnimeSearchResponse && !random.dubStatus.isNullOrEmpty()) {
random.dubStatus?.joinToString(
prefix = "",
separator = " | "
) { it.name }
} else ""
}
}
home_main_poster_recyclerview?.layoutManager = centerLayoutManager // scale
}
reloadStored()
loadHomePage()

View File

@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.*
@ -32,7 +33,6 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import kotlin.collections.set
@ -58,7 +58,9 @@ class HomeViewModel : ViewModel() {
val bookmarks: LiveData<Pair<Boolean, List<SearchResponse>>> = _bookmarks
private val _resumeWatching = MutableLiveData<List<SearchResponse>>()
private val _preview = MutableLiveData<Resource<LoadResponse>>()
val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching
val preview: LiveData<Resource<LoadResponse>> = _preview
fun loadResumeWatching() = viewModelScope.launchSafe {
val resumeWatching = withContext(Dispatchers.IO) {
@ -207,6 +209,7 @@ class HomeViewModel : ViewModel() {
expandAndReturn(name)
}
private fun load(api: MainAPI?) = viewModelScope.launchSafe {
repo = if (api != null) {
APIRepository(api)
@ -219,6 +222,7 @@ class HomeViewModel : ViewModel() {
if (repo?.hasMainPage == true) {
_page.postValue(Resource.Loading())
_preview.postValue(Resource.Loading())
when (val data = repo?.getMainPage(1, null)) {
is Resource.Success -> {
@ -232,8 +236,38 @@ class HomeViewModel : ViewModel() {
ExpandableHomepageList(filteredList, 1, home.hasNext)
}
}
_page.postValue(Resource.Success(expandable))
val items = data.value.mapNotNull { it?.items }.flatten()
items.randomOrNull()?.list?.randomOrNull()?.url?.let { url ->
// backup request in case first fails
var first = repo?.load(url)
if(first == null ||first is Resource.Failure) {
first = repo?.load(items.random().list.random().url)
}
first?.let {
_preview.postValue(it)
} ?: run {
_preview.postValue(
Resource.Failure(
false,
null,
null,
"No repo found, this should never happen"
)
)
}
} ?: run {
_preview.postValue(
Resource.Failure(
false,
null,
null,
"No homepage items"
)
)
}
_page.postValue(Resource.Success(expandable))
//val home = data.value
if (items.isNotEmpty()) {
@ -263,6 +297,7 @@ class HomeViewModel : ViewModel() {
}
} else {
_page.postValue(Resource.Success(emptyMap()))
_preview.postValue(Resource.Failure(false, null, null, "No homepage"))
}
}

View File

@ -17,10 +17,7 @@ import com.google.android.exoplayer2.text.TextRenderer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.TrackSelector
import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.HttpDataSource
import com.google.android.exoplayer2.upstream.*
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache
@ -880,7 +877,16 @@ class CS3IPlayer : IPlayer {
}
override fun onPlayerError(error: PlaybackException) {
playerError?.invoke(error)
// If the Network fails then ignore the exception if the duration is set.
// This is to switch mirrors automatically if the stream has not been fetched, but
// allow playing the buffer without internet as then the duration is fetched.
if (error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED
&& exoPlayer?.duration != C.TIME_UNSET
) {
exoPlayer?.prepare()
} else {
playerError?.invoke(error)
}
super.onPlayerError(error)
}

View File

@ -412,6 +412,29 @@ class ResultViewModel2 : ViewModel() {
return this?.firstOrNull { it.season == season }
}
fun updateWatchStatus(currentResponse : LoadResponse, status: WatchType) {
val currentId = currentResponse.getId()
val resultPage = currentResponse
DataStoreHelper.setResultWatchState(currentId, status.internalId)
val current = DataStoreHelper.getBookmarkedData(currentId)
val currentTime = System.currentTimeMillis()
DataStoreHelper.setBookmarkedData(
currentId,
DataStoreHelper.BookmarkedData(
currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
resultPage.name,
resultPage.url,
resultPage.apiName,
resultPage.type,
resultPage.posterUrl,
resultPage.year
)
)
}
private fun filterName(name: String?): String? {
if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
@ -764,28 +787,10 @@ class ResultViewModel2 : ViewModel() {
private val _selectPopup: MutableLiveData<Some<SelectPopup>> = MutableLiveData(Some.None)
val selectPopup: LiveData<Some<SelectPopup>> get() = _selectPopup
fun updateWatchStatus(status: WatchType) {
val currentId = currentId ?: return
val resultPage = currentResponse ?: return
_watchStatus.postValue(status)
DataStoreHelper.setResultWatchState(currentId, status.internalId)
val current = DataStoreHelper.getBookmarkedData(currentId)
val currentTime = System.currentTimeMillis()
DataStoreHelper.setBookmarkedData(
currentId,
DataStoreHelper.BookmarkedData(
currentId,
current?.bookmarkedTime ?: currentTime,
currentTime,
resultPage.name,
resultPage.url,
resultPage.apiName,
resultPage.type,
resultPage.posterUrl,
resultPage.year
)
)
fun updateWatchStatus(status: WatchType) {
updateWatchStatus(currentResponse ?: return,status)
_watchStatus.postValue(status)
}
private fun startChromecast(

View File

@ -26,15 +26,19 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.APIHolder.getApiSettings
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.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.home.HomeFragment
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.Coroutines.main
@ -46,6 +50,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlinx.android.synthetic.main.fragment_search.*
import kotlinx.android.synthetic.main.tvtypes_chips.*
import java.util.concurrent.locks.ReentrantLock
const val SEARCH_PREF_TAGS = "search_pref_tags"
@ -130,65 +135,26 @@ class SearchFragment : Fragment() {
// Null if defined as a variable
// This needs to be run after view created
private fun getPairList(): List<Pair<MaterialButton?, List<TvType>>> {
return HomeFragment.getPairList(
search_select_anime,
search_select_cartoons,
search_select_tv_series,
search_select_documentaries,
search_select_movies,
search_select_asian,
search_select_livestreams,
search_select_nsfw,
search_select_others
)
}
private fun reloadRepos(success: Boolean = false) = main {
val pairList = getPairList()
searchViewModel.reloadRepos()
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
for ((button, validTypes) in pairList) {
val isValid =
validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
button?.isVisible = isValid
if (isValid) {
fun buttonContains(): Boolean {
return selectedSearchTypes.any { validTypes.contains(it) }
}
button?.isSelected = buttonContains()
button?.setOnClickListener {
val last = selectedSearchTypes.toSet()
selectedSearchTypes.clear()
selectedSearchTypes.addAll(validTypes)
for ((otherButton, _) in pairList) {
otherButton?.isSelected = false
}
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
it?.isSelected = true
if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing
search(main_search?.query?.toString())
}
button?.setOnLongClickListener {
if (!buttonContains()) {
it?.isSelected = true
selectedSearchTypes.addAll(validTypes)
} else {
it?.isSelected = false
selectedSearchTypes.removeAll(validTypes)
}
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
search(main_search?.query?.toString())
return@setOnLongClickListener true
}
bindChips(
home_select_group,
selectedSearchTypes,
validAPIs.flatMap { api -> api.supportedTypes }.distinct()
) { list ->
if (selectedSearchTypes.toSet() != list.toSet()) {
setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
selectedSearchTypes.clear()
selectedSearchTypes.addAll(list)
search(main_search?.query?.toString())
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -239,31 +205,67 @@ class SearchFragment : Fragment() {
builder.let { dialog ->
val isMultiLang = ctx.getApiProviderLangSettings().size > 1
val anime = dialog.findViewById<MaterialButton>(R.id.home_select_anime)
val cartoons = dialog.findViewById<MaterialButton>(R.id.home_select_cartoons)
val tvs = dialog.findViewById<MaterialButton>(R.id.home_select_tv_series)
val docs = dialog.findViewById<MaterialButton>(R.id.home_select_documentaries)
val movies = dialog.findViewById<MaterialButton>(R.id.home_select_movies)
val asian = dialog.findViewById<MaterialButton>(R.id.home_select_asian)
val livestream =
dialog.findViewById<MaterialButton>(R.id.home_select_livestreams)
val nsfw = dialog.findViewById<MaterialButton>(R.id.home_select_nsfw)
val other = dialog.findViewById<MaterialButton>(R.id.home_select_others)
val cancelBtt = dialog.findViewById<MaterialButton>(R.id.cancel_btt)
val applyBtt = dialog.findViewById<MaterialButton>(R.id.apply_btt)
val pairList =
HomeFragment.getPairList(
anime,
cartoons,
tvs,
docs,
movies,
asian,
livestream,
nsfw,
other
)
val listView = dialog.findViewById<ListView>(R.id.listview1)
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
listView?.adapter = arrayAdapter
listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
listView?.setOnItemClickListener { _, _, i, _ ->
if (currentValidApis.isNotEmpty()) {
val api = currentValidApis[i].name
if (currentSelectedApis.contains(api)) {
listView.setItemChecked(i, false)
currentSelectedApis -= api
} else {
listView.setItemChecked(i, true)
currentSelectedApis += api
}
}
}
fun updateList(types: List<TvType>) {
setKey(SEARCH_PREF_TAGS, types.map {it.name})
arrayAdapter.clear()
currentValidApis = validAPIs.filter { api ->
api.supportedTypes.any {
types.contains(it)
}
}.sortedBy { it.name.lowercase() }
val names = currentValidApis.map {
if (isMultiLang) "${
SubtitleHelper.getFlagFromIso(
it.lang
)?.plus(" ") ?: ""
}${it.name}" else it.name
}
for ((index, api) in currentValidApis.map { it.name }.withIndex()) {
listView?.setItemChecked(index, currentSelectedApis.contains(api))
}
//arrayAdapter.notifyDataSetChanged()
arrayAdapter.addAll(names)
arrayAdapter.notifyDataSetChanged()
}
val selectedSearchTypes = getKey<List<String>>(SEARCH_PREF_TAGS)
?.mapNotNull { listName ->
TvType.values().firstOrNull { it.name == listName }
}
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
bindChips(
dialog.home_select_group,
selectedSearchTypes,
TvType.values().toList()
) { list ->
updateList(list)
}
cancelBtt?.setOnClickListener {
dialog.dismissSafe()
@ -284,90 +286,7 @@ class SearchFragment : Fragment() {
context?.setKey(SEARCH_PREF_PROVIDERS, currentSelectedApis.toList())
selectedApis = currentSelectedApis
}
val selectedSearchTypes = context?.getKey<List<String>>(SEARCH_PREF_TAGS)
?.mapNotNull { listName ->
TvType.values().firstOrNull { it.name == listName }
}
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
val listView = dialog.findViewById<ListView>(R.id.listview1)
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
listView?.adapter = arrayAdapter
listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
listView?.setOnItemClickListener { _, _, i, _ ->
if (currentValidApis.isNotEmpty()) {
val api = currentValidApis[i].name
if (currentSelectedApis.contains(api)) {
listView.setItemChecked(i, false)
currentSelectedApis -= api
} else {
listView.setItemChecked(i, true)
currentSelectedApis += api
}
}
}
fun updateList() {
arrayAdapter.clear()
currentValidApis = validAPIs.filter { api ->
api.supportedTypes.any {
selectedSearchTypes.contains(it)
}
}.sortedBy { it.name.lowercase() }
val names = currentValidApis.map {
if (isMultiLang) "${
SubtitleHelper.getFlagFromIso(
it.lang
)?.plus(" ") ?: ""
}${it.name}" else it.name
}
for ((index, api) in currentValidApis.map { it.name }.withIndex()) {
listView?.setItemChecked(index, currentSelectedApis.contains(api))
}
//arrayAdapter.notifyDataSetChanged()
arrayAdapter.addAll(names)
arrayAdapter.notifyDataSetChanged()
}
for ((button, validTypes) in pairList) {
val isValid =
validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
button?.isVisible = isValid
if (isValid) {
fun buttonContains(): Boolean {
return selectedSearchTypes.any { validTypes.contains(it) }
}
button?.isSelected = buttonContains()
button?.setOnClickListener {
selectedSearchTypes.clear()
selectedSearchTypes.addAll(validTypes)
for ((otherButton, _) in pairList) {
otherButton?.isSelected = false
}
button.isSelected = true
updateList()
}
button?.setOnLongClickListener {
if (!buttonContains()) {
button.isSelected = true
selectedSearchTypes.addAll(validTypes)
} else {
button.isSelected = false
selectedSearchTypes.removeAll(validTypes)
}
updateList()
return@setOnLongClickListener true
}
}
}
updateList()
updateList(selectedSearchTypes.toList())
}
}
}
@ -380,14 +299,6 @@ class SearchFragment : Fragment() {
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
val pairList = getPairList()
fun updateSelectedList(list: MutableList<TvType>) {
selectedSearchTypes = list
for ((button, validTypes) in pairList) {
button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) }
}
}
if (isTrueTvSettings()) {
search_filter.isFocusable = true
search_filter.isFocusableInTouchMode = true
@ -500,7 +411,7 @@ class SearchFragment : Fragment() {
SEARCH_HISTORY_OPEN -> {
searchViewModel.clearSearch()
if (searchItem.type.isNotEmpty())
updateSelectedList(searchItem.type.toMutableList())
updateChips(home_select_group, searchItem.type.toMutableList())
main_search?.setQuery(searchItem.searchText, true)
}
SEARCH_HISTORY_REMOVE -> {

View File

@ -79,6 +79,7 @@ val appLanguages = arrayListOf(
Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"),
Triple("", "Czech", "cs"),
Triple("", "Croatian", "hr"),
Triple("", "Bulgarian", "bg"),
).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top
class SettingsGeneral : PreferenceFragmentCompat() {

View File

@ -9,8 +9,9 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.appLanguages
@ -18,6 +19,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.fragment_plugins.*
import kotlinx.android.synthetic.main.tvtypes_chips.*
import kotlinx.android.synthetic.main.tvtypes_chips_scroll.*
const val PLUGINS_BUNDLE_NAME = "name"
const val PLUGINS_BUNDLE_URL = "url"
@ -145,56 +148,10 @@ class PluginsFragment : Fragment() {
pluginViewModel.updatePluginList(context, url)
tv_types_scroll_view?.isVisible = true
// 💀💀💀💀💀💀💀 Recyclerview when
val pairList = getPairList(
home_select_anime,
home_select_cartoons,
home_select_tv_series,
home_select_documentaries,
home_select_movies,
home_select_asian,
home_select_livestreams,
home_select_nsfw,
home_select_others
)
// val supportedTypes: Array<String> =
// pluginViewModel.filteredPlugins.value!!.second.flatMap { it -> it.plugin.second.tvTypes ?: listOf("Other") }.distinct().toTypedArray()
// Copy pasted code
for ((button, validTypes) in pairList) {
val validTypesMapped = validTypes.map { it.name }
val isValid = true
//validTypes.any { it -> supportedTypes.contains(it.name) }
button?.isVisible = isValid
if (isValid) {
fun buttonContains(): Boolean {
return pluginViewModel.tvTypes.any { validTypesMapped.contains(it) }
}
button?.isSelected = buttonContains()
button?.setOnClickListener {
pluginViewModel.tvTypes.clear()
pluginViewModel.tvTypes.addAll(validTypesMapped)
for ((otherButton, _) in pairList) {
otherButton?.isSelected = false
}
button.isSelected = true
pluginViewModel.updateFilteredPlugins()
}
button?.setOnLongClickListener {
if (!buttonContains()) {
button.isSelected = true
pluginViewModel.tvTypes.addAll(validTypesMapped)
} else {
button.isSelected = false
pluginViewModel.tvTypes.removeAll(validTypesMapped)
}
pluginViewModel.updateFilteredPlugins()
return@setOnLongClickListener true
}
}
bindChips(home_select_group, emptyList(), TvType.values().toList()) { list ->
pluginViewModel.tvTypes.clear()
pluginViewModel.tvTypes.addAll(list.map { it.name })
pluginViewModel.updateFilteredPlugins()
}
}
}

View File

@ -235,6 +235,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Vidgomunime(),
Sbflix(),
Streamsss(),
Sbspeed(),
Fastream(),
@ -246,6 +247,8 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
LayarKaca(),
Rasacintaku(),
FEnet(),
Kotakajair(),
Cdnplayer(),
// WatchSB(), 'cause StreamSB.kt works
Uqload(),
Uqload1(),
@ -317,6 +320,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Linkbox(),
Acefile(),
SpeedoStream(),
SpeedoStream1(),
Zorofile(),
Embedgram(),
Mvidoo(),
@ -324,6 +328,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Vidmoly(),
Vidmolyme(),
Voe(),
Moviehab(),
Gdriveplayerapi(),
Gdriveplayerapp(),
@ -336,6 +341,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Gdriveplayerco(),
Gdriveplayer(),
DatabaseGdrive(),
DatabaseGdrive2(),
YoutubeExtractor(),
YoutubeShortLinkExtractor(),

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_checked="true"/>
<item android:color="?attr/primaryGrayBackground" android:state_checked="false"/>
</selector>

View File

@ -1,79 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/homeRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation"
android:paddingTop="0dp">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/homeRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation"
android:paddingTop="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="match_parent"
android:layout_width="match_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.navigationrail.NavigationRailView
android:layout_width="62dp"
android:layout_height="match_parent"
android:id="@+id/nav_rail_view"
android:background="?attr/primaryGrayBackground"
app:itemTextColor="@color/item_select_color"
android:id="@+id/nav_rail_view"
android:layout_width="62dp"
android:layout_height="match_parent"
android:background="?attr/primaryGrayBackground"
app:itemIconTint="@color/item_select_color"
app:itemIconTint="@color/item_select_color"
app:menuGravity="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:labelVisibilityMode="unlabeled"
app:menu="@menu/bottom_nav_menu">
app:itemTextColor="@color/item_select_color"
app:labelVisibilityMode="unlabeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/bottom_nav_menu"
app:menuGravity="center">
</com.google.android.material.navigationrail.NavigationRailView>
<!-- android:layout_height="65dp"
app:labelVisibilityMode="unlabeled"
-->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground"
android:id="@+id/nav_view"
android:layout_height="wrap_content"
android:layout_width="0dp"
app:labelVisibilityMode="labeled"
android:background="?attr/primaryGrayBackground"
app:labelVisibilityMode="labeled"
app:itemTextColor="@color/item_select_color"
app:itemIconTint="@color/item_select_color"
app:itemIconTint="@color/item_select_color"
app:itemTextColor="@color/item_select_color"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu"
app:layout_constraintBottom_toBottomOf="parent" />
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation"
app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
app:layout_constraintBottom_toTopOf="@+id/cast_mini_controller_holder"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@+id/cast_mini_controller_holder"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
<LinearLayout
tools:layout_height="100dp"
android:layout_width="0dp"
app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/nav_view"
android:id="@+id/cast_mini_controller_holder">
android:id="@+id/cast_mini_controller_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/nav_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
tools:layout_height="100dp">
<!--com.google.android.gms.cast.framework.media.widget.MiniControllerFragment-->
<fragment
app:customCastBackgroundColor="?attr/primaryGrayBackground"
app:castControlButtons="@array/cast_mini_controller_control_buttons"
android:id="@+id/cast_mini_controller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment"
tools:ignore="FragmentTagUsage" />
android:id="@+id/cast_mini_controller"
class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:castControlButtons="@array/cast_mini_controller_control_buttons"
app:customCastBackgroundColor="?attr/primaryGrayBackground"
tools:ignore="FragmentTagUsage" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -1,87 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/home_root"
android:layout_width="match_parent"
android:id="@+id/home_root"
tools:context=".ui.home.HomeFragment">
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment">
<FrameLayout
android:visibility="gone"
tools:visibility="gone"
android:id="@+id/home_loading"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/home_loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="gone">
<ProgressBar
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="gone"
android:layout_width="50dp"
android:layout_height="50dp" />
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:visibility="gone"
tools:visibility="gone" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/home_loading_shimmer"
app:shimmer_base_alpha="0.2"
app:shimmer_highlight_alpha="0.3"
app:shimmer_duration="@integer/loading_time"
app:shimmer_auto_start="true"
android:paddingTop="40dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="15dp"
android:orientation="vertical">
android:id="@+id/home_loading_shimmer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginTop="15dp"
android:orientation="vertical"
android:paddingTop="40dp"
app:shimmer_auto_start="true"
app:shimmer_base_alpha="0.2"
app:shimmer_duration="@integer/loading_time"
app:shimmer_highlight_alpha="0.3">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<FrameLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.cardview.widget.CardView
android:layout_margin="@dimen/loading_margin"
android:layout_gravity="center"
app:cardCornerRadius="@dimen/loading_radius"
android:background="@color/grayShimmer"
android:translationX="-164dp"
android:layout_width="125dp"
android:layout_height="200dp" />
android:layout_width="125dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:layout_margin="@dimen/loading_margin"
android:background="@color/grayShimmer"
android:translationX="-164dp"
app:cardCornerRadius="@dimen/loading_radius" />
<androidx.cardview.widget.CardView
android:layout_margin="@dimen/loading_margin"
android:layout_gravity="center"
app:cardCornerRadius="@dimen/loading_radius"
android:background="@color/grayShimmer"
android:layout_width="148dp"
android:layout_height="234dp" />
android:layout_width="148dp"
android:layout_height="234dp"
android:layout_gravity="center"
android:layout_margin="@dimen/loading_margin"
android:background="@color/grayShimmer"
app:cardCornerRadius="@dimen/loading_radius" />
<androidx.cardview.widget.CardView
android:layout_margin="@dimen/loading_margin"
android:layout_gravity="center"
app:cardCornerRadius="@dimen/loading_radius"
android:background="@color/grayShimmer"
android:translationX="164dp"
android:layout_width="125dp"
android:layout_height="200dp" />
android:layout_width="125dp"
android:layout_height="200dp"
android:layout_gravity="center"
android:layout_margin="@dimen/loading_margin"
android:background="@color/grayShimmer"
android:translationX="164dp"
app:cardCornerRadius="@dimen/loading_radius" />
</FrameLayout>
<include layout="@layout/loading_line_short_center" />
<LinearLayout
android:orientation="vertical"
android:layout_marginTop="@dimen/result_padding"
android:layout_marginStart="@dimen/result_padding"
android:layout_marginEnd="@dimen/result_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/result_padding"
android:layout_marginTop="@dimen/result_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_marginEnd="@dimen/result_padding"
android:orientation="vertical">
<include layout="@layout/loading_list" />
@ -93,168 +93,170 @@
</com.facebook.shimmer.ShimmerFrameLayout>
<FrameLayout
android:id="@+id/home_loading_statusbar"
android:layout_width="match_parent"
android:layout_height="70dp">
android:id="@+id/home_loading_statusbar"
android:layout_width="match_parent"
android:layout_height="70dp">
<ImageView
android:id="@+id/home_change_api_loading"
android:layout_margin="10dp"
android:layout_gravity="center_vertical|end"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:id="@+id/home_change_api_loading"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_baseline_keyboard_arrow_down_24"
android:layout_width="30dp"
android:layout_height="30dp"
android:contentDescription="@string/home_change_provider_img_des" />
android:layout_margin="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/home_change_provider_img_des"
android:src="@drawable/ic_baseline_keyboard_arrow_down_24" />
</FrameLayout>
</FrameLayout>
<LinearLayout
android:visibility="gone"
tools:visibility="gone"
android:id="@+id/home_loading_error"
android:orientation="vertical"
android:id="@+id/home_loading_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="gone">
<com.google.android.material.button.MaterialButton
android:id="@+id/home_reload_connectionerror"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_margin="5dp"
android:minWidth="200dp"
android:text="@string/reload_error"
app:icon="@drawable/ic_baseline_autorenew_24" />
<com.google.android.material.button.MaterialButton
android:layout_gravity="center"
style="@style/WhiteButton"
android:id="@+id/home_reload_connection_open_in_browser"
style="@style/BlackButton"
android:layout_margin="5dp"
android:layout_width="wrap_content"
app:icon="@drawable/ic_baseline_autorenew_24"
android:text="@string/reload_error"
android:id="@+id/home_reload_connectionerror"
android:layout_width="wrap_content"
android:minWidth="200dp" />
<com.google.android.material.button.MaterialButton
android:layout_gravity="center"
style="@style/BlackButton"
android:layout_margin="5dp"
app:icon="@drawable/ic_baseline_public_24"
android:text="@string/result_open_in_browser"
android:id="@+id/home_reload_connection_open_in_browser"
android:layout_width="wrap_content"
android:minWidth="200dp" />
android:layout_gravity="center"
android:layout_margin="5dp"
android:minWidth="200dp"
android:text="@string/result_open_in_browser"
app:icon="@drawable/ic_baseline_public_24" />
<TextView
android:layout_margin="5dp"
android:gravity="center"
android:layout_gravity="center"
android:id="@+id/result_error_text"
android:textColor="?attr/textColor"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:id="@+id/result_error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:gravity="center"
android:textColor="?attr/textColor" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:background="?attr/primaryBlackBackground"
tools:visibility="visible"
android:visibility="gone"
android:id="@+id/home_loaded"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:id="@+id/home_loaded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBlackBackground"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:background="?attr/primaryGrayBackground"
android:id="@+id/home_statusbar"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
android:id="@+id/home_statusbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground" />
<FrameLayout
android:id="@+id/home_settings_bar"
android:background="?attr/primaryGrayBackground"
android:layout_width="match_parent"
android:layout_height="70dp">
android:id="@+id/home_settings_bar"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="?attr/primaryGrayBackground"
android:visibility="gone">
<LinearLayout
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:orientation="horizontal"
android:paddingStart="10dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:paddingTop="10dp"
android:paddingEnd="10dp"
android:paddingBottom="10dp">
<androidx.cardview.widget.CardView
android:id="@+id/home_profile_picture_holder"
android:layout_marginEnd="10dp"
app:cardCornerRadius="100dp"
android:layout_gravity="center_vertical"
android:layout_width="40dp"
android:layout_height="40dp">
android:id="@+id/home_profile_picture_holder"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp"
app:cardCornerRadius="100dp">
<ImageView
android:scaleType="centerCrop"
android:id="@+id/home_profile_picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
android:id="@+id/home_profile_picture"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:ignore="ContentDescription" />
</androidx.cardview.widget.CardView>
<FrameLayout
android:layout_gravity="center_vertical"
android:visibility="visible"
android:background="@drawable/search_background"
android:layout_width="match_parent"
android:layout_height="40dp">
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:background="@drawable/search_background"
android:visibility="visible">
<androidx.appcompat.widget.SearchView
android:id="@+id/home_search"
app:queryBackground="@color/transparent"
android:id="@+id/home_search2"
android:layout_width="match_parent"
app:searchIcon="@drawable/search_icon"
android:paddingStart="-10dp"
android:iconifiedByDefault="false"
app:queryHint="@string/search_hint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
app:iconifiedByDefault="false"
tools:ignore="RtlSymmetry" />
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:iconifiedByDefault="false"
android:paddingStart="-10dp"
app:iconifiedByDefault="false"
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
tools:ignore="RtlSymmetry" />
</FrameLayout>
<LinearLayout
android:visibility="gone"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:id="@+id/home_provider_name"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:textColor="?attr/textColor"
android:textSize="20sp"
tools:text="Hello World" />
<TextView
android:gravity="center_vertical"
android:layout_gravity="center"
android:id="@+id/home_provider_name"
android:textColor="?attr/textColor"
android:textSize="20sp"
tools:text="Hello World"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:gravity="center_vertical"
android:layout_gravity="center"
android:id="@+id/home_provider_meta_info"
android:textColor="?attr/grayTextColor"
android:textSize="14sp"
tools:text="Hello World"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:id="@+id/home_provider_meta_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:textColor="?attr/grayTextColor"
android:textSize="14sp"
tools:text="Hello World" />
</LinearLayout>
</LinearLayout>
@ -278,273 +280,309 @@
</ImageView>-->
</FrameLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:id="@+id/home_main_holder"
android:orientation="vertical"
<FrameLayout
tools:visibility="visible"
android:visibility="gone"
android:id="@+id/home_preview"
android:layout_width="match_parent"
android:layout_height="500dp">
<ImageView
android:alpha="0.8"
android:id="@+id/home_preview_image"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent"
android:scaleType="centerCrop"
tools:src="@drawable/example_poster" />
<androidx.recyclerview.widget.RecyclerView
android:descendantFocusability="afterDescendants"
android:nextFocusUp="@id/home_change_api"
android:nextFocusDown="@id/home_main_info"
android:clipToPadding="false"
android:paddingStart="148dp"
android:paddingEnd="148dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_main_poster_recyclerview"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:listitem="@layout/home_result_grid" />
<!--<ImageView
android:foreground="@drawable/outline_drawable"
android:id="@+id/home_main_poster"
tools:src="@drawable/example_poster"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:layout_width="150dp"
android:layout_height="212dp"
android:contentDescription="@string/home_main_poster_img_des">
</ImageView>-->
<TextView
android:id="@+id/home_main_text"
android:layout_gravity="center"
android:gravity="center"
tools:text="Perfect Run"
android:textStyle="bold"
android:layout_margin="5dp"
android:textSize="15sp"
android:textColor="?attr/textColor"
android:layout_width="match_parent"
android:maxLines="2"
android:ellipsize="end"
android:layout_height="40sp" />
<LinearLayout
android:visibility="visible"
android:padding="5dp"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_main_poster_recyclerview"
android:nextFocusRight="@id/home_main_info"
android:nextFocusDown="@id/home_watch_child_more_info"
style="@style/WhiteButton"
android:visibility="visible"
android:layout_gravity="center"
android:id="@+id/home_main_play"
android:text="@string/home_play"
app:icon="@drawable/ic_baseline_play_arrow_24"
android:minWidth="120dp"
android:layout_width="wrap_content" />
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/home_main_play"
android:nextFocusUp="@id/home_main_poster_recyclerview"
android:nextFocusRight="@id/home_change_api"
android:nextFocusDown="@id/home_watch_child_more_info"
style="@style/BlackButton"
android:visibility="visible"
android:layout_gravity="center"
android:text="@string/home_info"
app:icon="@drawable/ic_outline_info_24"
android:id="@+id/home_main_info"
android:clickable="true"
android:focusable="true"
android:minWidth="120dp"
android:layout_width="wrap_content" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/home_watch_holder"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"
<View
android:id="@+id/title_shadow"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="300dp"
android:layout_gravity="bottom"
android:background="@drawable/background_shadow" />
<FrameLayout
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_main_info"
android:nextFocusDown="@id/home_watch_child_recyclerview"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:id="@+id/home_watch_child_more_info"
android:padding="12dp"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_gravity="top"
android:visibility="gone">
<androidx.appcompat.widget.SearchView
android:id="@+id/home_search"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:iconifiedByDefault="false"
android:paddingStart="-10dp"
app:iconifiedByDefault="false"
app:queryBackground="@color/transparent"
app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon"
tools:ignore="RtlSymmetry" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<TextView
android:textStyle="bold"
android:paddingBottom="10dp"
android:id="@+id/home_preview_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/white"
android:textSize="17sp"
tools:text="The Perfect Run" />
<!--<TextView
android:paddingStart="30dp"
android:paddingEnd="30dp"
android:id="@+id/home_season_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/white"
android:textSize="14sp"
tools:text="5 seasons 50 episodes" />-->
<TextView
android:paddingStart="30dp"
android:paddingEnd="30dp"
android:id="@+id/home_preview_tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/white"
android:textSize="14sp"
tools:text="Hello • World • Tags" />
<LinearLayout
android:padding="20dp"
android:gravity="center"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/home_preview_bookmark"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:layout_gravity="center"
android:gravity="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:text="@string/none"
android:textColor="@color/white"
app:drawableTopCompat="@drawable/ic_baseline_add_24"
app:tint="@color/white" />
<com.google.android.material.button.MaterialButton
android:layout_gravity="center"
style="@style/WhiteButton"
android:id="@+id/home_preview_play"
android:layout_width="wrap_content"
android:text="@string/home_play"
app:icon="@drawable/ic_baseline_play_arrow_24" />
<TextView
android:background="?android:attr/selectableItemBackgroundBorderless"
android:id="@+id/home_preview_info"
android:gravity="center"
android:layout_gravity="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:layout_width="70dp"
android:layout_height="wrap_content"
android:text="@string/home_info"
android:textColor="@color/white"
app:drawableTopCompat="@drawable/ic_outline_info_24"
app:tint="@color/white" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
<LinearLayout
android:id="@+id/home_watch_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<FrameLayout
android:id="@+id/home_watch_child_more_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusDown="@id/home_watch_child_recyclerview"
android:padding="12dp">
<TextView
android:layout_gravity="center_vertical"
android:id="@+id/home_watch_parent_item_title"
style="@style/WatchHeaderText"
android:text="@string/continue_watching" />
android:id="@+id/home_watch_parent_item_title"
style="@style/WatchHeaderText"
android:layout_gravity="center_vertical"
android:text="@string/continue_watching" />
<ImageView
app:tint="?attr/textColor"
android:layout_marginEnd="5dp"
android:layout_gravity="end|center_vertical"
android:src="@drawable/ic_baseline_arrow_forward_24"
android:layout_width="30dp"
android:layout_height="match_parent"
android:contentDescription="@string/home_more_info" />
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="5dp"
android:contentDescription="@string/home_more_info"
android:src="@drawable/ic_baseline_arrow_forward_24"
app:tint="?attr/textColor" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:paddingHorizontal="5dp"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_watch_child_recyclerview"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:listitem="@layout/home_result_grid" />
android:id="@+id/home_watch_child_recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:orientation="horizontal"
android:paddingHorizontal="5dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/home_result_grid" />
</LinearLayout>
<LinearLayout
android:id="@+id/home_bookmarked_holder"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:id="@+id/home_bookmarked_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<FrameLayout
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusForward="@id/home_bookmarked_child_recyclerview"
android:id="@+id/home_bookmarked_child_more_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:id="@+id/home_bookmarked_child_more_info"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusUp="@id/home_watch_child_recyclerview"
android:nextFocusForward="@id/home_bookmarked_child_recyclerview"
android:paddingStart="12dp"
android:paddingTop="5dp"
android:paddingEnd="12dp"
android:paddingBottom="5dp">
<HorizontalScrollView
android:fadingEdge="horizontal"
android:requiresFadingEdge="horizontal"
android:layout_marginEnd="50dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="50dp"
android:fadingEdge="horizontal"
android:requiresFadingEdge="horizontal">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusRight="@id/home_plan_to_watch_btt"
android:id="@+id/home_type_watching_btt"
style="@style/RoundedSelectableButton"
android:id="@+id/home_type_watching_btt"
android:text="@string/type_watching"
style="@style/RoundedSelectableButton" />
android:nextFocusLeft="@id/nav_rail_view"
android:nextFocusRight="@id/home_plan_to_watch_btt"
android:text="@string/type_watching" />
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/home_type_watching_btt"
android:nextFocusRight="@id/home_type_on_hold_btt"
android:id="@+id/home_plan_to_watch_btt"
style="@style/RoundedSelectableButton"
android:id="@+id/home_plan_to_watch_btt"
android:text="@string/type_plan_to_watch"
style="@style/RoundedSelectableButton" />
android:nextFocusLeft="@id/home_type_watching_btt"
android:nextFocusRight="@id/home_type_on_hold_btt"
android:text="@string/type_plan_to_watch" />
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/home_plan_to_watch_btt"
android:nextFocusRight="@id/home_type_dropped_btt"
android:id="@+id/home_type_on_hold_btt"
style="@style/RoundedSelectableButton"
android:id="@+id/home_type_on_hold_btt"
android:text="@string/type_on_hold"
style="@style/RoundedSelectableButton" />
android:nextFocusLeft="@id/home_plan_to_watch_btt"
android:nextFocusRight="@id/home_type_dropped_btt"
android:text="@string/type_on_hold" />
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/home_type_on_hold_btt"
android:nextFocusRight="@id/home_type_completed_btt"
android:id="@+id/home_type_dropped_btt"
style="@style/RoundedSelectableButton"
android:id="@+id/home_type_dropped_btt"
android:text="@string/type_dropped"
style="@style/RoundedSelectableButton" />
android:nextFocusLeft="@id/home_type_on_hold_btt"
android:nextFocusRight="@id/home_type_completed_btt"
android:text="@string/type_dropped" />
<com.google.android.material.button.MaterialButton
android:nextFocusLeft="@id/home_type_dropped_btt"
android:id="@+id/home_type_completed_btt"
android:id="@+id/home_type_completed_btt"
android:text="@string/type_completed"
style="@style/RoundedSelectableButton" />
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_type_dropped_btt"
android:text="@string/type_completed" />
</LinearLayout>
</HorizontalScrollView>
<ImageView
app:tint="?attr/textColor"
android:layout_marginEnd="5dp"
android:layout_gravity="end|center_vertical"
android:src="@drawable/ic_baseline_arrow_forward_24"
android:layout_width="30dp"
android:layout_height="match_parent"
android:contentDescription="@string/home_more_info" />
android:layout_width="30dp"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="5dp"
android:contentDescription="@string/home_more_info"
android:src="@drawable/ic_baseline_arrow_forward_24"
app:tint="?attr/textColor" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:paddingHorizontal="5dp"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:id="@+id/home_bookmarked_child_recyclerview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_bookmarked_child_recyclerview"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:listitem="@layout/home_result_grid" />
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:orientation="horizontal"
android:paddingHorizontal="5dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/home_result_grid" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:nextFocusLeft="@id/nav_rail_view"
android:descendantFocusability="afterDescendants"
android:id="@+id/home_master_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/homepage_parent" />
android:id="@+id/home_master_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="afterDescendants"
android:nextFocusLeft="@id/nav_rail_view"
tools:listitem="@layout/homepage_parent" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:visibility="gone"
tools:visibility="visible"
android:text="@string/home_source"
android:id="@+id/home_api_fab"
app:icon="@drawable/ic_baseline_filter_list_24"
style="@style/ExtendedFloatingActionButton"
android:textColor="?attr/textColor"
tools:ignore="ContentDescription" />
android:id="@+id/home_api_fab"
style="@style/ExtendedFloatingActionButton"
android:text="@string/home_source"
android:textColor="?attr/textColor"
android:visibility="gone"
app:icon="@drawable/ic_baseline_filter_list_24"
tools:ignore="ContentDescription"
tools:visibility="visible" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:visibility="gone"
tools:visibility="visible"
android:text="@string/home_random"
android:id="@+id/home_random"
android:layout_gravity="bottom|start"
app:icon="@drawable/ic_baseline_play_arrow_24"
style="@style/ExtendedFloatingActionButton"
android:textColor="?attr/textColor"
tools:ignore="ContentDescription" />
android:id="@+id/home_random"
style="@style/ExtendedFloatingActionButton"
android:layout_gravity="bottom|start"
android:text="@string/home_random"
android:textColor="?attr/textColor"
android:visibility="gone"
app:icon="@drawable/ic_baseline_play_arrow_24"
tools:ignore="ContentDescription"
tools:visibility="visible" />
</FrameLayout>

View File

@ -25,108 +25,7 @@
app:titleTextColor="?attr/textColor"
tools:title="Overlord" />
<HorizontalScrollView
android:id="@+id/tv_types_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground"
android:clipToPadding="true"
android:fadingEdge="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:requiresFadingEdge="horizontal"
app:layout_scrollFlags="scroll|enterAlways">
<!-- Man what the fuck we need a recyclerview -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- android:minWidth="0dp"
app:iconTint="?attr/textColor"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:iconGravity="textStart"
app:iconPadding="0dp"
android:layout_height="35dp"-->
<!-- <com.google.android.material.button.MaterialButton
android:nextFocusRight="@id/home_select_tv_series"
app:icon="@drawable/ic_baseline_close_24"
android:id="@+id/home_select_none"
style="@style/RoundedSelectableButtonIcon"/>-->
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_movies"
style="@style/RoundedSelectableButton"
android:nextFocusRight="@id/home_select_tv_series"
android:text="@string/movies" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_tv_series"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_movies"
android:nextFocusRight="@id/home_select_anime"
android:text="@string/tv_series" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_anime"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_tv_series"
android:nextFocusRight="@id/home_select_asian"
android:text="@string/anime" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_asian"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_anime"
android:nextFocusRight="@id/home_select_cartoons"
android:text="@string/asian_drama" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_cartoons"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_asian"
android:nextFocusRight="@id/home_select_documentaries"
android:text="@string/cartoons" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_documentaries"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_cartoons"
android:nextFocusRight="@id/home_select_livestreams"
android:text="@string/documentaries" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_livestreams"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_documentaries"
android:nextFocusRight="@id/home_select_nsfw"
android:text="@string/livestreams" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_nsfw"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_livestreams"
android:nextFocusRight="@id/home_select_nsfw"
android:text="@string/nsfw" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_others"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_nsfw"
android:text="@string/others" />
</LinearLayout>
</HorizontalScrollView>
<include layout="@layout/tvtypes_chips_scroll" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView

View File

@ -82,86 +82,7 @@
app:tint="?attr/textColor" />
</FrameLayout>
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fadingEdge="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:requiresFadingEdge="horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_movies"
style="@style/RoundedSelectableButton"
android:nextFocusRight="@id/search_select_tv_series"
android:text="@string/movies" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_tv_series"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_movies"
android:nextFocusRight="@id/search_select_anime"
android:text="@string/tv_series" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_anime"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_tv_series"
android:nextFocusRight="@id/search_select_asian"
android:text="@string/anime" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_asian"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_anime"
android:nextFocusRight="@id/search_select_cartoons"
android:text="@string/asian_drama" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_cartoons"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_asian"
android:nextFocusRight="@id/search_select_documentaries"
android:text="@string/cartoons" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_documentaries"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_cartoons"
android:nextFocusRight="@id/search_select_livestreams"
android:text="@string/documentaries" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_livestreams"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_documentaries"
android:nextFocusRight="@id/search_select_nsfw"
android:text="@string/livestreams" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_nsfw"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_livestreams"
android:nextFocusRight="@id/search_select_others"
android:text="@string/nsfw" />
<com.google.android.material.button.MaterialButton
android:id="@+id/search_select_others"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/search_select_nsfw"
android:text="@string/others" />
</LinearLayout>
</HorizontalScrollView>
<include layout="@layout/tvtypes_chips_scroll" />
<com.lagradost.cloudstream3.ui.AutofitRecyclerView

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -10,143 +10,47 @@
android:id="@+id/listview1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="true"
android:layout_rowWeight="1"
android:layout_marginTop="10dp"
android:layout_marginBottom="60dp"
android:clipToPadding="false"
android:minHeight="0dp"
android:nestedScrollingEnabled="true"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt"
android:requiresFadingEdge="vertical"
tools:listitem="@layout/sort_bottom_single_choice" />
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="-60dp"
android:clipToPadding="true"
android:fadingEdge="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:requiresFadingEdge="horizontal">
<!-- Man what the fuck we need a recyclerview -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!-- android:minWidth="0dp"
app:iconTint="?attr/textColor"
android:insetLeft="0dp"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
app:iconGravity="textStart"
app:iconPadding="0dp"
android:layout_height="35dp"-->
<!-- <com.google.android.material.button.MaterialButton
android:nextFocusRight="@id/home_select_tv_series"
app:icon="@drawable/ic_baseline_close_24"
android:id="@+id/home_select_none"
style="@style/RoundedSelectableButtonIcon"/>-->
<!--
If you reorder this fix getPairList() too!
That shit is responsible for focus selection
-->
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_movies"
style="@style/RoundedSelectableButton"
android:nextFocusRight="@id/home_select_tv_series"
android:text="@string/movies" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_tv_series"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_movies"
android:nextFocusRight="@id/home_select_anime"
android:text="@string/tv_series" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_anime"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_tv_series"
android:nextFocusRight="@id/home_select_asian"
android:text="@string/anime" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_asian"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_anime"
android:nextFocusRight="@id/home_select_cartoons"
android:text="@string/asian_drama" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_cartoons"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_asian"
android:nextFocusRight="@id/home_select_documentaries"
android:text="@string/cartoons" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_documentaries"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_cartoons"
android:nextFocusRight="@id/home_select_livestreams"
android:text="@string/documentaries" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_livestreams"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_documentaries"
android:nextFocusRight="@id/home_select_nsfw"
android:text="@string/livestreams" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_nsfw"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_livestreams"
android:nextFocusRight="@id/home_select_others"
android:text="@string/nsfw" />
<com.google.android.material.button.MaterialButton
android:id="@+id/home_select_others"
style="@style/RoundedSelectableButton"
android:nextFocusLeft="@id/home_select_nsfw"
android:text="@string/others" />
</LinearLayout>
</HorizontalScrollView>
<LinearLayout
android:id="@+id/apply_btt_holder"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="vertical"
android:layout_gravity="bottom"
android:gravity="bottom|end"
android:orientation="horizontal"
android:visibility="gone">
android:layout_width="match_parent"
android:layout_height="60dp">
<include layout="@layout/tvtypes_chips_scroll" />
<LinearLayout
android:id="@+id/apply_btt_holder"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="-60dp"
android:layout_gravity="bottom"
android:gravity="bottom|end"
android:orientation="horizontal"
android:visibility="gone">
<com.google.android.material.button.MaterialButton
android:id="@+id/apply_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/sort_apply" />
<com.google.android.material.button.MaterialButton
android:id="@+id/apply_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/sort_apply" />
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/sort_cancel" />
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/sort_cancel" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.ChipGroup android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:id="@+id/home_select_group"
app:singleSelection="false"
xmlns:android="http://schemas.android.com/apk/res/android">
<com.google.android.material.chip.Chip
android:id="@+id/home_select_movies"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/movies" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_tv_series"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tv_series" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_anime"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/anime" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_asian"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/asian_drama" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_cartoons"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cartoons" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_documentaries"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/documentaries" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_livestreams"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/livestreams" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_nsfw"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nsfw" />
<com.google.android.material.chip.Chip
android:id="@+id/home_select_others"
style="@style/ChipFilled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/others" />
</com.google.android.material.chip.ChipGroup>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView android:id="@+id/tv_types_scroll_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fadingEdge="horizontal"
android:requiresFadingEdge="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/tvtypes_chips" />
</HorizontalScrollView>

View File

@ -0,0 +1,533 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string formatted="true" name="app_dub_sub_episode_text_format">%s еп. %d</string>
<string formatted="true" name="cast_format">Актьори: %s</string>
<string formatted="true" name="next_episode_format">Епизод %d ще бъде пуснат след</string>
<string formatted="true" name="next_episode_time_day_format">%DD %dh %dm</string>
<string formatted="true" name="next_episode_time_hour_format">%dh %dm</string>
<string formatted="true" name="next_episode_time_min_format">%dm</string>
<string name="result_poster_img_des">Плакат</string>
<string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="episode_poster_img_des">Плакат на епизод</string>
<string name="home_main_poster_img_des">Основен плакат</string>
<string name="home_next_random_img_des">Следващ произволен</string>
<string name="go_back_img_des">Върни се</string>
<string name="home_change_provider_img_des">Смяна на доставчика</string>
<string name="preview_background_img_des">Визуализация на фона</string>
<string formatted="true" name="player_speed_text_format">Скорост (%.2fx)</string>
<string formatted="true" name="rated_format">Оценка: %.1f</string>
<string formatted="true" name="new_update_format">Намерена е нова актуализация!\n%s -&gt; %s</string>
<string formatted="true" name="filler">Шаблон</string>
<string formatted="true" name="duration_format">%d мин</string>
<string name="app_name">CloudStream</string>
<string name="play_with_app_name">Пусни с CloudStream</string>
<string name="title_home">Начало</string>
<string name="title_search">Търсене</string>
<string name="title_downloads">Изтегляния</string>
<string name="title_settings">Настройки</string>
<string name="search_hint">Търсене…</string>
<string formatted="true" name="search_hint_site">Търсете %s...</string>
<string name="no_data">Няма данни</string>
<string name="episode_more_options_des">Още опций</string>
<string name="next_episode">Следващ епизод</string>
<string name="result_tags">Жанрове</string>
<string name="result_share">Сподели</string>
<string name="result_open_in_browser">Отвори в браузъра</string>
<string name="skip_loading">Пропусни зареждането</string>
<string name="loading">Зареждане…</string>
<string name="type_watching">Гледане</string>
<string name="type_on_hold">На изчакване</string>
<string name="type_completed">Завършено</string>
<string name="type_dropped">Изпуснат</string>
<string name="type_plan_to_watch">План за гледане</string>
<string name="type_none">Нито един</string>
<string name="type_re_watching">Повторно гледане</string>
<string name="play_movie_button">Пускане на филм</string>
<string name="play_livestream_button">Възпроизвеждане на живо</string>
<string name="play_torrent_button">Поточно предаване на торент</string>
<string name="pick_source">Източници</string>
<string name="pick_subtitle">субтитри</string>
<string name="reload_error">Повторен опит за свързване...</string>
<string name="go_back">Върни се</string>
<string name="play_episode">Пусни епизод</string>
<string name="download">Изтегли</string>
<string name="downloaded">Изтеглено</string>
<string name="downloading">Изтегля се</string>
<string name="download_paused">Изтеглянето е на пауза</string>
<string name="download_started">Изтеглянето започна</string>
<string name="download_failed">Изтеглянето се провали</string>
<string name="download_canceled">Изтеглянето е отменено</string>
<string name="download_done">Изтеглянето е завършено</string>
<string name="stream">Поток</string>
<string name="error_loading_links_toast">Грешка при зареждане на връзки</string>
<string name="download_storage_text">Вътрешна памет</string>
<string name="app_dubbed_text">Дублаж</string>
<string name="app_subbed_text">Субтитри</string>
<string name="popup_delete_file">Изтрий файла</string>
<string name="popup_play_file">Възпроизвеждане на файл</string>
<string name="popup_resume_download">Възобновете изтеглянето</string>
<string name="popup_pause_download">Пауза на изтеглянето</string>
<string name="pref_disable_acra">Деактивирайте автоматичното докладване на грешки</string>
<string name="home_more_info">Повече информация</string>
<string name="home_expanded_hide">Скрий</string>
<string name="home_play">Пусни</string>
<string name="home_info">Информация</string>
<string name="filter_bookmarks">Филтриране на отметки</string>
<string name="error_bookmarks_text">Отметки</string>
<string name="action_remove_from_bookmarks">Премахване</string>
<string name="action_add_to_bookmarks">Задайте статус на гледане</string>
<string name="sort_apply">Приложи</string>
<string name="sort_cancel">Отказ</string>
<string name="sort_copy">Копирай</string>
<string name="sort_close">Затвори</string>
<string name="sort_clear">Изчисти</string>
<string name="sort_save">Запазване</string>
<string name="player_speed">Скорост на възпроизвеждане</string>
<string name="subtitles_settings">Настройки на субтитрите</string>
<string name="subs_text_color">Цвят на текста</string>
<string name="subs_outline_color">Цвят на контура</string>
<string name="subs_background_color">Цвят на фона</string>
<string name="subs_window_color">Цвят на прозореца</string>
<string name="subs_edge_type">Тип ръб</string>
<string name="subs_subtitle_elevation">Височина на субтитрите</string>
<string name="subs_font">Шрифт</string>
<string name="subs_font_size">Размер на шрифта</string>
<string name="search_provider_text_providers">Търсене чрез доставчици</string>
<string name="search_provider_text_types">Търсене с помощта на типове</string>
<string name="benene_count_text">%d Банан/а даден/и на разработчиците</string>
<string name="benene_count_text_none">Не е/са даден/и Банан/и</string>
<string name="subs_auto_select_language">Автоматичен избор на език</string>
<string name="subs_download_languages">Изтегляне на езици</string>
<string name="subs_subtitle_languages">Език на субтитрите</string>
<string name="subs_hold_to_reset_to_default">Задръжте, за да нулирате по подразбиране</string>
<string formatted="true" name="subs_import_text">Импортирайте шрифтове, като ги поставите в %s</string>
<string name="continue_watching">Продължете да гледате</string>
<string name="action_remove_watching">Премахване</string>
<string name="action_open_watching">Повече информация</string>
<string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Може да е необходим VPN, за да работи правилно този доставчик</string>
<string name="vpn_torrent">Този доставчик е торент, препоръчва се VPN</string>
<string name="provider_info_meta">Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта.</string>
<string name="torrent_plot">Описание</string>
<string name="normal_no_plot">Няма намерен съдържание</string>
<string name="torrent_no_plot">Няма намерено описание</string>
<string name="show_log_cat">Покажи logcat 🐈</string>
<string name="picture_in_picture">Картина в картина</string>
<string name="picture_in_picture_des">Продължава възпроизвеждането в миниатюрен плейър върху други приложения</string>
<string name="player_size_settings">Бутон за преоразмеряване на плейъра</string>
<string name="player_size_settings_des">Премахнете черните граници</string>
<string name="player_subtitles_settings">Субтитри</string>
<string name="player_subtitles_settings_des">Настройки на субтитрите на плейъра</string>
<string name="chromecast_subtitles_settings">Chromecast субтитри</string>
<string name="chromecast_subtitles_settings_des">Настройки за субтитри на Chromecast</string>
<string name="eigengraumode_settings">Режим Eigengravy (промяна скорост на възпроизвеждане)</string>
<string name="eigengraumode_settings_des">Добавя опция за скорост в плейъра</string>
<string name="swipe_to_seek_settings">Плъзнете за преместване</string>
<string name="swipe_to_seek_settings_des">Плъзнете наляво или надясно, за да контролирате времето във видеоплейъра</string>
<string name="swipe_to_change_settings">Плъзнете, за да промените настройките</string>
<string name="swipe_to_change_settings_des">Плъзнете наляво или надясно, за да промените яркостта или силата на звука</string>
<string name="autoplay_next_settings">Автоматично пускане на следващ епизод</string>
<string name="autoplay_next_settings_des">Започнете следващия епизод, когато текущият приключи</string>
<string name="double_tap_to_seek_settings">Докоснете двукратно за превъртане</string>
<string name="double_tap_to_pause_settings">Докоснете два пъти за пауза</string>
<string name="double_tap_to_seek_amount_settings">Размер на превъртане</string>
<string name="double_tap_to_seek_settings_des">Докоснете два пъти от дясната или лявата страна, за да превъртите напред или назад</string>
<string name="double_tap_to_pause_settings_des">Докоснете в средата, за да направите пауза</string>
<string name="use_system_brightness_settings">Използвайте яркостта на системата</string>
<string name="use_system_brightness_settings_des">Използвайте системна яркост в плейъра на приложението вместо тъмно
наслагване</string>
<string name="episode_sync_settings">Актуализирайте прогреса на гледане</string>
<string name="episode_sync_settings_des">Автоматично синхронизирайте прогреса на текущия си епизод</string>
<string name="restore_settings">Възстановете данните от архив</string>
<string name="backup_settings">Архивиране на данни</string>
<string name="restore_success">Зареден архивен файл</string>
<string formatted="true" name="restore_failed_format">Неуспешно възстановяване на данни от файл %s</string>
<string name="backup_success">Успешно съхранени данни</string>
<string name="backup_failed">Липсват разрешения за съхранение, моля, опитайте отново</string>
<string name="backup_failed_error_format">Грешка при архивирането на %s</string>
<string name="search">Търсене</string>
<string name="category_account">Акаунти</string>
<string name="category_updates">Актуализации и архивиране</string>
<string name="settings_info">Информация</string>
<string name="advanced_search">Подробно търсене</string>
<string name="advanced_search_des">Дава ви резултатите от търсенето, разделени по доставчик</string>
<string name="bug_report_settings_off">Изпраща данни само за сривове</string>
<string name="bug_report_settings_on">Не изпраща данни</string>
<string name="show_fillers_settings">Показване заместващ епизод за аниме</string>
<string name="show_trailers_settings">Показване на трейлъри</string>
<string name="kitsu_settings">Покажете плакати от kitsu</string>
<string name="pref_filter_search_quality">Скриване на избраното видео качество в резултатите от търсенето</string>
<string name="automatic_plugin_updates">Автоматични актуализации на плъгини</string>
<string name="updates_settings">Показвай актуализации на приложението</string>
<string name="updates_settings_des">Автоматично търси нови актуализации при стартиране</string>
<string name="uprereleases_settings">Актуализация до експериментални версии</string>
<string name="uprereleases_settings_des">Търсете експериментални актуализации вместо само пълни версии</string>
<string name="github">Github</string>
<string name="lightnovel">Light novel - приложение от същите разработчици</string>
<string name="anim">Приложение за аниме от същите разработчици</string>
<string name="discord">Присъединете се към Discord</string>
<string name="benene">Дайте банан/и разработчиците</string>
<string name="benene_des">Даден/и банан/и</string>
<string name="app_language">Език на приложението</string>
<string name="no_chromecast_support_toast">Този доставчик няма поддръжка за Chromecast</string>
<string name="no_links_found_toast">Няма намерени връзки</string>
<string name="copy_link_toast">Връзката е копирана в клипборда</string>
<string name="play_episode_toast">Пусни епизода</string>
<string name="subs_default_reset_toast">Възстановяване на стойността по подразбиране</string>
<string name="acra_report_toast">За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до
разработчиците</string>
<string name="season">Сезон</string>
<string name="season_format">%s %d%s</string>
<string name="no_season">Без сезон</string>
<string name="episode">Епизод</string>
<string name="episodes">Епизоди</string>
<string name="episodes_range">%d-%d</string>
<string formatted="true" name="episode_format">%d %s</string>
<string name="season_short">С</string>
<string name="episode_short">Е</string>
<string name="no_episodes_found">Няма намерени епизоди</string>
<string name="delete_file">Изтрий файла</string>
<string name="delete">Изтрий</string>
<string name="pause">Пауза</string>
<string name="resume">Продължи</string>
<string name="go_back_30">-30</string>
<string name="go_forward_30">30</string>
<string formatted="true" name="delete_message">Това ще изтрие за постоянно %s\nСигурни ли сте?</string>
<string formatted="true" name="resume_time_left">%dm\nостава</string>
<string name="status_ongoing">Продължава</string>
<string name="status_completed">Завършен</string>
<string name="status">Статус</string>
<string name="year">Година</string>
<string name="rating">Рейтинг</string>
<string name="duration">Продължителност</string>
<string name="site">Уебсайт</string>
<string name="synopsis">Синопсис</string>
<string name="queued">На опашката</string>
<string name="no_subtitles">Без субтитри</string>
<string name="default_subtitles">По подразбиране</string>
<string name="free_storage">Безплатно</string>
<string name="used_storage">Използвано</string>
<string name="app_storage">Приложения</string>
<string name="movies">Филми</string>
<string name="tv_series">Телевизионен сериал</string>
<string name="cartoons">Анимационни филми</string>
<string name="anime">Аниме</string>
<string name="torrent">Торенти</string>
<string name="documentaries">Документални</string>
<string name="ova">OVA</string>
<string name="asian_drama">Азиатски драми</string>
<string name="livestreams">На живо</string>
<string name="nsfw">Разпоретини</string>
<string name="others">други</string>
<string name="movies_singular">Филм</string>
<string name="tv_series_singular">Серия</string>
<string name="cartoons_singular">Анимационен филм</string>
<string name="anime_singular">@string/anime</string>
<string name="ova_singular">@string/ova</string>
<string name="torrent_singular">Торент</string>
<string name="documentaries_singular">Документален филм</string>
<string name="asian_drama_singular">Азиатска драма</string>
<string name="live_singular">Поток на живо</string>
<string name="nsfw_singular">Разпоретинa</string>
<string name="other_singular">Видео</string>
<string name="source_error">Грешка в източника</string>
<string name="remote_error">Дистанционна грешка</string>
<string name="render_error">Грешка в рендъра</string>
<string name="unexpected_error">Неочаквана грешка на плеъра</string>
<string name="storage_error">Грешка при изтегляне, проверете разрешенията за съхранение</string>
<string name="episode_action_chromecast_episode">Епизод за Chromecast</string>
<string name="episode_action_chromecast_mirror">Chromecast огледало</string>
<string name="episode_action_play_in_app">Пусни в приложението</string>
<string name="episode_action_play_in_format">Пусни в %s</string>
<string name="episode_action_play_in_browser">Пусни в браузър</string>
<string name="episode_action_copy_link">Копирай връзка</string>
<string name="episode_action_auto_download">Автоматично изтегляне</string>
<string name="episode_action_download_mirror">Изтегляне на огледало</string>
<string name="episode_action_reload_links">Презареждане на връзки</string>
<string name="episode_action_download_subtitle">Изтегляне на субтитри</string>
<string name="show_hd">покажи качество</string>
<string name="show_dub">Покажи дублаж</string>
<string name="show_sub">Покажи субтитри</string>
<string name="show_title">Заглавие</string>
<string name="poster_ui_settings">Превключване на елементите на потребителския интерфейс на плаката</string>
<string name="no_update_found">Няма намерена актуализация</string>
<string name="check_for_update">Проверка за актуализация</string>
<string name="video_lock">Заключен</string>
<string name="video_aspect_ratio_resize">Преоразмеряване</string>
<string name="video_source">Източник</string>
<string name="video_skip_op">Пропусни</string>
<string name="dont_show_again">Не показвай отново</string>
<string name="skip_update">Пропуснете тази актуализация</string>
<string name="update">Актуализация</string>
<string name="watch_quality_pref">Предпочитано качество за гледане</string>
<string name="limit_title">Максимален брой знаци за заглавие във видеоплейъра</string>
<string name="limit_title_rez">Разделителна способност на видео плейъра</string>
<string name="video_buffer_size_settings">Размер на видео буфера</string>
<string name="video_buffer_length_settings">Дължина на видео буфера</string>
<string name="video_buffer_disk_settings">Видео кеш на диск</string>
<string name="video_buffer_clear_settings">Изчистете кеша за видео и изображения</string>
<string name="video_ram_description">Ще предизвика случайни сривове, ако е зададено твърде високо. Не променяйте, ако имате малко количество RAM, като Android TV или стар телефон</string>
<string name="video_disk_description">Може да причини проблеми на системи с малко място за съхранение, като устройства с Android TV, ако го зададете твърде високо</string>
<string name="dns_pref">DNS през HTTPS</string>
<string name="dns_pref_summary">Полезно за заобикаляне на блокирания от ISP доставчик</string>
<string name="add_site_pref">Сайт за клониране</string>
<string name="remove_site_pref">Премахване на сайта</string>
<string name="add_site_summary">Добавяне на клонинг на съществуващ сайт с различен URL адрес</string>
<string name="download_path_pref">Път за изтегляне</string>
<string name="nginx_url_pref">URL адрес на сървъра на Nginx</string>
<string name="display_subbed_dubbed_settings">Показване на дублирани/субирани аниме</string>
<string name="resize_fit">Побиране в екрана</string>
<string name="resize_fill">Разтягане</string>
<string name="resize_zoom">Мащабиране</string>
<string name="legal_notice">Опровержение</string>
<string name="category_general">Общ</string>
<string name="random_button_settings">Случаен бутон</string>
<string name="random_button_settings_desc">Показване на произволен бутон на началната страница</string>
<string name="provider_lang_settings">Езици на доставчика</string>
<string name="app_layout">Оформление на приложението</string>
<string name="preferred_media_settings">Предпочитана медия</string>
<string name="enable_nsfw_on_providers">Активирайте разпоретините на поддържани доставчици</string>
<string name="subtitles_encoding">Кодиране на субтитрите</string>
<string name="category_providers">Доставчици</string>
<string name="category_ui">Оформление</string>
<string name="automatic">Автоматично</string>
<string name="tv_layout">ТВ оформление</string>
<string name="phone_layout">Оформление като телефон</string>
<string name="emulator_layout">Оформление като емулатор</string>
<string name="primary_color_settings">Основен цвят</string>
<string name="app_theme_settings">Тема на приложението</string>
<string name="bottom_title_settings">Местоположение на заглавието на плаката</string>
<string name="bottom_title_settings_des">Поставете заглавието под плаката</string>
<string name="example_password">парола123</string>
<string name="example_username">MyCoolUsername</string>
<string name="example_email">hello@world.com</string>
<string name="example_ip">127.0.0.1</string>
<string name="example_site_name">MyCoolSite</string>
<string name="example_site_url">example.com</string>
<string name="example_lang_name">Езиков код (en)</string>
<!--
-->
<string name="login_format" formatted="true">%s %s</string>
<string name="account">Акаунт</string>
<string name="logout">Излизане</string>
<string name="login">Влизане</string>
<string name="switch_account">Превключване на акаунт</string>
<string name="add_account">Добавяне на акаунт</string>
<string name="create_account">Създай акаунт</string>
<string name="add_sync">Добавете проследяване</string>
<string formatted="true" name="added_sync_format">Добавен %s</string>
<string name="upload_sync">Синхронизиране</string>
<string name="sync_score">Оценен</string>
<string name="sync_score_format" formatted="true">%d / 10</string>
<string name="sync_total_episodes_none">/??</string>
<string name="sync_total_episodes_some" formatted="true">/%d</string>
<string formatted="true" name="authenticated_user">Удостоверен %s</string>
<string formatted="true" name="authenticated_user_fail">Неуспешно удостоверяване на %s</string>
<string name="none">Нито един</string>
<string name="normal">Нормално</string>
<string name="all">Всичко</string>
<string name="max">Макс</string>
<string name="min">Мин</string>
<string name="subtitles_outline">Контур</string>
<string name="subtitles_depressed">Удвоени</string>
<string name="subtitles_shadow">Сянка</string>
<string name="subtitles_raised">Повдигнати</string>
<string name="subtitle_offset">Синхронизиране на суб</string>
<string name="subtitle_offset_hint">1000ms</string>
<string name="subtitle_offset_title">Забавяне на субтитрите</string>
<string name="subtitle_offset_extra_hint_later_format">Използвайте това, ако субтитрите се показват %dms твърде рано</string>
<string name="subtitle_offset_extra_hint_before_format">Използвайте това, ако субтитрите се показват %dms твърде късно</string>
<string name="subtitle_offset_extra_hint_none_format">Без забавяне на субтитрите</string>
<!--
Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet,
see:
https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
-->
<string name="subtitles_example_text">Бързата кафява лисица прескача мързеливото куче</string>
<string name="recommended">Препоръчва се</string>
<string formatted="true" name="player_loaded_subtitles">Заредено %s</string>
<string name="player_load_subtitles">Зареди от файл</string>
<string name="player_load_subtitles_online">Зареди от Интернет</string>
<string name="downloaded_file">Изтеглен файл</string>
<string name="actor_main">Главен</string>
<string name="actor_supporting">Поддържащ</string>
<string name="actor_background">Заден план</string>
<string name="home_source">Източник</string>
<string name="home_random">Случаен</string>
<string name="coming_soon">Очаквайте скоро…</string>
<string name="quality_cam">Cam</string>
<string name="quality_cam_rip">Cam</string>
<string name="quality_cam_hd">Cam</string>
<string name="quality_hq">HQ</string>
<string name="quality_hd">HD</string>
<string name="quality_ts">TS</string>
<string name="quality_tc">TC</string>
<string name="quality_blueray">BlueRay</string>
<string name="quality_workprint">WP</string>
<string name="quality_dvd">DVD</string>
<string name="quality_4k">4K</string>
<string name="quality_sd">SD</string>
<string name="quality_uhd">UHD</string>
<string name="quality_hdr">HDR</string>
<string name="quality_sdr">SDR</string>
<string name="quality_webrip">Web</string>
<string name="poster_image">Снимка на плакат</string>
<string name="category_player">Плеър</string>
<string name="resolution_and_title">Резолюция и заглавие</string>
<string name="title">Заглавие</string>
<string name="resolution">Резолюция</string>
<string name="error_invalid_id">Невалиден идентификатор</string>
<string name="error_invalid_data">Невалидни данни</string>
<string name="error_invalid_url">Невалиден адрес</string>
<string name="error">Грешка</string>
<string name="subtitles_remove_captions">Премахнете затворените надписи от субтитрите</string>
<string name="subtitles_remove_bloat">Премахнете рекламирането в субтитрите</string>
<string name="subtitles_filter_lang">Филтриране по предпочитан медиен език</string>
<string name="extras">Екстри</string>
<string name="trailer">Трейлър</string>
<string name="network_adress_example">Връзка към потока</string>
<string name="referer">Обратно към</string>
<string name="next">Следващ</string>
<string name="provider_languages_tip">Гледайте видеоклипове на тези езици</string>
<string name="previous">Предишен</string>
<string name="skip_setup">Пропуснете настройката</string>
<string name="app_layout_subtext">Променете външния вид на приложението, за да отговаря на вашето устройство</string>
<string name="crash_reporting_title">Докладване за сривове</string>
<string name="preferred_media_subtext">Какво искате да видите</string>
<string name="setup_done">Край</string>
<string name="extensions">Разширения</string>
<string name="add_repository">Добавяне на хранилище</string>
<string name="repository_name_hint">Име на хранилище</string>
<string name="repository_url_hint">URL адрес на хранилището</string>
<string name="plugin_loaded">Приставката е заредена</string>
<string name="plugin_deleted">Приставката е изтрита</string>
<string name="plugin_load_fail" formatted="true">Неуспешно зареждане %s</string>
<string name="is_adult">18+</string>
<string name="batch_download_start_format" formatted="true">Започна да изтегля %d %s</string>
<string name="batch_download_finish_format" formatted="true">Изтеглено %d %s успешно</string>
<string name="batch_download_nothing_to_download_format" formatted="true">Всички %s вече са изтеглени</string>
<string name="batch_download">Пакетно изтегляне</string>
<string name="plugin_singular">Плъгин</string>
<string name="plugin">Плъгини</string>
<string name="delete_repository_plugins">Това също ще изтрие всички хранилища за плъгини</string>
<string name="delete_repository">Изтриване на хранилище</string>
<string name="setup_extensions_subtext">Изтеглете списъка със сайтове, които искате да използвате</string>
<string formatted="true" name="plugins_downloaded">Изтеглено: %d</string>
<string formatted="true" name="plugins_disabled">Деактивирано: %d</string>
<string formatted="true" name="plugins_not_downloaded">Не е изтеглено: %d</string>
<string formatted="true" name="plugins_updated">Актуализирани %d плъгини</string>
<string name="blank_repo_message">CloudStream няма инсталирани сайтове по подразбиране. Трябва да инсталирате сайтовете от хранилища.\n\nПоради безмозъчно премахване на DMCA от Sky Uk Limited 🤮 не можем да свържем сайтовете на хранилищата в приложението.\n\nПрисъединете се към нашия дискорд за връзки или търсете онлайн.</string>
<string name="view_public_repositories_button">Вижте хранилищата на общността</string>
<string name="view_public_repositories_button_short">Публичен списък</string>
<string name="uppercase_all_subtitles">Всички субтитри с главни букви</string>
<string name="download_all_plugins_from_repo">Изтегляне на всички добавки от това хранилище?</string>
<string name="single_plugin_disabled" formatted="true">%s (Disabled)</string>
<string name="tracks">Потоци</string>
<string name="audio_tracks">Аудио потоци</string>
<string name="video_tracks">Видео потоци</string>
<string name="apply_on_restart">Приложете при рестартиране</string>
<string name="safe_mode_title">Безопасният режим е активиран</string>
<string name="safe_mode_description">Възникна непоправим срив и ние автоматично деактивирахме всички разширения, така че можете да намерите и премахнете разширението, което причинява проблеми.</string>
<string name="safe_mode_crash_info">Вижте информация за срива</string>
<string formatted="true" name="extension_rating">Оценка: %s</string>
<string name="extension_description">Описание</string>
<string name="extension_version">Версия</string>
<string name="extension_status">Статус</string>
<string name="extension_size">Размер</string>
<string name="extension_authors">Автори</string>
<string name="extension_types">Поддържани</string>
<string name="extension_language">език</string>
<string name="extension_install_first">Първо инсталирайте разширението</string>
<string name="hls_playlist">HLS плейлист</string>
<string name="player_pref">Предпочитан видео плеър</string>
<string name="player_settings_play_in_app">Вътрешен плеър</string>
<string name="player_settings_play_in_vlc">VLC</string>
<string name="player_settings_play_in_mpv">MPV</string>
<string name="player_settings_play_in_web">Уеб видео предаване</string>
<string name="player_settings_play_in_browser">Браузър</string>
<string name="app_not_found_error">Приложението не е намерено</string>
</resources>

View File

@ -75,6 +75,10 @@
<item name="primaryGrayBackground">@color/amoledModeLight</item>
</style>
<style name="ChipFilled" parent="@style/Widget.Material3.Chip.Filter">
<item name="chipBackgroundColor">@color/chip_color</item>
</style>
<style name="AmoledMode">
<item name="primaryGrayBackground">@color/black</item>
<item name="primaryBlackBackground">@color/black</item>

View File

@ -1,27 +0,0 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.7.10"
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.5.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

26
build.gradle.kts Normal file
View File

@ -0,0 +1,26 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle.kts files
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}

View File

@ -1,6 +1,6 @@
#Fri Apr 30 17:11:15 CEST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

View File

@ -1,2 +0,0 @@
include ':app'
rootProject.name = "CloudStream"

3
settings.gradle.kts Normal file
View File

@ -0,0 +1,3 @@
rootProject.name = "CloudStream"
include(":app")