mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
update
This commit is contained in:
parent
645d251369
commit
9d733eed40
92 changed files with 2715 additions and 1815 deletions
4
.github/workflows/issue_action.yml
vendored
4
.github/workflows/issue_action.yml
vendored
|
@ -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
|
||||
|
|
4
.github/workflows/prerelease.yml
vendored
4
.github/workflows/prerelease.yml
vendored
|
@ -40,7 +40,7 @@ jobs:
|
|||
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "keystore_password.txt" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore_password.txt"
|
||||
KEY_PWD="$(cat keystore_password.txt)"
|
||||
echo "::add-mask::${KEY_PWD}"
|
||||
echo "::set-output name=key_pwd::$KEY_PWD"
|
||||
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
|
||||
- name: Run Gradle
|
||||
run: |
|
||||
./gradlew assemblePrerelease makeJar androidSourcesJar
|
||||
|
@ -56,6 +56,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
|
||||
|
|
4
.github/workflows/pull_request.yml
vendored
4
.github/workflows/pull_request.yml
vendored
|
@ -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"
|
||||
|
|
5
.idea/jarRepositories.xml
generated
5
.idea/jarRepositories.xml
generated
|
@ -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>
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
***The list of supported languages:***
|
||||
* 🇱🇧 Arabic
|
||||
* 🇧🇬 Bulgarian
|
||||
* 🇭🇷 Croatian
|
||||
* 🇨🇿 Czech
|
||||
* 🇳🇱 Dutch
|
||||
|
|
233
app/build.gradle
233
app/build.gradle
|
@ -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.lagradost.cloudstream3"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 30
|
||||
|
||||
versionCode 51
|
||||
versionName "3.1.5"
|
||||
|
||||
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
250
app/build.gradle.kts
Normal 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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -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
|
||||
|
|
|
@ -180,7 +180,7 @@ class ExampleInstrumentedTest {
|
|||
@Test
|
||||
fun providerCorrectHomepage() {
|
||||
runBlocking {
|
||||
getAllProviders().apmap { api ->
|
||||
getAllProviders().amap { api ->
|
||||
if (api.hasMainPage) {
|
||||
try {
|
||||
val homepage = api.getMainPage()
|
||||
|
@ -217,7 +217,7 @@ class ExampleInstrumentedTest {
|
|||
runBlocking {
|
||||
val invalidProvider = ArrayList<Pair<MainAPI, Exception?>>()
|
||||
val providers = getAllProviders()
|
||||
providers.apmap { api ->
|
||||
providers.amap { api ->
|
||||
try {
|
||||
println("Trying $api")
|
||||
if (testSingleProviderApi(api)) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1066,7 +1066,7 @@ interface LoadResponse {
|
|||
) {
|
||||
if (!isTrailersEnabled || trailerUrls == null) return
|
||||
trailers.addAll(trailerUrls.map { TrailerData(it, referer, addRaw) })
|
||||
/*val trailers = trailerUrls.filter { it.isNotBlank() }.apmap { trailerUrl ->
|
||||
/*val trailers = trailerUrls.filter { it.isNotBlank() }.amap { trailerUrl ->
|
||||
val links = arrayListOf<ExtractorLink>()
|
||||
val subs = arrayListOf<SubtitleFile>()
|
||||
if (!loadExtractor(
|
||||
|
|
|
@ -600,7 +600,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
api.init()
|
||||
}
|
||||
|
||||
inAppAuths.apmap { api ->
|
||||
inAppAuths.amap { api ->
|
||||
try {
|
||||
api.initialize()
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
//https://stackoverflow.com/questions/34697828/parallel-operations-on-kotlin-collections
|
||||
/*
|
||||
|
@ -26,10 +25,25 @@ fun <T, R> Iterable<T>.pmap(
|
|||
return ArrayList<R>(destination)
|
||||
}*/
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <K, V, R> Map<out K, V>.amap(f: suspend (Map.Entry<K, V>) -> R): List<R> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
fun <K, V, R> Map<out K, V>.apmap(f: suspend (Map.Entry<K, V>) -> R): List<R> = runBlocking {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <A, B> List<A>.amap(f: suspend (A) -> B): List<B> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
||||
|
||||
fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
|
||||
map { async { f(it) } }.map { it.await() }
|
||||
}
|
||||
|
@ -38,6 +52,12 @@ fun <A, B> List<A>.apmapIndexed(f: suspend (index: Int, A) -> B): List<B> = runB
|
|||
mapIndexed { index, a -> async { f(index, a) } }.map { it.await() }
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun <A, B> List<A>.amapIndexed(f: suspend (index: Int, A) -> B): List<B> =
|
||||
with(CoroutineScope(GlobalScope.coroutineContext)) {
|
||||
mapIndexed { index, a -> async { f(index, a) } }.map { it.await() }
|
||||
}
|
||||
|
||||
// run code in parallel
|
||||
/*fun <R> argpmap(
|
||||
vararg transforms: () -> R,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
@ -21,10 +21,10 @@ class Fastream: ExtractorApi() {
|
|||
Pair("file_code",id),
|
||||
Pair("auto","1")
|
||||
)).document
|
||||
response.select("script").apmap { script ->
|
||||
response.select("script").amap { script ->
|
||||
if (script.data().contains("sources")) {
|
||||
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
|
||||
val m3u8 = m3u8regex.find(script.data())?.value ?: return@apmap
|
||||
val m3u8 = m3u8regex.find(script.data())?.value ?: return@amap
|
||||
generateM3u8(
|
||||
name,
|
||||
m3u8,
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
@ -35,7 +35,7 @@ class Pelisplus(val mainUrl: String) {
|
|||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
try {
|
||||
normalApis.apmap { api ->
|
||||
normalApis.amap { api ->
|
||||
val url = api.getExtractorUrl(id)
|
||||
api.getSafeUrl(url, subtitleCallback = subtitleCallback, callback = callback)
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ class Pelisplus(val mainUrl: String) {
|
|||
val qualityRegex = Regex("(\\d+)P")
|
||||
|
||||
//a[download]
|
||||
pageDoc.select(".dowload > a")?.apmap { element ->
|
||||
val href = element.attr("href") ?: return@apmap
|
||||
pageDoc.select(".dowload > a")?.amap { element ->
|
||||
val href = element.attr("href") ?: return@amap
|
||||
val qual = if (element.text()
|
||||
.contains("HDP")
|
||||
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
|
||||
|
@ -84,7 +84,7 @@ class Pelisplus(val mainUrl: String) {
|
|||
//val name = element.text()
|
||||
|
||||
// Matches vidstream links with extractors
|
||||
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
|
||||
extractorApis.filter { !it.requiresReferer || !isCasting }.amap { api ->
|
||||
if (link.startsWith(api.mainUrl)) {
|
||||
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import kotlinx.coroutines.delay
|
||||
|
@ -69,12 +69,12 @@ open class VidSrcExtractor : ExtractorApi() {
|
|||
} else ""
|
||||
}
|
||||
|
||||
serverslist.apmap { server ->
|
||||
serverslist.amap { server ->
|
||||
val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/")
|
||||
if (linkfixed.contains("/pro")) {
|
||||
val srcresponse = app.get(server, referer = absoluteUrl).text
|
||||
val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)")
|
||||
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap
|
||||
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@amap
|
||||
val passRegex = Regex("""['"](.*set_pass[^"']*)""")
|
||||
val pass = passRegex.find(srcresponse)?.groupValues?.get(1)?.replace(
|
||||
Regex("""^//"""), "https://"
|
||||
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.argamap
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
@ -37,7 +37,7 @@ class Vidstream(val mainUrl: String) {
|
|||
val extractorUrl = getExtractorUrl(id)
|
||||
argamap(
|
||||
{
|
||||
normalApis.apmap { api ->
|
||||
normalApis.amap { api ->
|
||||
val url = api.getExtractorUrl(id)
|
||||
api.getSafeUrl(
|
||||
url,
|
||||
|
@ -55,8 +55,8 @@ class Vidstream(val mainUrl: String) {
|
|||
val qualityRegex = Regex("(\\d+)P")
|
||||
|
||||
//a[download]
|
||||
pageDoc.select(".dowload > a")?.apmap { element ->
|
||||
val href = element.attr("href") ?: return@apmap
|
||||
pageDoc.select(".dowload > a")?.amap { element ->
|
||||
val href = element.attr("href") ?: return@amap
|
||||
val qual = if (element.text()
|
||||
.contains("HDP")
|
||||
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
|
||||
|
@ -87,7 +87,7 @@ class Vidstream(val mainUrl: String) {
|
|||
//val name = element.text()
|
||||
|
||||
// Matches vidstream links with extractors
|
||||
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
|
||||
extractorApis.filter { !it.requiresReferer || !isCasting }.amap { api ->
|
||||
if (link.startsWith(api.mainUrl)) {
|
||||
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
@ -36,7 +36,7 @@ open class ZplayerV2 : ExtractorApi() {
|
|||
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
|
||||
m3u8regex.findAll(testdata).map {
|
||||
it.value
|
||||
}.toList().apmap { urlm3u8 ->
|
||||
}.toList().amap { urlm3u8 ->
|
||||
if (urlm3u8.contains("m3u8")) {
|
||||
val testurl = app.get(urlm3u8, headers = mapOf("Referer" to url)).text
|
||||
if (testurl.contains("EXTM3U")) {
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.extractors.helper
|
|||
|
||||
import android.util.Log
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
|
@ -18,7 +18,7 @@ class AsianEmbedHelper {
|
|||
val doc = app.get(url).document
|
||||
val links = doc.select("div#list-server-more > ul > li.linkserver")
|
||||
if (!links.isNullOrEmpty()) {
|
||||
links.apmap {
|
||||
links.amap {
|
||||
val datavid = it.attr("data-video") ?: ""
|
||||
//Log.i("AsianEmbed", "Result => (datavid) ${datavid}")
|
||||
if (datavid.isNotBlank()) {
|
||||
|
|
|
@ -39,7 +39,7 @@ class CrossTmdbProvider : TmdbProvider() {
|
|||
): Boolean {
|
||||
tryParseJson<CrossMetaData>(data)?.let { metaData ->
|
||||
if (!metaData.isSuccess) return false
|
||||
metaData.movies?.apmap { (apiName, data) ->
|
||||
metaData.movies?.amap { (apiName, data) ->
|
||||
getApiFromNameNull(apiName)?.let {
|
||||
try {
|
||||
it.loadLinks(data, isCasting, subtitleCallback, callback)
|
||||
|
@ -64,10 +64,10 @@ class CrossTmdbProvider : TmdbProvider() {
|
|||
val matchName = filterName(this.name)
|
||||
when (this) {
|
||||
is MovieLoadResponse -> {
|
||||
val data = validApis.apmap { api ->
|
||||
val data = validApis.amap { api ->
|
||||
try {
|
||||
if (api.supportedTypes.contains(TvType.Movie)) { //|| api.supportedTypes.contains(TvType.AnimeMovie)
|
||||
return@apmap api.search(this.name)?.first {
|
||||
return@amap api.search(this.name)?.first {
|
||||
if (filterName(it.name).equals(
|
||||
matchName,
|
||||
ignoreCase = true
|
||||
|
|
|
@ -45,7 +45,7 @@ class MultiAnimeProvider : MainAPI() {
|
|||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
return syncApi.getResult(url)?.let { res ->
|
||||
val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).apmap { url ->
|
||||
val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).amap { url ->
|
||||
validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url)
|
||||
}.filterNotNull()
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ import com.lagradost.cloudstream3.AcraApplication
|
|||
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||
import com.lagradost.cloudstream3.USER_AGENT
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mvvm.debugException
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
|
||||
|
@ -65,9 +67,15 @@ class WebViewResolver(
|
|||
method: String = "GET",
|
||||
requestCallBack: (Request) -> Boolean = { false },
|
||||
): Pair<Request?, List<Request>> {
|
||||
return resolveUsingWebView(
|
||||
requestCreator(method, url, referer = referer), requestCallBack
|
||||
)
|
||||
return try {
|
||||
resolveUsingWebView(
|
||||
requestCreator(method, url, referer = referer), requestCallBack
|
||||
)
|
||||
} catch (e: java.lang.IllegalArgumentException) {
|
||||
logError(e)
|
||||
debugException { "ILLEGAL URL IN resolveUsingWebView!" }
|
||||
return null to emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,7 +137,7 @@ class WebViewResolver(
|
|||
println("Loading WebView URL: $webViewUrl")
|
||||
|
||||
if (interceptUrl.containsMatchIn(webViewUrl)) {
|
||||
fixedRequest = request.toRequest().also {
|
||||
fixedRequest = request.toRequest()?.also {
|
||||
requestCallBack(it)
|
||||
}
|
||||
println("Web-view request finished: $webViewUrl")
|
||||
|
@ -138,9 +146,9 @@ class WebViewResolver(
|
|||
}
|
||||
|
||||
if (additionalUrls.any { it.containsMatchIn(webViewUrl) }) {
|
||||
extraRequestList.add(request.toRequest().also {
|
||||
request.toRequest()?.also {
|
||||
if (requestCallBack(it)) destroyWebView()
|
||||
})
|
||||
}?.let(extraRequestList::add)
|
||||
}
|
||||
|
||||
// Suppress image requests as we don't display them anywhere
|
||||
|
@ -251,14 +259,19 @@ class WebViewResolver(
|
|||
|
||||
}
|
||||
|
||||
fun WebResourceRequest.toRequest(): Request {
|
||||
fun WebResourceRequest.toRequest(): Request? {
|
||||
val webViewUrl = this.url.toString()
|
||||
|
||||
return requestCreator(
|
||||
this.method,
|
||||
webViewUrl,
|
||||
this.requestHeaders,
|
||||
)
|
||||
// If invalid url then it can crash with
|
||||
// java.lang.IllegalArgumentException: Expected URL scheme 'http' or 'https' but was 'data'
|
||||
// At Request.Builder().url(addParamsToUrl(url, params))
|
||||
return normalSafeApiCall {
|
||||
requestCreator(
|
||||
this.method,
|
||||
webViewUrl,
|
||||
this.requestHeaders,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Response.toWebResourceResponse(): WebResourceResponse {
|
||||
|
|
|
@ -217,18 +217,17 @@ object PluginManager {
|
|||
* 3. If outdated download and load the plugin
|
||||
* 4. Else load the plugin normally
|
||||
**/
|
||||
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
|
||||
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) = ioSafe {
|
||||
// Load all plugins as fast as possible!
|
||||
loadAllOnlinePlugins(activity)
|
||||
|
||||
ioSafe {
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
}
|
||||
|
||||
|
||||
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
||||
?: emptyArray()) + PREBUILT_REPOSITORIES
|
||||
|
||||
val onlinePlugins = urls.toList().apmap {
|
||||
val onlinePlugins = urls.toList().amap {
|
||||
getRepoPlugins(it.url)?.toList() ?: emptyList()
|
||||
}.flatten().distinctBy { it.second.url }
|
||||
|
||||
|
@ -249,7 +248,7 @@ object PluginManager {
|
|||
|
||||
val updatedPlugins = mutableListOf<String>()
|
||||
|
||||
outdatedPlugins.apmap { pluginData ->
|
||||
outdatedPlugins.amap { pluginData ->
|
||||
if (pluginData.isDisabled) {
|
||||
//updatedPlugins.add(activity.getString(R.string.single_plugin_disabled, pluginData.onlineData.second.name))
|
||||
unloadPlugin(pluginData.savedData.filePath)
|
||||
|
@ -270,9 +269,9 @@ object PluginManager {
|
|||
createNotification(activity, updatedPlugins)
|
||||
}
|
||||
|
||||
ioSafe {
|
||||
// ioSafe {
|
||||
afterPluginsLoadedEvent.invoke(true)
|
||||
}
|
||||
// }
|
||||
|
||||
Log.i(TAG, "Plugin update done!")
|
||||
}
|
||||
|
@ -280,9 +279,9 @@ object PluginManager {
|
|||
/**
|
||||
* Use updateAllOnlinePluginsAndLoadThem
|
||||
* */
|
||||
fun loadAllOnlinePlugins(activity: Activity) {
|
||||
fun loadAllOnlinePlugins(activity: Activity) = ioSafe {
|
||||
// Load all plugins as fast as possible!
|
||||
(getPluginsOnline()).toList().apmap { pluginData ->
|
||||
(getPluginsOnline()).toList().amap { pluginData ->
|
||||
loadPlugin(
|
||||
activity,
|
||||
File(pluginData.filePath),
|
||||
|
@ -291,7 +290,7 @@ object PluginManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun loadAllLocalPlugins(activity: Activity) {
|
||||
fun loadAllLocalPlugins(activity: Activity) = ioSafe {
|
||||
val dir = File(LOCAL_PLUGINS_PATH)
|
||||
removeKey(PLUGINS_KEY_LOCAL)
|
||||
|
||||
|
@ -299,7 +298,7 @@ object PluginManager {
|
|||
val res = dir.mkdirs()
|
||||
if (!res) {
|
||||
Log.w(TAG, "Failed to create local directories")
|
||||
return
|
||||
return@ioSafe
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.content.Context
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||
|
@ -95,7 +95,7 @@ object RepositoryManager {
|
|||
* */
|
||||
suspend fun getRepoPlugins(repositoryUrl: String): List<Pair<String, SitePlugin>>? {
|
||||
val repo = parseRepository(repositoryUrl) ?: return null
|
||||
return repo.pluginLists.apmap { url ->
|
||||
return repo.pluginLists.amap { url ->
|
||||
parsePlugins(url).map {
|
||||
repositoryUrl to it
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
|||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope.coroutineContext
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class APIRepository(val api: MainAPI) {
|
||||
|
@ -26,6 +30,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 +45,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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,12 +82,18 @@ class APIRepository(val api: MainAPI) {
|
|||
delay(delta)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource<List<HomePageResponse?>> {
|
||||
return safeApiCall {
|
||||
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
|
||||
|
@ -90,11 +108,15 @@ class APIRepository(val api: MainAPI) {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
api.mainPage.apmap { data ->
|
||||
api.getMainPage(
|
||||
page,
|
||||
MainPageRequest(data.name, data.data, data.horizontalImages)
|
||||
)
|
||||
with(CoroutineScope(coroutineContext)) {
|
||||
api.mainPage.map { data ->
|
||||
async {
|
||||
api.getMainPage(
|
||||
page,
|
||||
MainPageRequest(data.name, data.data, data.horizontalImages)
|
||||
)
|
||||
}
|
||||
}.map { it.await() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,6 +14,7 @@ import android.view.ViewGroup
|
|||
import android.widget.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.content.ContextCompat.getDrawable
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
|
@ -21,16 +22,18 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSnapHelper
|
||||
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 +46,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 +54,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 +66,12 @@ 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.getResourceColor
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
|
@ -82,14 +88,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
|
||||
|
@ -99,9 +103,13 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_holder
|
|||
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.fragment_search.*
|
||||
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"
|
||||
const val HOME_PREF_HOMEPAGE = "home_pref_homepage"
|
||||
|
||||
|
@ -247,16 +255,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 +279,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 +357,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 +404,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 +433,6 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
|
||||
private fun toggleMainVisibility(visible: Boolean) {
|
||||
home_main_holder?.isVisible = visible
|
||||
home_main_poster_recyclerview?.isVisible = visible
|
||||
}
|
||||
|
||||
|
@ -531,6 +541,86 @@ class HomeFragment : Fragment() {
|
|||
home_random?.visibility = View.GONE
|
||||
}
|
||||
|
||||
observe(homeViewModel.preview) { preview ->
|
||||
// Always reset the padding, otherwise the will move lower and lower
|
||||
home_watch_holder?.setPadding(0, 0, 0, 0)
|
||||
when (preview) {
|
||||
is Resource.Success -> {
|
||||
home_preview?.isVisible = true
|
||||
(home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply {
|
||||
setItems(preview.value)
|
||||
// home_preview_viewpager?.setCurrentItem(1000, false)
|
||||
}
|
||||
|
||||
//.also {
|
||||
//home_preview_viewpager?.adapter =
|
||||
//}
|
||||
}
|
||||
else -> {
|
||||
home_preview?.isVisible = false
|
||||
context?.fixPaddingStatusbar(home_watch_holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val searchText =
|
||||
home_search?.findViewById<SearchView.SearchAutoComplete>(androidx.appcompat.R.id.search_src_text)
|
||||
searchText?.context?.getResourceColor(R.attr.white)?.let { color ->
|
||||
searchText.setTextColor(color)
|
||||
searchText.setHintTextColor(color)
|
||||
}
|
||||
|
||||
home_preview_viewpager?.apply {
|
||||
setPageTransformer(false, HomeScrollTransformer())
|
||||
adapter = HomeScrollAdapter { load ->
|
||||
load.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(load.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(load.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(load, newValue)
|
||||
reloadStored()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
observe(homeViewModel.apiName) { apiName ->
|
||||
currentApiName = apiName
|
||||
// setKey(USER_SELECTED_HOMEPAGE_API, apiName)
|
||||
|
@ -563,17 +653,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)
|
||||
|
@ -695,14 +785,32 @@ class HomeFragment : Fragment() {
|
|||
Pair(home_type_on_hold_btt, WatchType.ONHOLD),
|
||||
Pair(home_plan_to_watch_btt, WatchType.PLANTOWATCH),
|
||||
)
|
||||
val currentSet = getKey<IntArray>(HOME_BOOKMARK_VALUE_LIST)
|
||||
?.map { WatchType.fromInternalId(it) }?.toSet() ?: emptySet()
|
||||
|
||||
for ((chip, watch) in toggleList) {
|
||||
chip.isChecked = currentSet.contains(watch)
|
||||
chip?.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
homeViewModel.loadStoredData(
|
||||
setOf(watch)
|
||||
// If we filter all buttons then two can be checked at the same time
|
||||
// Revert this if you want to go back to multi selection
|
||||
// toggleList.filter { it.first?.isChecked == true }.map { it.second }.toSet()
|
||||
)
|
||||
}
|
||||
// Else if all are unchecked -> Do not load data
|
||||
else if (toggleList.all { it.first?.isChecked != true }) {
|
||||
homeViewModel.loadStoredData(emptySet())
|
||||
}
|
||||
}
|
||||
/*chip?.setOnClickListener {
|
||||
|
||||
|
||||
for (item in toggleList) {
|
||||
val watch = item.second
|
||||
item.first?.setOnClickListener {
|
||||
homeViewModel.loadStoredData(EnumSet.of(watch))
|
||||
}
|
||||
|
||||
item.first?.setOnLongClickListener { itemView ->
|
||||
chip?.setOnLongClickListener { itemView ->
|
||||
val list = EnumSet.noneOf(WatchType::class.java)
|
||||
itemView.context.getKey<IntArray>(HOME_BOOKMARK_VALUE_LIST)
|
||||
?.map { WatchType.fromInternalId(it) }?.let {
|
||||
|
@ -716,7 +824,7 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
homeViewModel.loadStoredData(list)
|
||||
return@setOnLongClickListener true
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
observe(homeViewModel.availableWatchStatusTypes) { availableWatchStatusTypes ->
|
||||
|
@ -938,7 +1046,8 @@ class HomeFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
context?.fixPaddingStatusbarView(home_statusbar)
|
||||
//context?.fixPaddingStatusbarView(home_statusbar)
|
||||
context?.fixPaddingStatusbar(home_padding)
|
||||
context?.fixPaddingStatusbar(home_loading_statusbar)
|
||||
|
||||
home_master_recycler.adapter =
|
||||
|
@ -959,33 +1068,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()
|
||||
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package com.lagradost.cloudstream3.ui.home
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import com.lagradost.cloudstream3.LoadResponse
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
|
||||
|
||||
class HomeScrollAdapter(private val onPrimaryCallback: (LoadResponse) -> Unit) : PagerAdapter() {
|
||||
private var items: List<LoadResponse> = listOf()
|
||||
|
||||
fun setItems(newItems: List<LoadResponse>) {
|
||||
items = newItems
|
||||
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return Int.MAX_VALUE//items.size
|
||||
}
|
||||
|
||||
override fun getItemPosition(`object`: Any): Int {
|
||||
return POSITION_NONE//super.getItemPosition(`object`)
|
||||
}
|
||||
|
||||
private fun getItemAtPosition(idx: Int): LoadResponse {
|
||||
return items[idx % items.size]
|
||||
}
|
||||
|
||||
override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
super.setPrimaryItem(container, position, `object`)
|
||||
onPrimaryCallback.invoke(getItemAtPosition(position))
|
||||
}
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val image = ImageView(container.context)
|
||||
val item = getItemAtPosition(position)
|
||||
image.scaleType = ImageView.ScaleType.CENTER_CROP
|
||||
image.setImage(item.posterUrl ?: item.backgroundPosterUrl, item.posterHeaders)
|
||||
|
||||
// val itemView: View = mLayoutInflater.inflate(R.layout.pager_item, container, false)
|
||||
|
||||
// val imageView: ImageView = itemView.findViewById<View>(R.id.imageView) as ImageView
|
||||
// imageView.setImageResource(mResources.get(position))
|
||||
|
||||
container.addView(image)
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
|
||||
container.removeView(`object` as View)
|
||||
}
|
||||
|
||||
override fun isViewFromObject(view: View, `object`: Any): Boolean {
|
||||
return view === `object`
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.lagradost.cloudstream3.ui.home
|
||||
|
||||
import android.view.View
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
|
||||
class HomeScrollTransformer : ViewPager.PageTransformer {
|
||||
override fun transformPage(page: View, position: Float) {
|
||||
page.setPadding(
|
||||
maxOf(0, (-position * page.width / 2).toInt()), 0,
|
||||
maxOf(0, (position * page.width / 2).toInt()), 0
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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.*
|
||||
|
@ -20,6 +21,8 @@ import com.lagradost.cloudstream3.ui.APIRepository
|
|||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
|
||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
||||
|
@ -32,7 +35,7 @@ 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.async
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
import kotlin.collections.set
|
||||
|
@ -51,14 +54,16 @@ class HomeViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
private val _availableWatchStatusTypes =
|
||||
MutableLiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>>()
|
||||
val availableWatchStatusTypes: LiveData<Pair<EnumSet<WatchType>, EnumSet<WatchType>>> =
|
||||
MutableLiveData<Pair<Set<WatchType>, Set<WatchType>>>()
|
||||
val availableWatchStatusTypes: LiveData<Pair<Set<WatchType>, Set<WatchType>>> =
|
||||
_availableWatchStatusTypes
|
||||
private val _bookmarks = MutableLiveData<Pair<Boolean, List<SearchResponse>>>()
|
||||
val bookmarks: LiveData<Pair<Boolean, List<SearchResponse>>> = _bookmarks
|
||||
|
||||
private val _resumeWatching = MutableLiveData<List<SearchResponse>>()
|
||||
private val _preview = MutableLiveData<Resource<List<LoadResponse>>>()
|
||||
val resumeWatching: LiveData<List<SearchResponse>> = _resumeWatching
|
||||
val preview: LiveData<Resource<List<LoadResponse>>> = _preview
|
||||
|
||||
fun loadResumeWatching() = viewModelScope.launchSafe {
|
||||
val resumeWatching = withContext(Dispatchers.IO) {
|
||||
|
@ -96,7 +101,7 @@ class HomeViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun loadStoredData(preferredWatchStatus: EnumSet<WatchType>?) = viewModelScope.launchSafe {
|
||||
fun loadStoredData(preferredWatchStatus: Set<WatchType>?) = viewModelScope.launchSafe {
|
||||
val watchStatusIds = withContext(Dispatchers.IO) {
|
||||
getAllWatchStateIds()?.map { id ->
|
||||
Pair(id, getResultWatchState(id))
|
||||
|
@ -104,7 +109,7 @@ class HomeViewModel : ViewModel() {
|
|||
}?.distinctBy { it.first } ?: return@launchSafe
|
||||
|
||||
val length = WatchType.values().size
|
||||
val currentWatchTypes = EnumSet.noneOf(WatchType::class.java)
|
||||
val currentWatchTypes = mutableSetOf<WatchType>()
|
||||
|
||||
for (watch in watchStatusIds) {
|
||||
currentWatchTypes.add(watch.second)
|
||||
|
@ -207,7 +212,8 @@ class HomeViewModel : ViewModel() {
|
|||
expandAndReturn(name)
|
||||
}
|
||||
|
||||
private fun load(api: MainAPI?) = viewModelScope.launchSafe {
|
||||
|
||||
private fun load(api: MainAPI?) = ioSafe {
|
||||
repo = if (api != null) {
|
||||
APIRepository(api)
|
||||
} else {
|
||||
|
@ -219,6 +225,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 +239,66 @@ class HomeViewModel : ViewModel() {
|
|||
ExpandableHomepageList(filteredList, 1, home.hasNext)
|
||||
}
|
||||
}
|
||||
_page.postValue(Resource.Success(expandable))
|
||||
|
||||
val items = data.value.mapNotNull { it?.items }.flatten()
|
||||
val responses = ioWork {
|
||||
items.flatMap { it.list }.shuffled().take(6).map { searchResponse ->
|
||||
async { repo?.load(searchResponse.url) }
|
||||
}.map { it.await() }.mapNotNull { if (it != null && it is Resource.Success) it.value else null } }
|
||||
//.amap { searchResponse ->
|
||||
// repo?.load(searchResponse.url)
|
||||
///}
|
||||
|
||||
//.map { searchResponse ->
|
||||
// async { repo?.load(searchResponse.url) }
|
||||
// }.map { it.await() }
|
||||
|
||||
|
||||
if (responses.isEmpty()) {
|
||||
_preview.postValue(
|
||||
Resource.Failure(
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
"No homepage responses"
|
||||
)
|
||||
)
|
||||
} else {
|
||||
_preview.postValue(Resource.Success(responses))
|
||||
}
|
||||
|
||||
/*
|
||||
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 +328,7 @@ class HomeViewModel : ViewModel() {
|
|||
}
|
||||
} else {
|
||||
_page.postValue(Resource.Success(emptyMap()))
|
||||
_preview.postValue(Resource.Failure(false, null, null, "No homepage"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -344,7 +344,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
seasonNumber = currentTempMeta.season,
|
||||
lang = currentLanguageTwoLetters.ifBlank { null }
|
||||
)
|
||||
val results = providers.apmap {
|
||||
val results = providers.amap {
|
||||
try {
|
||||
it.search(search)
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.lagradost.cloudstream3.ui.player
|
||||
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import java.net.URI
|
||||
|
@ -46,7 +46,7 @@ class LinkGenerator(
|
|||
subtitleCallback: (SubtitleData) -> Unit,
|
||||
offset: Int
|
||||
): Boolean {
|
||||
links.apmap { link ->
|
||||
links.amap { link ->
|
||||
if (!extract || !loadExtractor(link, referer, {
|
||||
subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it))
|
||||
}) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import androidx.core.widget.doOnTextChanged
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||
|
@ -99,6 +99,7 @@ import kotlinx.android.synthetic.main.result_sync.*
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
import com.google.android.material.chip.ChipDrawable
|
||||
|
||||
const val START_ACTION_RESUME_LATEST = 1
|
||||
const val START_ACTION_LOAD_EP = 2
|
||||
|
@ -926,18 +927,38 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
|
||||
val tags = d.tags
|
||||
result_tag_holder?.isVisible = tags.isNotEmpty()
|
||||
if (tags.isNotEmpty()) {
|
||||
//result_tag_holder?.visibility = VISIBLE
|
||||
val isOnTv = isTrueTvSettings()
|
||||
for ((index, tag) in tags.withIndex()) {
|
||||
result_tag?.apply {
|
||||
tags.forEach { tag ->
|
||||
val chip = Chip(context)
|
||||
val chipDrawable = ChipDrawable.createFromAttributes(
|
||||
context,
|
||||
null,
|
||||
0,
|
||||
R.style.ChipFilled
|
||||
)
|
||||
chip.setChipDrawable(chipDrawable)
|
||||
chip.text = tag
|
||||
chip.isChecked = false
|
||||
chip.isCheckable = false
|
||||
chip.isFocusable = false
|
||||
chip.isClickable = false
|
||||
addView(chip)
|
||||
}
|
||||
}
|
||||
// if (tags.isNotEmpty()) {
|
||||
//result_tag_holder?.visibility = VISIBLE
|
||||
//val isOnTv = isTrueTvSettings()
|
||||
|
||||
|
||||
/*for ((index, tag) in tags.withIndex()) {
|
||||
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
|
||||
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
|
||||
btt.text = tag
|
||||
btt.isFocusable = !isOnTv
|
||||
btt.isClickable = !isOnTv
|
||||
result_tag?.addView(viewBtt, index)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
//}
|
||||
}
|
||||
is Resource.Failure -> {
|
||||
result_error_text.text = storedData?.url?.plus("\n") + data.errorString
|
||||
|
|
|
@ -27,8 +27,10 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
|
@ -127,6 +129,8 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
down.nextFocusUpId = upper.id
|
||||
}
|
||||
|
||||
var selectSeason : String? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val apiName = arguments?.getString(API_NAME_BUNDLE) ?: return
|
||||
|
||||
|
@ -297,6 +301,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
observe(viewModel.selectedSeason) { text ->
|
||||
result_season_button.setText(text)
|
||||
|
||||
selectSeason = (if (text is Some.Success) text.value else null)?.asStringNull(result_season_button?.context)
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_season_button?.isVisible == true)
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
|
@ -366,17 +371,24 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.setOnClickListener { view ->
|
||||
|
||||
view?.context?.let { ctx ->
|
||||
val names = seasonList
|
||||
.mapNotNull { (text, r) ->
|
||||
r to (text?.asStringNull(ctx) ?: return@mapNotNull null)
|
||||
}
|
||||
|
||||
view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
index to name
|
||||
}) {
|
||||
activity?.showDialog(names.map { it.second },names.indexOfFirst { it.second == selectSeason },"",false,{}) { itemId->
|
||||
viewModel.changeSeason(names[itemId].first)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//view.popupMenuNoIconsAndNoStringRes(names.mapIndexed { index, (_, name) ->
|
||||
// index to name
|
||||
//}) {
|
||||
// viewModel.changeSeason(names[itemId].first)
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
@ -1967,7 +1972,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
): List<ExtractedTrailerData> =
|
||||
coroutineScope {
|
||||
var currentCount = 0
|
||||
return@coroutineScope loadResponse.trailers.apmap { trailerData ->
|
||||
return@coroutineScope loadResponse.trailers.amap { trailerData ->
|
||||
try {
|
||||
val links = arrayListOf<ExtractorLink>()
|
||||
val subs = arrayListOf<SubtitleFile>()
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.util.Log
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
||||
|
@ -197,7 +197,7 @@ class SyncViewModel : ViewModel() {
|
|||
/// modifies the current sync data, return null if you don't want to change it
|
||||
private fun modifyData(update: ((SyncAPI.SyncStatus) -> (SyncAPI.SyncStatus?))) =
|
||||
ioSafe {
|
||||
syncs.apmap { (prefix, id) ->
|
||||
syncs.amap { (prefix, id) ->
|
||||
repos.firstOrNull { it.idPrefix == prefix }?.let { repo ->
|
||||
if (repo.hasAccount()) {
|
||||
val result = repo.getStatus(id)
|
||||
|
|
|
@ -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"
|
||||
|
@ -112,83 +117,62 @@ class SearchFragment : Fragment() {
|
|||
var selectedSearchTypes = mutableListOf<TvType>()
|
||||
var selectedApis = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* Will filter all providers by preferred media and selectedSearchTypes.
|
||||
* If that results in no available providers then only filter
|
||||
* providers by preferred media
|
||||
**/
|
||||
fun search(query: String?) {
|
||||
if (query == null) return
|
||||
context?.getApiSettings()?.let { settings ->
|
||||
|
||||
context?.let { ctx ->
|
||||
val default = enumValues<TvType>().sorted().filter { it != TvType.NSFW }
|
||||
.map { it.ordinal.toString() }.toSet()
|
||||
val preferredTypes = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
.getStringSet(this.getString(R.string.prefer_media_type_key), default)
|
||||
?.mapNotNull { it.toIntOrNull() ?: return@mapNotNull null } ?: default
|
||||
|
||||
val settings = ctx.getApiSettings()
|
||||
|
||||
val notFilteredBySelectedTypes = selectedApis.filter { name ->
|
||||
settings.contains(name)
|
||||
}.map { name ->
|
||||
name to getApiFromNameNull(name)?.supportedTypes
|
||||
}.filter { (_, types) ->
|
||||
types?.any { preferredTypes.contains(it.ordinal) } == true
|
||||
}
|
||||
|
||||
searchViewModel.searchAndCancel(
|
||||
query = query,
|
||||
providersActive = selectedApis.filter { name ->
|
||||
settings.contains(name) && getApiFromNameNull(name)?.supportedTypes?.any {
|
||||
selectedSearchTypes.contains(
|
||||
it
|
||||
)
|
||||
} == true
|
||||
}.toSet()
|
||||
providersActive = notFilteredBySelectedTypes.filter { (_, types) ->
|
||||
types?.any { selectedSearchTypes.contains(it) } == true
|
||||
}.ifEmpty { notFilteredBySelectedTypes }.map { it.first }.toSet()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +223,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 +304,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 +317,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 +429,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 -> {
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
|||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.launchSafe
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
|
@ -108,9 +108,9 @@ class SearchViewModel : ViewModel() {
|
|||
|
||||
repos.filter { a ->
|
||||
(ignoreSettings || (providersActive.isEmpty() || providersActive.contains(a.name))) && (!isQuickSearch || a.hasQuickSearch)
|
||||
}.apmap { a -> // Parallel
|
||||
}.amap { a -> // Parallel
|
||||
val search = if (isQuickSearch) a.quickSearch(query) else a.search(query)
|
||||
if (currentSearchIndex != currentIndex) return@apmap
|
||||
if (currentSearchIndex != currentIndex) return@amap
|
||||
currentList.add(OnGoingSearch(a.name, search))
|
||||
_currentSearch.postValue(currentList)
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel
|
|||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.mvvm.Some
|
||||
import com.lagradost.cloudstream3.mvvm.debugAssert
|
||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
||||
|
@ -49,7 +49,7 @@ class ExtensionsViewModel : ViewModel() {
|
|||
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
||||
?: emptyArray()) + PREBUILT_REPOSITORIES
|
||||
|
||||
val onlinePlugins = urls.toList().apmap {
|
||||
val onlinePlugins = urls.toList().amap {
|
||||
RepositoryManager.getRepoPlugins(it.url)?.toList() ?: emptyList()
|
||||
}.flatten().distinctBy { it.second.url }
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.apmap
|
||||
import com.lagradost.cloudstream3.amap
|
||||
import com.lagradost.cloudstream3.mvvm.launchSafe
|
||||
import com.lagradost.cloudstream3.plugins.PluginManager
|
||||
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath
|
||||
|
@ -101,7 +101,7 @@ class PluginsViewModel : ViewModel() {
|
|||
Toast.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
}.apmap { (repo, metadata) ->
|
||||
}.amap { (repo, metadata) ->
|
||||
PluginManager.downloadAndLoadPlugin(
|
||||
activity,
|
||||
metadata.url,
|
||||
|
|
|
@ -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(),
|
||||
|
|
5
app/src/main/res/color/chip_color.xml
Normal file
5
app/src/main/res/color/chip_color.xml
Normal 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>
|
5
app/src/main/res/color/chip_color_text.xml
Normal file
5
app/src/main/res/color/chip_color_text.xml
Normal 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/colorOnPrimary" android:state_checked="true"/>
|
||||
<item android:color="?attr/textColor" android:state_checked="false"/>
|
||||
</selector>
|
9
app/src/main/res/drawable/home_alt.xml
Normal file
9
app/src/main/res/drawable/home_alt.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11.05,39H17.95V26.15H30.1V39H36.95V19.55L24,9.85L11.05,19.55ZM11.05,42.65Q9.5,42.65 8.45,41.6Q7.4,40.55 7.4,39V19.55Q7.4,18.7 7.775,17.925Q8.15,17.15 8.85,16.65L21.8,6.9Q22.3,6.55 22.875,6.375Q23.45,6.2 24,6.2Q24.6,6.2 25.15,6.375Q25.7,6.55 26.2,6.9L39.15,16.65Q39.85,17.15 40.225,17.925Q40.6,18.7 40.6,19.55V39Q40.6,40.55 39.55,41.6Q38.5,42.65 36.95,42.65H26.45V29.8H21.6V42.65ZM24,24.4Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:height="24dp" android:tint="?attr/white"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M24,38.35Q23.25,38.35 22.725,37.825Q22.2,37.3 22.2,36.5V25.8H11.45Q10.7,25.8 10.175,25.25Q9.65,24.7 9.65,23.95Q9.65,23.2 10.175,22.675Q10.7,22.15 11.45,22.15H22.2V11.4Q22.2,10.65 22.725,10.125Q23.25,9.6 24,9.6Q24.75,9.6 25.3,10.125Q25.85,10.65 25.85,11.4V22.15H36.55Q37.3,22.15 37.85,22.675Q38.4,23.2 38.4,23.95Q38.4,24.75 37.85,25.275Q37.3,25.8 36.55,25.8H25.85V36.5Q25.85,37.3 25.3,37.825Q24.75,38.35 24,38.35Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="?attr/white" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M22,39.35 L7.95,25.3Q7.65,25.05 7.525,24.725Q7.4,24.4 7.4,24Q7.4,23.65 7.525,23.325Q7.65,23 7.95,22.75L22.05,8.6Q22.6,8.1 23.325,8.125Q24.05,8.15 24.6,8.7Q25.1,9.25 25.1,10Q25.1,10.75 24.55,11.3L13.65,22.2H38.1Q38.85,22.2 39.375,22.725Q39.9,23.25 39.9,24Q39.9,24.8 39.375,25.325Q38.85,25.85 38.1,25.85H13.65L24.65,36.8Q25.15,37.35 25.125,38.075Q25.1,38.8 24.55,39.35Q24,39.9 23.25,39.9Q22.5,39.9 22,39.35Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:tint="#FFFFFF" android:viewportHeight="24"
|
||||
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M22.7,39.3Q22.15,38.75 22.175,38Q22.2,37.25 22.75,36.7L33.6,25.85H9.2Q8.45,25.85 7.925,25.325Q7.4,24.8 7.4,24Q7.4,23.25 7.925,22.725Q8.45,22.2 9.2,22.2H33.6L22.75,11.35Q22.2,10.8 22.175,10.025Q22.15,9.25 22.7,8.7Q23.25,8.15 24,8.15Q24.75,8.15 25.3,8.7L39.35,22.75Q39.65,23.05 39.8,23.35Q39.95,23.65 39.95,24Q39.95,24.35 39.8,24.675Q39.65,25 39.35,25.3L25.3,39.35Q24.75,39.9 24,39.875Q23.25,39.85 22.7,39.3Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11.75,41.85Q10.85,42.25 10.05,41.725Q9.25,41.2 9.25,40.25V8.5Q9.25,7.05 10.35,5.95Q11.45,4.85 12.9,4.85H35.1Q36.55,4.85 37.65,5.95Q38.75,7.05 38.75,8.5V40.25Q38.75,41.2 37.95,41.725Q37.15,42.25 36.25,41.85L24,36.65Z"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/white"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="24dp">
|
||||
<path
|
||||
android:fillColor="?white"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,9h8v10L8,19L8,9zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z" />
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12.95,42.8Q11.5,42.8 10.4,41.7Q9.3,40.6 9.3,39.15V10.4H8.75Q8,10.4 7.475,9.85Q6.95,9.3 6.95,8.55Q6.95,7.8 7.475,7.275Q8,6.75 8.75,6.75H17.35Q17.35,6 17.875,5.525Q18.4,5.05 19.15,5.05H28.9Q29.65,5.05 30.175,5.525Q30.7,6 30.7,6.75H39.3Q40.05,6.75 40.6,7.275Q41.15,7.8 41.15,8.55Q41.15,9.35 40.6,9.875Q40.05,10.4 39.3,10.4H38.75V39.15Q38.75,40.6 37.65,41.7Q36.55,42.8 35.1,42.8ZM12.95,10.4V39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15H35.1Q35.1,39.15 35.1,39.15Q35.1,39.15 35.1,39.15V10.4ZM17.85,33Q17.85,33.75 18.375,34.275Q18.9,34.8 19.65,34.8Q20.4,34.8 20.95,34.275Q21.5,33.75 21.5,33V16.5Q21.5,15.75 20.95,15.2Q20.4,14.65 19.65,14.65Q18.9,14.65 18.375,15.2Q17.85,15.75 17.85,16.5ZM26.6,33Q26.6,33.75 27.125,34.275Q27.65,34.8 28.4,34.8Q29.15,34.8 29.7,34.275Q30.25,33.75 30.25,33V16.5Q30.25,15.75 29.7,15.2Q29.15,14.65 28.4,14.65Q27.65,14.65 27.125,15.2Q26.6,15.75 26.6,16.5ZM12.95,10.4V39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15Q12.95,39.15 12.95,39.15V10.4Z"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M21.5,37.35Q20.7,37.35 20.175,36.825Q19.65,36.3 19.65,35.55Q19.65,34.8 20.175,34.25Q20.7,33.7 21.5,33.7H26.5Q27.25,33.7 27.8,34.25Q28.35,34.8 28.35,35.55Q28.35,36.3 27.8,36.825Q27.25,37.35 26.5,37.35ZM7.35,13.1Q6.55,13.1 6.025,12.575Q5.5,12.05 5.5,11.3Q5.5,10.55 6.025,10Q6.55,9.45 7.35,9.45H40.65Q41.4,9.45 41.95,10Q42.5,10.55 42.5,11.3Q42.5,12.05 41.95,12.575Q41.4,13.1 40.65,13.1ZM13.4,25.25Q12.6,25.25 12.075,24.7Q11.55,24.15 11.55,23.4Q11.55,22.65 12.075,22.125Q12.6,21.6 13.4,21.6H34.6Q35.35,21.6 35.875,22.125Q36.4,22.65 36.4,23.4Q36.4,24.2 35.875,24.725Q35.35,25.25 34.6,25.25Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M24,30.55Q23.65,30.55 23.325,30.4Q23,30.25 22.75,29.95L12.8,20.05Q12.3,19.55 12.325,18.725Q12.35,17.9 12.85,17.4Q13.45,16.8 14.175,16.875Q14.9,16.95 15.45,17.45L24,26.05L32.6,17.45Q33.1,16.95 33.9,16.9Q34.7,16.85 35.25,17.45Q35.8,17.95 35.75,18.75Q35.7,19.55 35.2,20.05L25.3,29.95Q25,30.25 24.675,30.4Q24.35,30.55 24,30.55Z"/>
|
||||
</vector>
|
|
@ -1,5 +1,8 @@
|
|||
<vector android:height="24dp" android:tint="?attr/white"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z"/>
|
||||
<vector android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.35,38.4Q6.55,38.4 6.025,37.85Q5.5,37.3 5.5,36.55Q5.5,35.8 6.025,35.275Q6.55,34.75 7.35,34.75H16.95Q17.7,34.75 18.225,35.275Q18.75,35.8 18.75,36.55Q18.75,37.35 18.225,37.875Q17.7,38.4 16.95,38.4ZM7.35,13.25Q6.55,13.25 6.025,12.7Q5.5,12.15 5.5,11.4Q5.5,10.65 6.025,10.125Q6.55,9.6 7.35,9.6H24.9Q25.65,9.6 26.2,10.125Q26.75,10.65 26.75,11.4Q26.75,12.2 26.2,12.725Q25.65,13.25 24.9,13.25ZM23.1,42.5Q22.3,42.5 21.775,41.975Q21.25,41.45 21.25,40.7V32.4Q21.25,31.65 21.8,31.1Q22.35,30.55 23.1,30.55Q23.85,30.55 24.375,31.1Q24.9,31.65 24.9,32.4V34.75H40.65Q41.4,34.75 41.95,35.275Q42.5,35.8 42.5,36.55Q42.5,37.35 41.95,37.875Q41.4,38.4 40.65,38.4H24.9V40.7Q24.9,41.45 24.375,41.975Q23.85,42.5 23.1,42.5ZM16.95,29.95Q16.15,29.95 15.625,29.425Q15.1,28.9 15.1,28.1V25.8H7.35Q6.55,25.8 6.025,25.275Q5.5,24.75 5.5,24Q5.5,23.25 6.025,22.7Q6.55,22.15 7.35,22.15H15.1V19.75Q15.1,19 15.65,18.475Q16.2,17.95 16.95,17.95Q17.7,17.95 18.225,18.475Q18.75,19 18.75,19.75V28.1Q18.75,28.9 18.225,29.425Q17.7,29.95 16.95,29.95ZM23.1,25.8Q22.3,25.8 21.775,25.275Q21.25,24.75 21.25,24Q21.25,23.25 21.775,22.7Q22.3,22.15 23.1,22.15H40.65Q41.4,22.15 41.95,22.7Q42.5,23.25 42.5,24Q42.5,24.75 41.95,25.275Q41.4,25.8 40.65,25.8ZM31.05,17.4Q30.3,17.4 29.775,16.875Q29.25,16.35 29.25,15.6V7.3Q29.25,6.55 29.775,6Q30.3,5.45 31.05,5.45Q31.8,5.45 32.35,6Q32.9,6.55 32.9,7.3V9.6H40.65Q41.4,9.6 41.95,10.125Q42.5,10.65 42.5,11.4Q42.5,12.2 41.95,12.725Q41.4,13.25 40.65,13.25H32.9V15.6Q32.9,16.35 32.35,16.875Q31.8,17.4 31.05,17.4Z"/>
|
||||
</vector>
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:name="vector"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="850"
|
||||
android:viewportHeight="850">
|
||||
<path
|
||||
android:name="path"
|
||||
android:pathData="M 425.2 172.91 C 363.2 172.91 303.676 197.566 259.836 241.406 C 215.996 285.246 191.34 344.77 191.34 406.77 C 191.34 468.77 215.996 528.294 259.836 572.134 C 303.676 615.974 363.2 640.63 425.2 640.63 C 487.2 640.63 546.724 615.974 590.564 572.134 C 634.404 528.294 659.06 468.77 659.06 406.77 C 659.06 344.77 634.404 285.246 590.564 241.406 C 546.724 197.566 487.2 172.91 425.2 172.91 Z"
|
||||
android:strokeColor="?attr/white"
|
||||
android:strokeWidth="60"
|
||||
android:strokeMiterLimit="10"/>
|
||||
<path
|
||||
android:name="path_1"
|
||||
android:pathData="M 599.86 562.28 L 719.87 682.29"
|
||||
android:strokeColor="?attr/white"
|
||||
android:strokeWidth="60"
|
||||
android:strokeMiterLimit="10"/>
|
||||
<vector android:name="vector"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="850"
|
||||
android:viewportHeight="850"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:name="path" android:pathData="M 425.2 172.91 C 363.2 172.91 303.676 197.566 259.836 241.406 C 215.996 285.246 191.34 344.77 191.34 406.77 C 191.34 468.77 215.996 528.294 259.836 572.134 C 303.676 615.974 363.2 640.63 425.2 640.63 C 487.2 640.63 546.724 615.974 590.564 572.134 C 634.404 528.294 659.06 468.77 659.06 406.77 C 659.06 344.77 634.404 285.246 590.564 241.406 C 546.724 197.566 487.2 172.91 425.2 172.91 Z" android:strokeColor="#ffffff" android:strokeWidth="60" android:strokeMiterLimit="10"/>
|
||||
<path android:name="path_1" android:pathData="M 583.631 547.663 L 583.624 547.67 C 587.388 543.906 592.078 541.198 597.22 539.82 C 602.363 538.442 607.778 538.442 612.92 539.82 C 618.063 541.198 622.752 543.906 626.517 547.67 L 719.049 640.202 C 722.813 643.966 725.521 648.656 726.899 653.798 C 728.277 658.941 728.277 664.356 726.899 669.498 C 725.521 674.641 722.813 679.331 719.049 683.095 L 719.056 683.088 C 713.37 688.774 705.65 691.971 697.609 691.971 C 689.568 691.971 681.849 688.774 676.163 683.088 L 583.631 590.556 C 577.945 584.87 574.747 577.15 574.747 569.109 C 574.747 561.068 577.945 553.349 583.631 547.663" android:fillColor="#ffffff" android:strokeWidth="1"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/settings_alt.xml
Normal file
9
app/src/main/res/drawable/settings_alt.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48"
|
||||
android:tint="?attr/white"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M27.6,44.8H20.4Q19.7,44.8 19.175,44.4Q18.65,44 18.6,43.3L17.8,38.3Q17,38 16.025,37.475Q15.05,36.95 14.35,36.35L9.75,38.45Q9.15,38.8 8.45,38.55Q7.75,38.3 7.4,37.65L3.8,31.15Q3.4,30.55 3.6,29.9Q3.8,29.25 4.35,28.85L8.5,25.75Q8.45,25.4 8.425,24.85Q8.4,24.3 8.4,23.9Q8.4,23.55 8.425,23.025Q8.45,22.5 8.5,22.1L4.35,19.1Q3.75,18.7 3.6,18.025Q3.45,17.35 3.8,16.75L7.4,10.3Q7.8,9.7 8.45,9.45Q9.1,9.2 9.75,9.5L14.25,11.6Q14.95,11.05 15.95,10.475Q16.95,9.9 17.8,9.6L18.6,4.65Q18.65,3.95 19.175,3.55Q19.7,3.15 20.4,3.15H27.6Q28.3,3.15 28.825,3.55Q29.35,3.95 29.45,4.65L30.2,9.6Q31.05,9.9 32.075,10.475Q33.1,11.05 33.75,11.6L38.25,9.5Q38.85,9.2 39.55,9.45Q40.25,9.7 40.6,10.3L44.2,16.7Q44.55,17.3 44.4,18Q44.25,18.7 43.7,19.1L39.45,22.05Q39.5,22.5 39.55,23.05Q39.6,23.6 39.6,24Q39.6,24.4 39.55,24.925Q39.5,25.45 39.5,25.85L43.65,28.85Q44.2,29.25 44.375,29.925Q44.55,30.6 44.2,31.2L40.6,37.7Q40.2,38.35 39.525,38.575Q38.85,38.8 38.25,38.45L33.65,36.35Q32.95,36.9 32,37.475Q31.05,38.05 30.25,38.3L29.45,43.3Q29.35,44 28.825,44.4Q28.3,44.8 27.6,44.8ZM23.95,31.15Q26.95,31.15 29.05,29.05Q31.15,26.95 31.15,23.95Q31.15,20.95 29.05,18.85Q26.95,16.75 23.95,16.75Q20.95,16.75 18.85,18.85Q16.75,20.95 16.75,23.95Q16.75,26.95 18.85,29.05Q20.95,31.15 23.95,31.15ZM23.9,27.5Q22.45,27.5 21.425,26.45Q20.4,25.4 20.4,23.95Q20.4,22.45 21.425,21.425Q22.45,20.4 23.95,20.4Q25.4,20.4 26.45,21.45Q27.5,22.5 27.5,23.95Q27.5,25.45 26.45,26.475Q25.4,27.5 23.9,27.5ZM24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95Q24,23.95 24,23.95ZM21.8,41.15H26.2L26.9,35.5Q28.6,35.1 30.1,34.25Q31.6,33.4 32.85,32.15L38.1,34.45L40.1,30.85L35.45,27.4Q35.65,26.55 35.8,25.7Q35.95,24.85 35.95,23.95Q35.95,23.05 35.825,22.225Q35.7,21.4 35.5,20.5L40.1,17.1L38.15,13.5L32.85,15.75Q31.7,14.45 30.2,13.525Q28.7,12.6 26.9,12.35L26.25,6.8H21.75L21.1,12.35Q19.3,12.75 17.775,13.625Q16.25,14.5 15.1,15.75L9.9,13.5L7.85,17.1L12.5,20.55Q12.3,21.45 12.175,22.275Q12.05,23.1 12.05,23.9Q12.05,24.75 12.175,25.6Q12.3,26.45 12.5,27.4L7.85,30.85L9.9,34.45L15.15,32.15Q16.45,33.45 17.95,34.3Q19.45,35.15 21.1,35.55Z"/>
|
||||
</vector>
|
7
app/src/main/res/drawable/storage_bar_left.xml
Normal file
7
app/src/main/res/drawable/storage_bar_left.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/white" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/storage_radius"
|
||||
android:topLeftRadius="@dimen/storage_radius" />
|
||||
</shape>
|
5
app/src/main/res/drawable/storage_bar_left_box.xml
Normal file
5
app/src/main/res/drawable/storage_bar_left_box.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/white" />
|
||||
<corners android:radius="@dimen/storage_radius" />
|
||||
</shape>
|
4
app/src/main/res/drawable/storage_bar_mid.xml
Normal file
4
app/src/main/res/drawable/storage_bar_mid.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/colorPrimary" />
|
||||
</shape>
|
5
app/src/main/res/drawable/storage_bar_mid_box.xml
Normal file
5
app/src/main/res/drawable/storage_bar_mid_box.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/colorPrimary" />
|
||||
<corners android:radius="@dimen/storage_radius" />
|
||||
</shape>
|
7
app/src/main/res/drawable/storage_bar_right.xml
Normal file
7
app/src/main/res/drawable/storage_bar_right.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/grayTextColor" />
|
||||
<corners
|
||||
android:bottomRightRadius="@dimen/storage_radius"
|
||||
android:topRightRadius="@dimen/storage_radius" />
|
||||
</shape>
|
5
app/src/main/res/drawable/storage_bar_right_box.xml
Normal file
5
app/src/main/res/drawable/storage_bar_right_box.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?attr/grayTextColor" />
|
||||
<corners android:radius="@dimen/storage_radius" />
|
||||
</shape>
|
|
@ -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>
|
|
@ -1,149 +1,158 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/download_root"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
tools:context=".ui.download.DownloadFragment">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/download_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.download.DownloadFragment">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
tools:layout_height="100dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
tools:layout_height="100dp">
|
||||
<!--
|
||||
For Scroll add to LinearLayout
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
-->
|
||||
<LinearLayout
|
||||
android:id="@+id/download_storage_appbar"
|
||||
tools:visibility="visible"
|
||||
android:visibility="gone"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:id="@+id/download_storage_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="10dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="@string/download_storage_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="@string/download_storage_text"
|
||||
android:textColor="?attr/textColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="12dp"
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:elevation="0dp"
|
||||
app:cardCornerRadius="@dimen/storage_radius"
|
||||
app:cardElevation="0dp"
|
||||
app:cardMaxElevation="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:layout_weight="0.5"
|
||||
<View
|
||||
android:id="@+id/download_used"
|
||||
android:background="?attr/white"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.5"
|
||||
android:background="@drawable/storage_bar_left" />
|
||||
|
||||
<View
|
||||
<View
|
||||
android:id="@+id/download_app"
|
||||
android:layout_weight="0.10"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.10"
|
||||
android:background="@drawable/storage_bar_mid" />
|
||||
|
||||
<View
|
||||
<View
|
||||
android:id="@+id/download_free"
|
||||
android:layout_weight="0.10"
|
||||
android:background="?attr/grayTextColor"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.10"
|
||||
android:background="@drawable/storage_bar_right" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
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">
|
||||
|
||||
<View
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/white"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp" />
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@drawable/storage_bar_left_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_used_txt"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="Used • 30.58GB"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/download_used_txt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="Used • 30.58GB" />
|
||||
|
||||
<View
|
||||
android:layout_margin="5dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp" />
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/storage_bar_mid_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_app_txt"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="App • 30.58GB"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/download_app_txt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="App • 30.58GB" />
|
||||
|
||||
<View
|
||||
android:layout_margin="5dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/grayTextColor"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp" />
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:background="@drawable/storage_bar_right_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/download_free_txt"
|
||||
android:textSize="12sp"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:text="Free • 30.58GB"
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:id="@+id/download_free_txt"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="?attr/textColor"
|
||||
android:textSize="12sp"
|
||||
tools:text="Free • 30.58GB" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:id="@+id/download_list"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:id="@+id/download_list"
|
||||
android:layout_width="match_parent"
|
||||
tools:listitem="@layout/download_header_episode"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:nextFocusLeft="@id/nav_rail_view"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:listitem="@layout/download_header_episode" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_no_downloads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
android:id="@+id/text_no_downloads"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!--
|
||||
<ProgressBar
|
||||
|
@ -155,24 +164,24 @@
|
|||
</ProgressBar>-->
|
||||
|
||||
<com.facebook.shimmer.ShimmerFrameLayout
|
||||
tools:visibility="gone"
|
||||
android:id="@+id/download_loading"
|
||||
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:orientation="vertical">
|
||||
android:id="@+id/download_loading"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
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"
|
||||
tools:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginStart="@dimen/loading_margin"
|
||||
android:layout_marginEnd="@dimen/loading_margin"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/loading_margin"
|
||||
android:layout_marginEnd="@dimen/loading_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/loading_downloads" />
|
||||
|
||||
|
@ -189,10 +198,10 @@
|
|||
</com.facebook.shimmer.ShimmerFrameLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
android:text="@string/stream"
|
||||
android:id="@+id/download_stream_button"
|
||||
app:icon="@drawable/netflix_play"
|
||||
style="@style/ExtendedFloatingActionButton"
|
||||
android:textColor="?attr/textColor"
|
||||
tools:ignore="ContentDescription" />
|
||||
android:id="@+id/download_stream_button"
|
||||
style="@style/ExtendedFloatingActionButton"
|
||||
android:text="@string/stream"
|
||||
android:textColor="?attr/textColor"
|
||||
app:icon="@drawable/netflix_play"
|
||||
tools:ignore="ContentDescription" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -11,12 +11,12 @@
|
|||
<include layout="@layout/standard_toolbar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:nextFocusUp="@id/settings_toolbar"
|
||||
android:nextFocusDown="@id/plugin_storage_appbar"
|
||||
android:id="@+id/repo_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:nextFocusUp="@id/settings_toolbar"
|
||||
android:nextFocusDown="@id/plugin_storage_appbar"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
app:layout_constraintBottom_toTopOf="@id/download_storage_appbar"
|
||||
|
@ -47,32 +47,32 @@
|
|||
android:text="@string/blank_repo_message"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- <com.google.android.material.button.MaterialButton-->
|
||||
<!-- android:id="@+id/list_repositories"-->
|
||||
<!-- style="@style/WhiteButton"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:nextFocusDown="@id/add_repo_button"-->
|
||||
<!-- android:text="@string/view_public_repositories_button" />-->
|
||||
<!-- <com.google.android.material.button.MaterialButton-->
|
||||
<!-- android:id="@+id/list_repositories"-->
|
||||
<!-- style="@style/WhiteButton"-->
|
||||
<!-- android:layout_width="wrap_content"-->
|
||||
<!-- android:nextFocusDown="@id/add_repo_button"-->
|
||||
<!-- android:text="@string/view_public_repositories_button" />-->
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:focusable="true"
|
||||
android:id="@+id/plugin_storage_appbar"
|
||||
android:nextFocusRight="@id/add_repo_button_imageview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?attr/primaryGrayBackground"
|
||||
android:focusable="true"
|
||||
android:foreground="@drawable/outline_drawable"
|
||||
android:nextFocusRight="@id/add_repo_button_imageview"
|
||||
android:orientation="horizontal"
|
||||
android:padding="10dp"
|
||||
android:visibility="visible"
|
||||
|
||||
android:orientation="horizontal">
|
||||
android:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -83,33 +83,42 @@
|
|||
android:text="@string/extensions"
|
||||
android:textColor="?attr/textColor" />
|
||||
|
||||
<LinearLayout
|
||||
<androidx.cardview.widget.CardView
|
||||
app:cardElevation="0dp"
|
||||
android:elevation="0dp"
|
||||
app:cardMaxElevation="0dp"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:orientation="horizontal">
|
||||
app:cardCornerRadius="@dimen/storage_radius">
|
||||
|
||||
<View
|
||||
android:id="@+id/plugin_download"
|
||||
android:layout_width="0dp"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/white"
|
||||
tools:layout_weight="0.5" />
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:id="@+id/plugin_disabled"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorPrimary"
|
||||
tools:layout_weight="0.10" />
|
||||
<View
|
||||
android:id="@+id/plugin_download"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/storage_bar_left"
|
||||
tools:layout_weight="0.5" />
|
||||
|
||||
<View
|
||||
android:id="@+id/plugin_not_downloaded"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/grayTextColor"
|
||||
tools:layout_weight="0.10" />
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:id="@+id/plugin_disabled"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/storage_bar_mid"
|
||||
tools:layout_weight="0.10" />
|
||||
|
||||
<View
|
||||
android:id="@+id/plugin_not_downloaded"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/storage_bar_right"
|
||||
tools:layout_weight="0.10" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
@ -123,7 +132,7 @@
|
|||
android:layout_marginTop="5dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="?attr/white" />
|
||||
android:background="@drawable/storage_bar_left_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/plugin_download_txt"
|
||||
|
@ -139,7 +148,7 @@
|
|||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:background="?attr/colorPrimary" />
|
||||
android:background="@drawable/storage_bar_mid_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/plugin_disabled_txt"
|
||||
|
@ -155,7 +164,7 @@
|
|||
android:layout_height="10dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="5dp"
|
||||
android:background="?attr/grayTextColor" />
|
||||
android:background="@drawable/storage_bar_right_box" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/plugin_not_downloaded_txt"
|
||||
|
@ -167,34 +176,35 @@
|
|||
tools:text="Not downloaded 3" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/add_repo_button_imageview_holder"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp">
|
||||
|
||||
<TextView
|
||||
android:textColor="?attr/textColor"
|
||||
android:layout_gravity="center"
|
||||
|
||||
android:text="@string/add_repository"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
<ImageView
|
||||
android:background="@drawable/outline_drawable"
|
||||
android:nextFocusLeft="@id/plugin_storage_appbar"
|
||||
android:layout_gravity="center"
|
||||
android:focusable="true"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/add_repository"
|
||||
android:textColor="?attr/textColor" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/add_repo_button_imageview"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
android:layout_gravity="center"
|
||||
|
||||
android:background="@drawable/outline_drawable"
|
||||
android:contentDescription="@string/add_repository"
|
||||
app:tint="?attr/textColor">
|
||||
</ImageView>
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@id/plugin_storage_appbar"
|
||||
android:src="@drawable/ic_baseline_add_24"
|
||||
app:tint="?attr/textColor" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -359,52 +359,60 @@
|
|||
android:fadingEdge="horizontal"
|
||||
android:requiresFadingEdge="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
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"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/home_type_watching_btt"
|
||||
style="@style/ChipFilled"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
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"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/home_plan_to_watch_btt"
|
||||
style="@style/ChipFilled"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
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"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/home_type_on_hold_btt"
|
||||
style="@style/ChipFilled"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
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"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/home_type_dropped_btt"
|
||||
style="@style/ChipFilled"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
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"
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/home_type_completed_btt"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
android:id="@+id/home_type_completed_btt"
|
||||
android:text="@string/type_completed"
|
||||
style="@style/RoundedSelectableButton" />
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
style="@style/ChipFilled"
|
||||
android:nextFocusLeft="@id/home_type_dropped_btt"
|
||||
android:text="@string/type_completed" />
|
||||
</com.google.android.material.chip.ChipGroup> </HorizontalScrollView>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:paddingHorizontal="5dp"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -487,11 +487,14 @@
|
|||
android:textStyle="normal"
|
||||
android:visibility="gone" />
|
||||
|
||||
|
||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/result_tag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<!--<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
android:id="@+id/result_tag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content" />-->
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_coming_soon"
|
||||
|
|
|
@ -354,7 +354,7 @@
|
|||
android:visibility="gone" />
|
||||
|
||||
|
||||
<com.lagradost.cloudstream3.widget.FlowLayout
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/result_tag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
68
app/src/main/res/layout/tvtypes_chips.xml
Normal file
68
app/src/main/res/layout/tvtypes_chips.xml
Normal 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>
|
10
app/src/main/res/layout/tvtypes_chips_scroll.xml
Normal file
10
app/src/main/res/layout/tvtypes_chips_scroll.xml
Normal 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>
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<item
|
||||
android:id="@+id/navigation_home"
|
||||
android:icon="@drawable/ic_outline_home_24"
|
||||
android:icon="@drawable/home_alt"
|
||||
android:title="@string/title_home"/>
|
||||
<item
|
||||
android:id="@+id/navigation_search"
|
||||
|
@ -15,6 +15,6 @@
|
|||
android:title="@string/title_downloads"/>
|
||||
<item
|
||||
android:id="@+id/navigation_settings"
|
||||
android:icon="@drawable/ic_outline_settings_24"
|
||||
android:icon="@drawable/settings_alt"
|
||||
android:title="@string/title_settings"/>
|
||||
</menu>
|
533
app/src/main/res/values-bg/strings.xml
Normal file
533
app/src/main/res/values-bg/strings.xml
Normal 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 -> %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>
|
|
@ -22,6 +22,9 @@
|
|||
<color name="white">#FFF</color>
|
||||
<color name="black">#000</color>
|
||||
|
||||
<color name="whiteText">#FFF</color>
|
||||
<color name="blackText">#000</color>
|
||||
|
||||
<color name="dubColor">#3d50fa</color> <!--3b65f5 f18c82 8294F1-->
|
||||
<color name="amoledModeLight">#121213</color>
|
||||
|
||||
|
|
|
@ -14,4 +14,7 @@
|
|||
<dimen name="loading_margin">15dp</dimen>
|
||||
|
||||
<integer name="loading_time">2000</integer>
|
||||
|
||||
<dimen name="storage_radius">3dp</dimen>
|
||||
|
||||
</resources>
|
|
@ -56,6 +56,7 @@
|
|||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="textColor">@color/textColor</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<item name="grayTextColor">@color/grayTextColor</item>
|
||||
<item name="primaryGrayBackground">@color/primaryGrayBackground</item>
|
||||
<item name="primaryBlackBackground">@color/primaryBlackBackground</item>
|
||||
|
@ -75,6 +76,13 @@
|
|||
<item name="primaryGrayBackground">@color/amoledModeLight</item>
|
||||
</style>
|
||||
|
||||
<style name="ChipFilled" parent="@style/Widget.Material3.Chip.Filter">
|
||||
<item name="chipBackgroundColor">@color/chip_color</item>
|
||||
<item name="chipStrokeColor">@color/transparent</item>
|
||||
<item name="textColor">@color/chip_color_text</item>
|
||||
<item name="android:textColor">@color/chip_color_text</item>
|
||||
</style>
|
||||
|
||||
<style name="AmoledMode">
|
||||
<item name="primaryGrayBackground">@color/black</item>
|
||||
<item name="primaryBlackBackground">@color/black</item>
|
||||
|
@ -83,6 +91,7 @@
|
|||
<item name="textColor">@color/textColor</item>
|
||||
<item name="grayTextColor">@color/grayTextColor</item>
|
||||
<item name="white">@color/white</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
</style>
|
||||
|
||||
<style name="LightMode">
|
||||
|
@ -93,6 +102,7 @@
|
|||
<item name="textColor">@color/lightTextColor</item>
|
||||
<item name="grayTextColor">@color/lightGrayTextColor</item>
|
||||
<item name="white">#000</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
</style>
|
||||
|
||||
<style name="MonetMode">
|
||||
|
@ -103,6 +113,7 @@
|
|||
<item name="textColor">@color/material_dynamic_neutral90</item>
|
||||
<item name="grayTextColor">@color/material_dynamic_neutral60</item>
|
||||
<item name="white">@color/material_dynamic_neutral90</item>
|
||||
<item name="colorOnPrimary">@color/material_on_primary_emphasis_medium</item>
|
||||
</style>
|
||||
|
||||
<style name="OverlayPrimaryColorNormal">
|
||||
|
@ -110,7 +121,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="colorOnPrimary">@color/colorAccent</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
@ -140,7 +151,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryBlue</item>
|
||||
<item name="colorPrimaryDark">#4855A2</item>
|
||||
<item name="colorAccent">#5A6BCB</item>
|
||||
<item name="colorOnPrimary">#5A6BCB</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryBlue</item>
|
||||
</style>
|
||||
|
@ -150,7 +161,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryPurple</item>
|
||||
<item name="colorPrimaryDark">#4704A3</item>
|
||||
<item name="colorAccent">#7125DB</item>
|
||||
<item name="colorOnPrimary">#7125DB</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryPurple</item>
|
||||
</style>
|
||||
|
||||
|
@ -159,7 +170,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryGreen</item>
|
||||
<item name="colorPrimaryDark">#007363</item>
|
||||
<item name="colorAccent">#39C1AE</item>
|
||||
<item name="colorOnPrimary">#39C1AE</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryGreen</item>
|
||||
</style>
|
||||
|
||||
|
@ -168,7 +179,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryGreenApple</item>
|
||||
<item name="colorPrimaryDark">#319B5A</item>
|
||||
<item name="colorAccent">#51C57E</item>
|
||||
<item name="colorOnPrimary">#51C57E</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryGreenApple</item>
|
||||
</style>
|
||||
|
||||
|
@ -177,7 +188,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryRed</item>
|
||||
<item name="colorPrimaryDark">#B62B2B</item>
|
||||
<item name="colorAccent">@color/colorPrimaryRed</item> <!--#F53B3B-->
|
||||
<item name="colorOnPrimary">@color/colorPrimaryRed</item> <!--#EC3838-->
|
||||
<item name="colorOnPrimary">@color/whiteText</item> <!--#EC3838-->
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryRed</item>
|
||||
</style>
|
||||
|
@ -187,7 +198,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryBanana</item>
|
||||
<item name="colorPrimaryDark">#9B7D31</item>
|
||||
<item name="colorAccent">#C5B251</item>
|
||||
<item name="colorOnPrimary">#C5A851</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryBanana</item>
|
||||
</style>
|
||||
|
||||
|
@ -196,7 +207,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryParty</item>
|
||||
<item name="colorPrimaryDark">#C1495B</item>
|
||||
<item name="colorAccent">#FD798C</item>
|
||||
<item name="colorOnPrimary">#BF5968</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryParty</item>
|
||||
</style>
|
||||
|
||||
|
@ -205,7 +216,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryPink</item>
|
||||
<item name="colorPrimaryDark">#DD1280</item>
|
||||
<item name="colorAccent">#FF4DAE</item>
|
||||
<item name="colorOnPrimary">#DD1280</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<item name="android:colorAccent">@color/colorPrimaryPink</item>
|
||||
</style>
|
||||
|
||||
|
@ -214,7 +225,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryCarnationPink</item>
|
||||
<item name="colorPrimaryDark">#83366f</item>
|
||||
<item name="colorAccent">#BD5DA5</item>
|
||||
<item name="colorOnPrimary">#BD5DA5</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryCarnationPink</item>
|
||||
</style>
|
||||
|
@ -224,7 +235,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryMaroon</item>
|
||||
<item name="colorPrimaryDark">#370C0C</item>
|
||||
<item name="colorAccent">#451010</item>
|
||||
<item name="colorOnPrimary">#451010</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryMaroon</item>
|
||||
</style>
|
||||
|
@ -234,7 +245,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryDarkGreen</item>
|
||||
<item name="colorPrimaryDark">#003d00</item>
|
||||
<item name="colorAccent">#004500</item>
|
||||
<item name="colorOnPrimary">#004500</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryDarkGreen</item>
|
||||
</style>
|
||||
|
@ -244,7 +255,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryNavyBlue</item>
|
||||
<item name="colorPrimaryDark">#000073</item>
|
||||
<item name="colorAccent">#000080</item>
|
||||
<item name="colorOnPrimary">#000080</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryNavyBlue</item>
|
||||
</style>
|
||||
|
@ -254,7 +265,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryGrey</item>
|
||||
<item name="colorPrimaryDark">#484848</item>
|
||||
<item name="colorAccent">#515151</item>
|
||||
<item name="colorOnPrimary">#515151</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryGrey</item>
|
||||
</style>
|
||||
|
@ -264,7 +275,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryWhite</item>
|
||||
<item name="colorPrimaryDark">#CCCCCC</item>
|
||||
<item name="colorAccent">#FFFFFF</item>
|
||||
<item name="colorOnPrimary">#FFFFFF</item>
|
||||
<item name="colorOnPrimary">@color/blackText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryWhite</item>
|
||||
</style>
|
||||
|
@ -274,7 +285,7 @@
|
|||
<item name="android:colorPrimary">@color/colorPrimaryBrown</item>
|
||||
<item name="colorPrimaryDark">#582700</item>
|
||||
<item name="colorAccent">#622C00</item>
|
||||
<item name="colorOnPrimary">#622C00</item>
|
||||
<item name="colorOnPrimary">@color/whiteText</item>
|
||||
<!-- Needed for leanback fuckery -->
|
||||
<item name="android:colorAccent">@color/colorPrimaryBrown</item>
|
||||
</style>
|
||||
|
|
|
@ -162,7 +162,7 @@ class ProviderTests {
|
|||
// @Test
|
||||
// fun providerCorrectHomepage() {
|
||||
// runBlocking {
|
||||
// getAllProviders().apmap { api ->
|
||||
// getAllProviders().amap { api ->
|
||||
// if (api.hasMainPage) {
|
||||
// try {
|
||||
// val homepage = api.getMainPage()
|
||||
|
@ -197,7 +197,7 @@ class ProviderTests {
|
|||
// suspend fun providerCorrect() {
|
||||
// val invalidProvider = ArrayList<Pair<MainAPI, Exception?>>()
|
||||
// val providers = getAllProviders()
|
||||
// providers.apmap { api ->
|
||||
// providers.amap { api ->
|
||||
// try {
|
||||
// println("Trying $api")
|
||||
// if (testSingleProviderApi(api)) {
|
||||
|
|
27
build.gradle
27
build.gradle
|
@ -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
26
build.gradle.kts
Normal 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)
|
||||
}
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
include ':app'
|
||||
rootProject.name = "CloudStream"
|
3
settings.gradle.kts
Normal file
3
settings.gradle.kts
Normal file
|
@ -0,0 +1,3 @@
|
|||
rootProject.name = "CloudStream"
|
||||
|
||||
include(":app")
|
Loading…
Add table
Add a link
Reference in a new issue