diff --git a/.github/workflows/build_to_archive.yml b/.github/workflows/build_to_archive.yml index 3b7aa9ae..e84bb08b 100644 --- a/.github/workflows/build_to_archive.yml +++ b/.github/workflows/build_to_archive.yml @@ -19,21 +19,21 @@ jobs: steps: - name: Generate access token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} repository: "recloudstream/secrets" - name: Generate access token (archive) id: generate_archive_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} repository: "recloudstream/cloudstream-archive" - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt' @@ -58,7 +58,7 @@ jobs: SIGNING_STORE_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }} SIMKL_CLIENT_ID: ${{ secrets.SIMKL_CLIENT_ID }} SIMKL_CLIENT_SECRET: ${{ secrets.SIMKL_CLIENT_SECRET }} - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: "recloudstream/cloudstream-archive" token: ${{ steps.generate_archive_token.outputs.token }} diff --git a/.github/workflows/generate_dokka.yml b/.github/workflows/generate_dokka.yml index abeee0b2..96e61644 100644 --- a/.github/workflows/generate_dokka.yml +++ b/.github/workflows/generate_dokka.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Generate access token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} @@ -43,12 +43,13 @@ jobs: rm -rf "./-cloudstream" - name: Setup JDK 17 - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: 17 + distribution: 'adopt' - name: Setup Android SDK - uses: android-actions/setup-android@v2 + uses: android-actions/setup-android@v3 - name: Generate Dokka run: | diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml index 108cec82..88ab3656 100644 --- a/.github/workflows/issue_action.yml +++ b/.github/workflows/issue_action.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Generate access token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} @@ -27,7 +27,7 @@ jobs: comment-body: '${index}. ${similarity} #${number}' - name: Label if possible duplicate if: steps.similarity.outputs.similar-issues-found =='true' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{ steps.generate_token.outputs.token }} script: | @@ -37,7 +37,7 @@ jobs: repo: context.repo.repo, labels: ["possible duplicate"] }) - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Automatically close issues that dont follow the issue template uses: lucasbento/auto-close-issues@v1.0.2 with: @@ -68,7 +68,7 @@ jobs: Found provider name: `${{ steps.provider_check.outputs.name }}` - name: Label if mentions provider if: steps.provider_check.outputs.name != 'none' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{ steps.generate_token.outputs.token }} script: | diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 44eb6402..f35cd58c 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -18,14 +18,14 @@ jobs: steps: - name: Generate access token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} repository: "recloudstream/secrets" - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt' diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b6177710..7f6dd412 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -6,9 +6,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v2 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt' @@ -17,7 +17,7 @@ jobs: - name: Run Gradle run: ./gradlew assemblePrereleaseDebug - name: Upload Artifact - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: pull-request-build path: "app/build/outputs/apk/prerelease/debug/*.apk" diff --git a/.github/workflows/update_locales.yml b/.github/workflows/update_locales.yml index 628e9bc9..ce140e55 100644 --- a/.github/workflows/update_locales.yml +++ b/.github/workflows/update_locales.yml @@ -18,12 +18,12 @@ jobs: steps: - name: Generate access token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.GH_APP_ID }} private_key: ${{ secrets.GH_APP_KEY }} repository: "recloudstream/cloudstream" - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: token: ${{ steps.generate_token.outputs.token }} - name: Install dependencies diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bae407fa..9ecae616 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -60,7 +60,7 @@ android { targetSdk = 33 /* Android 14 is Fu*ked ^ https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading*/ versionCode = 63 - versionName = "4.3.0" + versionName = "4.3.1" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "") diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index cf2e1cf0..a4edec33 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -9,7 +9,7 @@ import androidx.preference.PreferenceManager import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.fasterxml.jackson.module.kotlin.kotlinModule import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi @@ -34,7 +34,7 @@ const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" //val baseHeader = mapOf("User-Agent" to USER_AGENT) -val mapper = JsonMapper.builder().addModule(KotlinModule()) +val mapper = JsonMapper.builder().addModule(kotlinModule()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!! /** diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 2819ab98..71a24ced 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -77,6 +77,7 @@ import com.lagradost.cloudstream3.databinding.BottomResultviewPreviewBinding import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observeNullable import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.plugins.PluginManager @@ -91,7 +92,9 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths +import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.ui.APIRepository +import com.lagradost.cloudstream3.ui.SyncWatchType import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.home.HomeViewModel @@ -101,6 +104,7 @@ import com.lagradost.cloudstream3.ui.player.LinkGenerator import com.lagradost.cloudstream3.ui.result.LinearListLayout import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST +import com.lagradost.cloudstream3.ui.result.SyncViewModel import com.lagradost.cloudstream3.ui.result.setImage import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.txt @@ -439,13 +443,30 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } var lastPopup: SearchResponse? = null - fun loadPopup(result: SearchResponse) { + fun loadPopup(result: SearchResponse, load : Boolean = true) { lastPopup = result - viewModel.load( - this, result.url, result.apiName, false, if (getApiDubstatusSettings() - .contains(DubStatus.Dubbed) - ) DubStatus.Dubbed else DubStatus.Subbed, null - ) + val syncName = syncViewModel.syncName(result.apiName) + + // based on apiName we decide on if it is a local list or not, this is because + // we want to show a bit of extra UI to sync apis + if (result is SyncAPI.LibraryItem && syncName != null) { + isLocalList = false + syncViewModel.setSync(syncName, result.syncId) + syncViewModel.updateMetaAndUser() + } else { + isLocalList = true + syncViewModel.clear() + } + + if (load) { + viewModel.load( + this, result.url, result.apiName, false, if (getApiDubstatusSettings() + .contains(DubStatus.Dubbed) + ) DubStatus.Dubbed else DubStatus.Subbed, null + ) + }else { + viewModel.loadSmall(this,result) + } } override fun onColorSelected(dialogId: Int, color: Int) { @@ -733,10 +754,14 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } lateinit var viewModel: ResultViewModel2 - + lateinit var syncViewModel : SyncViewModel + /** kinda dirty, however it signals that we should use the watch status as sync or not*/ + var isLocalList : Boolean = false override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { viewModel = ViewModelProvider(this)[ResultViewModel2::class.java] + syncViewModel = + ViewModelProvider(this)[SyncViewModel::class.java] return super.onCreateView(name, context, attrs) } @@ -1234,6 +1259,48 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { builder.show().setDefaultFocus() } + + fun setUserData(status : Resource?) { + if (isLocalList) return + bottomPreviewBinding?.apply { + when (status) { + is Resource.Success -> { + resultviewPreviewBookmark.isEnabled = true + resultviewPreviewBookmark.setText(status.value.status.stringRes) + resultviewPreviewBookmark.setIconResource(status.value.status.iconRes) + } + + is Resource.Failure -> { + resultviewPreviewBookmark.isEnabled = false + resultviewPreviewBookmark.setIconResource(R.drawable.ic_baseline_bookmark_border_24) + resultviewPreviewBookmark.text = status.errorString + } + + else -> { + resultviewPreviewBookmark.isEnabled = false + resultviewPreviewBookmark.setIconResource(R.drawable.ic_baseline_bookmark_border_24) + resultviewPreviewBookmark.setText(R.string.loading) + } + } + } + } + + fun setWatchStatus(state : WatchType?) { + if (!isLocalList || state == null) return + + bottomPreviewBinding?.resultviewPreviewBookmark?.apply { + setIconResource(state.iconRes) + setText(state.stringRes) + } + } + + observe(viewModel.watchStatus) { state -> + setWatchStatus(state) + } + observe(syncViewModel.userData) { status -> + setUserData(status) + } + observeNullable(viewModel.page) { resource -> if (resource == null) { hidePreviewPopupDialog() @@ -1273,17 +1340,37 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { d.posterImage ?: d.posterBackgroundImage ) - resultviewPreviewPoster.setOnClickListener { - //viewModel.updateWatchStatus(WatchType.PLANTOWATCH) - val value = viewModel.watchStatus.value ?: WatchType.NONE + setUserData(syncViewModel.userData.value) + setWatchStatus(viewModel.watchStatus.value) - this@MainActivity.showBottomDialog( - WatchType.values().map { getString(it.stringRes) }.toList(), - value.ordinal, - this@MainActivity.getString(R.string.action_add_to_bookmarks), - showApply = false, - {}) { - viewModel.updateWatchStatus(WatchType.values()[it], this@MainActivity) + resultviewPreviewBookmark.setOnClickListener { + //viewModel.updateWatchStatus(WatchType.PLANTOWATCH) + if (isLocalList) { + val value = viewModel.watchStatus.value ?: WatchType.NONE + + this@MainActivity.showBottomDialog( + WatchType.values().map { getString(it.stringRes) }.toList(), + value.ordinal, + this@MainActivity.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + viewModel.updateWatchStatus( + WatchType.values()[it], + this@MainActivity + ) + } + } else { + val value = (syncViewModel.userData.value as? Resource.Success)?.value?.status ?: SyncWatchType.NONE + + this@MainActivity.showBottomDialog( + SyncWatchType.values().map { getString(it.stringRes) }.toList(), + value.ordinal, + this@MainActivity.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + syncViewModel.setStatus(SyncWatchType.values()[it].internalId) + syncViewModel.publishUserData() + } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/ContentXExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/ContentXExtractor.kt new file mode 100644 index 00000000..61943b70 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/ContentXExtractor.kt @@ -0,0 +1,54 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class ContentX : ExtractorApi() { + override val name = "ContentX" + override val mainUrl = "https://contentx.me" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + Log.d("Kekik_${this.name}", "url » ${url}") + + val i_source = app.get(url, referer=ext_ref).text + val i_extract = Regex("""window\.openPlayer\('([^']+)'""").find(i_source)!!.groups[1]?.value ?: throw ErrorLoadingException("i_extract is null") + + val vid_source = app.get("https://contentx.me/source2.php?v=${i_extract}", referer=ext_ref).text + val vid_extract = Regex("""file\":\"([^\"]+)""").find(vid_source)!!.groups[1]?.value ?: throw ErrorLoadingException("vid_extract is null") + val m3u_link = vid_extract.replace("\\", "") + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = m3u_link, + referer = url, + quality = Qualities.Unknown.value, + isM3u8 = true + ) + ) + + val i_dublaj = Regex(""",\"([^']+)\",\"Türkçe""").find(i_source)!!.groups[1]?.value + if (i_dublaj != null) { + val dublaj_source = app.get("https://contentx.me/source2.php?v=${i_dublaj}", referer=ext_ref).text + val dublaj_extract = Regex("""file\":\"([^\"]+)""").find(dublaj_source)!!.groups[1]?.value ?: throw ErrorLoadingException("dublaj_extract is null") + val dublaj_link = dublaj_extract.replace("\\", "") + + callback.invoke( + ExtractorLink( + source = "${this.name} Türkçe Dublaj", + name = "${this.name} Türkçe Dublaj", + url = dublaj_link, + referer = url, + quality = Qualities.Unknown.value, + isM3u8 = true + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt new file mode 100644 index 00000000..03586386 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDMomPlayerExtractor.kt @@ -0,0 +1,71 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.extractors.helper.AesHelper +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue + +open class HDMomPlayer : ExtractorApi() { + override val name = "HDMomPlayer" + override val mainUrl = "https://hdmomplayer.com" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val m3u_link:String? + val ext_ref = referer ?: "" + val i_source = app.get(url, referer=ext_ref).text + + val bePlayer = Regex("""bePlayer\('([^']+)',\s*'(\{[^\}]+\})'\);""").find(i_source)?.groupValues + if (bePlayer != null) { + val bePlayerPass = bePlayer.get(1) + val bePlayerData = bePlayer.get(2) + val encrypted = AesHelper.cryptoAESHandler(bePlayerData, bePlayerPass.toByteArray(), false)?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") + Log.d("Kekik_${this.name}", "encrypted » ${encrypted}") + + m3u_link = Regex("""video_location\":\"([^\"]+)""").find(encrypted)?.groupValues?.get(1) + } else { + m3u_link = Regex("""file:\"([^\"]+)""").find(i_source)?.groupValues?.get(1) + + val track_str = Regex("""tracks:\[([^\]]+)""").find(i_source)?.groupValues?.get(1) + if (track_str != null) { + val tracks:List = jacksonObjectMapper().readValue("[${track_str}]") + + for (track in tracks) { + if (track.file == null || track.label == null) continue + if (track.label.contains("Forced")) continue + + subtitleCallback.invoke( + SubtitleFile( + lang = track.label, + url = fixUrl(mainUrl + track.file) + ) + ) + } + } + } + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = m3u_link ?: throw ErrorLoadingException("m3u link not found"), + referer = url, + quality = Qualities.Unknown.value, + isM3u8 = true + ) + ) + } + + data class Track( + @JsonProperty("file") val file: String?, + @JsonProperty("label") val label: String?, + @JsonProperty("kind") val kind: String?, + @JsonProperty("language") val language: String?, + @JsonProperty("default") val default: String? + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt new file mode 100644 index 00000000..14333d35 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDPlayerSystemExtractor.kt @@ -0,0 +1,59 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class HDPlayerSystem : ExtractorApi() { + override val name = "HDPlayerSystem" + override val mainUrl = "https://hdplayersystem.live" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val vid_id = if (url.contains("video/")) { + url.substringAfter("video/") + } else { + url.substringAfter("?data=") + } + val post_url = "${mainUrl}/player/index.php?data=${vid_id}&do=getVideo" + Log.d("Kekik_${this.name}", "post_url » ${post_url}") + + val response = app.post( + post_url, + data = mapOf( + "hash" to vid_id, + "r" to ext_ref + ), + referer = ext_ref, + headers = mapOf( + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest" + ) + ) + + val video_response = response.parsedSafe() ?: throw ErrorLoadingException("failed to parse response") + val m3u_link = video_response.securedLink + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = m3u_link, + referer = ext_ref, + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } + + data class SystemResponse( + @JsonProperty("hls") val hls: String, + @JsonProperty("videoImage") val videoImage: String? = null, + @JsonProperty("videoSource") val videoSource: String, + @JsonProperty("securedLink") val securedLink: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/HDStreamAbleExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDStreamAbleExtractor.kt new file mode 100644 index 00000000..214cd2d4 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/HDStreamAbleExtractor.kt @@ -0,0 +1,8 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +class HDStreamAble : PeaceMakerst() { + override var name = "HDStreamAble" + override var mainUrl = "https://hdstreamable.com" +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt new file mode 100644 index 00000000..4a77cbf3 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt @@ -0,0 +1,8 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +class Hotlinger : ContentX() { + override var name = "Hotlinger" + override var mainUrl = "https://hotlinger.com" +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt new file mode 100644 index 00000000..766c7762 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/MailRuExtractor.kt @@ -0,0 +1,54 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class MailRu : ExtractorApi() { + override val name = "MailRu" + override val mainUrl = "https://my.mail.ru" + override val requiresReferer = false + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + Log.d("Kekik_${this.name}", "url » ${url}") + + val vid_id = url.substringAfter("video/embed/").trim() + val video_req = app.get("${mainUrl}/+/video/meta/${vid_id}", referer=url) + val video_key = video_req.cookies["video_key"].toString() + Log.d("Kekik_${this.name}", "video_key » ${video_key}") + + val video_data = AppUtils.tryParseJson(video_req.text) ?: throw ErrorLoadingException("Video not found") + + for (video in video_data.videos) { + Log.d("Kekik_${this.name}", "video » ${video}") + + val video_url = if (video.url.startsWith("//")) "https:${video.url}" else video.url + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = video_url, + referer = url, + headers = mapOf("Cookie" to "video_key=${video_key}"), + quality = getQualityFromName(video.key), + isM3u8 = false + ) + ) + } + } + + data class MailRuData( + @JsonProperty("provider") val provider: String, + @JsonProperty("videos") val videos: List + ) + + data class MailRuVideoData( + @JsonProperty("url") val url: String, + @JsonProperty("key") val key: String + ) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt new file mode 100644 index 00000000..46f6ad0f --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/OdnoklassnikiExtractor.kt @@ -0,0 +1,61 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class Odnoklassniki : ExtractorApi() { + override val name = "Odnoklassniki" + override val mainUrl = "https://odnoklassniki.ru" + override val requiresReferer = false + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + Log.d("Kekik_${this.name}", "url » ${url}") + + val user_agent = mapOf("User-Agent" to "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36") + + val video_req = app.get(url, headers=user_agent).text.replace("\\"", "\"").replace("\\\\", "\\") + .replace(Regex("\\\\u([0-9A-Fa-f]{4})")) { matchResult -> + Integer.parseInt(matchResult.groupValues[1], 16).toChar().toString() + } + val videos_str = Regex("""\"videos\":(\[[^\]]*\])""").find(video_req)?.groupValues?.get(1) ?: throw ErrorLoadingException("Video not found") + val videos = AppUtils.tryParseJson>(videos_str) ?: throw ErrorLoadingException("Video not found") + + for (video in videos) { + Log.d("Kekik_${this.name}", "video » ${video}") + + val video_url = if (video.url.startsWith("//")) "https:${video.url}" else video.url + + val quality = video.name.uppercase() + .replace("MOBILE", "144p") + .replace("LOWEST", "240p") + .replace("LOW", "360p") + .replace("SD", "480p") + .replace("HD", "720p") + .replace("FULL", "1080p") + .replace("QUAD", "1440p") + .replace("ULTRA", "4k") + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = video_url, + referer = url, + quality = getQualityFromName(quality), + headers = user_agent, + isM3u8 = false + ) + ) + } + } + + data class OkRuVideo( + @JsonProperty("name") val name: String, + @JsonProperty("url") val url: String, + ) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/OkRuExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/OkRuExtractor.kt index 70e87fbf..824481eb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/OkRuExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/OkRuExtractor.kt @@ -1,67 +1,13 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + package com.lagradost.cloudstream3.extractors -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils.parseJson - -data class DataOptionsJson ( - @JsonProperty("flashvars") var flashvars : Flashvars? = Flashvars(), -) -data class Flashvars ( - @JsonProperty("metadata") var metadata : String? = null, - @JsonProperty("hlsManifestUrl") var hlsManifestUrl : String? = null, //m3u8 -) - -data class MetadataOkru ( - @JsonProperty("videos") var videos: ArrayList = arrayListOf(), -) - -data class Videos ( - @JsonProperty("name") var name : String, - @JsonProperty("url") var url : String, - @JsonProperty("seekSchema") var seekSchema : Int? = null, - @JsonProperty("disallowed") var disallowed : Boolean? = null -) - -class OkRuHttps: OkRu(){ +class OkRuSSL : Odnoklassniki() { + override var name = "OkRuSSL" override var mainUrl = "https://ok.ru" } -open class OkRu : ExtractorApi() { - override var name = "Okru" +class OkRuHTTP : Odnoklassniki() { + override var name = "OkRuHTTP" override var mainUrl = "http://ok.ru" - override val requiresReferer = false - - override suspend fun getUrl(url: String, referer: String?): List? { - val doc = app.get(url).document - val sources = ArrayList() - val datajson = doc.select("div[data-options]").attr("data-options") - if (datajson.isNotBlank()) { - val main = parseJson(datajson) - val metadatajson = parseJson(main.flashvars?.metadata!!) - val servers = metadatajson.videos - servers.forEach { - val quality = it.name.uppercase() - .replace("MOBILE","144p") - .replace("LOWEST","240p") - .replace("LOW","360p") - .replace("SD","480p") - .replace("HD","720p") - .replace("FULL","1080p") - .replace("QUAD","1440p") - .replace("ULTRA","4k") - val extractedurl = it.url.replace("\\\\u0026", "&") - sources.add(ExtractorLink( - name, - name = this.name, - extractedurl, - url, - getQualityFromName(quality), - isM3u8 = false - )) - } - } - return sources - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt new file mode 100644 index 00000000..b57449bf --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/PeaceMakerstExtractor.kt @@ -0,0 +1,89 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class PeaceMakerst : ExtractorApi() { + override val name = "PeaceMakerst" + override val mainUrl = "https://peacemakerst.com" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val m3u_link:String? + val ext_ref = referer ?: "" + val post_url = "${url}?do=getVideo" + Log.d("Kekik_${this.name}", "post_url » ${post_url}") + + val response = app.post( + post_url, + data = mapOf( + "hash" to url.substringAfter("video/"), + "r" to ext_ref, + "s" to "" + ), + referer = ext_ref, + headers = mapOf( + "Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8", + "X-Requested-With" to "XMLHttpRequest" + ) + ) + if (response.text.contains("teve2.com.tr\\/embed\\/")) { + val teve2_id = response.text.substringAfter("teve2.com.tr\\/embed\\/").substringBefore("\"") + val teve2_response = app.get( + "https://www.teve2.com.tr/action/media/${teve2_id}", + referer = "https://www.teve2.com.tr/embed/${teve2_id}" + ).parsedSafe() ?: throw ErrorLoadingException("teve2 response is null") + + m3u_link = teve2_response.media.link.serviceUrl + "//" + teve2_response.media.link.securePath + } else { + val video_response = response.parsedSafe() ?: throw ErrorLoadingException("peace response is null") + val video_sources = video_response.videoSources + if (video_sources.isNotEmpty()) { + m3u_link = video_sources.lastOrNull()?.file + } else { + m3u_link = null + } + } + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = m3u_link ?: throw ErrorLoadingException("m3u link not found"), + referer = ext_ref, + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } + + data class PeaceResponse( + @JsonProperty("videoImage") val videoImage: String?, + @JsonProperty("videoSources") val videoSources: List, + @JsonProperty("sIndex") val sIndex: String, + @JsonProperty("sourceList") val sourceList: Map + ) + + data class VideoSource( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("type") val type: String + ) + + data class Teve2ApiResponse( + @JsonProperty("Media") val media: Teve2Media + ) + + data class Teve2Media( + @JsonProperty("Link") val link: Teve2Link + ) + + data class Teve2Link( + @JsonProperty("ServiceUrl") val serviceUrl: String, + @JsonProperty("SecurePath") val securePath: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/PixelDrainExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/PixelDrainExtractor.kt new file mode 100644 index 00000000..1c70cb54 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/PixelDrainExtractor.kt @@ -0,0 +1,31 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class PixelDrain : ExtractorApi() { + override val name = "PixelDrain" + override val mainUrl = "https://pixeldrain.com" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val pixel_id = Regex("""([^\/]+)(?=\?download)""").find(url)?.groupValues?.get(1) + val downloadLink = "${mainUrl}/api/file/${pixel_id}?download" + Log.d("Kekik_${this.name}", "downloadLink » ${downloadLink}") + + callback.invoke( + ExtractorLink( + source = "pixeldrain - ${pixel_id}", + name = "pixeldrain - ${pixel_id}", + url = downloadLink, + referer = "${mainUrl}/u/${pixel_id}?download", + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pixeldrain.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Pixeldrain.kt deleted file mode 100644 index 9b481240..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pixeldrain.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lagradost.cloudstream3.extractors - -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities - -open class Pixeldrain : ExtractorApi() { - override val name = "Pixeldrain" - override val mainUrl = "https://pixeldrain.com" - override val requiresReferer = false - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val mId = Regex("/([ul]/[\\da-zA-Z\\-]+)").find(url)?.groupValues?.get(1)?.split("/") - callback.invoke( - ExtractorLink( - this.name, - this.name, - "$mainUrl/api/file/${mId?.last() ?: return}?download", - url, - Qualities.Unknown.value, - ) - ) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt index d5b52dd7..498eb6a4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Rabbitstream.kt @@ -16,13 +16,13 @@ import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec -// Code found in https://github.com/theonlymo/keys -// special credits to @theonlymo for providing key +// Code found in https://github.com/Claudemirovsky/keys +// special credits to @Claudemirovsky for providing key class Megacloud : Rabbitstream() { override val name = "Megacloud" override val mainUrl = "https://megacloud.tv" override val embed = "embed-2/ajax/e-1" - override val key = "https://raw.githubusercontent.com/theonlymo/keys/e1/key" + override val key = "https://raw.githubusercontent.com/Claudemirovsky/keys/e1/key" } class Dokicloud : Rabbitstream() { @@ -35,7 +35,7 @@ open class Rabbitstream : ExtractorApi() { override val mainUrl = "https://rabbitstream.net" override val requiresReferer = false open val embed = "ajax/embed-4" - open val key = "https://raw.githubusercontent.com/theonlymo/keys/e4/key" + open val key = "https://raw.githubusercontent.com/Claudemirovsky/keys/e4/key" override suspend fun getUrl( url: String, diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/RapidVidExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/RapidVidExtractor.kt new file mode 100644 index 00000000..a0d830cf --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/RapidVidExtractor.kt @@ -0,0 +1,50 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class RapidVid : ExtractorApi() { + override val name = "RapidVid" + override val mainUrl = "https://rapidvid.net" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val video_req = app.get(url, referer=ext_ref).text + + val sub_urls = mutableSetOf() + Regex("""captions\",\"file\":\"([^\"]+)\",\"label\":\"([^\"]+)\"""").findAll(video_req).forEach { + val (sub_url, sub_lang) = it.destructured + + if (sub_url in sub_urls) { return@forEach } + sub_urls.add(sub_url) + + subtitleCallback.invoke( + SubtitleFile( + lang = sub_lang.replace("\\u0131", "ı").replace("\\u0130", "İ").replace("\\u00fc", "ü").replace("\\u00e7", "ç"), + url = fixUrl(sub_url.replace("\\", "")) + ) + ) + } + + val extracted_value = Regex("""file": "(.*)",""").find(video_req)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") + + val bytes = extracted_value.split("\\x").filter { it.isNotEmpty() }.map { it.toInt(16).toByte() }.toByteArray() + val decoded = String(bytes, Charsets.UTF_8) + Log.d("Kekik_${this.name}", "decoded » ${decoded}") + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = decoded, + referer = ext_ref, + quality = Qualities.Unknown.value, + isM3u8 = true + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/SibNetExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/SibNetExtractor.kt new file mode 100644 index 00000000..a8bcee31 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/SibNetExtractor.kt @@ -0,0 +1,33 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class SibNet : ExtractorApi() { + override val name = "SibNet" + override val mainUrl = "https://video.sibnet.ru" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val i_source = app.get(url, referer=ext_ref).text + var m3u_link = Regex("""player.src\(\[\{src: \"([^\"]+)""").find(i_source)?.groupValues?.get(1) ?: throw ErrorLoadingException("m3u link not found") + + m3u_link = "${mainUrl}${m3u_link}" + Log.d("Kekik_${this.name}", "m3u_link » ${m3u_link}") + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = m3u_link, + referer = url, + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt new file mode 100644 index 00000000..645d7c0e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/TRsTXExtractor.kt @@ -0,0 +1,73 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class TRsTX : ExtractorApi() { + override val name = "TRsTX" + override val mainUrl = "https://trstx.org" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + + val video_req = app.get(url, referer=ext_ref).text + + val file = Regex("""file\":\"([^\"]+)""").find(video_req)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") + val postLink = "${mainUrl}/" + file.replace("\\", "") + val rawList = app.post(postLink, referer=ext_ref).parsedSafe>() ?: throw ErrorLoadingException("Post link not found") + + val postJson: List = rawList.drop(1).map { item -> + val mapItem = item as Map<*, *> + TrstxVideoData( + title = mapItem["title"] as? String, + file = mapItem["file"] as? String + ) + } + Log.d("Kekik_${this.name}", "postJson » ${postJson}") + + val vid_links = mutableSetOf() + val vid_map = mutableListOf>() + for (item in postJson) { + if (item.file == null || item.title == null) continue + + val fileUrl = "${mainUrl}/playlist/" + item.file.substring(1) + ".txt" + val videoData = app.post(fileUrl, referer=ext_ref).text + + if (videoData in vid_links) { continue } + vid_links.add(videoData) + + vid_map.add(mapOf( + "title" to item.title, + "videoData" to videoData + )) + } + + + for (mapEntry in vid_map) { + Log.d("Kekik_${this.name}", "mapEntry » ${mapEntry}") + val title = mapEntry["title"] ?: continue + val m3u_link = mapEntry["videoData"] ?: continue + + callback.invoke( + ExtractorLink( + source = this.name, + name = "${this.name} - ${title}", + url = m3u_link, + referer = ext_ref, + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } + } + + data class TrstxVideoData( + @JsonProperty("title") val title: String? = null, + @JsonProperty("file") val file: String? = null + ) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt new file mode 100644 index 00000000..2478edc1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/TauVideoExtractor.kt @@ -0,0 +1,45 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty + +open class TauVideo : ExtractorApi() { + override val name = "TauVideo" + override val mainUrl = "https://tau-video.xyz" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val video_key = url.split("/").last() + val video_url = "${mainUrl}/api/video/${video_key}" + Log.d("Kekik_${this.name}", "video_url » ${video_url}") + + val api = app.get(video_url).parsedSafe() ?: throw ErrorLoadingException("TauVideo") + + for (video in api.urls) { + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = video.url, + referer = ext_ref, + quality = getQualityFromName(video.label), + type = INFER_TYPE + ) + ) + } + } + + data class TauVideoUrls( + @JsonProperty("urls") val urls: List + ) + + data class TauVideoData( + @JsonProperty("url") val url: String, + @JsonProperty("label") val label: String, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidMoxyExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidMoxyExtractor.kt new file mode 100644 index 00000000..b963fe56 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidMoxyExtractor.kt @@ -0,0 +1,50 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* + +open class VidMoxy : ExtractorApi() { + override val name = "VidMoxy" + override val mainUrl = "https://vidmoxy.com" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val video_req = app.get(url, referer=ext_ref).text + + val sub_urls = mutableSetOf() + Regex("""captions\",\"file\":\"([^\"]+)\",\"label\":\"([^\"]+)\"""").findAll(video_req).forEach { + val (sub_url, sub_lang) = it.destructured + + if (sub_url in sub_urls) { return@forEach } + sub_urls.add(sub_url) + + subtitleCallback.invoke( + SubtitleFile( + lang = sub_lang.replace("\\u0131", "ı").replace("\\u0130", "İ").replace("\\u00fc", "ü").replace("\\u00e7", "ç"), + url = fixUrl(sub_url.replace("\\", "")) + ) + ) + } + + val extracted_value = Regex("""file": "(.*)",""").find(video_req)?.groupValues?.get(1) ?: throw ErrorLoadingException("File not found") + + val bytes = extracted_value.split("\\x").filter { it.isNotEmpty() }.map { it.toInt(16).toByte() }.toByteArray() + val decoded = String(bytes, Charsets.UTF_8) + Log.d("Kekik_${this.name}", "decoded » ${decoded}") + + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = decoded, + referer = ext_ref, + quality = Qualities.Unknown.value, + isM3u8 = true + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt new file mode 100644 index 00000000..3060bb92 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoSeyredExtractor.kt @@ -0,0 +1,72 @@ +// ! Bu araç @keyiflerolsun tarafından | @KekikAkademi için yazılmıştır. + +package com.lagradost.cloudstream3.extractors + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue + +open class VideoSeyred : ExtractorApi() { + override val name = "VideoSeyred" + override val mainUrl = "https://videoseyred.in" + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + val ext_ref = referer ?: "" + val video_id = url.substringAfter("embed/").substringBefore("?") + val video_url = "${mainUrl}/playlist/${video_id}.json" + Log.d("Kekik_${this.name}", "video_url » ${video_url}") + + val response_raw = app.get(video_url) + val response_list:List = jacksonObjectMapper().readValue(response_raw.text) ?: throw ErrorLoadingException("VideoSeyred") + val response = response_list[0] ?: throw ErrorLoadingException("VideoSeyred") + + for (track in response.tracks) { + if (track.label != null && track.kind == "captions") { + subtitleCallback.invoke( + SubtitleFile( + lang = track.label, + url = fixUrl(track.file) + ) + ) + } + } + + for (source in response.sources) { + callback.invoke( + ExtractorLink( + source = this.name, + name = this.name, + url = source.file, + referer = ext_ref, + quality = Qualities.Unknown.value, + type = INFER_TYPE + ) + ) + } + } + + data class VideoSeyredSource( + @JsonProperty("image") val image: String, + @JsonProperty("title") val title: String, + @JsonProperty("sources") val sources: List, + @JsonProperty("tracks") val tracks: List + ) + + data class VSSource( + @JsonProperty("file") val file: String, + @JsonProperty("type") val type: String, + @JsonProperty("default") val default: String + ) + + data class VSTrack( + @JsonProperty("file") val file: String, + @JsonProperty("kind") val kind: String, + @JsonProperty("language") val language: String? = null, + @JsonProperty("label") val label: String? = null, + @JsonProperty("default") val default: String? = null + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt index 6e3e16a2..463c1d1f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt @@ -12,10 +12,22 @@ import javax.crypto.spec.SecretKeySpec // Code found in https://github.com/Claudemirovsky/worstsource-keys // special credits to @Claudemirovsky for providing key + +class MyCloud : Vidplay() { + override val name = "MyCloud" + override val mainUrl = "https://mcloud.bz" +} + +class VidplayOnline : Vidplay() { + override val mainUrl = "https://vidplay.online" +} + open class Vidplay : ExtractorApi() { override val name = "Vidplay" override val mainUrl = "https://vidplay.site" override val requiresReferer = true + open val key = + "https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json" override suspend fun getUrl( url: String, @@ -44,8 +56,7 @@ open class Vidplay : ExtractorApi() { } private suspend fun getKeys(): List { - return app.get("https://raw.githubusercontent.com/Claudemirovsky/worstsource-keys/keys/keys.json") - .parsed() + return app.get(key).parsed() } private suspend fun callFutoken(id: String, url: String): String? { @@ -89,4 +100,4 @@ open class Vidplay : ExtractorApi() { @JsonProperty("result") val result: Result? = null, ) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt index 8e87cc99..025e6fb6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -477,13 +477,13 @@ object PluginManager { Log.i(TAG, "Loading plugin: $data") return try { - /* in case of android 14 then + // in case of android 14 then try { File(filePath).setReadOnly() - } catch (t : Throwable) { + } catch (t: Throwable) { Log.e(TAG, "Failed to set dex as readonly") logError(t) - }*/ + } val loader = PathClassLoader(filePath, context.classLoader) var manifest: Plugin.Manifest diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index ed496326..045fdc94 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,6 +1,8 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.SyncWatchType +import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.UiText import me.xdrop.fuzzywuzzy.FuzzySearch @@ -61,7 +63,7 @@ interface SyncAPI : OAuth2API { ) : SearchResponse abstract class AbstractSyncStatus { - abstract var status: Int + abstract var status: SyncWatchType /** 1-10 */ abstract var score: Int? @@ -70,8 +72,9 @@ interface SyncAPI : OAuth2API { abstract var maxEpisodes: Int? } + data class SyncStatus( - override var status: Int, + override var status: SyncWatchType, /** 1-10 */ override var score: Int?, override var watchedEpisodes: Int?, @@ -166,5 +169,8 @@ interface SyncAPI : OAuth2API { override var posterHeaders: Map?, override var quality: SearchQuality?, override var id: Int? = null, + val plot : String? = null, + val rating: Int? = null, + val tags: List? = null, ) : SearchResponse } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index d0c88901..5c02e7f7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.SyncWatchType import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson @@ -165,7 +166,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return SyncAPI.SyncStatus( score = data.score, watchedEpisodes = data.progress, - status = data.type?.value ?: return null, + status = SyncWatchType.fromInternalId(data.type?.value ?: return null), isFavorite = data.isFavourite, maxEpisodes = data.episodes, ) @@ -174,7 +175,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean { return postDataAboutId( id.toIntOrNull() ?: return false, - fromIntToAnimeStatus(status.status), + fromIntToAnimeStatus(status.status.internalId), status.score, status.watchedEpisodes ).also { @@ -595,7 +596,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { //@JsonProperty("source") val source: String, @JsonProperty("episodes") val episodes: Int, @JsonProperty("title") val title: Title, - //@JsonProperty("description") val description: String, + @JsonProperty("description") val description: String?, @JsonProperty("coverImage") val coverImage: CoverImage, @JsonProperty("synonyms") val synonyms: List, @JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?, @@ -629,7 +630,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ?: this.media.coverImage.medium, null, null, - null + null, + plot = this.media.description ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index 02826401..fdbe763a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -16,6 +16,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.SyncWatchType import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson @@ -94,7 +95,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean { return setScoreRequest( id.toIntOrNull() ?: return false, - fromIntToAnimeStatus(status.status), + fromIntToAnimeStatus(status.status.internalId), status.score, status.watchedEpisodes ).also { @@ -245,7 +246,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { getDataAboutMalId(internalId)?.my_list_status //?: throw ErrorLoadingException("No my_list_status") return SyncAPI.SyncStatus( score = data?.score, - status = malStatusAsString.indexOf(data?.status), + status = SyncWatchType.fromInternalId(malStatusAsString.indexOf(data?.status)) , isFavorite = null, watchedEpisodes = data?.num_episodes_watched, ) @@ -442,6 +443,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { this.node.main_picture?.large ?: this.node.main_picture?.medium, null, null, + plot = this.node.synopsis, ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt index 3a37a228..e0b13ba6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt @@ -24,6 +24,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.SyncWatchType import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -671,7 +672,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { this.movie.poster?.let { getPosterUrl(it) }, null, null, - movie.ids.simkl + movie.ids.simkl, ) } } @@ -779,7 +780,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { } class SimklSyncStatus( - override var status: Int, + override var status: SyncWatchType, override var score: Int?, val oldScore: Int?, override var watchedEpisodes: Int?, @@ -826,7 +827,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { if (foundItem != null) { return SimklSyncStatus( - status = foundItem.status?.let { SimklListStatusType.fromString(it)?.value } + status = foundItem.status?.let { SyncWatchType.fromInternalId(SimklListStatusType.fromString(it)?.value) } ?: return null, score = foundItem.user_rating, watchedEpisodes = foundItem.watched_episodes_count, @@ -838,7 +839,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { ) } else { return SimklSyncStatus( - status = SimklListStatusType.None.value, + status = SyncWatchType.fromInternalId(SimklListStatusType.None.value) , score = 0, watchedEpisodes = 0, maxEpisodes = if (searchResult.type == "movie") 0 else searchResult.total_episodes, @@ -858,7 +859,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { val builder = SimklScoreBuilder.Builder() .apiUrl(this.mainUrl) .score(status.score, simklStatus?.oldScore) - .status(status.status, (status as? SimklSyncStatus)?.oldStatus?.let { oldStatus -> + .status(status.status.internalId, (status as? SimklSyncStatus)?.oldStatus?.let { oldStatus -> SimklListStatusType.values().firstOrNull { it.originalName == oldStatus }?.value @@ -871,7 +872,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI { val episodes = simklStatus?.episodeConstructor?.getEpisodes() // All episodes if marked as completed - val watchedEpisodes = if (status.status == SimklListStatusType.Completed.value) { + val watchedEpisodes = if (status.status.internalId == SimklListStatusType.Completed.value) { episodes?.size } else { status.watchedEpisodes diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt index 6c0e7796..688363e9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt @@ -8,7 +8,7 @@ import android.widget.* import androidx.appcompat.app.AlertDialog import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.fasterxml.jackson.module.kotlin.kotlinModule import com.google.android.gms.cast.MediaQueueItem import com.google.android.gms.cast.MediaSeekOptions import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF @@ -98,7 +98,7 @@ data class MetadataHolder( class SelectSourceController(val view: ImageView, val activity: ControllerActivity) : UIController() { - private val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule()) + private val mapper: JsonMapper = JsonMapper.builder().addModule(kotlinModule()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build() init { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt index eb4eb666..9532d1a9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt @@ -15,4 +15,27 @@ enum class WatchType(val internalId: Int, @StringRes val stringRes: Int, @Drawab companion object { fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE } -} \ No newline at end of file +} + +enum class SyncWatchType(val internalId: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) { + /* + -1 -> None + 0 -> Watching + 1 -> Completed + 2 -> OnHold + 3 -> Dropped + 4 -> PlanToWatch + 5 -> ReWatching + */ + NONE(-1, R.string.type_none, R.drawable.ic_baseline_add_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), + REWATCHING(5, R.string.type_re_watching, R.drawable.ic_baseline_bookmark_24); + + companion object { + fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt index c60c8ef0..a37fbcb3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -33,6 +33,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.FragmentLibraryBinding import com.lagradost.cloudstream3.mvvm.Resource @@ -293,11 +294,12 @@ class LibraryFragment : Fragment() { when (searchClickCallback.action) { SEARCH_ACTION_SHOW_METADATA -> { - activity?.showPluginSelectionDialog( + (activity as? MainActivity)?.loadPopup(searchClickCallback.card, load = false) + /*activity?.showPluginSelectionDialog( syncId, syncName, searchClickCallback.card.apiName - ) + )*/ } SEARCH_ACTION_LOAD -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 74ea71e7..031f14fe 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -499,7 +499,11 @@ class CS3IPlayer : IPlayer { if (saveTime) updatedTime() - exoPlayer?.release() + exoPlayer?.apply { + setPlayWhenReady(false) + stop() + release() + } //simpleCache?.release() currentTextRenderer = null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 819e50ba..6508721a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -75,6 +75,7 @@ private const val SUBTITLE_DELAY_BUNDLE_KEY = "subtitle_delay" // All the UI Logic for the player open class FullScreenPlayer : AbstractPlayerFragment() { + private var isVerticalOrientation: Boolean = false protected open var lockRotation = true protected open var isFullScreenPlayer = true protected open var isTv = false @@ -111,6 +112,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() { protected var playerResizeEnabled = false protected var doubleTapEnabled = false protected var doubleTapPauseEnabled = true + protected var playerRotateEnabled = false + protected var autoPlayerRotateEnabled = false protected var subtitleDelay set(value) = try { @@ -286,6 +289,38 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player.getCurrentPreferredSubtitle() == null } + private fun restoreOrientationWithSensor(activity: Activity){ + val currentOrientation = activity.resources.configuration.orientation + var orientation = 0 + when (currentOrientation) { + Configuration.ORIENTATION_LANDSCAPE -> + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + + Configuration.ORIENTATION_SQUARE, Configuration.ORIENTATION_UNDEFINED -> + orientation = dynamicOrientation() + + Configuration.ORIENTATION_PORTRAIT -> + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + } + activity.requestedOrientation = orientation + } + + private fun toggleOrientationWithSensor(activity: Activity){ + val currentOrientation = activity.resources.configuration.orientation + var orientation = 0 + when (currentOrientation) { + Configuration.ORIENTATION_LANDSCAPE -> + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + + Configuration.ORIENTATION_SQUARE, Configuration.ORIENTATION_UNDEFINED -> + orientation = dynamicOrientation() + + Configuration.ORIENTATION_PORTRAIT -> + orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } + activity.requestedOrientation = orientation + } + open fun lockOrientation(activity: Activity) { val display = (activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay @@ -293,24 +328,39 @@ open class FullScreenPlayer : AbstractPlayerFragment() { val currentOrientation = activity.resources.configuration.orientation var orientation = 0 when (currentOrientation) { - Configuration.ORIENTATION_LANDSCAPE -> orientation = - if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE + Configuration.ORIENTATION_LANDSCAPE -> + orientation = + if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) + ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + else + ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE - Configuration.ORIENTATION_SQUARE, Configuration.ORIENTATION_UNDEFINED, Configuration.ORIENTATION_PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE - //Configuration.ORIENTATION_PORTRAIT -> orientation = - // if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT + Configuration.ORIENTATION_SQUARE, Configuration.ORIENTATION_UNDEFINED -> + orientation = dynamicOrientation() + + Configuration.ORIENTATION_PORTRAIT -> + orientation = + if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270) + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + else + ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT } activity.requestedOrientation = orientation } - private fun updateOrientation() { + private fun updateOrientation(ignoreDynamicOrientation: Boolean = false) { activity?.apply { if(lockRotation) { if(isLocked) { lockOrientation(this) } else { - requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + if(ignoreDynamicOrientation){ + // restore when lock is disabled + restoreOrientationWithSensor(this) + } else { + this.requestedOrientation = dynamicOrientation() + } } } } @@ -584,7 +634,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { } isLocked = !isLocked - updateOrientation() + updateOrientation(true) // set true to ignore auto rotate to stay in current orientation if (isLocked && isShowing) { playerBinding?.playerHolder?.postDelayed({ @@ -1326,6 +1376,14 @@ open class FullScreenPlayer : AbstractPlayerFragment() { ctx.getString(R.string.playback_speed_enabled_key), false ) + playerRotateEnabled = settingsManager.getBoolean( + ctx.getString(R.string.rotate_video_key), + false + ) + autoPlayerRotateEnabled = settingsManager.getBoolean( + ctx.getString(R.string.auto_rotate_video_key), + false + ) playerResizeEnabled = settingsManager.getBoolean( ctx.getString(R.string.player_resize_enabled_key), @@ -1362,6 +1420,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { playerBinding?.apply { playerSpeedBtt.isVisible = playBackSpeedEnabled playerResizeBtt.isVisible = playerResizeEnabled + playerRotateBtt.isVisible = playerRotateEnabled } } catch (e: Exception) { logError(e) @@ -1376,6 +1435,11 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player.handleEvent(CSPlayerEvent.SkipCurrentChapter) } + playerRotateBtt.setOnClickListener { + autoHide() + toggleRotate() + } + // init clicks playerResizeBtt.setOnClickListener { autoHide() @@ -1481,4 +1545,28 @@ open class FullScreenPlayer : AbstractPlayerFragment() { logError(e) } } + + @SuppressLint("SourceLockedOrientationActivity") + private fun toggleRotate() { + activity?.let { + toggleOrientationWithSensor(it) + } + } + + override fun playerDimensionsLoaded(width: Int, height: Int) { + isVerticalOrientation = height > width + updateOrientation() + } + + private fun dynamicOrientation(): Int { + return if (autoPlayerRotateEnabled) { + if (isVerticalOrientation) { + ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT + } else { + ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } + } else { + ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE // default orientation + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index 43e51e55..0a626471 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -1237,6 +1237,7 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun playerDimensionsLoaded(width: Int, height: Int) { + super.playerDimensionsLoaded(width, height) setPlayerDimen(width to height) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt index 0541a40f..ad34309c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt @@ -851,7 +851,7 @@ open class ResultFragmentPhone : FullScreenPlayer() { val d = status.value resultSyncRating.value = d.score?.toFloat() ?: 0.0f - resultSyncCheck.setItemChecked(d.status + 1, true) + resultSyncCheck.setItemChecked(d.status.internalId + 1, true) val watchedEpisodes = d.watchedEpisodes ?: 0 currentSyncProgress = watchedEpisodes diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 101a65ef..e60c388b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -826,16 +826,15 @@ class ResultViewModel2 : ViewModel() { private val _selectPopup: MutableLiveData = MutableLiveData(null) val selectPopup: LiveData = _selectPopup - fun updateWatchStatus( status: WatchType, context: Context?, loadResponse: LoadResponse? = null, statusChangedCallback: ((statusChanged: Boolean) -> Unit)? = null ) { - val response = loadResponse ?: currentResponse ?: return - - val currentId = response.getId() + val (response,currentId) = loadResponse?.let { load -> + (load to load.getId()) + } ?: ((currentResponse ?: return) to (currentId ?: return)) val currentStatus = getResultWatchState(currentId) @@ -885,13 +884,17 @@ class ResultViewModel2 : ViewModel() { response.type, response.posterUrl, response.year, - response.syncData + response.syncData, + plot = response.plot, + tags = response.tags, + rating = response.rating ) ) } if (currentStatus != status) { MainActivity.bookmarksUpdatedEvent(true) + MainActivity.reloadLibraryEvent(true) } _watchStatus.postValue(status) @@ -926,7 +929,7 @@ class ResultViewModel2 : ViewModel() { val response = currentResponse ?: return if (response !is EpisodeResponse) return - val currentId = response.getId() + val currentId = currentId ?: return if (isSubscribed) { removeSubscribedData(currentId) @@ -969,7 +972,10 @@ class ResultViewModel2 : ViewModel() { response.type, response.posterUrl, response.year, - response.syncData + response.syncData, + plot = response.plot, + rating = response.rating, + tags = response.tags ) ) @@ -994,7 +1000,7 @@ class ResultViewModel2 : ViewModel() { val isFavorite = _favoriteStatus.value ?: return val response = currentResponse ?: return - val currentId = response.getId() + val currentId = currentId ?: return if (isFavorite) { removeFavoritesData(currentId) @@ -1036,7 +1042,10 @@ class ResultViewModel2 : ViewModel() { response.type, response.posterUrl, response.year, - response.syncData + response.syncData, + plot = response.plot, + rating = response.rating, + tags = response.tags ) ) @@ -1839,10 +1848,10 @@ class ResultViewModel2 : ViewModel() { this.japName ).filter { it.length > 2 } .distinct().map { - // this actually would be nice if we improved a bit as 3rd season == season 3 == III ect + // this actually would be nice if we improved a bit as 3rd season == season 3 == III ect // right now it just removes the dubbed status it.lowercase().replace(Regex("""\(?[ds]ub(bed)?\)?(\s|$)""") , "").trim() - }, + }, TrackerType.getTypes(this.type), this.year ) @@ -1936,6 +1945,7 @@ class ResultViewModel2 : ViewModel() { postSuccessful( value ?: return@launchSafe, + currentId ?: return@launchSafe, currentRepo ?: return@launchSafe, updateEpisodes ?: return@launchSafe, false @@ -2050,9 +2060,9 @@ class ResultViewModel2 : ViewModel() { } private fun postFavorites(loadResponse: LoadResponse) { - val id = loadResponse.getId() - val isFavorite = getFavoritesData(id) != null - _favoriteStatus.postValue(isFavorite) + val id = loadResponse.getId() + val isFavorite = getFavoritesData(id) != null + _favoriteStatus.postValue(isFavorite) } private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) { @@ -2185,26 +2195,25 @@ class ResultViewModel2 : ViewModel() { private suspend fun postSuccessful( loadResponse: LoadResponse, + mainId : Int, apiRepository: APIRepository, updateEpisodes: Boolean, updateFillers: Boolean, ) { + currentId = mainId currentResponse = loadResponse postPage(loadResponse, apiRepository) postSubscription(loadResponse) postFavorites(loadResponse) + _watchStatus.postValue(getResultWatchState(mainId)) + if (updateEpisodes) - postEpisodes(loadResponse, updateFillers) + postEpisodes(loadResponse, mainId, updateFillers) } - private suspend fun postEpisodes(loadResponse: LoadResponse, updateFillers: Boolean) { + private suspend fun postEpisodes(loadResponse: LoadResponse, mainId : Int, updateFillers: Boolean) { _episodes.postValue(Resource.Loading()) - val mainId = loadResponse.getId() - currentId = mainId - - _watchStatus.postValue(getResultWatchState(mainId)) - if (updateFillers && loadResponse is AnimeLoadResponse) { updateFillers(loadResponse.name) } @@ -2561,6 +2570,49 @@ class ResultViewModel2 : ViewModel() { } } + data class LoadResponseFromSearch( + override var name: String, + override var url: String, + override var apiName: String, + override var type: TvType, + override var posterUrl: String?, + override var year: Int? = null, + override var plot: String? = null, + override var rating: Int? = null, + override var tags: List? = null, + override var duration: Int? = null, + override var trailers: MutableList = mutableListOf(), + override var recommendations: List? = null, + override var actors: List? = null, + override var comingSoon: Boolean = false, + override var syncData: MutableMap = mutableMapOf(), + override var posterHeaders: Map? = null, + override var backgroundPosterUrl: String? = null, + override var contentRating: String? = null, + ) : LoadResponse + + fun loadSmall(activity: Activity?, searchResponse : SearchResponse) = ioSafe { + val url = searchResponse.url + _page.postValue(Resource.Loading(url)) + _episodes.postValue(Resource.Loading()) + val api = APIHolder.getApiFromNameNull(searchResponse.apiName) ?: APIHolder.getApiFromUrlNull(searchResponse.url) ?: APIRepository.noneApi + val repo = APIRepository(api) + val response = LoadResponseFromSearch(name = searchResponse.name, url = searchResponse.url, apiName = api.name, type = searchResponse.type ?: TvType.Others, + posterUrl = searchResponse.posterUrl).apply { + if (searchResponse is SyncAPI.LibraryItem) { + this.plot = searchResponse.plot + this.rating = searchResponse.personalRating?.times(100) ?: searchResponse.rating + this.tags = searchResponse.tags + } + } + val mainId = searchResponse.id ?: response.getId() + + postSuccessful( + loadResponse = response, + mainId = mainId, + apiRepository = repo, updateEpisodes = false, updateFillers = false) + } + fun load( activity: Activity?, url: String, @@ -2646,6 +2698,7 @@ class ResultViewModel2 : ViewModel() { loadTrailers(data.value) postSuccessful( data.value, + mainId, updateEpisodes = true, updateFillers = showFillers, apiRepository = repo diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt index a3e2ed87..51d3f50c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt @@ -10,7 +10,9 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.SyncWatchType import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.SyncUtil import java.util.* @@ -31,15 +33,15 @@ class SyncViewModel : ViewModel() { private val repos = SyncApis - private val _metaResponse: MutableLiveData> = - MutableLiveData() + private val _metaResponse: MutableLiveData?> = + MutableLiveData(null) - val metadata: LiveData> get() = _metaResponse + val metadata: LiveData?> = _metaResponse private val _userDataResponse: MutableLiveData?> = MutableLiveData(null) - val userData: LiveData?> get() = _userDataResponse + val userData: LiveData?> = _userDataResponse // prefix, id private val syncs = mutableMapOf() @@ -55,7 +57,7 @@ class SyncViewModel : ViewModel() { MutableLiveData(getMissing()) // pair of name idPrefix isSynced - val synced: LiveData> get() = _currentSynced + val synced: LiveData> = _currentSynced private fun getMissing(): List { return repos.map { @@ -169,7 +171,7 @@ class SyncViewModel : ViewModel() { if (which < -1 || which > 5) return // validate input val user = userData.value if (user is Resource.Success) { - user.value.status = which + user.value.status = SyncWatchType.fromInternalId(which) _userDataResponse.postValue(Resource.Success(user.value)) } } @@ -279,7 +281,33 @@ class SyncViewModel : ViewModel() { setEpisodesDelta(0) } + fun syncName(syncName: String) : String? { + // fix because of bad old data :pensive: + val realName = when(syncName) { + "MAL" -> malApi.idPrefix + "Simkl" -> simklApi.idPrefix + "AniList" -> aniListApi.idPrefix + else -> syncName + } + return repos.firstOrNull { it.idPrefix == realName }?.idPrefix + } + + fun setSync(syncName : String, syncId : String) { + syncs.clear() + syncs[syncName] = syncId + } + + fun clear() { + syncs.clear() + _metaResponse.postValue(null) + _currentSynced.postValue(getMissing()) + _userDataResponse.postValue(null) + } + fun updateMetaAndUser() { + _userDataResponse.postValue(Resource.Loading()) + _metaResponse.postValue(Resource.Loading()) + Log.i(TAG, "updateMetaAndUser") updateMetadata() updateUserData() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index ec61edf5..abd7fb9a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -95,6 +95,7 @@ val appLanguages = arrayListOf( Triple("", "മലയാളം", "ml"), Triple("", "bahasa Melayu", "ms"), Triple("", "ဗမာစာ", "my"), + Triple("", "नेपाली", "ne"), Triple("", "Nederlands", "nl"), Triple("", "norsk nynorsk", "nn"), Triple("", "norsk bokmål", "no"), diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt index abcef753..01a64d7d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStore.kt @@ -5,7 +5,7 @@ import android.content.SharedPreferences import androidx.preference.PreferenceManager import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.json.JsonMapper -import com.fasterxml.jackson.module.kotlin.KotlinModule +import com.fasterxml.jackson.module.kotlin.kotlinModule import com.lagradost.cloudstream3.AcraApplication.Companion.getKeyClass import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKeyClass @@ -51,7 +51,7 @@ class PreferenceDelegate( } object DataStore { - val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule()) + val mapper: JsonMapper = JsonMapper.builder().addModule(kotlinModule()) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build() private fun getPreferences(context: Context): SharedPreferences { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 6babe273..04387d80 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -209,7 +209,10 @@ object DataStoreHelper { @JsonProperty("year") open val year: Int?, @JsonProperty("syncData") open val syncData: Map?, @JsonProperty("quality") override var quality: SearchQuality?, - @JsonProperty("posterHeaders") override var posterHeaders: Map? + @JsonProperty("posterHeaders") override var posterHeaders: Map?, + @JsonProperty("plot") open val plot : String? = null, + @JsonProperty("rating") open val rating : Int? = null, + @JsonProperty("tags") open val tags : List? = null, ) : SearchResponse data class SubscribedData( @@ -225,8 +228,11 @@ object DataStoreHelper { override val year: Int?, override val syncData: Map? = null, override var quality: SearchQuality? = null, - override var posterHeaders: Map? = null - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) { + override var posterHeaders: Map? = null, + override val plot: String? = null, + override val rating: Int? = null, + override val tags: List? = null, + ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders, plot,rating,tags) { fun toLibraryItem(): SyncAPI.LibraryItem? { return SyncAPI.LibraryItem( name, @@ -236,7 +242,7 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, this.id + apiName, type, posterUrl, posterHeaders, quality, this.id, plot = this.plot, rating = this.rating, tags = this.tags ) } } @@ -253,8 +259,11 @@ object DataStoreHelper { override val year: Int?, override val syncData: Map? = null, override var quality: SearchQuality? = null, - override var posterHeaders: Map? = null - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) { + override var posterHeaders: Map? = null, + override val plot: String? = null, + override val rating: Int? = null, + override val tags: List? = null, + ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders, plot) { fun toLibraryItem(id: String): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( name, @@ -264,7 +273,7 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, this.id + apiName, type, posterUrl, posterHeaders, quality, this.id, plot = this.plot, rating = this.rating, tags = this.tags ) } } @@ -281,8 +290,11 @@ object DataStoreHelper { override val year: Int?, override val syncData: Map? = null, override var quality: SearchQuality? = null, - override var posterHeaders: Map? = null - ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders) { + override var posterHeaders: Map? = null, + override val plot: String? = null, + override val rating: Int? = null, + override val tags: List? = null, + ) : LibrarySearchResponse(id, latestUpdatedTime, name, url, apiName, type, posterUrl, year, syncData, quality, posterHeaders,plot) { fun toLibraryItem(): SyncAPI.LibraryItem? { return SyncAPI.LibraryItem( name, @@ -292,7 +304,7 @@ object DataStoreHelper { null, null, latestUpdatedTime, - apiName, type, posterUrl, posterHeaders, quality, this.id + apiName, type, posterUrl, posterHeaders, quality, this.id, plot = this.plot, rating = this.rating, tags = this.tags ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 2e9dd691..deb0eb3c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -96,12 +96,27 @@ import com.lagradost.cloudstream3.extractors.Moviesm4u import com.lagradost.cloudstream3.extractors.Mp4Upload import com.lagradost.cloudstream3.extractors.Mvidoo import com.lagradost.cloudstream3.extractors.MwvnVizcloudInfo +import com.lagradost.cloudstream3.extractors.MyCloud import com.lagradost.cloudstream3.extractors.Neonime7n import com.lagradost.cloudstream3.extractors.Neonime8n -import com.lagradost.cloudstream3.extractors.OkRu -import com.lagradost.cloudstream3.extractors.OkRuHttps +import com.lagradost.cloudstream3.extractors.Odnoklassniki +import com.lagradost.cloudstream3.extractors.TauVideo +import com.lagradost.cloudstream3.extractors.SibNet +import com.lagradost.cloudstream3.extractors.ContentX +import com.lagradost.cloudstream3.extractors.Hotlinger +import com.lagradost.cloudstream3.extractors.HDMomPlayer +import com.lagradost.cloudstream3.extractors.HDPlayerSystem +import com.lagradost.cloudstream3.extractors.VideoSeyred +import com.lagradost.cloudstream3.extractors.PeaceMakerst +import com.lagradost.cloudstream3.extractors.HDStreamAble +import com.lagradost.cloudstream3.extractors.RapidVid +import com.lagradost.cloudstream3.extractors.TRsTX +import com.lagradost.cloudstream3.extractors.VidMoxy +import com.lagradost.cloudstream3.extractors.PixelDrain +import com.lagradost.cloudstream3.extractors.MailRu +import com.lagradost.cloudstream3.extractors.OkRuSSL +import com.lagradost.cloudstream3.extractors.OkRuHTTP import com.lagradost.cloudstream3.extractors.Okrulink -import com.lagradost.cloudstream3.extractors.Pixeldrain import com.lagradost.cloudstream3.extractors.PlayLtXyz import com.lagradost.cloudstream3.extractors.PlayerVoxzer import com.lagradost.cloudstream3.extractors.Rabbitstream @@ -168,6 +183,7 @@ import com.lagradost.cloudstream3.extractors.Vidmoly import com.lagradost.cloudstream3.extractors.Vidmolyme import com.lagradost.cloudstream3.extractors.Vido import com.lagradost.cloudstream3.extractors.Vidplay +import com.lagradost.cloudstream3.extractors.VidplayOnline import com.lagradost.cloudstream3.extractors.Vidstreamz import com.lagradost.cloudstream3.extractors.Vizcloud import com.lagradost.cloudstream3.extractors.Vizcloud2 @@ -662,11 +678,27 @@ val extractorApis: MutableList = arrayListOf( Evoload1(), UpstreamExtractor(), + Odnoklassniki(), + TauVideo(), + SibNet(), + ContentX(), + Hotlinger(), + HDMomPlayer(), + HDPlayerSystem(), + VideoSeyred(), + PeaceMakerst(), + HDStreamAble(), + RapidVid(), + TRsTX(), + VidMoxy(), + PixelDrain(), + MailRu(), + Tomatomatela(), TomatomatelalClub(), Cinestart(), - OkRu(), - OkRuHttps(), + OkRuSSL(), + OkRuHTTP(), Okrulink(), Sendvid(), @@ -746,7 +778,6 @@ val extractorApis: MutableList = arrayListOf( Movhide(), StreamhideCom(), StreamhideTo(), - Pixeldrain(), Wibufile(), FileMoonIn(), Moviesm4u(), @@ -795,6 +826,8 @@ val extractorApis: MutableList = arrayListOf( PlayLtXyz(), AStreamHub(), Vidplay(), + VidplayOnline(), + MyCloud(), Cda(), Dailymotion(), diff --git a/app/src/main/res/drawable/ic_baseline_open_in_new_24.xml b/app/src/main/res/drawable/ic_baseline_open_in_new_24.xml new file mode 100644 index 00000000..2651015c --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_open_in_new_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/screen_rotation.xml b/app/src/main/res/drawable/screen_rotation.xml new file mode 100644 index 00000000..da0ac0fd --- /dev/null +++ b/app/src/main/res/drawable/screen_rotation.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout/bottom_resultview_preview.xml b/app/src/main/res/layout/bottom_resultview_preview.xml index ce41cb65..fe3f9b8d 100644 --- a/app/src/main/res/layout/bottom_resultview_preview.xml +++ b/app/src/main/res/layout/bottom_resultview_preview.xml @@ -114,17 +114,38 @@ - + android:layout_height="wrap_content"> + + + + + + + diff --git a/app/src/main/res/layout/player_custom_layout_tv.xml b/app/src/main/res/layout/player_custom_layout_tv.xml index 12c61baa..de9e04ae 100644 --- a/app/src/main/res/layout/player_custom_layout_tv.xml +++ b/app/src/main/res/layout/player_custom_layout_tv.xml @@ -638,6 +638,11 @@ app:icon="@drawable/video_locked" /> + + + + تنزلت الإضافة رجاع جَرِب تعمل إتصال… زيد - نزلو الحلقات بالجملة + نزلو المصادر بالجملة فتاح بـ راينيگ محي الريپو كمان بمحي الإضافات يلي في @@ -588,4 +588,11 @@ \nعلامت المصدر والجودة تبعو بينجمعو مع بعض (8 + 1 = 9). يلي علامتو 10 أو أعلى، بينحط تلقائيًا، من دون ما ينعمل لود لكل المصادر! حطو الأرقام السرية الحالية صوت + حط كبسة لبرم إتجاه الشاشة + rotate_video_key + auto_rotate_video_key + برم الشاشة أوتوماتيكيًا + برومو + غير إتجاه الشاشة أوتوماتيكيًا حسب شكل الڤيديو + رجع نعمل لاود لاللينك diff --git a/app/src/main/res/values-am/strings.xml b/app/src/main/res/values-am/strings.xml index 3ea31b54..7fd3274b 100644 --- a/app/src/main/res/values-am/strings.xml +++ b/app/src/main/res/values-am/strings.xml @@ -1,15 +1,15 @@ - %s ክፍል %d + %1$s ክፍል %2$d ተዋናዮች: %s ፍጥነት(%.2fx) ቀጣይ በዘፈቀደ ዳራ ቅድም እይታ - %dሰዓት %dደቂቃ + %1$dሰዓት %2$dደቂቃ ፖስተር የወረዱ አዲስ ማሻሻያ ተገኝቷል! -\n%s -> %s +\n%1$s -> %2$s ተመለስ ተጨማሪ አማራጮች በማየት ላይ @@ -21,7 +21,7 @@ ፍለጋ… ተመዘነ: %.1f አሳሽ - %dቀን %dሰዓት %dደቂቃ + %1$dቀን %2$dሰዓት %3$dደቂቃ ቀጣይ ክፍል %dደቂቃ %d ደቂቃ diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 11fe0ba3..c79c96b1 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -616,4 +616,11 @@ تخطي اختيار الحساب عند بدء التشغيل إدارة الحسابات تعديل الحساب + تم إعادة تحميل الروابط + عرض زر تبديل لاتجاه الشاشة + مفتاح تدوير الفيديو + مفتاح تدوير الفيديو التلقائي + الدوران التلقائي + تدوير + تمكين التبديل التلقائي لاتجاه الشاشة بناءً على اتجاه الفيديو diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index e8c5d0f5..32f5d72b 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,11 +1,11 @@ - %s еп. %d + %1$s еп. %2$d Актьори: %s Епизод %d ще бъде пуснат след - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm Poster Episode Poster @@ -17,7 +17,7 @@ Скорост (%.2fx) Оценка: %.1f Намерена е нова актуализация! -\n%s -> %s +\n%1$s -> %2$s Шаблон %d мин CloudStream @@ -174,12 +174,12 @@ Възстановяване на стойността по подразбиране За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до разработчиците Сезон - %s %d%s + %1$s %2$d%3$s Без сезон Епизод Епизоди - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s С Е Няма намерени епизоди @@ -307,7 +307,7 @@ Езиков код (en) - %s %s + %1$s %2$s Акаунт Излизане Влизане @@ -404,8 +404,8 @@ Приставката е изтрита Не може да зареди %s 18+ - Започна да изтегля %d %s… - Изтеглено %d %s + Започна да изтегля %1$d %2$s… + Изтеглено %1$d %2$s Всички %s вече са изтеглени Пакетно изтегляне Плъгин diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 12f4343c..4f4891c6 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -4,8 +4,8 @@ ক্লাউডস্ট্রিম দিয়ে চালান হোম অভিনয়েঃ %s - %dদিন %dঘন্টা %dমিনিট - %dঘন্টা %dমিনিট + %1$dদিন %2$dঘন্টা %3$dমিনিট + %1$dঘন্টা %2$dমিনিট %d মিনিট পর্বের পোস্টার মূল পোস্টার @@ -16,7 +16,7 @@ গতি (%.2f গুণ) মূল্যায়নঃ %.1f নতুন আপডেট এসেছে! -\n%s -> %s +\n%1$s -> %2$s ফিলার %d মিনিট ক্লাউডস্ট্রিম diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml index 1c26c236..3c2f1896 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -2,11 +2,11 @@ - %s Ep %d + %1$s Ep %2$d Elenco: %s O episódio %d vai ser lançado em - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm Poster @@ -21,7 +21,7 @@ Velocidade (%.2fx) Nota: %.1f Nova atualização encontrada! -\n%s -> %s +\n%1$s -> %2$s Filler %d min CloudStream @@ -310,7 +310,7 @@ Kitsu Trakt --> - %s %s + %1$s %2$s Conta Sair Entrar @@ -405,8 +405,8 @@ Plugin Carregado Plugin Apagado Não consegui carregar %s :-( - Iniciada a transferência %d %s… - Transferido %d %s + Iniciada a transferência %1$d %2$s… + Transferido %1$d %2$s Tudo %s já transferido Transferência em batch Plugin @@ -507,11 +507,11 @@ Marcar como assistido Aplicativo precisa ser fechado para atualizar Mostrar popups pulados para abertura e finalização - %d-%d + %1$d-%2$d Player interno Tamanho Abrindo - %s %d%s + %1$s %2$d%3$s %d plugins atualizados Todos as extensões serão desligadas para ajuda se talvez estejam causando algum bug. Aplicativo não encontrado @@ -524,7 +524,7 @@ Linguagem Lista de reprodução HLS Terminando - %d %s + %1$d %2$s Adicionado em (antigo para novo) Introdução plug-ins não foram encontrados no repositório diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 609d96ef..27ed3d11 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -608,4 +608,11 @@ Přeskočit výběr účtu při spuštění Správce účtů Upravit účet + Odkazy znovu načteny + Zobrazit tlačítko pro přepnutí otočení obrazovky + rotate_video_key + auto_rotate_video_key + Automatické otáčení + Otočení + Zapnout automatické otáčení obrazovky v závislosti na orientaci videa diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 112afc8b..0f6c4d90 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -399,7 +399,7 @@ Plugin gelöscht Fehler beim Laden von %s 18+ - Herunterladen begonnen %d %s… + Herunterladen von %1$d %2$s gestartet… %1$d %2$s heruntergeladen Alle %s bereits heruntergeladen Batch-Download @@ -570,4 +570,19 @@ PIN eingeben PIN Aktuelle PIN eingeben + Angemeldet als %s + PIN für %s eingeben + Standard-Konto verwenden + Kontoauswahl beim Starten überspringen + Konten verwalten + Konto bearbeiten + Es wurden potentielle Duplikate in ihrer Bibliothek gefunden: +\n +\n%s +\n +\nWollen sie dieses Element trotzdem hinzufügen, das existierende Element ersetzen oder diese Aktion abbrechen? + Ihre Bibliothek enthält möglicherweise schon ein Duplikat dieses Elements: \'%s\' +\n +\nWollen sie dieses Element trotzdem hinzufügen, das existierende ersetzen oder diese Aktion abbrechen? + Links wurden neu geladen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 72a7c4c9..c2e24792 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -118,7 +118,7 @@ Ταχύτητα (%.2fx) Βαθμολογία: %.1f Νέα διαθέσιμη ενημέρωση! -\n%s -> %s +\n%1$s -> %2$s Πατήστε δύο φορές στη μέση για παύση Χρήση φωτεινότητας συστήματος Χρήση φωτεινότητας συστήματος στο ενσωματωμένο πρόγραμμα αναπαραγωγής, αντί εφαρμογής προεπιλεγμένου σκούρου επικαλύμματος @@ -142,12 +142,12 @@ Δεν βρέθηκαν διαθέσιμοι σύνδεσμοι Ο σύνδεσμος αντιγράφηκε στο πρόχειρο Κύκλος - %s %d%s + %1$s %2$d%3$s Κανένας κύκλος Επεισόδιο Επεισόδια - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s Σ E Δεν βρέθηκαν επεισόδια @@ -320,8 +320,8 @@ Το πρόσθετο φορτώθηκε Το πρόσθετο διαγράφηκε Αποτυχία φόρτωσης του %s - Ξεκίνησε η λήψη %d %s… - Κατέβηκε το %d %s επιτυχώς + Ξεκίνησε η λήψη %1$d %2$s… + Κατέβηκε το %1$d %2$s επιτυχώς Όλα τα %s έχουν ήδη κατέβει Μαζική λήψη Πρόσθετο @@ -367,13 +367,13 @@ Web Video Cast Περιηγητής Η εφαρμογή δεν βρέθηκε - %s Επ %d + %1$s Επ %2$d Το επεισόδιο %d θα κυκλοφορήσει σε Φίλτρο %d λεπτά Ηθοποιοί: %s - %dμ %dω %dλ - %dω %dλ + %1$dμ %2$dω %3$dλ + %1$dω %2$dλ %dλ Αναζήτηση %s… Αναπαραγωγή ζωντανής μετάδοσης @@ -461,7 +461,7 @@ Υπότιτλοι Δομή Προεπιλεγμένα - %s %s + %1$s %2$s Μέγεθος γραμματοσειράς Άνιμε Σύνδεσμοι diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5712700e..508f28fe 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -584,4 +584,11 @@ Omitir la selección de cuenta al inicio Gestionar las cuentas Editar la cuenta + Enlaces recargados + Mostrar un botón para cambiar la orientación de la pantalla + rotate_video_key + auto_rotate_video_key + Giro automático + Girar + Activar el cambio automático de la orientación de la pantalla en función de la orientación del vídeo diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index f9d28e24..6a98a860 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -26,11 +26,11 @@ پوستر پوستر پوستر قسمت - %dروز %dساعت %dدقیقه - %s قسمت %d + %1$dروز %2$dساعت %3$dدقیقه + %1$s قسمت %2$d بازیگران: %s قسمت %d پخش خواهد شد - %dساعت %dدقیقه + %1$dساعت %2$dدقیقه %dدقیقه پوستر اصلی تورنت diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 588c28ff..c571046c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -126,8 +126,8 @@ Mettre à jour Utile pour contourner les bloquages des FAI Emplacement de téléchargement - Nouvelle mise à jour trouvée ! -\n%s -> %s + Nouvelle mise à jour trouvée ! +\n%1$s -> %2$s Épisode spécial Qualité de visionnage préférée (WiFi) Taille de la mémoire cache @@ -140,7 +140,7 @@ DNS avec HTTPS Afficher les animés en Anglais (Dub) / sous-titrés Disposition en mode téléphone - %s Ep %d + %1$s Episode %2$d Note : %.1f Zoom Adapter à l\'écran @@ -300,7 +300,7 @@ Application Light Novel par les mêmes devs Anime app by the same devs Rejoignez le Discord - %d h %d min + %1$d h %2$d min %d min Lire avec CloudStream Lire en direct @@ -315,7 +315,7 @@ Intro Effacer l\'historique Oui - %d j %d h %d min + %1$d j %2$d h %3$d min Stream Êtes-vous sûr·e de vouloir quitter \? Non @@ -366,9 +366,9 @@ DVD Suivant Blu-ray - %s %d%s - %d-%d - %d %s + %1$s %2$d%3$s + %1$d-%2$d + %1$d %2$s NSFW 127.0.0.1 %d / 10 @@ -387,7 +387,7 @@ @string/anime OAV NSFW - %s %s + %1$s %2$s Filtrez par langue préférée Extras Lien vers le stream @@ -414,8 +414,8 @@ Plugin Chargé Plugin Supprimé Téléchargement par lots - Début du téléchargement %d %s… - Téléchargé %d %s + Début du téléchargement %1$d %2$s… + Téléchargé %1$d %2$s Tous les %s déjà téléchargés Télécharger la liste de sites que vous voulez utiliser Téléchargé : %d @@ -568,4 +568,21 @@ Sélectionnez un compte Entrer PIN PIN + Favoris + Connecté en tant que %s + Des doublons potentiels ont été trouvés dans votre bibliothèque : +\n +\n%s +\n +\nSouhaitez-vous ajouter cet élément, remplacer les éléments existants ou annuler l\'action ? + Entrer le code PIN pour %s + Doublon potentiel trouvé + Verrouiller le profil + Passer la sélection de compte au démarrage + Se désabonner + S\'abonner + Il semble qu\'un élément potentiellement dupliqué existe déjà dans votre bibliothèque : \'%s. +\n +\nSouhaitez-vous ajouter cet élément, remplacer l\'élément existant ou annuler l\'action ? + Entrer le code PIN actuel diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 92754082..d0a05bb0 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -1,11 +1,11 @@ - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm Póster Principal Seguinte ó azar - %s Ep %d + %1$s Ep %2$d Elenco: %s O episodio %d se lanzará en Póster @@ -14,7 +14,7 @@ Regresar Cambiar provedor Nova actualización atopada! -\n%s -> %s +\n%1$s -> %2$s Recheo %d min Configuración diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index ccc69da4..9785e0b7 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -161,4 +161,35 @@ %d मिनट क्लाउडस्ट्रीम क्लाउडस्ट्रीम के साथ चलाएं + आपकी लाइब्रेरी में संभावित डुप्लिकेट आइटम पाए गए हैं: +\n +\n%s +\n +\nक्या आप किसी भी तरह इस आइटम को जोड़ना चाहेंगे, मौजूदा आइटम को बदलना चाहेंगे, या कार्रवाई रद्द करना चाहेंगे? + %s के लिए पिन दर्ज करें + संभावित डुप्लिकेट मिला + अपडेट शुरू हुआ + प्रोफ़ाइल लॉक करें + सबको बदली करें + ग़लत पिन. कृपया पुन: प्रयास करें। + ब्राउज़र + उपशीर्षक + पिन 4 अक्षर का होना चाहिए + बदली + जोड़े + ट्रेलर देखे + लाइवस्ट्रीम देखे + विज्ञापन + दोबारा देखे + स्वत: घुमाएँ + अकाउंट चुनिये + लोडिंग स्किप करे + लोडिंग… + ऐसा प्रतीत होता है कि संभावित रूप से डुप्लिकेट आइटम आपकी लाइब्रेरी में पहले से मौजूद है: \'%s.\' +\n +\nक्या आप किसी भी तरह इस आइटम को जोड़ना चाहेंगे, मौजूदा आइटम को बदलना चाहेंगे, या कार्रवाई रद्द करना चाहेंगे? + पिन दर्ज करें + पिन + लिंक पुन्ह खुली + वर्तमान पिन दर्ज करें diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 58afb665..5b1dbcf0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,8 +1,8 @@ Stáblista: %s - %dn %dó%dp - %dó%dp + %1$dn %2$dó%3$dp + %1$dó%2$dp %dp Epizód Plakát Fő Plakát @@ -11,9 +11,9 @@ Sebesség (%.2fx) Értékelés: %.1f Új frissítés található! -\n%s -> %s +\n%1$s -> %2$s %d perc - %sEp%d + %1$sEp%2$d CloudStream Háttér Előnézet Letöltés sikertelen @@ -146,8 +146,8 @@ Epizódok Év Film - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s Ep Nem található epizód Fájl törlése @@ -210,7 +210,7 @@ Alapértelmezett értékre visszaállítása Az alkalmazás összeomlott. Egy névtelen hibabejelentés küldünk a fejlesztőknek Évad - %s %d%s + %1$s %2$d%3$s Nincs évad +30 Ezzel véglegesen törli a %s @@ -365,7 +365,7 @@ Érvénytelen adatok Link a streamhez Nem sikerült betölteni: %s - Elkezdődött a(z) %d %s letöltése… + Elkezdődött a(z) %1$d %2$s letöltése… Töltse le az összes bővítményt ebből a tárolóból\? Biztonságos mód bekapcsolva Méret @@ -391,7 +391,7 @@ Érvénytelen azonosító Videók megtekintése ezeken a nyelveken Előző - %d%s letöltve + %1$d%2$s letöltve Batch letöltés bővítmények Legacy diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d6be0eed..74839a47 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,11 +1,11 @@ - %s Ep %d + %1$s Ep %2$d Cast: %s L\'episodio %d uscirà in - %d g %d o %d min - %d o %d min + %1$dd %2$dh %3$dm + %1$dh %2$dm %d min Poster @@ -177,12 +177,12 @@ Ripristina il valore predefinito Spiacente, l\'applicazione è andata in crash. Una segnalazione anonima di bug sarà inviata agli sviluppatori Stagione - %s %d%s + %1$s %2$d%3$s Nessuna stagione Episodio Episodi - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S E Nessun episodio trovato @@ -311,7 +311,7 @@ IlMioSito example.com Codice lingua (it) - %s %s + %1$s %2$s account Disconnetti Accedi @@ -409,7 +409,7 @@ Plugin eliminato Impossibile caricare %s 18+ - Download iniziato %1$d %2$s… + Avviato il download di %1$d %2$s… Scaricato %1$d %2$s Tutti %s già scaricati Download in blocco @@ -593,10 +593,19 @@ Iscriviti Rimuovi dai preferiti Seleziona un Account - Sembra che un oggetto potenziale duplicato sia già presente nella tua libreria: \'%1$s.\' + Sembra che nella tua libreria esista già un elemento potenzialmente duplicato: \'%s.\' \n -\nVorresti aggiungere l\'oggetto lo stesso, rimpiazzare l\'esistente, o cancellare l\'azione\? +\nDesideri aggiungere comunque questo elemento, sostituire quello esistente o annullare l\'azione? Inserisci PIN PIN Inserisci PIN Corrente + Entrato come %s + Inserisci il PIN per %s + Blocca profilo + Usa Account Default + Salta la selezione dell\'account all\'avvio + Gestisci Accounts + Modifica account + Collegamenti ricaricati + Ruota diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 55666df5..d5c2ad5e 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -7,7 +7,7 @@ מהירות (%.2fx) דירוג: %.1f נמצא עדכון חדש! -\n%s -> %s +\n%1$s -> %2$s סינון %d דקות קלאודסטרים @@ -24,8 +24,8 @@ לפתוח בדפדפן דלג על טעינה פרק %d יצא בעוד - %dימים %dשעות %dחודשים - %dשעות %dחודשים + %1$dימים %2$dשעות %3$dחודשים + %1$dשעות %2$dחודשים %dחודשים פוסטר הפוסטר של הפרק @@ -70,7 +70,7 @@ לסגור נקה לשמור - %s פרק %d + %1$s פרק %2$d נגן עם קלאודסטרים פרק הבא טוען… @@ -136,9 +136,9 @@ עדכן למהדורות מוקדמות חפש עדכוני מהדרות מוקדמות במקום מהדורות מלאות בלבד פרקים - %d-%d + %1$d-%2$d פרק - %d %s + %1$d %2$s עו פר לא נמצאו פרקים @@ -208,7 +208,7 @@ נראה אוטומטי פריסת טלוויזיה - %s %s + %1$s %2$s סנן לפי שפת מדיה מועדפת נושא אפליקציה מיקום כותרת פוסטר @@ -305,7 +305,7 @@ תקציר בתור לתת בניני למפתחים - %s %d%s + %1$s %2$d%3$s אפליקציה סרטים אנימה @@ -424,8 +424,8 @@ תוסף הורד התוסף נמחק לא יכול לטעון %s - התחיל להוריד %d %s… - הוריד %d %s + התחיל להוריד %1$d %2$s… + הוריד %1$d %2$s הצג חלונות קופצים של דילוג בשביל פתיח/שיר סיום מחק מאגר פתיח מעורב diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 138c0957..fbd44d91 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -9,8 +9,8 @@ ホーム ライブラリ 再生 - %9$d%9$d%9$d%9$d%d日 %d時間%d分 - %d時間%d分 + %1$d日 %2$d時間%3$d分 + %1$d時間%2$d分 検索… ダウンロード 情報 @@ -74,7 +74,7 @@ 閉じる 保存 消去 - %99$d%99$s%sエピ%d + %1$sエピ%2$d 出演者:%s ポスター エピソードポスター @@ -83,7 +83,7 @@ 戻り 視聴率 %.1f 新しいアップデートを発見! -\n%s -> %s +\n%1$s -> %2$s %d分 %sを検索… ソース diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 07ff89e4..f3fb665d 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,6 +1,6 @@ - %sಎಪಿ%d + %1$sಎಪಿ%2$d ಕ್ಯಾಸ್ಟ್:%s ಹಿಂದೆ ಹೋಗು ಫಿಲ್ಲರ್ @@ -16,7 +16,7 @@ ಈ ಪೂರೈಕೆದಾರರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಲು VPN ಬೇಕಾಗಬಹುದು ಕಪ್ಪು ಗಡಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ ಸಂಚಿಕೆ%d ಬಿಡುಗಡೆಯಾಗಲಿದೆ - %dh %dm + %1$dh %2$dm ಪೋಸ್ಟರ್ ಪೋಸ್ಟರ್ ಸಂಚಿಕೆ ಪೋಸ್ಟರ್ @@ -85,7 +85,7 @@ ಫೈಲ್ ಅಳಿಸಿ ಹೆಚ್ಚಿನ ಮಾಹಿತಿ ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ -\n%s-%s +\n%1$s-%2$s ಲೋಡಿಂಗ್… ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ ಲೈವ್‌ಸ್ಟ್ರೀಮ್ ಪ್ಲೇ ಮಾಡಿ @@ -99,7 +99,7 @@ ಕಾಪಿ ನೋ ಡೇಟಾ ಪ್ಲೇಯರ್ ಸ್ಪೀಡ್ - %d %dh %dm + %1$d %2$dh %3$dm ಹುಡುಕು %s… ಹೆಚ್ಚಿನ ಆಯ್ಕೆ ಫಾಂಟ್‌ಗಳನ್ನು ಇರಿಸುವ ಮೂಲಕ ಆಮದು ಮಾಡಿ %s diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index ef2f6cc5..0bf3bd9b 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -12,7 +12,7 @@ 속도 (%.2fx) 평점: %.1f 새로운 업데이트! -\n%s -> %s +\n%1$s -> %2$s %d분 CloudStream CloudStream에서 재생 @@ -82,10 +82,10 @@ 계속 시청 제거 -30 - %s 에피소드 %d + %1$s 에피소드 %2$d 포스터 - %d일 %d시간 %d분 - %d시간 %d분 + %1$d일 %2$d시간 %3$d분 + %1$d시간 %2$d분 %d분 검색 파일 삭제 @@ -159,7 +159,7 @@ 기본값으로 재설정 죄송합니다, 애플리케이션이 충돌했습니다. 버그 보고서가 익명으로 개발자에게 전송됩니다 에피소드 - %d-%d + %1$d-%2$d 진행중 시청 완료 상태 @@ -294,8 +294,8 @@ 확장 기능 버전 18+ - 다운로드 시작 %d %s… - 다운로드 %d %s + 다운로드 시작 %1$d %2$s… + 다운로드 %1$d %2$s 모든 %s가 이미 다운로드되었습니다 일괄 다운로드 플러그인 @@ -351,12 +351,12 @@ 시즌 삭제 취소 - %s %d%s + %1$s %2$d%3$s 파일 삭제 일시정지 에피소드 에피소드 - %d %s + %1$d %2$s 에피소드를 찾을 수 없음 시작 실패 @@ -412,7 +412,7 @@ 사용자 이름 언어 코드 (ko) 사이트 이름 - %s %s + %1$s %2$s 자막이 %d ms 너무 늦게 표시되는 경우, 사용하세요 자막 지연 없음 다운로드한 파일 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index db5ac011..54f9be82 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -4,7 +4,7 @@ Pašalinti Greitis (%.2fx) Žymos - %d %s + %1$d %2$s Siuntimas pradėtas Sekantis atsitiktinai Peržiūros fonas @@ -33,7 +33,7 @@ Atsiuntimas baigtas Tęsti žiūrėjimą Rastas atnaujinimas! -\n%s -> %s +\n%1$s -> %2$s Atsisiųsti kalbas Ieškoti naudojant tiekėjus Grįžti atgal @@ -110,7 +110,7 @@ Nėra duomenų Šriftas Perdaryti nustatymo procesą - %d-%d + %1$d-%2$d Duoti bananą kūrėjams Sugryšti Nuoroda nukopijuota į iškarpinę @@ -183,7 +183,7 @@ Šaltinio klaida Viešas sąrašas 127.0.0.1 - Atsiųsta %d %s + Atsiųsta %1$d %2$s Praleisti %s Pagal numatytuosius nustatymus „CloudStream“ neturi įdiegtų svetainių. Turite įdiegti svetaines iš saugyklų. \n @@ -196,7 +196,7 @@ Naršyklė Visos kalbos 4K - Pradėta siųsti %d %s… + Pradėta siųsti %1$d %2$s… Aprašymas Saugus režimas įjungtas HDR @@ -257,7 +257,7 @@ DVD Integruotas grotuvas Atsisiųskite sąrašą puslapiu jūs norite naudoti - %s Ser %d + %1$s Ser %2$d Ar tikrai norite išeiti\? Pašalinti iš žiūrimų Garso takelis diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 14e5b600..ab8db2a9 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -1,7 +1,7 @@ Plakāts - %s Ep %d + %1$s Ep %2$d Cast: %s Plakāts Epizodes plakāts @@ -13,7 +13,7 @@ Ātrums (%.2fx) Lidzīgi: %.1f Jauns atjauninājums atrasts! -\n%s -> %s +\n%1$s -> %2$s %d galvenais Claudstream Atskaņo ar cloudstream @@ -96,9 +96,9 @@ Iztīrīt Teksta krāsa Automātiski-iestādīt valodu - %dh %dm + %1$dh %2$dm %dm - %dd %dh %dm + %1$dd %2$dh %3$dm Ielādēt valodas Subtitru valoda Tūri lai restartētu uz sākumu @@ -179,12 +179,12 @@ Links kopēts cliobordā Restartēt uz parasto value Sezona - %s %d%s + %1$s %2$d%3$s Nav sezonas Epizode Epizodes - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S E Epizodes netika atrastas @@ -402,8 +402,8 @@ Plugins dzēsts Nevarēja ielādēt %s 18+ - Sākta %d %s lejupielāde… - Lejuplādēts %d %s + Sākta %1$d %2$s lejupielāde… + Lejuplādēts %1$d %2$s Pakešu lejupielāde Plugins Plugins @@ -493,7 +493,7 @@ Neizdevās sasniegt GitHub. Pieslēdzas starpniekserverim caur jsDelivr… Iztiept Galvenā krāsa - %s %s + %1$s %2$s Likt nosaukumu zem plakāta Izmantojiet šo, ja subtitri tiek rādīti %d ms pārāk vēlu Rekomendācijas diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 7badfd18..956f18e5 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -3,8 +3,8 @@ Брзина (%.2fx) Оценето: %.1f - Пронајдена нова верзија на апликацијата! -\n%s -> %s + "Пронајдена нова верзија! +\n%1$s -> %2$s" Филтер CloudStream Дома @@ -32,7 +32,7 @@ Преводи Повтори конекција… Оди назад - Пушти епизода + Вклучи епизода Превземи Превземено @@ -195,7 +195,7 @@ Изглед за гледање на телефон Примарна боја Тема на апликацијата - %s %s + %1$s %2$s Корисничко име Одјави се Најавете се @@ -214,8 +214,8 @@ Историја Голема буква на сите преводи Автоматски инсталирајте ги сите сè уште неинсталирани приклучоци од додадените складишта. - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s Пренос во живо NSFW Други @@ -369,7 +369,7 @@ Не можев да се најавам на %s Премногу текст. Не може да се зачува во таблата со исечоци. Неважечки ID - Преземено %d %s + Преземено %1$d %2$s Користете го ова ако преводите се прикажани %d ms премногу рано Неважечка URL адреса Безбедниот режим е вклучен @@ -428,7 +428,7 @@ VLC Рестартирај Цртан филм - Почна да презема %d %s… + Почна да презема %1$d %2$s… Автоматски ажурирања на приклучоци -30 %dm @@ -452,7 +452,7 @@ Апликацијата не е пронајдена MyCoolUsername Отвори со - %s %d%s + %1$s %2$d%3$s Повторете го процесот на поставување Линкови Повторување @@ -517,13 +517,13 @@ Поставете статус на пратење Пушти Livestream %s е автентициран - %s Епизода %d - %dч %dм + %1$sЕп.%2$d + %1$dч%2$dм %dм Следен рандом Постер Постер - %dд %dч %dм + %1$dч%2$dд%3$dм Главен постер Епизодата %d ќе биде објавена на Постер за епизода diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index cdc50cea..82f4a657 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -4,7 +4,7 @@ വേഗം (%.2fx) റേറ്റിംഗ്: %.1f പുതിയ അപ്ഡേറ്റ് -\n%s -> %s +\n%1$s -> %2$s CloudStream ഹോം തിരയുക @@ -169,10 +169,10 @@ ഔചിത്യ വീഡിയോ ക്വാളിറ്റി ചരിത്രം കണ്ടതാണെന്ന് അടയാളപ്പെടുത്തുക - %dd% + %1$d%2$d yg5t4r%dujyhtg qeWERT - %fghj%gf + %1$sghj%2$d rtf:% അക്കൗണ്ട് ഉണ്ടാക്കുക പുറത്ത്പോകുന്നതോടുകൂടി ആപ് അപ്ഡേറ്റ് ആവുന്നതാണ് diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index e579381e..0c90b0c2 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -26,8 +26,8 @@ Episod %d akan disiarkan dalam Pelakon:%s Mod Selamat Hidup - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm Poster Episod Poster Utama diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml index e0346f19..f60362ae 100644 --- a/app/src/main/res/values-my/strings.xml +++ b/app/src/main/res/values-my/strings.xml @@ -1,8 +1,8 @@ သရုပ်ဆောင်များ: %s - %dရက် %ddနာရီ %ddမိနစ် - %ddနာရီ %ddမိနစ် + %1$dရက် %2$ddနာရီ %3$ddမိနစ် + %1$ddနာရီ %2$ddမိနစ် %ddမိနစ် ပိုစတာ အပိုင်း ပိုစတာ @@ -12,7 +12,7 @@ နောက်ခံပုံရိပ်ကို အကြိုကြည့်ရန် အဆင့်: %.1f အပ်ဒိတ်အသစ်! -\n%s -> %s +\n%1$s -> %2$s စစ်ထုတ်မှု %d မိနစ် CloudStream @@ -111,7 +111,7 @@ အတွဲမရှိပါ အပိုင်း အပိုင်းများ - %d-%d + %1$d-%2$d ဒါကအပြီးဖျက်ခြင်းဖြစ်ပါသည် %s \nသင်သေချာပါသလား။ %dမိနစ် @@ -204,7 +204,7 @@ ပုံပန်းသဏ္ဌာန် အထွေထွေ ပင်မစာမျက်နှာမှာကျပန်းခလုတ်ကိုပြပါ - %s အပိုင်း %d + %1$s အပိုင်း %2$d အပိုင်း %d ထုတ်လွှင့်ပြသမည် ပိုစတာ ပံ့ပိုးပေးသောဝန်ဆောင်မှုပြောင်းရန် @@ -291,9 +291,9 @@ အက်ပ်ဘာသာစကား မူလအခြေအနေများကိုပြန်ထားပါ စိတ်မကောင်းပါ။အက်ပ်ရပ်တန့်သွားပါတယ်။အမည်မဖော်ထားတဲ့တင်ပြချက်ကို အက်ပ်ရေးသားသူများထံ ပို့မှာဖြစ်ပါတယ် - %s %d%s + %1$s %2$d%3$s အတွဲ - %d %s + %1$d %2$s အပိုင်း အပိုင်းများမတွေ့ပါ ဖိုင်ကိုဖျက်ရန် @@ -442,7 +442,7 @@ အရည်အသွေးများ စာတန်းထိုး ကုဒ်လုပ်ခြင်း ပံ့ပိုးပေးသူ စစ်ဆေးမှု - %s %s + %1$s %2$s အကောင့်ဝင်မည် အကောင့်ပြောင်းမည် ချိန်ညိှခြင်း @@ -478,8 +478,8 @@ ဤဘာသာစကားများဖြင့် ဗီဒီယိုများကို ကြည့်ရှုပါ ဖြည့်စွက်များ ဒေါင်းလုဒ်လုပ်ပြီး ဖြည့်စွက်များဖျက်ပြီး - ဒေါင်းလုဒ်လုပ်ခြင်း စတင်သည် %d %s… - ဒေါင်းလုဒ်လုပ်ပြီး %d %s + ဒေါင်းလုဒ်လုပ်ခြင်း စတင်သည် %1$d %2$s… + ဒေါင်းလုဒ်လုပ်ပြီး %1$d %2$s အားလုံး %s ဒေါင်းလုဒ်လုပ်ပြီးသား ရီပိုစစ်ထရီထဲတွင်ဖြည့်စွက်များမတွေ့ပါ ရီပိုစစ်ထရီမတွေ့ပါ၊URLကိုပြန်စစ်ပြီးဗီပီအန်ဖြင့်ကြိုးစားကြည့်ပါ diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml new file mode 100644 index 00000000..42eba3cc --- /dev/null +++ b/app/src/main/res/values-ne/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index c70ebd4b..95c527f9 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -16,12 +16,12 @@ Forhandsvis bakgrunnsbilete Vurdert: %.1f Ny oppdatering tilgjengeleg! -\n%s -> %s +\n%1$s -> %2$s %d minutt Miniatyrbilete Episode %d vil bli sleppt om - %dd %dt %dm - %dt %dm + %1$dd %2$dt %3$dm + %1$dt %2$dm Rollebesetning: %s Hovudminiatyrbilete Endre leverandør @@ -104,7 +104,7 @@ Spel av episode Tilbakestill til standardverdi Beklager, programmet har krasjet. Ein anonymisert feilrapport vil bli sendt til utviklarane - %s %d%s + %1$s %2$d%3$s Ingen sesong Episode S diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 9845120b..a1ccec46 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -13,7 +13,7 @@ Avspillingshastighet (%.2fx) Vurdert: %.1f Ny oppdatering funnet! -\n%s -> %s +\n%1$s -> %2$s CloudStream Hjem Søk @@ -207,15 +207,15 @@ %dm Tøm Kontoer - %s Ep %d + %1$s Ep %2$d Spill med CloudStream Kopier Episode %d vil bli sluppet om - %dt %dm + %1$dt %2$dm %d min Lukk Undertekstspråk - %dd %dt %dm + %1$dd %2$dt %3$dm Lagre Vis Logcat 🐈 Innstillinger for Chromecast-innstillinger @@ -262,7 +262,7 @@ Maks Film Last ned undertekster - %s %d%s + %1$s %2$d%3$s Klon siden Vis tilfeldig-knapp på hjemmesiden Torrent @@ -353,18 +353,18 @@ Forrige Krasjrapportering Utvidelser - %d %s + %1$d %2$s MittKuleBrukernavn Hopp over oppsett Hva du ønsker å se - %s %s + %1$s %2$s Se videoer på disse språkene Ferdig programtillegg Trygt modus påslått Sett visningsstatus Synkroniser automatisk fremdriften din for gjeldende episode - %d-%d + %1$d-%2$d Offentlig liste programtillegg %dm @@ -455,9 +455,9 @@ Tilbakeblikk SD Forfilm - Lastet ned %d %s + Lastet ned %1$d %2$s Navn under plakat - Begynte nedlasting av %d %s … + Begynte nedlasting av %1$d %2$s … Bruk etter omstart av programmet Cam Cam diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index 22f7d1ca..eb3d7cd3 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -2,8 +2,8 @@ ଅଧିକ ଵିକଳ୍ପ ଦେଖୁଛନ୍ତି - %dଦି %dଘ %dମି - %dଘ %dମି + %1$dଦି %2$dଘ %3$dମି + %1$dଘ %2$dମି %dମି ପୁନଃଦେଖୁଛନ୍ତି ଲୁଚାଅ @@ -89,7 +89,7 @@ ଆଣ୍ଡ୍ରଏଡ୍ ଟିଵି ଅଙ୍ଗଭଙ୍ଗୀ ନୂଆ ଅଦ୍ୟତନ ମିଳିଲା! -\n%s -> %s +\n%1$s -> %2$s ଅଵଧି ଆପ୍ ବ୍ୟାକଅପ୍ ଫାଇଲ୍ ଧାରଣ ହେଲା @@ -157,5 +157,5 @@ %s ସନ୍ଧାନ କରିବା… ପରବର୍ତ୍ତୀ ଅଧ୍ୟାୟ କୌଣସି ତଥ୍ୟ ନାହିଁ - %s ଅ %d + %1$s ଅ %2$d diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 95a3e1c6..6ebb4575 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -3,10 +3,10 @@ Prędkość (%.2fx) Ocena: %.1f Znaleziono nową aktualizację! -\n%s -> %s +\n%1$s -> %2$s Filler %d min - %s Odc. %d + %1$s Odc. %2$d Obrazek Obrazek odcinka Główny obrazek @@ -168,12 +168,12 @@ Zresetowano Awaria aplikacji. Anonimowe zgłoszenie błędu zostanie wysłane programistom Sezon - %s %d%s + %1$s %2$d%3$s Brak sezonu Odcinek Odcinki - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S O Nie znaleziono odcinków @@ -293,7 +293,7 @@ Pozycja tytułu względem obrazka Ustaw tytuł pod obrazkiem Kod języka (pl) - %s %s + %1$s %2$s konto Wyloguj się Zaloguj się @@ -382,8 +382,8 @@ Usunięto rozszerzenie Błąd ładowania %s 18+ - Rozpoczęto pobieranie %d %s… - Pobrano %d %s + Rozpoczęto pobieranie %1$d %2$s… + Pobrano %1$d %2$s Wszystkie %s już pobrane Pobierz wszystko rozszerzenie @@ -436,11 +436,11 @@ Link do odtwarzania Odtwórz w CloudStream Pomiń %s - %dh %dm + %1$dh %2$dm Tak Nie Aktualizacja aplikacji nie powiodła się - %dd %dh %dm + %1$dd %2$dh %3$dm Instalowanie aktualizacji aplikacji… %dm Oznacz jako obejrzane diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 0cd970c4..398a1aa3 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -362,8 +362,8 @@ Plugin Carregado Plugin Apagado Falha ao carregar %s - Download iniciado %d %s… - Transferido %d %s com sucesso + Download iniciado %1$d %2$s… + Transferido %1$d %2$s com sucesso Tudo %s já transferido Transferência em batch plugin @@ -395,13 +395,13 @@ -30 Vídeo +30 - %s %d%s + %1$s %2$d%3$s Elenco: %s Atualização iniciada Log Alguns aparelhos não suportam o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s Iniciar Falha Sucesso @@ -506,7 +506,7 @@ Cache Android TV Legendas - %s %s + %1$s %2$s TS Cam Cam diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index e1191d2b..72f16012 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -195,12 +195,12 @@ a ou oh ouhuouhoaaha aaooohhouhhha hauauuu aaaaaaa uuuuuu -\n%s -> %s - %s aaou %d +\n%1$s -> %2$s + %1$s aaou %2$d oouaaahh %s aaaaaaugh ouh %d uuoogahaaah ooua-h-ha - %daaa %duuu %dhhhg - %daaaaaaauuhh %doouuahaaha + %1$daaa %2$duuu %3$dhhhg + %1$daaaaaaauuhh %2$doouuahaaha %dmmmm... aaaaaaaaaaaaahh (%.2foouo) aghaaaooo-ough %.1f diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 75388b23..1a084e02 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1,11 +1,11 @@ - %s Ep %d + %1$s Ep %2$d Distribuție: %s Episodul %d va fi lansat în - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm Poster @@ -20,7 +20,7 @@ Viteză (%.2fx) Evaluare: %.1f Actualizare nouă găsită! -\n%s -> %s +\n%1$s -> %2$s Filler %d min @@ -295,7 +295,7 @@ David Popovici davidpopovici.com Cod limbă (RO) - %s %s + %1$s %2$s Cont Deconectare Conectare @@ -392,9 +392,9 @@ Altele Trecut Start - %d %s + %1$d %2$s NSFW - %d-%d + %1$d-%2$d Player Afișat - Căutați Suma Player Ascuns - Căutați Suma Livestream-uri @@ -407,7 +407,7 @@ Video Instalator APK Instalează automat toate plugin-urile neinstalate din depozite adăugate. - %s %d%s + %1$s %2$d%3$s Unele telefoane nu acceptă noul program de instalare a pachetului. Încercați opțiunea veche dacă nu se instalează actualizările. Refaceți procesul de configurare Treci peste partea de configurare @@ -489,7 +489,7 @@ \nDin cauza unui DMCA takedown fără creier de către Sky UK Limited 🤮 nu putem lega site-ul de depozit în aplicație. \n \nAlăturați-vă Discordului nostru sau căutați online. - A început să descarce %d %s… + A început să descarce %1$d %2$s… Mod sigur pornit Fișier Mod Sigur găsit! \nNu încarcă nicio extensie la pornire până când fișierul nu este eliminat. @@ -531,7 +531,7 @@ Link către stream Acest lucru va șterge, de asemenea, toate plugin-urile din depozit Se instalează actualizarea aplicației… - S-a descărcat %d %s + S-a descărcat %1$d %2$s Suportat Playlist HLS Piste video diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 173c92cb..393ca0ba 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -35,7 +35,7 @@ Скорость (%.2fx) Оценили: %.1f Новое обновление найдено! -\n%s -> %s +\n%1$s -> %2$s Заполнитель CloudStream Убирать @@ -200,7 +200,7 @@ Присоединится в Discord Бесплатно %dm - %d ч. %d мин. + %1$d ч. %2$d мин. Фильмы Мультфильм Сериалы @@ -282,10 +282,10 @@ Поиск предварительных обновлений вместо полных выпусков Повторить процесс настройки Этот провайдер не поддерживает Chromecast - %s %d%s + %1$s %2$d%3$s Нет сезона - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s Прямые трансляции Прямая трансляция Ошибка источника @@ -390,7 +390,7 @@ Cam HQ Отключено: %d - %s %s + %1$s %2$s %s аутентифицировано Не удается логин на %s Макс @@ -450,9 +450,9 @@ Кодировка субтитров Загрузить из файла Рейтинг: %s - Скачано %d %s + Скачано %1$d %2$s Все %s уже скачаны - Начата загрузка %d %s… + Начата загрузка %1$d %2$s… Не скачано: %d Скачать все плагины из этого репозитория\? Включен безопасный режим diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index c3d107f5..a63d44fe 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -1,10 +1,10 @@ Metalaya: %s - %dm %ds %dd - %ds %dd + %1$dm %2$ds %3$dd + %1$ds %2$dd %dd - %s Xlq %d + %1$s Xlq %2$d Xalqadda %d waxa lasoo deyn doonaa Daji Dejintii ma guulaysan @@ -28,7 +28,7 @@ Kadinka Qiimaysan: %.1f App cusub baa soo baxay! -\n%s -> %s +\n%1$s -> %2$s Soo dejinta Raadi Dookhyo kale @@ -163,7 +163,7 @@ Light novel app, isla dhiseyaasha appkan baa leh Tus wixii appka ku cusub Dib uso bilow fadhiisinta appka - %d %s + %1$d %2$s Ku cusbooneysii baahinta ugu horreysa Rakibaha APKga Socda @@ -179,10 +179,10 @@ Dulucuda Raalliahow, wuu duqeeyey appku, waxa warbixin cillad-saarka ka caawisa loo direy dhiseyaasha Kalka - %s %d%s + %1$s %2$d%3$s Xalqad Xalqado - %d illaa %d + %1$d illaa %2$d K X Kartoon @@ -361,7 +361,7 @@ Midna Websaydkayga.com Koodhka Luuqadda (en) - %s %s + %1$s %2$s Akoon samee Talantaalli Qiimaysan @@ -467,8 +467,8 @@ Waa la tiray sidkanahan Waa ka keeni kari waayey %s 18+ - Waa la dejinayaa %d %s… - Waa la dejiyey %d %s + Waa la dejinayaa %1$d %2$s… + Waa la dejiyey %1$d %2$s Dhammaan %s way dejisan yihiin Kordhiyeyaasha Liiska bulshada kale diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 806a1ca3..14f388a7 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -3,7 +3,7 @@ Betygsatt: %.1f Hastighet (%.2fx) Ny uppdatering hittad! -\n%s -> %s +\n%1$s -> %2$s CloudStream Hem Sök @@ -210,8 +210,8 @@ DNS över HTTPS " " Appens tema - %s A%d - %dh %dm + %1$s A%2$d + %1$dh %2$dm %dm Spela med CloudStream Chromecast-undertexter @@ -220,7 +220,7 @@ Konton Uppdateringar och backup Automatiska pluginuppdateringar - %dd %dh %dm + %1$dd %2$dh %3$dm Sök %s… Kopiera Stäng @@ -236,9 +236,9 @@ Visa trailers @string/home_play OVA - %d-%d - %d %s - %s %d%s + %1$d-%2$d + %1$d %2$s + %1$s %2$d%3$s -30 %dm \nåterstår @@ -321,7 +321,7 @@ Ändra appens utseende så att den passar din enhet Skådespelare: %s Spela Livestream - %s %s + %1$s %2$s Ingen undertextfördröjning Spelarfunktioner Trailer diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index ca4a1377..234207b9 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -106,18 +106,18 @@ Chromecast வசன அமைப்புகள் இருண்ட மேலடுக்குக்குப் பதிலாக ஆப் பிளேயரில் சிஸ்டம் பிரகாசத்தைப் பயன்படுத்தவும் அத்தியாயம் %d-இன் வெளியீட்டு நேரம் - %dம %dநி + %1$dம %2$dநி %dநி அடுத்து ஏதாவது உலாவி %d நிமி CloudStream-உடன் இயக்கு புதிய புதுப்பிப்பு உள்ளது -\n%s->%s +\n%1$s->%2$s நிரப்பி போஸ்டர் எபிசோட்டின் போஸ்டர் போஸ்டர் பிரதான போஸ்டர் - %s Ep %d + %1$s Ep %2$d diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml index a9079ed5..46235bbd 100644 --- a/app/src/main/res/values-ti/strings.xml +++ b/app/src/main/res/values-ti/strings.xml @@ -1,6 +1,6 @@ - %s ክፋል %d + %1$s ክፋል %2$d ክፋል %d በ ላይ ይወጣል ተዋሳእቲ፡ %s diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 03138259..c5e29006 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -2,7 +2,7 @@ - %s Ep %d + %1$s Ep %2$d Poster @string/result_poster_img_des @@ -16,7 +16,7 @@ Bilis (%.2fx) Rated: %.1f Bagong update! -\n%s -> %s +\n%1$s -> %2$s CloudStream Home Maghanap @@ -214,8 +214,8 @@ Episode %d ay ipapalabas sa loob ng Hanapin ang %s… %d min - %dd, %dh:%dm - %dh : %dm + %1$dd, %2$dh:%3$dm + %1$dh : %2$dm I-play gamit ang CloudStream Pinapanuod Uli Panuorin ang Livestream diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3445c57a..4d8ae42c 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -631,4 +631,11 @@ PIN girin PIN Geçerli PIN\'i Giriniz + Ekran yönü için bir geçiş düğmesi göster + rotate_video_key + auto_rotate_video_key + Otomatik döndürme + Döndür + Video yönüne göre ekran yönünün otomatik olarak değişmesini sağla + Bağlantılar Yeniden Yüklendi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 224b621a..aae04cb0 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -584,4 +584,11 @@ Пропускати вибір акаунту під час запуску Керування акаунтами Редагувати акаунт + Показувати кнопку перемикання орієнтації екрана + rotate_video_key + Поворот + Посилання перезавантажені + auto_rotate_video_key + Автоповорот + Ввімкнути автоматичний поворот екрана на основі орієнтації відео diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index b437e2d0..28880f17 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -11,11 +11,11 @@ پس منظر کا دیکھنا درجہ بندی: %.1f نیا update آگیا ہے! -\n%s -> %s +\n%1$s -> %2$s بھرنے والا %d منٹ - %d دن %d گھنٹے %d منٹ - %d گھنٹے %d منٹ + %1$d دن %2$d گھنٹے %3$d منٹ + %1$d گھنٹے %2$d منٹ %d منٹ CloudStream CloudStream کے ساتھ چلائیں @@ -146,7 +146,7 @@ کلپ بورڈ میں نقل ہوا قسط چلائیں طے شدہ قدر پر ری سیٹ کریں - %s قسط %d + %1$s قسط %2$d پوسٹر شئیر کریں انواع @@ -180,12 +180,12 @@ کچھ فون نئے پیکیج انسٹالر کو سپورٹ نہیں کرتے ہیں. اگر اپ ڈیٹس انسٹال نہیں ہوتے ہیں تو لیگیسی آپشن کو آزمائیں. معذرت، ایپلی کیشن کریش ہو گئی. ایک گمنام بگ رپورٹ ڈویلپرز کو بھیجی جائے گی سیزن - %s %d%s + %1$s %2$d%3$s کوئی سیزن نہیں قسط اقساط - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S E کوئی اقساط نہیں ملی @@ -344,7 +344,7 @@ اسکرین پر فٹ کھینچیں اکاؤنٹ بنائیں - %s %s + %1$s %2$s اکاؤنٹ سوئچ کریں ٹریکنگ شامل کریں تواقت @@ -406,8 +406,8 @@ ذخیرہ URL پلگ ان لوڈ ہو گیا %s کو لوڈ نہیں کیا جا سکا - ڈاؤن لوڈ ہونا شروع ہو گیا %d %s… - ڈاؤن لوڈ کیا گیا %d %s + ڈاؤن لوڈ ہونا شروع ہو گیا %1$d %2$s… + ڈاؤن لوڈ کیا گیا %1$d %2$s سبھی %s پہلے ہی ڈاؤن لوڈ ہو چکے ہیں رابطہ بحال کرو پلگ ان diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 52897633..bb30afc7 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -11,11 +11,11 @@ %d %.1f/10.0 %d - %s 共 %d 集 + %1$s 共 %2$d 集 演員:%s 第 %d 集即將發佈於 - %dd %dh %dm - %dh %dm + %1$dd %2$dh %3$dm + %1$dh %2$dm %dm 封面 @@ -32,12 +32,12 @@ 速度(%.2fx) 評分:%.1f 發現新版本! -\n%s -> %s +\n%1$s -> %2$s 填充 %d 分鐘 CloudStream 使用 CloudStream 播放 - 主頁 + 主畫面 搜尋 下載 設定 @@ -78,14 +78,14 @@ %s - %s 播放 載入連結錯誤 - 內部存儲 + 內部儲存空間 配音 字幕 刪除檔案 播放檔案 繼續下載 暫停下載 - 禁用自動錯誤報告 + 停用自動錯誤報告 更多資訊 隱藏 播放 @@ -98,7 +98,7 @@ 複製 關閉 清除 - 保存 + 儲存 播放速度 字幕設定 字體顏色 @@ -167,14 +167,14 @@ 資訊 進階搜尋 為您提供按片源分開的搜尋結果 - 僅在崩潰時傳送資料 + 僅在程式崩潰時傳送相關資料 不傳送資料 顯示動畫外傳 顯示預告片 顯示來自 Kitsu 的封面 - 在搜尋結果中隱藏選中的影片畫質 + 在搜尋結果中隱藏所選的影片畫質 自動更新外掛程式 - 顯示應用更新 + 顯示應用程式更新 啟動應用程式後自動搜尋更新。 更新至預覽版 搜尋預覽版更新而不是僅搜尋正式版 @@ -190,18 +190,18 @@ 連結已複製到剪貼簿 播放劇集 重設為預設值 - 很抱歉,應用崩潰了,將傳送一份匿名錯誤報告給開發者 + 很抱歉,應用程式崩潰了,將傳送一份匿名錯誤報告給開發者 - %s %d%s + %1$s %2$d%3$s 無季 - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S E 未找到劇集 - 刪除文件 + 刪除檔案 刪除 取消 暫停 @@ -210,8 +210,8 @@ +30 這將永遠刪除 %s \n你確定嗎\? - %d 分鐘 -\n剩餘的 + 剩下 +\n%d 分鐘 連載中 已完結 狀態 @@ -252,7 +252,7 @@ 其他 來源錯誤 遠端錯誤 - 渲染器錯誤 + 算繪器錯誤 意料之外的播放器錯誤 下載錯誤,請檢查儲存權限 Chromecast 劇集 @@ -290,7 +290,7 @@ 影片緩衝長度 影片快取存儲 清除影片和圖片快取 - 如果在記憶體不足的裝置(例如 Android TV)上設定得太高會導致崩潰。 + 如果在記憶體不足的裝置(例如 Android TV)上設定得太高會導致程式崩潰。 如果在儲存空間較小的裝置(例如 Android TV)上設定過高會導致問題。 DNS over HTTPS 用於繞過網路服務供應商的封鎖 @@ -308,18 +308,18 @@ Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. 通用 隨機按鈕 - 在主頁中顯示隨機按鈕 + 在主畫面中顯示隨機按鈕 片源語言 - 應用佈局 + 應用程式版面配置 偏好類型 在支援的片源中啟用 NSFW 內容 字幕編碼 片源 佈局 自動 - 電視佈局 - 手機佈局 - 模擬器佈局 + 電視版面配置 + 手機版面配置 + 模擬器版面配置 主題色 應用程式主題 封面標題位置 @@ -344,15 +344,15 @@ Kitsu Trakt --> - %s %s + %1$s %2$s 帳號 登出 登入 切換帳號 - 添加帳號 - 創建帳號 - 添加同步 - 已添加 %s + 新增帳號 + 建立帳號 + 加入同步 + 已新增 %s 同步 評分 %d / 10 @@ -374,8 +374,8 @@ 同步字幕 1000 毫秒 字幕延遲 - 如果字幕過早顯示 %d 毫秒,請使用此選項 - 如果字幕過晚顯示 %d 毫秒,請使用此選項 + 如果字幕提早 %d 毫秒顯示,請使用此選項 + 如果字幕延遲 %d 毫秒顯示,請使用此選項 無字幕延遲 封面 @@ -32,7 +32,7 @@ 速度(%.2fx) 评分:%.1f 发现新版本! -\n%s -> %s +\n%1$s -> %2$s 填充 %d 分钟 CloudStream @@ -193,12 +193,12 @@ 重置为默认值 抱歉,应用崩溃了,将发送一份匿名错误报告给开发者 - %s %d%s + %1$s %2$d%3$s 无季 - %d-%d - %d %s + %1$d-%2$d + %1$d %2$s S E 未找到剧集 @@ -345,7 +345,7 @@ Kitsu Trakt --> - %s %s + %1$s %2$s 账户 注销 登录 @@ -444,8 +444,8 @@ 插件已删除 无法加载 %s 18+ - 开始下载 %d %s… - 已下载 %d %s + 开始下载 %1$d %2$s… + 已下载 %1$d %2$s 全部 %s 已经下载 批量下载 插件 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 79e3d89d..6048d5cc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -734,4 +734,10 @@ Logged in as %s Skip account selection at startup Use Default Account + Rotate + rotate_video_key + Display a toggle button for screen orientation + auto_rotate_video_key + Enable automatic switching of screen orientation based on video orientation + Auto rotate diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml index ad33e036..eccb78e3 100644 --- a/app/src/main/res/xml/settings_player.xml +++ b/app/src/main/res/xml/settings_player.xml @@ -86,6 +86,18 @@ app:defaultValue="true" android:summary="@string/enable_skip_op_from_database_des" app:key="@string/enable_skip_op_from_database" /> + +