From 67fe6730e02c846e9ef1e83308e3d20ace62524e Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 19 Nov 2022 20:44:12 +0100 Subject: [PATCH 001/104] library work in progress --- .../lagradost/cloudstream3/MainActivity.kt | 1 + .../cloudstream3/syncproviders/SyncAPI.kt | 3 + .../cloudstream3/syncproviders/SyncRepo.kt | 15 +- .../syncproviders/providers/AniListApi.kt | 26 ++- .../syncproviders/providers/MALApi.kt | 23 ++- .../ui/library/LibraryFragment.kt | 106 ++++++++++++ .../ui/library/LibraryViewModel.kt | 74 ++++++++ .../cloudstream3/ui/library/PageAdapter.kt | 58 +++++++ .../ui/library/ViewpagerAdapter.kt | 85 ++++++++++ .../lagradost/cloudstream3/ui/library/test.kt | 11 ++ .../lagradost/cloudstream3/utils/AppUtils.kt | 46 +++++ app/src/main/res/color/item_select_color.xml | 2 + .../ic_baseline_collections_bookmark_24.xml | 6 + .../main/res/drawable/ic_baseline_sort_24.xml | 5 + .../main/res/drawable/ic_baseline_star_24.xml | 4 +- .../res/drawable/indicator_background.xml | 6 + app/src/main/res/drawable/rating_bg_color.xml | 6 + app/src/main/res/layout/fragment_library.xml | 63 +++++++ .../res/layout/library_viewpager_page.xml | 17 ++ .../layout/search_result_grid_expanded.xml | 159 ++++++++++-------- app/src/main/res/menu/bottom_nav_menu.xml | 28 +-- app/src/main/res/menu/library_menu.xml | 17 ++ .../main/res/navigation/mobile_navigation.xml | 9 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 7 + app/src/main/res/values/styles.xml | 4 + 26 files changed, 692 insertions(+), 90 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt create mode 100644 app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_sort_24.xml create mode 100644 app/src/main/res/drawable/indicator_background.xml create mode 100644 app/src/main/res/drawable/rating_bg_color.xml create mode 100644 app/src/main/res/layout/fragment_library.xml create mode 100644 app/src/main/res/layout/library_viewpager_page.xml create mode 100644 app/src/main/res/menu/library_menu.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 6c9fadd8..9937f183 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -177,6 +177,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val isNavVisible = listOf( R.id.navigation_home, R.id.navigation_search, + R.id.navigation_library, R.id.navigation_downloads, R.id.navigation_settings, R.id.navigation_download_child, 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 5aa56a02..ddb9f660 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,7 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.library.LibraryItem interface SyncAPI : OAuth2API { val mainUrl: String @@ -22,6 +23,8 @@ interface SyncAPI : OAuth2API { suspend fun search(name: String): List? + suspend fun getPersonalLibrary(): List? + fun getIdFromUrl(url : String) : String data class SyncSearchResult( diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt index b621e81a..04e15f55 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt @@ -4,6 +4,7 @@ import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.ui.library.LibraryItem class SyncRepo(private val repo: SyncAPI) { val idPrefix = repo.idPrefix @@ -16,21 +17,25 @@ class SyncRepo(private val repo: SyncAPI) { return safeApiCall { repo.score(id, status) } } - suspend fun getStatus(id : String) : Resource { + suspend fun getStatus(id: String): Resource { return safeApiCall { repo.getStatus(id) ?: throw ErrorLoadingException("No data") } } - suspend fun getResult(id : String) : Resource { + suspend fun getResult(id: String): Resource { return safeApiCall { repo.getResult(id) ?: throw ErrorLoadingException("No data") } } - suspend fun search(query : String) : Resource> { + suspend fun search(query: String): Resource> { return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() } } - fun hasAccount() : Boolean { + suspend fun getPersonalLibrary(): Resource> { + return safeApiCall { repo.getPersonalLibrary() ?: throw ErrorLoadingException() } + } + + fun hasAccount(): Boolean { return normalSafeApiCall { repo.loginInfo() != null } ?: false } - fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url) + fun getIdFromUrl(url: String): String = repo.getIdFromUrl(url) } \ 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 606fee97..b4f0f790 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.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.library.LibraryItem import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -595,7 +596,26 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("score") val score: Int, @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media - ) + ) { + fun toLibraryItem(listName: String?): LibraryItem? { + return LibraryItem( + // English title first + this.media.title.english ?: this.media.title.romaji ?: this.media.synonyms.firstOrNull() + ?: "", + this.media.id.toString(), + listName ?: return null, + this.progress, + this.media.episodes, + this.score, + "AniList", + TvType.Anime, + this.media.coverImage.large ?: this.media.coverImage.medium, + null, + null, + null + ) + } + } data class Lists( @JsonProperty("status") val status: String?, @@ -630,6 +650,10 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } + override suspend fun getPersonalLibrary(): List? { + return getAnilistAnimeListSmart()?.map { it.entries.mapNotNull { entry -> entry.toLibraryItem(entry.status ?: it.status) } }?.flatten() + } + private suspend fun getFullAnilistList(): FullAnilistList? { var userID: Int? = null /** WARNING ASSUMES ONE USER! **/ 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 ea27720a..d9011a1b 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 @@ -7,11 +7,13 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ShowStatus +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.library.LibraryItem import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -381,7 +383,22 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { data class Data( @JsonProperty("node") val node: Node, @JsonProperty("list_status") val list_status: ListStatus?, - ) + ) { + fun toLibraryItem(): LibraryItem { + return LibraryItem( + this.node.title, + this.node.id.toString(), + this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", + this.list_status?.num_episodes_watched, + this.node.num_episodes, + this.list_status?.score, + "MAL", + TvType.Anime, + this.node.main_picture?.large ?: this.node.main_picture?.medium, + null, null, null + ) + } + } data class Paging( @JsonProperty("next") val next: String? @@ -424,6 +441,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } + override suspend fun getPersonalLibrary(): List? { + return getMalAnimeListSmart()?.map { it.toLibraryItem() } + } + private suspend fun getMalAnimeList(): Array { checkMalToken() var offset = 0 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 new file mode 100644 index 00000000..932d3916 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -0,0 +1,106 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.Context +import androidx.lifecycle.ViewModelProvider +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.appcompat.widget.SearchView +import androidx.fragment.app.activityViewModels +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.mvvm.observe +import com.lagradost.cloudstream3.syncproviders.AccountManager +import com.lagradost.cloudstream3.syncproviders.providers.MALApi +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import kotlinx.android.synthetic.main.fragment_library.* +import kotlinx.android.synthetic.main.library_viewpager_page.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +class LibraryFragment : Fragment() { + + companion object { + fun newInstance() = LibraryFragment() + } + + private val libraryViewModel: LibraryViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_library, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.fixPaddingStatusbar(library_root) + + val sortView = + menu_toolbar?.menu?.findItem(R.id.sort_button) + sortView?.setOnMenuItemClickListener { + val methods = libraryViewModel.sortingMethods + .map { txt(it.stringRes).asString(context ?: view.context) } + + activity?.showBottomDialog( + methods, + libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod), + "Sort by", + false, + {}, + { + val method = libraryViewModel.sortingMethods[it] + libraryViewModel.sort(method) + } + ) + true + } + + val searchView = + menu_toolbar?.menu?.findItem(R.id.search_button)?.actionView as? MenuSearchView + searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + return true + } + + override fun onQueryTextChange(newText: String?): Boolean { + libraryViewModel.sort(ListSorting.Query, newText) + return true + } + }) + + libraryViewModel.loadPages() + + viewpager?.setPageTransformer(HomeScrollTransformer()) + viewpager?.adapter = viewpager.adapter ?: ViewpagerAdapter(emptyList()) + viewpager?.offscreenPageLimit = 10 + + observe(libraryViewModel.pages) { pages -> + (viewpager.adapter as? ViewpagerAdapter)?.pages = pages + viewpager.adapter?.notifyItemChanged(viewpager?.currentItem ?: 0) + + TabLayoutMediator( + library_tab_layout, + viewpager, + ) { tab, position -> + tab.text = pages.getOrNull(position)?.title + }.attach() + } + } +} + +class MenuSearchView(context: Context) : SearchView(context) { + override fun onActionViewCollapsed() { + super.onActionViewCollapsed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt new file mode 100644 index 00000000..167290e1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -0,0 +1,74 @@ +package com.lagradost.cloudstream3.ui.library + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import me.xdrop.fuzzywuzzy.FuzzySearch + +enum class ListSorting(@StringRes val stringRes: Int) { + Query(R.string.none), + RatingHigh(R.string.sort_rating_desc), + RatingLow(R.string.sort_rating_asc), + UpdatedNew(R.string.sort_updated_new), + UpdatedOld(R.string.sort_updated_old), + AlphabeticalA(R.string.sort_alphabetical_a), + AlphabeticalZ(R.string.sort_alphabetical_z), +} + +class LibraryViewModel : ViewModel() { + private val _pages: MutableLiveData> = MutableLiveData(emptyList()) + val pages: LiveData> = _pages + + private val _currentApiName: MutableLiveData = MutableLiveData("") + val currentApiName: LiveData = _currentApiName + + private val listApis = SyncApis.filter { it.hasAccount() } + private var currentApi = listApis.firstOrNull() + + val sortingMethods = listOf( + ListSorting.RatingHigh, + ListSorting.RatingLow, +// ListSorting.UpdatedNew, +// ListSorting.UpdatedOld, + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ) + + var currentSortingMethod: ListSorting = sortingMethods.first() + private set + + fun switchList() { + currentApi = listApis[(listApis.indexOf(currentApi) + 1) % listApis.size] + loadPages() + } + + fun sort(method: ListSorting, query: String? = null) { + val currentList = pages.value ?: return + currentSortingMethod = method + currentList.forEachIndexed { index, page -> + page.sort(method, query) + } + _pages.postValue(currentList) + } + + fun loadPages() { + ioSafe { + currentApi?.let { repo -> + val list = (repo.getPersonalLibrary() as? Resource.Success)?.value + val pages = (list ?: emptyList()).groupBy { it.listName }.map { + Page( + it.key, + it.value + ) + } + _pages.postValue(pages) + _currentApiName.postValue(repo.name) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt new file mode 100644 index 00000000..7db40f88 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -0,0 +1,58 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.utils.AppUtils +import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* + +class PageAdapter( + override val items: MutableList, +) : + AppUtils.DiffAdapter(items) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return LibraryItemViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.search_result_grid_expanded, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is LibraryItemViewHolder -> { + holder.bind(items[position], position) + } + } + } + + inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun bind(item: LibraryItem, position: Int) { + SearchResultBuilder.bind( + { println("CLICKED ${it.action}") }, + item, + position, + itemView, + ) + + // Set watch progress bar +// val showProgress = item.episodesCompleted != null && item.episodesTotal != null +// itemView.watchProgress.isVisible = showProgress +// +// if (showProgress) { +// itemView.watchProgress.max = item.episodesTotal!! +// itemView.watchProgress.progress = item.episodesCompleted!! +// } + itemView.imageText.text = item.name + val showRating = (item.personalRating ?: 0) != 0 + itemView.text_rating.isVisible = showRating + if (showRating) { + itemView.text_rating.text = item.personalRating.toString() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt new file mode 100644 index 00000000..3939f50e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -0,0 +1,85 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.SearchQuality +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.TvType +import kotlinx.android.synthetic.main.library_viewpager_page.view.* +import me.xdrop.fuzzywuzzy.FuzzySearch + +data class Page( + val title: String, var items: List +) { + fun sort(method: ListSorting?, query: String? = null) { + items = when (method) { + ListSorting.Query -> + if (query != null) { + items.sortedBy { + -FuzzySearch.partialRatio( + query.lowercase(), it.name.lowercase() + ) + } + } else items + ListSorting.RatingHigh -> items.sortedBy { -(it.personalRating ?: 0) } + ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) } + ListSorting.AlphabeticalA -> items.sortedBy { it.name } + ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed() + else -> items + } + } +} + +data class LibraryItem( + override val name: String, + override val url: String, + val listName: String, + val episodesCompleted: Int?, + val episodesTotal: Int?, + /** Out of 100 */ + val personalRating: Int?, + override val apiName: String, + override var type: TvType?, + override var posterUrl: String?, + override var posterHeaders: Map?, + override var id: Int?, + override var quality: SearchQuality?, +) : SearchResponse + + +class ViewpagerAdapter(var pages: List) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return PageViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.library_viewpager_page, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is PageViewHolder -> { + holder.bind(pages[position]) + } + } + } + + inner class PageViewHolder(private val itemViewTest: View) : + RecyclerView.ViewHolder(itemViewTest) { + fun bind(page: Page) { + if (itemViewTest.page_recyclerview?.adapter == null) { + itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList()) + itemView.page_recyclerview?.spanCount = 4 + } else { + (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) + itemViewTest.page_recyclerview?.scrollToPosition(0) + } + } + } + + override fun getItemCount(): Int { + return pages.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt new file mode 100644 index 00000000..9eabac47 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt @@ -0,0 +1,11 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 + +class HomeScrollTransformer : ViewPager2.PageTransformer { + + override fun transformPage(view: View, position: Float) { + } +} + diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 1c7bb214..2e9e0c88 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -32,6 +32,7 @@ import androidx.core.text.HtmlCompat import androidx.core.text.toSpanned import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.tvprovider.media.tv.PreviewChannelHelper @@ -64,6 +65,7 @@ import okhttp3.Cache import java.io.* import java.net.URL import java.net.URLDecoder +import kotlin.system.measureTimeMillis object AppUtils { fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) { @@ -263,6 +265,50 @@ object AppUtils { } } + abstract class DiffAdapter( + open val items: MutableList, + val comparison: (first: T, second: T) -> Boolean = { first, second -> + first.hashCode() == second.hashCode() + } + ) : + RecyclerView.Adapter() { + override fun getItemCount(): Int { + return items.size + } + + fun updateList(newList: List) { + val time = measureTimeMillis { + + val diffResult = DiffUtil.calculateDiff( + GenericDiffCallback(this.items, newList) + ) + + items.clear() + items.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + println("TIME TAKEn $time") + } + + inner class GenericDiffCallback( + private val oldList: List, + private val newList: List + ) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + comparison(oldList[oldItemPosition], newList[newItemPosition]) + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] + } + } + + fun Activity.downloadAllPluginsDialog(repositoryUrl: String, repositoryName: String) { runOnUiThread { val context = this diff --git a/app/src/main/res/color/item_select_color.xml b/app/src/main/res/color/item_select_color.xml index 0d2834dd..3d69c540 100644 --- a/app/src/main/res/color/item_select_color.xml +++ b/app/src/main/res/color/item_select_color.xml @@ -1,5 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml new file mode 100644 index 00000000..fc90e300 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_sort_24.xml b/app/src/main/res/drawable/ic_baseline_sort_24.xml new file mode 100644 index 00000000..96d46231 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_sort_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_star_24.xml b/app/src/main/res/drawable/ic_baseline_star_24.xml index ab099425..2dcadb7f 100644 --- a/app/src/main/res/drawable/ic_baseline_star_24.xml +++ b/app/src/main/res/drawable/ic_baseline_star_24.xml @@ -1,5 +1,5 @@ - + android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/drawable/indicator_background.xml b/app/src/main/res/drawable/indicator_background.xml new file mode 100644 index 00000000..ef44fb7c --- /dev/null +++ b/app/src/main/res/drawable/indicator_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/rating_bg_color.xml b/app/src/main/res/drawable/rating_bg_color.xml new file mode 100644 index 00000000..60e62bab --- /dev/null +++ b/app/src/main/res/drawable/rating_bg_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml new file mode 100644 index 00000000..9c5cbf04 --- /dev/null +++ b/app/src/main/res/layout/fragment_library.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/library_viewpager_page.xml b/app/src/main/res/layout/library_viewpager_page.xml new file mode 100644 index 00000000..940b5a69 --- /dev/null +++ b/app/src/main/res/layout/library_viewpager_page.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 710c6cf8..afa75393 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -1,90 +1,111 @@ - + + android:id="@+id/background_card" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_margin="2dp" + android:layout_marginBottom="2dp" + android:elevation="10dp" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius" + app:layout_constraintBottom_toTopOf="@+id/imageText" + app:layout_constraintDimensionRatio="0.6333:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:layout_height="match_parent" + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> + android:id="@+id/text_quality" + style="@style/SearchBox" + android:background="@drawable/type_bg_color" + android:textColor="@color/textColor" + tools:text="@string/quality_hd" /> + + + android:background="@drawable/dub_bg_color" + android:text="@string/app_dubbed_text" /> + android:id="@+id/text_is_sub" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@drawable/sub_bg_color" + android:text="@string/app_subbed_text" /> + android:id="@+id/text_flag" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> + android:id="@+id/text_rating" + android:visibility="gone" + tools:visibility="visible" + tools:text="7.7" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@drawable/rating_bg_color" + android:text="@string/app_subbed_text" + app:drawableStartCompat="@drawable/ic_baseline_star_24" /> + + - \ No newline at end of file + android:id="@+id/imageText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_weight="0" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:minLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="?attr/textColor" + android:textSize="13sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="The Perfect Run\nThe Perfect Run" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index e3fd3d22..ba171654 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -2,19 +2,23 @@ + android:id="@+id/navigation_home" + android:icon="@drawable/ic_outline_home_24" + android:title="@string/title_home" /> + android:id="@+id/navigation_search" + android:icon="@drawable/search_icon" + android:title="@string/title_search" /> + android:id="@+id/navigation_library" + android:icon="@drawable/ic_baseline_collections_bookmark_24" + android:title="@string/library" /> + android:id="@+id/navigation_downloads" + android:icon="@drawable/netflix_download" + android:title="@string/title_downloads" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/library_menu.xml b/app/src/main/res/menu/library_menu.xml new file mode 100644 index 00000000..f21d998d --- /dev/null +++ b/app/src/main/res/menu/library_menu.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 3c45ee70..0afca83b 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -144,6 +144,15 @@ app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + #F53B66 #3BF585 ?attr/colorPrimaryDark + #3F51B5 #FF6F63 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 11c90d58..e1af1e0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,6 +253,7 @@ Error backing up %s Search + Library Accounts Updates and backup @@ -615,4 +616,10 @@ Safe Mode enabled An unrecoverable crash occurred and we\'ve automatically disabled all extensions, so you can find and remove the extension which is causing trouble. View crash info + Rating (High to Low) + Rating (Low to High) + Updated (New to Old) + Updated (Old to New) + Alphabetical (A to Z) + Alphabetical (Z to A) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2fb9b5b4..f758213f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -299,6 +299,10 @@ false + + From 05dc032df6be9f35b0dfa220b392350dceb88b88 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 20 Nov 2022 17:05:11 +0100 Subject: [PATCH 002/104] library fixes --- .../ui/library/LibraryFragment.kt | 34 ++-- .../ui/library/LibraryScrollTransformer.kt | 16 ++ .../ui/library/ViewpagerAdapter.kt | 27 +++- .../lagradost/cloudstream3/ui/library/test.kt | 11 -- .../lagradost/cloudstream3/utils/UIHelper.kt | 2 +- .../drawable/ic_outline_account_circle_24.xml | 6 + app/src/main/res/layout/fragment_library.xml | 149 ++++++++++++------ .../res/layout/library_viewpager_page.xml | 16 +- app/src/main/res/menu/bottom_nav_menu.xml | 2 +- 9 files changed, 165 insertions(+), 98 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt create mode 100644 app/src/main/res/drawable/ic_outline_account_circle_24.xml 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 932d3916..8a1a7e1f 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 @@ -1,31 +1,20 @@ package com.lagradost.cloudstream3.ui.library import android.content.Context -import androidx.lifecycle.ViewModelProvider import android.os.Bundle import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.WindowManager import androidx.appcompat.widget.SearchView import androidx.fragment.app.activityViewModels -import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.observe -import com.lagradost.cloudstream3.syncproviders.AccountManager -import com.lagradost.cloudstream3.syncproviders.providers.MALApi import com.lagradost.cloudstream3.ui.result.txt -import com.lagradost.cloudstream3.utils.Coroutines.ioSafe -import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import kotlinx.android.synthetic.main.fragment_library.* -import kotlinx.android.synthetic.main.library_viewpager_page.* -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock class LibraryFragment : Fragment() { @@ -46,9 +35,7 @@ class LibraryFragment : Fragment() { super.onViewCreated(view, savedInstanceState) context?.fixPaddingStatusbar(library_root) - val sortView = - menu_toolbar?.menu?.findItem(R.id.sort_button) - sortView?.setOnMenuItemClickListener { + sort_fab?.setOnClickListener { val methods = libraryViewModel.sortingMethods .map { txt(it.stringRes).asString(context ?: view.context) } @@ -63,13 +50,11 @@ class LibraryFragment : Fragment() { libraryViewModel.sort(method) } ) - true } - val searchView = - menu_toolbar?.menu?.findItem(R.id.search_button)?.actionView as? MenuSearchView - searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { + libraryViewModel.sort(ListSorting.Query, query) return true } @@ -81,9 +66,16 @@ class LibraryFragment : Fragment() { libraryViewModel.loadPages() - viewpager?.setPageTransformer(HomeScrollTransformer()) - viewpager?.adapter = viewpager.adapter ?: ViewpagerAdapter(emptyList()) - viewpager?.offscreenPageLimit = 10 + viewpager?.setPageTransformer(LibraryScrollTransformer()) + viewpager?.adapter = + viewpager.adapter ?: ViewpagerAdapter(emptyList()) { isScrollingDown: Boolean -> + if (isScrollingDown) { + sort_fab?.shrink() + } else { + sort_fab?.extend() + } + } + viewpager?.offscreenPageLimit = 2 observe(libraryViewModel.pages) { pages -> (viewpager.adapter as? ViewpagerAdapter)?.pages = pages diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt new file mode 100644 index 00000000..b954cc26 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt @@ -0,0 +1,16 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 +import kotlinx.android.synthetic.main.library_viewpager_page.view.* + +class LibraryScrollTransformer : ViewPager2.PageTransformer { + override fun transformPage(page: View, position: Float) { + val padding = (-position * page.width).toInt() + page.page_recyclerview.setPadding( + padding, 0, + -padding, 0 + ) + } +} + diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt index 3939f50e..eea26351 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -1,13 +1,18 @@ package com.lagradost.cloudstream3.ui.library +import android.os.Build import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.widget.NestedScrollView import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnFlingListener +import androidx.recyclerview.widget.RecyclerView.OnScrollListener import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import kotlinx.android.synthetic.main.library_viewpager_page.view.* import me.xdrop.fuzzywuzzy.FuzzySearch @@ -50,7 +55,7 @@ data class LibraryItem( ) : SearchResponse -class ViewpagerAdapter(var pages: List) : RecyclerView.Adapter() { +class ViewpagerAdapter(var pages: List, val scrollCallback: (isScrollingDown: Boolean) -> Unit) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return PageViewHolder( LayoutInflater.from(parent.context) @@ -71,11 +76,29 @@ class ViewpagerAdapter(var pages: List) : RecyclerView.Adapter= Build.VERSION_CODES.M) { + itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> + println("DOWN ${(scrollY - oldScrollY)}") + val diff = scrollY - oldScrollY + if (diff == 0) return@setOnScrollChangeListener + + scrollCallback.invoke(diff > 0) + } + } else { + itemViewTest.page_recyclerview.onFlingListener = object : OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + scrollCallback.invoke(velocityY > 0) + return false + } + } + } + } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt deleted file mode 100644 index 9eabac47..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.lagradost.cloudstream3.ui.library - -import android.view.View -import androidx.viewpager2.widget.ViewPager2 - -class HomeScrollTransformer : ViewPager2.PageTransformer { - - override fun transformPage(view: View, position: Float) { - } -} - diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index ab49492a..5e65429d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -102,7 +102,7 @@ object UIHelper { listView.requestLayout() } - fun Activity?.getSpanCount(): Int? { + fun Context?.getSpanCount(): Int? { val compactView = false val spanCountLandscape = if (compactView) 2 else 6 val spanCountPortrait = if (compactView) 1 else 3 diff --git a/app/src/main/res/drawable/ic_outline_account_circle_24.xml b/app/src/main/res/drawable/ic_outline_account_circle_24.xml new file mode 100644 index 00000000..cc564471 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_account_circle_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 9c5cbf04..ad695d6d 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -1,63 +1,110 @@ - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - + android:descendantFocusability="blocksDescendants" + android:focusable="false" - - + android:paddingHorizontal="5dp" + app:layout_scrollFlags="noScroll" + app:tabBackground="?attr/primaryGrayBackground" + app:tabGravity="center" + app:tabIndicator="@drawable/indicator_background" + app:tabIndicatorColor="@color/textColor" + app:tabIndicatorGravity="center" + app:tabIndicatorHeight="30dp" + app:tabMode="scrollable" + app:tabSelectedTextColor="@color/lightTextColor" + app:tabTextAppearance="@style/TabNoCaps" + app:tabTextColor="@color/textColor" /> + - - \ No newline at end of file + diff --git a/app/src/main/res/layout/library_viewpager_page.xml b/app/src/main/res/layout/library_viewpager_page.xml index 940b5a69..f69f68b5 100644 --- a/app/src/main/res/layout/library_viewpager_page.xml +++ b/app/src/main/res/layout/library_viewpager_page.xml @@ -1,17 +1,11 @@ - - - - + android:clipToPadding="false" + tools:listitem="@layout/home_result_grid_expanded" /> diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index ba171654..38e17675 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -11,7 +11,7 @@ android:title="@string/title_search" /> Date: Mon, 5 Dec 2022 20:49:45 +0100 Subject: [PATCH 003/104] backend fixes for opening from sync urls --- .../com/lagradost/cloudstream3/MainAPI.kt | 28 +++++++-- .../metaproviders/AnilistRedirector.kt | 30 ---------- .../metaproviders/SyncRedirector.kt | 57 +++++++++++++++++++ .../syncproviders/providers/AniListApi.kt | 2 +- .../syncproviders/providers/MALApi.kt | 2 +- .../ui/library/LibraryFragment.kt | 9 ++- .../cloudstream3/ui/library/PageAdapter.kt | 4 +- .../ui/library/ViewpagerAdapter.kt | 12 +++- .../ui/result/ResultViewModel2.kt | 21 ++++--- .../lagradost/cloudstream3/utils/SyncUtil.kt | 27 +++++---- app/src/main/res/values/styles.xml | 1 + 11 files changed, 134 insertions(+), 59 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index dc6cc454..f703e8ad 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -10,15 +10,14 @@ 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.lagradost.cloudstream3.metaproviders.SyncIdName import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* @@ -402,6 +401,20 @@ abstract class MainAPI { open val hasMainPage = false open val hasQuickSearch = false + /** + * A set of which ids the provider can open with getLoadUrl() + * If the set contains SyncIdName.Imdb then getLoadUrl() can be started with + * an Imdb class which inherits from SyncId. + * + * getLoadUrl() is then used to get page url based on that ID. + * + * Example: + * "tt6723592" -> getLoadUrl(ImdbSyncId("tt6723592")) -> "mainUrl/imdb/tt6723592" -> load("mainUrl/imdb/tt6723592") + * + * This is used to launch pages from personal lists or recommendations using IDs. + **/ + open val supportedSyncNames = setOf() + open val supportedTypes = setOf( TvType.Movie, TvType.TvSeries, @@ -412,7 +425,6 @@ abstract class MainAPI { open val vpnStatus = VPNStatus.None open val providerType = ProviderType.DirectProvider - open val mainPage = listOf(MainPageData("", "")) @WorkerThread @@ -471,6 +483,14 @@ abstract class MainAPI { open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? { return null } + + /** + * Get the load() url based on a sync ID like IMDb or MAL. + * Only contains SyncIds based on supportedSyncUrls. + **/ + open suspend fun getLoadUrl(name: SyncIdName, id: String): String? { + return null + } } /** Might need a different implementation for desktop*/ diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt deleted file mode 100644 index 208db14b..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lagradost.cloudstream3.metaproviders - -import com.lagradost.cloudstream3.ErrorLoadingException -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.utils.SyncUtil - -object SyncRedirector { - val syncApis = SyncApis - - suspend fun redirect(url: String, preferredUrl: String): String { - for (api in syncApis) { - if (url.contains(api.mainUrl)) { - val otherApi = when (api.name) { - aniListApi.name -> "anilist" - malApi.name -> "myanimelist" - else -> return url - } - - return SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> - realUrl.contains(preferredUrl) - } ?: run { - throw ErrorLoadingException("Page does not exist on $preferredUrl") - } - } - } - return url - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt new file mode 100644 index 00000000..41cf413c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt @@ -0,0 +1,57 @@ +package com.lagradost.cloudstream3.metaproviders + +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall +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.utils.SyncUtil + +enum class SyncIdName { + AniList, + MyAnimeList, + Trakt, + Imdb +} + +object SyncRedirector { + val syncApis = SyncApis + private val syncIds = + listOf( + SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""), + SyncIdName.AniList to Regex("""anilist\.co\/anime\/(\d+)""") + ) + + suspend fun redirect(url: String, providerApi: MainAPI): String { + // Tries built in ID -> ProviderUrl + for (api in syncApis) { + if (url.contains(api.mainUrl)) { + val otherApi = when (api.name) { + aniListApi.name -> "anilist" + malApi.name -> "myanimelist" + else -> return url + } + + SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> + realUrl.contains(providerApi.mainUrl) + }?.let { + return it + } +// ?: run { +// throw ErrorLoadingException("Page does not exist on $preferredUrl") +// } + } + } + + // Tries provider solution + return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) -> + if (providerApi.supportedSyncNames.contains(syncName)) { + syncRegex.find(url)?.value?.let { + suspendSafeApiCall { + providerApi.getLoadUrl(syncName, it) + } + } + } else null + } ?: url + } +} \ 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 b4f0f790..6976bfc5 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 @@ -602,7 +602,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { // English title first this.media.title.english ?: this.media.title.romaji ?: this.media.synonyms.firstOrNull() ?: "", - this.media.id.toString(), + "https://anilist.co/anime/${this.media.id}/", listName ?: return null, this.progress, this.media.episodes, 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 d9011a1b..8f734856 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 @@ -387,7 +387,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { fun toLibraryItem(): LibraryItem { return LibraryItem( this.node.title, - this.node.id.toString(), + "https://myanimelist.net/anime/${this.node.id}/", this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", this.list_status?.num_episodes_watched, this.node.num_episodes, 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 8a1a7e1f..9bb412b7 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 @@ -12,6 +12,8 @@ import com.google.android.material.tabs.TabLayoutMediator import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD +import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import kotlinx.android.synthetic.main.fragment_library.* @@ -68,12 +70,17 @@ class LibraryFragment : Fragment() { viewpager?.setPageTransformer(LibraryScrollTransformer()) viewpager?.adapter = - viewpager.adapter ?: ViewpagerAdapter(emptyList()) { isScrollingDown: Boolean -> + viewpager.adapter ?: ViewpagerAdapter(emptyList(), { isScrollingDown: Boolean -> if (isScrollingDown) { sort_fab?.shrink() } else { sort_fab?.extend() } + }) { searchClickCallback -> + println("SEARCH CLICK $searchClickCallback") + if (searchClickCallback.action == SEARCH_ACTION_LOAD) { + activity?.loadSearchResult(searchClickCallback.card) + } } viewpager?.offscreenPageLimit = 2 diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt index 7db40f88..4bac94e2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -6,12 +6,14 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.utils.AppUtils import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* class PageAdapter( override val items: MutableList, + val clickCallback: (SearchClickCallback) -> Unit ) : AppUtils.DiffAdapter(items) { @@ -33,7 +35,7 @@ class PageAdapter( inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: LibraryItem, position: Int) { SearchResultBuilder.bind( - { println("CLICKED ${it.action}") }, + this@PageAdapter.clickCallback, item, position, itemView, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt index eea26351..fbb3ec1e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import kotlinx.android.synthetic.main.library_viewpager_page.view.* import me.xdrop.fuzzywuzzy.FuzzySearch @@ -55,7 +56,11 @@ data class LibraryItem( ) : SearchResponse -class ViewpagerAdapter(var pages: List, val scrollCallback: (isScrollingDown: Boolean) -> Unit) : RecyclerView.Adapter() { +class ViewpagerAdapter( + var pages: List, + val scrollCallback: (isScrollingDown: Boolean) -> Unit, + val clickCallback: (SearchClickCallback) -> Unit +) : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return PageViewHolder( LayoutInflater.from(parent.context) @@ -75,8 +80,9 @@ class ViewpagerAdapter(var pages: List, val scrollCallback: (isScrollingDo RecyclerView.ViewHolder(itemViewTest) { fun bind(page: Page) { if (itemViewTest.page_recyclerview?.adapter == null) { - itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList()) - itemView.page_recyclerview?.spanCount = this@PageViewHolder.itemView.context.getSpanCount() ?: 3 + itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList(), clickCallback) + itemView.page_recyclerview?.spanCount = + this@PageViewHolder.itemView.context.getSpanCount() ?: 3 } else { (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) itemViewTest.page_recyclerview?.scrollToPosition(0) 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 b7e36f21..582ad4bd 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 @@ -14,6 +14,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.CommonActivity.getCastSession @@ -1264,12 +1265,18 @@ class ResultViewModel2 : ViewModel() { val realRecommendations = ArrayList() // TODO: fix - //val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name) - // meta.recommendations?.forEach { rec -> - // apiNames.forEach { name -> - // realRecommendations.add(rec.copy(apiName = name)) - // } - // } + val apiNames = apis.filter { + it.name.contains("gogoanime", true) || + it.name.contains("9anime", true) + }.map { + it.name + } + + meta.recommendations?.forEach { rec -> + apiNames.forEach { name -> + realRecommendations.add(rec.copy(apiName = name)) + } + } recommendations = recommendations?.union(realRecommendations)?.toList() ?: realRecommendations @@ -1913,7 +1920,7 @@ class ResultViewModel2 : ViewModel() { val validUrlResource = safeApiCall { SyncRedirector.redirect( url, - api.mainUrl + api ) } // TODO: fix diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt index 7dda3e18..e5f2f2dc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt @@ -4,6 +4,7 @@ package com.lagradost.cloudstream3.utils import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.apis //import com.lagradost.cloudstream3.animeproviders.AniflixProvider import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -78,17 +79,21 @@ object SyncUtil { return null } - suspend fun getUrlsFromId(id: String, type: String = "anilist") : List { - return arrayListOf() - // val url = - // "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" - // val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() - // val pages = response.pages ?: return emptyList() - // val current = pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url }.toMutableList() - // if(type == "anilist") { // TODO MAKE BETTER - // current.add("${AniflixProvider().mainUrl}/anime/$id") - // } - // return current + suspend fun getUrlsFromId(id: String, type: String = "anilist"): List { + val url = + "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" + val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() + val pages = response.pages ?: return emptyList() + val current = + pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values) + .mapNotNull { it.url }.toMutableList() + + if (type == "anilist") { // TODO MAKE BETTER + apis.filter { it.name.contains("Aniflix", ignoreCase = true) }.forEach { + current.add("${it.mainUrl}/anime/$id") + } + } + return current } data class SyncPage( diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index f758213f..517300ea 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -301,6 +301,7 @@ + + + + From 26320bb535b6cb4aa2789e3ee40a16f501ead02b Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Wed, 25 Jan 2023 19:20:53 +0100 Subject: [PATCH 019/104] Reworked backend to make list titles use UiText --- .../cloudstream3/syncproviders/SyncAPI.kt | 15 ++-- .../syncproviders/providers/AniListApi.kt | 47 ++++++------ .../syncproviders/providers/LocalList.kt | 22 +++--- .../syncproviders/providers/MALApi.kt | 72 +++++++++++-------- .../ui/library/LibraryFragment.kt | 2 +- .../ui/library/LibraryViewModel.kt | 13 ++-- .../cloudstream3/ui/library/PageAdapter.kt | 19 +++-- .../cloudstream3/utils/DataStoreHelper.kt | 3 +- .../layout/search_result_grid_expanded.xml | 2 +- 9 files changed, 106 insertions(+), 89 deletions(-) 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 ab9db4ea..71ea374d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.UiText import me.xdrop.fuzzywuzzy.FuzzySearch enum class SyncIdName { @@ -101,7 +102,7 @@ interface SyncAPI : OAuth2API { data class Page( - val title: String, var items: List + val title: UiText, var items: List ) { fun sort(method: ListSorting?, query: String? = null) { items = when (method) { @@ -123,11 +124,12 @@ interface SyncAPI : OAuth2API { } data class LibraryMetadata( - /** List of all available pages, useful to show empty pages - * if the user has no entry on that page */ - val allListNames: List, - /** Not necessarily sorted list of all library items, will be grouped by listName */ - val allLibraryItems: List + val allLibraryLists: List + ) + + data class LibraryList( + val name: UiText, + val items: List ) data class LibraryItem( @@ -135,7 +137,6 @@ interface SyncAPI : OAuth2API { override val url: String, /** Unique unchanging string used for data storage */ val syncId: String, - val listName: String, val episodesCompleted: Int?, val episodesTotal: Int?, /** Out of 100 */ 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 6b3c408d..ed0c3f78 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 @@ -1,9 +1,9 @@ package com.lagradost.cloudstream3.syncproviders.providers +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey @@ -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.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -316,14 +317,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } // Changing names of these will show up in UI - enum class AniListStatusType(var value: Int) { - Watching(0), - Completed(1), - Paused(2), - Dropped(3), - Planning(4), - ReWatching(5), - None(-1) + enum class AniListStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + Paused(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + Planning(4, R.string.type_plan_to_watch), + ReWatching(5, R.string.type_re_watching), + None(-1, R.string.none) } fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp } @@ -339,7 +340,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun convertAnilistStringToStatus(string: String): AniListStatusType { + fun convertAniListStringToStatus(string: String): AniListStatusType { return fromIntToAnimeStatus(aniListStatusString.indexOf(string)) } @@ -609,7 +610,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media ) { - fun toLibraryItem(listName: String?): SyncAPI.LibraryItem? { + fun toLibraryItem(): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( // English title first this.media.title.english ?: this.media.title.romaji @@ -617,7 +618,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { ?: "", "https://anilist.co/anime/${this.media.id}/", this.media.id.toString(), - listName?.lowercase()?.capitalize() ?: return null, this.progress, this.media.episodes, this.score, @@ -665,15 +665,20 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getAniListAnimeListSmart()?.groupBy { + convertAniListStringToStatus(it.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.entries.map { entry -> entry.toLibraryItem() } }.flatten() + } ?: emptyMap() + + // To fill empty lists when AniList does not return them + val baseMap = + AniListStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( - emptyList(), - getAniListAnimeListSmart()?.map { - it.entries.mapNotNull { entry -> - entry.toLibraryItem( - entry.status ?: it.status - ) - } - }?.flatten() ?: emptyList() + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } @@ -695,7 +700,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { startedAt { year month day } updatedAt progress - score + score (format: POINT_100) private media { diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt index 7a53e6a4..44384c92 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -1,7 +1,6 @@ package com.lagradost.cloudstream3.syncproviders.providers import androidx.fragment.app.FragmentActivity -import com.lagradost.cloudstream3.AcraApplication import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI @@ -68,20 +67,21 @@ class LocalList : SyncAPI { }?.distinctBy { it.first } ?: return null val list = ioWork { - watchStatusIds.mapNotNull { - getBookmarkedData(it.first)?.toLibraryItem(it.second) + watchStatusIds.groupBy { + it.second.stringRes + }.mapValues { group -> + group.value.mapNotNull { + getBookmarkedData(it.first)?.toLibraryItem() + } } } + val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { + // None is not something to display + it.stringRes to emptyList() + } return SyncAPI.LibraryMetadata( - WatchType.values().mapNotNull { - // None is not something to display - if (it == WatchType.NONE) return@mapNotNull null - - // Dirty hack for context! - txt(it.stringRes).asStringNull(AcraApplication.context) - }, - list + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } 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 7745f0d2..b91c9759 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 @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.syncproviders.providers import android.util.Base64 +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey @@ -15,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.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -257,6 +259,32 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { const val MAL_UNIXTIME_KEY: String = "mal_unixtime" // When token expires const val MAL_REFRESH_TOKEN_KEY: String = "mal_refresh_token" // refresh token const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api + + fun convertToStatus(string: String): MalStatusType { + return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) + } + + enum class MalStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + OnHold(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + PlanToWatch(4, R.string.type_plan_to_watch), + None(-1, R.string.type_none) + } + + private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } + return when (inp) { + -1 -> MalStatusType.None + 0 -> MalStatusType.Watching + 1 -> MalStatusType.Completed + 2 -> MalStatusType.OnHold + 3 -> MalStatusType.Dropped + 4 -> MalStatusType.PlanToWatch + 5 -> MalStatusType.Watching + else -> MalStatusType.None + } + } } override suspend fun handleRedirect(url: String): Boolean { @@ -394,10 +422,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { this.node.title, "https://myanimelist.net/anime/${this.node.id}/", this.node.id.toString(), - this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", this.list_status?.num_episodes_watched, this.node.num_episodes, - this.list_status?.score, + this.list_status?.score?.times(10), "MAL", TvType.Anime, this.node.main_picture?.large ?: this.node.main_picture?.medium, @@ -448,9 +475,20 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getMalAnimeListSmart()?.groupBy { + convertToStatus(it.list_status?.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.toLibraryItem() } + } ?: emptyMap() + + // To fill empty lists when MAL does not return them + val baseMap = + MalStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( - emptyList(), - getMalAnimeListSmart()?.map { it.toLibraryItem() } ?: emptyList() + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } ) } @@ -469,10 +507,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return fullList.toTypedArray() } - fun convertToStatus(string: String): MalStatusType { - return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) - } - private suspend fun getMalAnimeListSlice(offset: Int = 0): MalList? { val user = "@me" val auth = getAuth() ?: return null @@ -586,28 +620,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return user } - enum class MalStatusType(var value: Int) { - Watching(0), - Completed(1), - OnHold(2), - Dropped(3), - PlanToWatch(4), - None(-1) - } - - private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } - return when (inp) { - -1 -> MalStatusType.None - 0 -> MalStatusType.Watching - 1 -> MalStatusType.Completed - 2 -> MalStatusType.OnHold - 3 -> MalStatusType.Dropped - 4 -> MalStatusType.PlanToWatch - 5 -> MalStatusType.Watching - else -> MalStatusType.None - } - } - private suspend fun setScoreRequest( id: Int, status: MalStatusType? = null, 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 87ab0821..baf8f71e 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 @@ -297,7 +297,7 @@ class LibraryFragment : Fragment() { library_tab_layout, viewpager, ) { tab, position -> - tab.text = pages.getOrNull(position)?.title + tab.text = pages.getOrNull(position)?.title?.asStringNull(context) }.attach() loading_indicator?.hide() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt index 51bfb16f..d01c699a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -92,18 +92,13 @@ class LibraryViewModel : ViewModel() { repo.requireLibraryRefresh = false - val listSubset = library.allLibraryItems.groupBy { it.listName } - val allLists = - library.allListNames.associateWith { emptyList() } - - val filledLists = allLists + listSubset - - val pages = filledLists.map { + val pages = library.allLibraryLists.map { SyncAPI.Page( - it.key, - it.value + it.name, + it.items ) } + _pages.postValue(Resource.Success(pages)) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt index 0909d845..2435f8be 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -49,10 +49,10 @@ class PageAdapter( return ColorUtils.calculateLuminance(color) < 0.5 } - fun getDifferentColor(color: Int, ratio : Float = 0.7f) : Int { - return if(isDark(color)) { + fun getDifferentColor(color: Int, ratio: Float = 0.7f): Int { + return if (isDark(color)) { ColorUtils.blendARGB(color, Color.WHITE, ratio) - } else{ + } else { ColorUtils.blendARGB(color, Color.BLACK, ratio) } } @@ -74,7 +74,7 @@ class PageAdapter( itemView, colorCallback = { palette -> AcraApplication.context?.let { ctx -> - val defColor = ContextCompat.getColor(ctx,R.color.ratingColorBg) + val defColor = ContextCompat.getColor(ctx, R.color.ratingColorBg) var bg = palette.getDarkVibrantColor(defColor) if (bg == defColor) { bg = palette.getDarkMutedColor(defColor) @@ -83,11 +83,12 @@ class PageAdapter( bg = palette.getVibrantColor(defColor) } - val fg = getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) + val fg = + getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) itemView.text_rating.apply { setTextColor(ColorStateList.valueOf(fg)) } - itemView.text_rating_holder?.backgroundTintList =ColorStateList.valueOf(bg) + itemView.text_rating_holder?.backgroundTintList = ColorStateList.valueOf(bg) itemView.watchProgress?.apply { progressTintList = ColorStateList.valueOf(fg) progressBackgroundTintList = ColorStateList.valueOf(bg) @@ -118,7 +119,11 @@ class PageAdapter( val showRating = (item.personalRating ?: 0) != 0 itemView.text_rating_holder.isVisible = showRating if (showRating) { - itemView.text_rating.text = "★ ${item.personalRating.toString()}" + // We want to show 8.5 but not 8.0 hence the replace + val rating = ((item.personalRating ?: 0).toDouble() / 10).toString() + .replace(".0", "") + + itemView.text_rating.text = "★ $rating" } } } 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 074fcfcc..df6f209d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -55,12 +55,11 @@ object DataStoreHelper { @JsonProperty("quality") override var quality: SearchQuality? = null, @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse { - fun toLibraryItem(state: WatchType): SyncAPI.LibraryItem { + fun toLibraryItem(): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( name, url, url, - state.name.lowercase().capitalize(), null, null, null, diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 850191ba..d6deec2f 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -13,7 +13,7 @@ Date: Thu, 26 Jan 2023 18:09:22 +0100 Subject: [PATCH 020/104] Better notice when not logged in in library --- .../cloudstream3/ui/library/LibraryFragment.kt | 18 +++++++++++++++++- app/src/main/res/layout/fragment_library.xml | 1 - app/src/main/res/values/strings.xml | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) 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 baf8f71e..42f0928d 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 @@ -253,6 +253,12 @@ class LibraryFragment : Fragment() { activity?.loadSearchResult( searchClickCallback.card ) + } else { + // Search when no provider can open + QuickSearchFragment.pushSearch( + activity, + searchClickCallback.card.name + ) } } LibraryOpenerType.None -> {} @@ -283,7 +289,16 @@ class LibraryFragment : Fragment() { when (resource) { is Resource.Success -> { val pages = resource.value - empty_list_textview?.isVisible = pages.all { it.items.isEmpty() } + val showNotice = pages.all { it.items.isEmpty() } + empty_list_textview?.isVisible = showNotice + if (showNotice) { + if (libraryViewModel.availableApiNames.size > 1) { + empty_list_textview?.setText(R.string.empty_library_logged_in_message) + } else { + empty_list_textview?.setText(R.string.empty_library_no_accounts_message) + } + } + (viewpager.adapter as? ViewpagerAdapter)?.pages = pages // Using notifyItemRangeChanged keeps the animations when sorting viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0) @@ -303,6 +318,7 @@ class LibraryFragment : Fragment() { } is Resource.Loading -> { loading_indicator?.show() + empty_list_textview?.isVisible = false } is Resource.Failure -> { // No user indication it failed :( diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index c86e5d93..116a4e03 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -14,7 +14,6 @@ android:layout_gravity="center" android:layout_margin="30dp" android:gravity="center" - android:text="@string/empty_library_message" android:visibility="gone" tools:visibility="visible" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c935809b..acfc5932 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -627,5 +627,6 @@ Alphabetical (Z to A) Select Library Open with - Looks like your library is empty :(\nLogin to a library account or add shows to your local library + Looks like your library is empty :(\nLogin to a library account or add shows to your local library + Looks like this list is empty, try switching to another one From 1f67644290aeb176e30b7aaf28e6265d02c6d1bc Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Fri, 27 Jan 2023 14:54:24 +0100 Subject: [PATCH 021/104] Fix crash and sorting methods in library --- .../cloudstream3/syncproviders/SyncAPI.kt | 6 ++++- .../syncproviders/providers/AniListApi.kt | 12 +++++++++- .../syncproviders/providers/LocalList.kt | 11 +++++++++- .../syncproviders/providers/MALApi.kt | 22 ++++++++++++++++++- .../ui/library/LibraryViewModel.kt | 15 +++++-------- .../cloudstream3/ui/search/SearchAdaptor.kt | 4 +--- .../cloudstream3/utils/DataStoreHelper.kt | 1 + 7 files changed, 55 insertions(+), 16 deletions(-) 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 71ea374d..2f5e0050 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -118,13 +118,16 @@ interface SyncAPI : OAuth2API { ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) } ListSorting.AlphabeticalA -> items.sortedBy { it.name } ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed() + ListSorting.UpdatedNew -> items.sortedBy { it.lastUpdatedUnixTime?.times(-1) } + ListSorting.UpdatedOld -> items.sortedBy { it.lastUpdatedUnixTime } else -> items } } } data class LibraryMetadata( - val allLibraryLists: List + val allLibraryLists: List, + val supportedListSorting: Set ) data class LibraryList( @@ -141,6 +144,7 @@ interface SyncAPI : OAuth2API { val episodesTotal: Int?, /** Out of 100 */ val personalRating: Int?, + val lastUpdatedUnixTime: Long?, override val apiName: String, override var type: TvType?, override var posterUrl: String?, 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 ed0c3f78..7d9de43a 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.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery @@ -621,6 +622,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { this.progress, this.media.episodes, this.score, + this.updatedAt.toLong(), "AniList", TvType.Anime, this.media.coverImage.extraLarge ?: this.media.coverImage.large @@ -678,7 +680,15 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } return SyncAPI.LibraryMetadata( - (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt index 44384c92..61e9e294 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds @@ -81,7 +82,15 @@ class LocalList : SyncAPI { it.stringRes to emptyList() } return SyncAPI.LibraryMetadata( - (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, +// ListSorting.UpdatedNew, +// ListSorting.UpdatedOld, +// ListSorting.RatingHigh, +// ListSorting.RatingLow, + ) ) } 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 b91c9759..5164b606 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.library.ListSorting import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery @@ -285,6 +286,16 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { else -> MalStatusType.None } } + + private fun parseDateLong(string: String?): Long? { + return try { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse( + string ?: return null + )?.time?.div(1000) + } catch (e: Exception) { + null + } + } } override suspend fun handleRedirect(url: String): Boolean { @@ -425,6 +436,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { this.list_status?.num_episodes_watched, this.node.num_episodes, this.list_status?.score?.times(10), + parseDateLong(this.list_status?.updated_at), "MAL", TvType.Anime, this.node.main_picture?.large ?: this.node.main_picture?.medium, @@ -488,7 +500,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } return SyncAPI.LibraryMetadata( - (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) } + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt index d01c699a..14d31356 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -46,16 +46,10 @@ class LibraryViewModel : ViewModel() { val availableApiNames: List get() = availableSyncApis.map { it.name } - val sortingMethods = listOf( - ListSorting.RatingHigh, - ListSorting.RatingLow, -// ListSorting.UpdatedNew, -// ListSorting.UpdatedOld, - ListSorting.AlphabeticalA, - ListSorting.AlphabeticalZ, - ) + var sortingMethods = emptyList() + private set - var currentSortingMethod: ListSorting = sortingMethods.first() + var currentSortingMethod: ListSorting? = sortingMethods.firstOrNull() private set fun switchList(name: String) { @@ -90,6 +84,9 @@ class LibraryViewModel : ViewModel() { } val library = (libraryResource as? Resource.Success)?.value ?: return@let + sortingMethods = library.supportedListSorting.toList() + currentSortingMethod = null + repo.requireLibraryRefresh = false val pages = library.allLibraryLists.map { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt index 265489f7..649641c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt @@ -68,9 +68,7 @@ class SearchAdapter( cardList.clear() cardList.addAll(newList) - main { - diffResult.dispatchUpdatesTo(this) - } + diffResult.dispatchUpdatesTo(this) } class CardViewHolder 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 df6f209d..4e9c646f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -63,6 +63,7 @@ object DataStoreHelper { null, null, null, + null, apiName, type, posterUrl, posterHeaders, quality, id ) } From 7512dbcbf8a36ad8ea0b05e461981285667835fa Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Fri, 27 Jan 2023 22:26:24 +0100 Subject: [PATCH 022/104] Library loading fixes --- .../cloudstream3/ui/home/HomeFragment.kt | 4 +- .../ui/library/LibraryFragment.kt | 42 ++++- .../ui/library/LibraryViewModel.kt | 2 + .../ui/library/LoadingPosterAdapter.kt | 37 +++++ .../ui/quicksearch/QuickSearchFragment.kt | 2 +- .../cloudstream3/ui/result/ResultFragment.kt | 8 +- .../ui/result/ResultFragmentPhone.kt | 2 +- .../ui/result/ResultFragmentTv.kt | 2 +- .../cloudstream3/ui/search/SearchFragment.kt | 2 +- .../ui/settings/extensions/PluginsFragment.kt | 2 +- app/src/main/res/layout/fragment_library.xml | 77 +++++---- .../res/layout/loading_poster_dynamic.xml | 37 +++++ .../layout/search_result_grid_expanded.xml | 157 +++++++++--------- app/src/main/res/values/strings.xml | 1 + 14 files changed, 254 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt create mode 100644 app/src/main/res/layout/loading_poster_dynamic.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 8a8f90b4..5cf6fc8e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -569,7 +569,7 @@ class HomeFragment : Fragment() { val mutableListOfResponse = mutableListOf() listHomepageItems.clear() - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList( + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList( d.values.toMutableList(), home_master_recycler ) @@ -621,7 +621,7 @@ class HomeFragment : Fragment() { //home_loaded?.isVisible = false } is Resource.Loading -> { - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(listOf()) + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(listOf()) home_loading_shimmer?.startShimmer() home_loading?.isVisible = true home_loading_error?.isVisible = false 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 42f0928d..97fc4b72 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 @@ -2,13 +2,18 @@ package com.lagradost.cloudstream3.ui.library import android.app.Activity import android.content.Context +import android.content.res.Configuration import android.os.Bundle +import android.os.Handler +import android.os.Looper import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ArrayAdapter import androidx.annotation.StringRes import androidx.appcompat.widget.SearchView +import androidx.core.os.postDelayed import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import com.google.android.material.tabs.TabLayoutMediator @@ -20,6 +25,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.debugAssert +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName @@ -32,7 +38,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import kotlinx.android.synthetic.main.fragment_library.* +import kotlinx.coroutines.delay const val LIBRARY_FOLDER = "library_folder" @@ -285,9 +293,27 @@ class LibraryFragment : Fragment() { viewpager?.offscreenPageLimit = 2 viewpager?.reduceDragSensitivity() + val startLoading = Runnable { + gridview?.numColumns = context?.getSpanCount() ?: 3 + gridview?.adapter = + context?.let { LoadingPosterAdapter(it, 6 * 3) } + library_loading_overlay?.isVisible = true + library_loading_shimmer?.startShimmer() + empty_list_textview?.isVisible = false + } + + val stopLoading = Runnable { + gridview?.adapter = null + library_loading_overlay?.isVisible = false + library_loading_shimmer?.stopShimmer() + } + + val handler = Handler(Looper.getMainLooper()) + observe(libraryViewModel.pages) { resource -> when (resource) { is Resource.Success -> { + handler.removeCallbacks(startLoading) val pages = resource.value val showNotice = pages.all { it.items.isEmpty() } empty_list_textview?.isVisible = showNotice @@ -303,6 +329,11 @@ class LibraryFragment : Fragment() { // Using notifyItemRangeChanged keeps the animations when sorting viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0) + // Only stop loading after 300ms to hide the fade effect the viewpager produces when updating + // Without this there would be a flashing effect: + // loading -> show old viewpager -> black screen -> show new viewpager + handler.postDelayed(stopLoading, 300) + savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> viewpager?.setCurrentItem(currentPos, false) savedInstanceState.remove(VIEWPAGER_ITEM_KEY) @@ -314,20 +345,23 @@ class LibraryFragment : Fragment() { ) { tab, position -> tab.text = pages.getOrNull(position)?.title?.asStringNull(context) }.attach() - loading_indicator?.hide() } is Resource.Loading -> { - loading_indicator?.show() - empty_list_textview?.isVisible = false + // Only start loading after 200ms to prevent loading cached lists + handler.postDelayed(startLoading, 200) } is Resource.Failure -> { + stopLoading.run() // No user indication it failed :( // TODO - loading_indicator?.hide() } } } } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + } } class MenuSearchView(context: Context) : SearchView(context) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt index 14d31356..07f88fbf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import kotlinx.coroutines.delay enum class ListSorting(@StringRes val stringRes: Int) { Query(R.string.none), @@ -96,6 +97,7 @@ class LibraryViewModel : ViewModel() { ) } + delay(5000) _pages.postValue(Resource.Success(pages)) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt new file mode 100644 index 00000000..a637133b --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt @@ -0,0 +1,37 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.ListPopupWindow.MATCH_PARENT +import android.widget.RelativeLayout +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.android.synthetic.main.loading_poster_dynamic.view.* +import kotlin.math.roundToInt +import kotlin.math.sqrt + +class LoadingPosterAdapter(context: Context, private val itemCount: Int) : + BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int { + return itemCount + } + + override fun getItem(position: Int): Any? { + return null + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return convertView ?: inflater.inflate(R.layout.loading_poster_dynamic, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index ad3d9eb8..ba57d2de 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -220,7 +220,7 @@ class QuickSearchFragment : Fragment() { when (it) { is Resource.Success -> { it.value.let { data -> - (quick_search_autofit_results?.adapter as? SearchAdapter?)?.updateList( + (quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList( context?.filterSearchResultByFilmQuality(data) ?: data ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 9cfbf45c..2e2e46b7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -277,7 +277,7 @@ open class ResultFragment : ResultTrailerPlayer() { private var downloadButton: EasyDownloadButton? = null override fun onDestroyView() { updateUIListener = null - (result_episodes?.adapter as EpisodeAdapter?)?.killAdapter() + (result_episodes?.adapter as? EpisodeAdapter)?.killAdapter() downloadButton?.dispose() super.onDestroyView() @@ -458,7 +458,7 @@ open class ResultFragment : ResultTrailerPlayer() { temporary_no_focus?.requestFocus() } - (result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) + (result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value) if (isTv && hasEpisodes) main { delay(500) @@ -687,7 +687,7 @@ open class ResultFragment : ResultTrailerPlayer() { val newList = list.filter { it.isSynced && it.hasAccount } result_mini_sync?.isVisible = newList.isNotEmpty() - (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.mapNotNull { it.icon }) + (result_mini_sync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon }) } var currentSyncProgress = 0 @@ -900,7 +900,7 @@ open class ResultFragment : ResultTrailerPlayer() { result_cast_items?.isVisible = d.actors != null - (result_cast_items?.adapter as ActorAdaptor?)?.apply { + (result_cast_items?.adapter as? ActorAdaptor)?.apply { updateList(d.actors ?: emptyList()) } 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 9bae8753..b38e1765 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 @@ -485,7 +485,7 @@ class ResultFragmentPhone : ResultFragment() { result_recommendations?.post { rec?.let { list -> - (result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst }) + (result_recommendations?.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst }) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index d5cab1a6..2bd8ff0f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -107,7 +107,7 @@ class ResultFragmentTv : ResultFragment() { result_recommendations?.isGone = isInvalid result_recommendations_holder?.isGone = isInvalid val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName - (result_recommendations?.adapter as SearchAdapter?)?.updateList(rec?.filter { it.apiName == matchAgainst } + (result_recommendations?.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst } ?: emptyList()) rec?.map { it.apiName }?.distinct()?.let { apiNames -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 4144a042..b4a38216 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -420,7 +420,7 @@ class SearchFragment : Fragment() { is Resource.Success -> { it.value.let { data -> if (data.isNotEmpty()) { - (search_autofit_results?.adapter as SearchAdapter?)?.updateList(data) + (search_autofit_results?.adapter as? SearchAdapter)?.updateList(data) } } searchExitIcon.alpha = 1f diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index bd44a058..d328d226 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -143,7 +143,7 @@ class PluginsFragment : Fragment() { } observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) -> - (plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list) + (plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list) if (scrollToTop) plugin_recycler_view?.scrollToPosition(0) diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 116a4e03..28f21bf6 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -89,23 +89,53 @@ - - - - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior"> - + + + + + + + + + + + - - - - - - - diff --git a/app/src/main/res/layout/loading_poster_dynamic.xml b/app/src/main/res/layout/loading_poster_dynamic.xml new file mode 100644 index 00000000..11855acb --- /dev/null +++ b/app/src/main/res/layout/loading_poster_dynamic.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index d6deec2f..47fd7cd3 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -10,95 +10,104 @@ android:foreground="@drawable/outline_drawable" android:orientation="vertical"> - + android:layout_height="wrap_content"> - + android:layout_height="0dp" + android:layout_margin="2dp" + android:layout_marginBottom="2dp" + android:elevation="10dp" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius" + app:layout_constraintDimensionRatio="1:1.414" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - - - + + android:id="@+id/text_quality" + style="@style/TypeButton" /> - - - + android:orientation="vertical"> + + + + + + + + + + - + android:layout_gravity="end" + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> + - - - - - - + + PackageInstaller App will be updated upon exit Sort by + Sort Rating (High to Low) Rating (Low to High) Updated (New to Old) From ef7e0ecf0dd8aa1fc0827d0bd419be1f9e6093ab Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 00:54:47 +0100 Subject: [PATCH 023/104] Fixed rotation and loading delay --- .../ui/library/LibraryFragment.kt | 1 + .../ui/library/LibraryViewModel.kt | 1 - .../ui/library/ViewpagerAdapter.kt | 20 ++++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) 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 97fc4b72..5e2e5c45 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 @@ -360,6 +360,7 @@ class LibraryFragment : Fragment() { } override fun onConfigurationChanged(newConfig: Configuration) { + (viewpager.adapter as? ViewpagerAdapter)?.rebind() super.onConfigurationChanged(newConfig) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt index 07f88fbf..5f64880c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -97,7 +97,6 @@ class LibraryViewModel : ViewModel() { ) } - delay(5000) _pages.postValue(Resource.Success(pages)) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt index 3e31e875..33a40386 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -28,18 +28,28 @@ class ViewpagerAdapter( override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is PageViewHolder -> { - holder.bind(pages[position]) + holder.bind(pages[position], unbound.remove(position)) } } } + private val unbound = mutableSetOf() + /** + * Used to mark all pages for re-binding and forces all items to be refreshed + * Without this the pages will still use the same adapters + **/ + fun rebind() { + unbound.addAll(0..pages.size) + this.notifyItemRangeChanged(0, pages.size) + } + inner class PageViewHolder(private val itemViewTest: View) : RecyclerView.ViewHolder(itemViewTest) { - fun bind(page: SyncAPI.Page) { - if (itemViewTest.page_recyclerview?.adapter == null) { - itemView.page_recyclerview?.spanCount = - this@PageViewHolder.itemView.context.getSpanCount() ?: 3 + fun bind(page: SyncAPI.Page, rebind: Boolean) { + itemView.page_recyclerview?.spanCount = + this@PageViewHolder.itemView.context.getSpanCount() ?: 3 + if (itemViewTest.page_recyclerview?.adapter == null || rebind) { // Only add the items after it has been attached since the items rely on ItemWidth // Which is only determined after the recyclerview is attached. // If this fails then item height becomes 0 when there is only one item From e875d520deb56eeab03858dbd15e31e1cd39bc88 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 01:11:13 +0100 Subject: [PATCH 024/104] Fixed loading padding --- app/src/main/res/layout/fragment_library.xml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 28f21bf6..c9b1e3a0 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -103,21 +103,19 @@ tools:listitem="@layout/library_viewpager_page" /> + android:background="?attr/primaryBlackBackground" + android:visibility="gone" + tools:visibility="visible"> From e7732cc15fead262cb0f904a535a9e3d7c84cc37 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 14:57:06 +0100 Subject: [PATCH 025/104] Smoother fast scrolling in library & no labels --- .../ui/library/LibraryFragment.kt | 29 ++++++++++++++++--- app/src/main/res/layout/activity_main.xml | 4 +-- app/src/main/res/layout/fragment_library.xml | 1 + app/src/main/res/menu/bottom_nav_menu.xml | 6 ++-- 4 files changed, 31 insertions(+), 9 deletions(-) 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 5e2e5c45..7f025b74 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 @@ -10,10 +10,9 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.ArrayAdapter +import android.view.animation.AlphaAnimation import androidx.annotation.StringRes import androidx.appcompat.widget.SearchView -import androidx.core.os.postDelayed import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import com.google.android.material.tabs.TabLayoutMediator @@ -25,7 +24,6 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.debugAssert -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncIdName @@ -40,7 +38,7 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import kotlinx.android.synthetic.main.fragment_library.* -import kotlinx.coroutines.delay +import kotlin.math.abs const val LIBRARY_FOLDER = "library_folder" @@ -339,11 +337,34 @@ class LibraryFragment : Fragment() { savedInstanceState.remove(VIEWPAGER_ITEM_KEY) } + // Since the animation to scroll multiple items is so much its better to just hide + // the viewpager a bit while the fastest animation is running + fun hideViewpager(distance: Int) { + if (distance < 3) return + + val hideAnimation = AlphaAnimation(1f, 0f).apply { + duration = distance * 50L + fillAfter = true + } + val showAnimation = AlphaAnimation(0f, 1f).apply { + duration = distance * 50L + startOffset = distance * 100L + fillAfter = true + } + viewpager?.startAnimation(hideAnimation) + viewpager?.startAnimation(showAnimation) + } + TabLayoutMediator( library_tab_layout, viewpager, ) { tab, position -> tab.text = pages.getOrNull(position)?.title?.asStringNull(context) + tab.view.setOnClickListener { + val currentItem = viewpager?.currentItem ?: return@setOnClickListener + val distance = abs(position - currentItem) + hideViewpager(distance) + } }.attach() } is Resource.Loading -> { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ad29d22a..b6290865 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,9 +35,9 @@ --> + android:id="@+id/navigation_home" + android:icon="@drawable/home_alt" + android:title="@string/title_home" /> Date: Sat, 28 Jan 2023 22:49:58 +0100 Subject: [PATCH 026/104] fix library statusbar --- .../com/lagradost/cloudstream3/ui/library/LibraryFragment.kt | 2 +- app/src/main/res/layout/fragment_library.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 7f025b74..4b89e984 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 @@ -88,7 +88,7 @@ class LibraryFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - context?.fixPaddingStatusbar(library_root) + context?.fixPaddingStatusbar(search_status_bar_padding) sort_fab?.setOnClickListener { val methods = libraryViewModel.sortingMethods.map { diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 7230a34b..f9012148 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -24,6 +24,7 @@ android:background="?attr/primaryGrayBackground"> Date: Sat, 28 Jan 2023 23:08:55 +0100 Subject: [PATCH 027/104] mini fix --- .../com/lagradost/cloudstream3/ui/library/LibraryFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4b89e984..1c6af447 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 @@ -186,7 +186,7 @@ class LibraryFragment : Fragment() { items, selectedIndex, txt(R.string.open_with).asString(this), - true, + false, {}, ) { val savedData = if (it < baseOptions.size) { From df95931d0ade28f8ef1784864266d8e4d0e5c02f Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 23:36:24 +0100 Subject: [PATCH 028/104] future proofed SyncID --- .../com/lagradost/cloudstream3/syncproviders/SyncAPI.kt | 6 +++++- .../cloudstream3/syncproviders/providers/LocalList.kt | 2 +- .../com/lagradost/cloudstream3/utils/DataStoreHelper.kt | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) 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 2f5e0050..8c76c5bf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -138,7 +138,11 @@ interface SyncAPI : OAuth2API { data class LibraryItem( override val name: String, override val url: String, - /** Unique unchanging string used for data storage */ + /** + * Unique unchanging string used for data storage. + * This should be the actual id when you change scores and status + * since score changes from library might get added in the future. + **/ val syncId: String, val episodesCompleted: Int?, val episodesTotal: Int?, diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt index 61e9e294..0b081220 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -72,7 +72,7 @@ class LocalList : SyncAPI { it.second.stringRes }.mapValues { group -> group.value.mapNotNull { - getBookmarkedData(it.first)?.toLibraryItem() + getBookmarkedData(it.first)?.toLibraryItem(it.first.toString()) } } } 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 4e9c646f..281c9c44 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -55,16 +55,16 @@ object DataStoreHelper { @JsonProperty("quality") override var quality: SearchQuality? = null, @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse { - fun toLibraryItem(): SyncAPI.LibraryItem { + fun toLibraryItem(id: String): SyncAPI.LibraryItem { return SyncAPI.LibraryItem( name, url, - url, + id, null, null, null, null, - apiName, type, posterUrl, posterHeaders, quality, id + apiName, type, posterUrl, posterHeaders, quality, this.id ) } } From 3c82548c20a1ba523e8a5a76e4734c1620565bfb Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:38:02 +0000 Subject: [PATCH 029/104] Library merge (#343) --- app/build.gradle.kts | 3 + .../com/lagradost/cloudstream3/MainAPI.kt | 25 ++ .../lagradost/cloudstream3/MainActivity.kt | 6 + .../metaproviders/AnilistRedirector.kt | 30 -- .../metaproviders/SyncRedirector.kt | 56 +++ .../syncproviders/AccountManager.kt | 3 +- .../cloudstream3/syncproviders/SyncAPI.kt | 88 +++- .../cloudstream3/syncproviders/SyncRepo.kt | 22 +- .../syncproviders/providers/AniListApi.kt | 133 ++++-- .../syncproviders/providers/LocalList.kt | 100 +++++ .../syncproviders/providers/MALApi.kt | 135 ++++-- .../cloudstream3/ui/AutofitRecyclerView.kt | 5 +- .../cloudstream3/ui/home/HomeFragment.kt | 4 +- .../ui/library/LibraryFragment.kt | 393 ++++++++++++++++++ .../ui/library/LibraryScrollTransformer.kt | 17 + .../ui/library/LibraryViewModel.kt | 104 +++++ .../ui/library/LoadingPosterAdapter.kt | 37 ++ .../cloudstream3/ui/library/PageAdapter.kt | 130 ++++++ .../ui/library/ViewpagerAdapter.kt | 90 ++++ .../ui/quicksearch/QuickSearchFragment.kt | 2 +- .../cloudstream3/ui/result/ResultFragment.kt | 8 +- .../ui/result/ResultFragmentPhone.kt | 2 +- .../ui/result/ResultFragmentTv.kt | 2 +- .../ui/result/ResultViewModel2.kt | 21 +- .../cloudstream3/ui/search/SearchAdaptor.kt | 4 + .../cloudstream3/ui/search/SearchFragment.kt | 2 +- .../ui/search/SearchResultBuilder.kt | 5 +- .../ui/settings/extensions/PluginsFragment.kt | 2 +- .../lagradost/cloudstream3/utils/AppUtils.kt | 55 +++ .../cloudstream3/utils/BackupUtils.kt | 4 - .../cloudstream3/utils/DataStoreHelper.kt | 22 +- .../lagradost/cloudstream3/utils/SyncUtil.kt | 27 +- .../lagradost/cloudstream3/utils/UIHelper.kt | 54 ++- app/src/main/res/color/item_select_color.xml | 2 + .../ic_baseline_collections_bookmark_24.xml | 6 + .../main/res/drawable/ic_baseline_sort_24.xml | 5 + .../main/res/drawable/ic_baseline_star_24.xml | 4 +- .../drawable/ic_outline_account_circle_24.xml | 6 + .../res/drawable/indicator_background.xml | 6 + app/src/main/res/drawable/rating_bg_color.xml | 6 + app/src/main/res/layout/activity_main.xml | 4 +- app/src/main/res/layout/fragment_library.xml | 177 ++++++++ .../res/layout/library_viewpager_page.xml | 11 + .../res/layout/loading_poster_dynamic.xml | 37 ++ .../layout/search_result_grid_expanded.xml | 131 ++++-- app/src/main/res/menu/bottom_nav_menu.xml | 29 +- app/src/main/res/menu/library_menu.xml | 17 + .../main/res/navigation/mobile_navigation.xml | 9 + app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/strings.xml | 15 +- app/src/main/res/values/styles.xml | 15 + 51 files changed, 1855 insertions(+), 218 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt create mode 100644 app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_sort_24.xml create mode 100644 app/src/main/res/drawable/ic_outline_account_circle_24.xml create mode 100644 app/src/main/res/drawable/indicator_background.xml create mode 100644 app/src/main/res/drawable/rating_bg_color.xml create mode 100644 app/src/main/res/layout/fragment_library.xml create mode 100644 app/src/main/res/layout/library_viewpager_page.xml create mode 100644 app/src/main/res/layout/loading_poster_dynamic.xml create mode 100644 app/src/main/res/menu/library_menu.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1cbcec68..808c0cc3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -220,6 +220,9 @@ dependencies { // Library/extensions searching with Levenshtein distance implementation("me.xdrop:fuzzywuzzy:1.4.0") + + // color pallette for images -> colors + implementation("androidx.palette:palette-ktx:1.0.0") } tasks.register("androidSourcesJar", Jar::class) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 8c818027..73859021 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -13,7 +13,10 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi +import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.ui.player.SubtitleData +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf @@ -510,6 +513,20 @@ abstract class MainAPI { open val hasMainPage = false open val hasQuickSearch = false + /** + * A set of which ids the provider can open with getLoadUrl() + * If the set contains SyncIdName.Imdb then getLoadUrl() can be started with + * an Imdb class which inherits from SyncId. + * + * getLoadUrl() is then used to get page url based on that ID. + * + * Example: + * "tt6723592" -> getLoadUrl(ImdbSyncId("tt6723592")) -> "mainUrl/imdb/tt6723592" -> load("mainUrl/imdb/tt6723592") + * + * This is used to launch pages from personal lists or recommendations using IDs. + **/ + open val supportedSyncNames = setOf() + open val supportedTypes = setOf( TvType.Movie, TvType.TvSeries, @@ -580,6 +597,14 @@ abstract class MainAPI { open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? { return null } + + /** + * Get the load() url based on a sync ID like IMDb or MAL. + * Only contains SyncIds based on supportedSyncUrls. + **/ + open suspend fun getLoadUrl(name: SyncIdName, id: String): String? { + return null + } } /** Might need a different implementation for desktop*/ diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 857eaa6a..5720b7a7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -388,6 +388,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val isNavVisible = listOf( R.id.navigation_home, R.id.navigation_search, + R.id.navigation_library, R.id.navigation_downloads, R.id.navigation_settings, R.id.navigation_download_child, @@ -438,6 +439,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { nav_view?.isVisible = isNavVisible && !landscape nav_rail_view?.isVisible = isNavVisible && landscape + + // Hide library on TV since it is not supported yet :( + val isTrueTv = isTrueTvSettings() + nav_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv + nav_rail_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv } //private var mCastSession: CastSession? = null diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt deleted file mode 100644 index 208db14b..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lagradost.cloudstream3.metaproviders - -import com.lagradost.cloudstream3.ErrorLoadingException -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.utils.SyncUtil - -object SyncRedirector { - val syncApis = SyncApis - - suspend fun redirect(url: String, preferredUrl: String): String { - for (api in syncApis) { - if (url.contains(api.mainUrl)) { - val otherApi = when (api.name) { - aniListApi.name -> "anilist" - malApi.name -> "myanimelist" - else -> return url - } - - return SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> - realUrl.contains(preferredUrl) - } ?: run { - throw ErrorLoadingException("Page does not exist on $preferredUrl") - } - } - } - return url - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt new file mode 100644 index 00000000..75e96bec --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt @@ -0,0 +1,56 @@ +package com.lagradost.cloudstream3.metaproviders + +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.syncproviders.SyncIdName + +object SyncRedirector { + val syncApis = SyncApis + private val syncIds = + listOf( + SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""), + SyncIdName.Anilist to Regex("""anilist\.co\/anime\/(\d+)""") + ) + + suspend fun redirect( + url: String, + providerApi: MainAPI + ): String { + // Deprecated since providers should do this instead! + + // Tries built in ID -> ProviderUrl + /* + for (api in syncApis) { + if (url.contains(api.mainUrl)) { + val otherApi = when (api.name) { + aniListApi.name -> "anilist" + malApi.name -> "myanimelist" + else -> return url + } + + SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> + realUrl.contains(providerApi.mainUrl) + }?.let { + return it + } +// ?: run { +// throw ErrorLoadingException("Page does not exist on $preferredUrl") +// } + } + } + */ + + // Tries provider solution + // This goes through all sync ids and finds supported id by said provider + return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) -> + if (providerApi.supportedSyncNames.contains(syncName)) { + syncRegex.find(url)?.value?.let { + suspendSafeApiCall { + providerApi.getLoadUrl(syncName, it) + } + } + } else null + } ?: url + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index f09bf8fe..f17086c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -13,6 +13,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val openSubtitlesApi = OpenSubtitlesApi(0) val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() + val localListApi = LocalList() // used to login via app intent val OAuth2Apis @@ -29,7 +30,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { // used for active syncing val SyncApis get() = listOf( - SyncRepo(malApi), SyncRepo(aniListApi) + SyncRepo(malApi), SyncRepo(aniListApi), SyncRepo(localListApi) ) val inAppAuths 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 5aa56a02..8c76c5bf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,10 +1,31 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.UiText +import me.xdrop.fuzzywuzzy.FuzzySearch + +enum class SyncIdName { + Anilist, + MyAnimeList, + Trakt, + Imdb, + LocalList +} interface SyncAPI : OAuth2API { + /** + * Set this to true if the user updates something on the list like watch status or score + **/ + var requireLibraryRefresh: Boolean val mainUrl: String + /** + * Allows certain providers to open pages from + * library links. + **/ + val syncIdName: SyncIdName + /** -1 -> None 0 -> Watching @@ -22,7 +43,9 @@ interface SyncAPI : OAuth2API { suspend fun search(name: String): List? - fun getIdFromUrl(url : String) : String + suspend fun getPersonalLibrary(): LibraryMetadata? + + fun getIdFromUrl(url: String): String data class SyncSearchResult( override val name: String, @@ -42,7 +65,7 @@ interface SyncAPI : OAuth2API { val score: Int?, val watchedEpisodes: Int?, var isFavorite: Boolean? = null, - var maxEpisodes : Int? = null, + var maxEpisodes: Int? = null, ) data class SyncResult( @@ -63,9 +86,9 @@ interface SyncAPI : OAuth2API { var genres: List? = null, var synonyms: List? = null, var trailers: List? = null, - var isAdult : Boolean? = null, + var isAdult: Boolean? = null, var posterUrl: String? = null, - var backgroundPosterUrl : String? = null, + var backgroundPosterUrl: String? = null, /** In unixtime */ var startDate: Long? = null, @@ -76,4 +99,61 @@ interface SyncAPI : OAuth2API { var prevSeason: SyncSearchResult? = null, var actors: List? = null, ) + + + data class Page( + val title: UiText, var items: List + ) { + fun sort(method: ListSorting?, query: String? = null) { + items = when (method) { + ListSorting.Query -> + if (query != null) { + items.sortedBy { + -FuzzySearch.partialRatio( + query.lowercase(), it.name.lowercase() + ) + } + } else items + ListSorting.RatingHigh -> items.sortedBy { -(it.personalRating ?: 0) } + ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) } + ListSorting.AlphabeticalA -> items.sortedBy { it.name } + ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed() + ListSorting.UpdatedNew -> items.sortedBy { it.lastUpdatedUnixTime?.times(-1) } + ListSorting.UpdatedOld -> items.sortedBy { it.lastUpdatedUnixTime } + else -> items + } + } + } + + data class LibraryMetadata( + val allLibraryLists: List, + val supportedListSorting: Set + ) + + data class LibraryList( + val name: UiText, + val items: List + ) + + data class LibraryItem( + override val name: String, + override val url: String, + /** + * Unique unchanging string used for data storage. + * This should be the actual id when you change scores and status + * since score changes from library might get added in the future. + **/ + val syncId: String, + val episodesCompleted: Int?, + val episodesTotal: Int?, + /** Out of 100 */ + val personalRating: Int?, + val lastUpdatedUnixTime: Long?, + override val apiName: String, + override var type: TvType?, + override var posterUrl: String?, + override var posterHeaders: Map?, + override var quality: SearchQuality?, + override var id: Int? = null, + ) : SearchResponse } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt index b621e81a..85b877e0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt @@ -11,26 +11,38 @@ class SyncRepo(private val repo: SyncAPI) { val icon = repo.icon val mainUrl = repo.mainUrl val requiresLogin = repo.requiresLogin + val syncIdName = repo.syncIdName + var requireLibraryRefresh: Boolean + get() = repo.requireLibraryRefresh + set(value) { + repo.requireLibraryRefresh = value + } suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource { return safeApiCall { repo.score(id, status) } } - suspend fun getStatus(id : String) : Resource { + suspend fun getStatus(id: String): Resource { return safeApiCall { repo.getStatus(id) ?: throw ErrorLoadingException("No data") } } - suspend fun getResult(id : String) : Resource { + suspend fun getResult(id: String): Resource { return safeApiCall { repo.getResult(id) ?: throw ErrorLoadingException("No data") } } - suspend fun search(query : String) : Resource> { + suspend fun search(query: String): Resource> { return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() } } - fun hasAccount() : Boolean { + suspend fun getPersonalLibrary(): Resource { + return safeApiCall { repo.getPersonalLibrary() ?: throw ErrorLoadingException() } + } + + fun hasAccount(): Boolean { return normalSafeApiCall { repo.loginInfo() != null } ?: false } - fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url) + fun getIdFromUrl(url: String): String? = normalSafeApiCall { + repo.getIdFromUrl(url) + } } \ 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 d4742d94..7d9de43a 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 @@ -1,10 +1,10 @@ package com.lagradost.cloudstream3.syncproviders.providers +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.AcraApplication.Companion.getKey -import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.mvvm.logError @@ -12,6 +12,9 @@ import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall 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.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -27,10 +30,12 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { override val key = "6871" override val redirectUrl = "anilistlogin" override val idPrefix = "anilist" + override var requireLibraryRefresh = true override var mainUrl = "https://anilist.co" override val icon = R.drawable.ic_anilist_icon override val requiresLogin = false override val createAccountUrl = "$mainUrl/signup" + override val syncIdName = SyncIdName.Anilist override fun loginInfo(): AuthAPI.LoginInfo? { // context.getUser(true)?. @@ -45,6 +50,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } override fun logOut() { + requireLibraryRefresh = true removeAccountKeys() } @@ -64,8 +70,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { switchToNewAccount() setKey(accountId, ANILIST_UNIXTIME_KEY, endTime) setKey(accountId, ANILIST_TOKEN_KEY, token) - setKey(ANILIST_SHOULD_UPDATE_LIST, true) val user = getUser() + requireLibraryRefresh = true return user != null } @@ -140,7 +146,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { this.name, recMedia.id?.toString() ?: return@mapNotNull null, getUrlFromId(recMedia.id), - recMedia.coverImage?.large ?: recMedia.coverImage?.medium + recMedia.coverImage?.extraLarge ?: recMedia.coverImage?.large + ?: recMedia.coverImage?.medium ) }, trailers = when (season.trailer?.site?.lowercase()?.trim()) { @@ -170,7 +177,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) + ).also { + requireLibraryRefresh = requireLibraryRefresh || it + } } companion object { @@ -181,7 +190,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { const val ANILIST_TOKEN_KEY: String = "anilist_token" // anilist token for api const val ANILIST_USER_KEY: String = "anilist_user" // user data like profile const val ANILIST_CACHED_LIST: String = "anilist_cached_list" - const val ANILIST_SHOULD_UPDATE_LIST: String = "anilist_should_update_list" private fun fixName(name: String): String { return name.lowercase(Locale.ROOT).replace(" ", "") @@ -219,7 +227,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { romaji } idMal - coverImage { medium large } + coverImage { medium large extraLarge } averageScore } } @@ -232,7 +240,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { format id idMal - coverImage { medium large } + coverImage { medium large extraLarge } averageScore title { english @@ -292,15 +300,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { val shows = searchShows(name.replace(blackListRegex, "")) shows?.data?.Page?.media?.find { - malId ?: "NONE" == it.idMal.toString() + (malId ?: "NONE") == it.idMal.toString() }?.let { return it } val filtered = shows?.data?.Page?.media?.filter { - ( - it.startDate.year ?: year.toString() == year.toString() - || year == null - ) + (((it.startDate.year ?: year.toString()) == year.toString() + || year == null)) } filtered?.forEach { it.title.romaji?.let { romaji -> @@ -312,14 +318,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } // Changing names of these will show up in UI - enum class AniListStatusType(var value: Int) { - Watching(0), - Completed(1), - Paused(2), - Dropped(3), - Planning(4), - ReWatching(5), - None(-1) + enum class AniListStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + Paused(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + Planning(4, R.string.type_plan_to_watch), + ReWatching(5, R.string.type_re_watching), + None(-1, R.string.none) } fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp } @@ -335,7 +341,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun convertAnilistStringToStatus(string: String): AniListStatusType { + fun convertAniListStringToStatus(string: String): AniListStatusType { return fromIntToAnimeStatus(aniListStatusString.indexOf(string)) } @@ -526,7 +532,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { app.post( "https://graphql.anilist.co/", headers = mapOf( - "Authorization" to "Bearer " + (getAuth() ?: return@suspendSafeApiCall null), + "Authorization" to "Bearer " + (getAuth() + ?: return@suspendSafeApiCall null), if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache" ), cacheTime = 0, @@ -575,7 +582,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { data class CoverImage( @JsonProperty("medium") val medium: String?, - @JsonProperty("large") val large: String? + @JsonProperty("large") val large: String?, + @JsonProperty("extraLarge") val extraLarge: String? ) data class Media( @@ -602,7 +610,29 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("score") val score: Int, @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media - ) + ) { + fun toLibraryItem(): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + // English title first + this.media.title.english ?: this.media.title.romaji + ?: this.media.synonyms.firstOrNull() + ?: "", + "https://anilist.co/anime/${this.media.id}/", + this.media.id.toString(), + this.progress, + this.media.episodes, + this.score, + this.updatedAt.toLong(), + "AniList", + TvType.Anime, + this.media.coverImage.extraLarge ?: this.media.coverImage.large + ?: this.media.coverImage.medium, + null, + null, + null + ) + } + } data class Lists( @JsonProperty("status") val status: String?, @@ -617,40 +647,59 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("MediaListCollection") val MediaListCollection: MediaListCollection ) - fun getAnilistListCached(): Array? { + private fun getAniListListCached(): Array? { return getKey(ANILIST_CACHED_LIST) as? Array } - suspend fun getAnilistAnimeListSmart(): Array? { + private suspend fun getAniListAnimeListSmart(): Array? { if (getAuth() == null) return null if (checkToken()) return null - return if (getKey(ANILIST_SHOULD_UPDATE_LIST, true) == true) { - val list = getFullAnilistList()?.data?.MediaListCollection?.lists?.toTypedArray() + return if (requireLibraryRefresh) { + val list = getFullAniListList()?.data?.MediaListCollection?.lists?.toTypedArray() if (list != null) { setKey(ANILIST_CACHED_LIST, list) - setKey(ANILIST_SHOULD_UPDATE_LIST, false) } list } else { - getAnilistListCached() + getAniListListCached() } } - private suspend fun getFullAnilistList(): FullAnilistList? { - var userID: Int? = null - /** WARNING ASSUMES ONE USER! **/ - getKeys(ANILIST_USER_KEY)?.forEach { key -> - getKey(key, null)?.let { - userID = it.id - } - } + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getAniListAnimeListSmart()?.groupBy { + convertAniListStringToStatus(it.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.entries.map { entry -> entry.toLibraryItem() } }.flatten() + } ?: emptyMap() - val fixedUserID = userID ?: return null + // To fill empty lists when AniList does not return them + val baseMap = + AniListStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) + ) + } + + private suspend fun getFullAniListList(): FullAnilistList? { + /** WARNING ASSUMES ONE USER! **/ + + val userID = getKey(accountId, ANILIST_USER_KEY)?.id ?: return null val mediaType = "ANIME" val query = """ - query (${'$'}userID: Int = $fixedUserID, ${'$'}MEDIA: MediaType = $mediaType) { + query (${'$'}userID: Int = $userID, ${'$'}MEDIA: MediaType = $mediaType) { MediaListCollection (userId: ${'$'}userID, type: ${'$'}MEDIA) { lists { status @@ -661,7 +710,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { startedAt { year month day } updatedAt progress - score + score (format: POINT_100) private media { @@ -677,7 +726,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { english romaji } - coverImage { medium } + coverImage { extraLarge large medium } synonyms nextAiringEpisode { timeUntilAiring diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt new file mode 100644 index 00000000..0b081220 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -0,0 +1,100 @@ +package com.lagradost.cloudstream3.syncproviders.providers + +import androidx.fragment.app.FragmentActivity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.AuthAPI +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.utils.Coroutines.ioWork +import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds +import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData +import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState + +class LocalList : SyncAPI { + override val name = "Local" + override val icon: Int = R.drawable.ic_baseline_storage_24 + override val requiresLogin = false + override val createAccountUrl: Nothing? = null + override val idPrefix = "local" + override var requireLibraryRefresh = true + + override fun loginInfo(): AuthAPI.LoginInfo { + return AuthAPI.LoginInfo( + null, + null, + 0 + ) + } + + override fun logOut() { + + } + + override val key: String = "" + override val redirectUrl = "" + override suspend fun handleRedirect(url: String): Boolean { + return true + } + + override fun authenticate(activity: FragmentActivity?) { + } + + override val mainUrl = "" + override val syncIdName = SyncIdName.LocalList + override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean { + return true + } + + override suspend fun getStatus(id: String): SyncAPI.SyncStatus? { + return null + } + + override suspend fun getResult(id: String): SyncAPI.SyncResult? { + return null + } + + override suspend fun search(name: String): List? { + return null + } + + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata? { + val watchStatusIds = ioWork { + getAllWatchStateIds()?.map { id -> + Pair(id, getResultWatchState(id)) + } + }?.distinctBy { it.first } ?: return null + + val list = ioWork { + watchStatusIds.groupBy { + it.second.stringRes + }.mapValues { group -> + group.value.mapNotNull { + getBookmarkedData(it.first)?.toLibraryItem(it.first.toString()) + } + } + } + + val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { + // None is not something to display + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, +// ListSorting.UpdatedNew, +// ListSorting.UpdatedOld, +// ListSorting.RatingHigh, +// ListSorting.RatingLow, + ) + ) + } + + override fun getIdFromUrl(url: String): String { + return url + } +} \ No newline at end of file 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 c08958ce..5164b606 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 @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.syncproviders.providers import android.util.Base64 +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey @@ -8,11 +9,15 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ShowStatus +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError 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.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -31,13 +36,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { override val redirectUrl = "mallogin" override val idPrefix = "mal" override var mainUrl = "https://myanimelist.net" - val apiUrl = "https://api.myanimelist.net" + private val apiUrl = "https://api.myanimelist.net" override val icon = R.drawable.mal_logo override val requiresLogin = false - + override val syncIdName = SyncIdName.MyAnimeList + override var requireLibraryRefresh = true override val createAccountUrl = "$mainUrl/register.php" override fun logOut() { + requireLibraryRefresh = true removeAccountKeys() } @@ -90,7 +97,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) + ).also { + requireLibraryRefresh = requireLibraryRefresh || it + } } data class MalAnime( @@ -248,10 +257,45 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { const val MAL_USER_KEY: String = "mal_user" // user data like profile const val MAL_CACHED_LIST: String = "mal_cached_list" - const val MAL_SHOULD_UPDATE_LIST: String = "mal_should_update_list" const val MAL_UNIXTIME_KEY: String = "mal_unixtime" // When token expires const val MAL_REFRESH_TOKEN_KEY: String = "mal_refresh_token" // refresh token const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api + + fun convertToStatus(string: String): MalStatusType { + return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) + } + + enum class MalStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + OnHold(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + PlanToWatch(4, R.string.type_plan_to_watch), + None(-1, R.string.type_none) + } + + private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } + return when (inp) { + -1 -> MalStatusType.None + 0 -> MalStatusType.Watching + 1 -> MalStatusType.Completed + 2 -> MalStatusType.OnHold + 3 -> MalStatusType.Dropped + 4 -> MalStatusType.PlanToWatch + 5 -> MalStatusType.Watching + else -> MalStatusType.None + } + } + + private fun parseDateLong(string: String?): Long? { + return try { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse( + string ?: return null + )?.time?.div(1000) + } catch (e: Exception) { + null + } + } } override suspend fun handleRedirect(url: String): Boolean { @@ -275,7 +319,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { switchToNewAccount() storeToken(res) val user = getMalUser() - setKey(MAL_SHOULD_UPDATE_LIST, true) + requireLibraryRefresh = true return user != null } } @@ -308,9 +352,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime)) setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token) setKey(accountId, MAL_TOKEN_KEY, token.access_token) + requireLibraryRefresh = true } } catch (e: Exception) { - e.printStackTrace() + logError(e) } } @@ -329,7 +374,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { ).text storeToken(res) } catch (e: Exception) { - e.printStackTrace() + logError(e) } } @@ -382,7 +427,24 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { data class Data( @JsonProperty("node") val node: Node, @JsonProperty("list_status") val list_status: ListStatus?, - ) + ) { + fun toLibraryItem(): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + this.node.title, + "https://myanimelist.net/anime/${this.node.id}/", + this.node.id.toString(), + this.list_status?.num_episodes_watched, + this.node.num_episodes, + this.list_status?.score?.times(10), + parseDateLong(this.list_status?.updated_at), + "MAL", + TvType.Anime, + this.node.main_picture?.large ?: this.node.main_picture?.medium, + null, + null, + ) + } + } data class Paging( @JsonProperty("next") val next: String? @@ -413,18 +475,43 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return getKey(MAL_CACHED_LIST) as? Array } - suspend fun getMalAnimeListSmart(): Array? { + private suspend fun getMalAnimeListSmart(): Array? { if (getAuth() == null) return null - return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) { + return if (requireLibraryRefresh) { val list = getMalAnimeList() setKey(MAL_CACHED_LIST, list) - setKey(MAL_SHOULD_UPDATE_LIST, false) list } else { getMalAnimeListCached() } } + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getMalAnimeListSmart()?.groupBy { + convertToStatus(it.list_status?.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.toLibraryItem() } + } ?: emptyMap() + + // To fill empty lists when MAL does not return them + val baseMap = + MalStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) + ) + } + private suspend fun getMalAnimeList(): Array { checkMalToken() var offset = 0 @@ -440,10 +527,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return fullList.toTypedArray() } - fun convertToStatus(string: String): MalStatusType { - return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) - } - private suspend fun getMalAnimeListSlice(offset: Int = 0): MalList? { val user = "@me" val auth = getAuth() ?: return null @@ -557,28 +640,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return user } - enum class MalStatusType(var value: Int) { - Watching(0), - Completed(1), - OnHold(2), - Dropped(3), - PlanToWatch(4), - None(-1) - } - - private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } - return when (inp) { - -1 -> MalStatusType.None - 0 -> MalStatusType.Watching - 1 -> MalStatusType.Completed - 2 -> MalStatusType.OnHold - 3 -> MalStatusType.Dropped - 4 -> MalStatusType.PlanToWatch - 5 -> MalStatusType.Watching - else -> MalStatusType.None - } - } - private suspend fun setScoreRequest( id: Int, status: MalStatusType? = null, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt index 138084fc..b4c07792 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt @@ -7,7 +7,8 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import kotlin.math.abs -class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManager(context, _spanCount) { +class GrdLayoutManager(val context: Context, _spanCount: Int) : + GridLayoutManager(context, _spanCount) { override fun onFocusSearchFailed( focused: View, focusDirection: Int, @@ -34,7 +35,7 @@ class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManage val pos = maxOf(0, getPosition(focused!!) - 2) parent.scrollToPosition(pos) super.onRequestChildFocus(parent, state, child, focused) - } catch (e: Exception){ + } catch (e: Exception) { false } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 8a8f90b4..5cf6fc8e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -569,7 +569,7 @@ class HomeFragment : Fragment() { val mutableListOfResponse = mutableListOf() listHomepageItems.clear() - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList( + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList( d.values.toMutableList(), home_master_recycler ) @@ -621,7 +621,7 @@ class HomeFragment : Fragment() { //home_loaded?.isVisible = false } is Resource.Loading -> { - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(listOf()) + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(listOf()) home_loading_shimmer?.startShimmer() home_loading?.isVisible = true home_loading_error?.isVisible = false 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 new file mode 100644 index 00000000..1c6af447 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -0,0 +1,393 @@ +package com.lagradost.cloudstream3.ui.library + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AlphaAnimation +import androidx.annotation.StringRes +import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import com.google.android.material.tabs.TabLayoutMediator +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.APIHolder.allProviders +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.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.mvvm.debugAssert +import com.lagradost.cloudstream3.mvvm.observe +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA +import com.lagradost.cloudstream3.utils.AppUtils.loadResult +import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult +import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount +import kotlinx.android.synthetic.main.fragment_library.* +import kotlin.math.abs + +const val LIBRARY_FOLDER = "library_folder" + + +enum class LibraryOpenerType(@StringRes val stringRes: Int) { + Default(R.string.default_subtitles), // TODO FIX AFTER MERGE + Provider(R.string.none), + Browser(R.string.browser), + Search(R.string.search), + None(R.string.none), +} + +/** Used to store how the user wants to open said poster */ +data class LibraryOpener( + val openType: LibraryOpenerType, + val providerData: ProviderLibraryData?, +) + +data class ProviderLibraryData( + val apiName: String +) + +class LibraryFragment : Fragment() { + companion object { + fun newInstance() = LibraryFragment() + + /** + * Store which page was last seen when exiting the fragment and returning + **/ + const val VIEWPAGER_ITEM_KEY = "viewpager_item" + } + + private val libraryViewModel: LibraryViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_library, container, false) + } + + override fun onSaveInstanceState(outState: Bundle) { + viewpager?.currentItem?.let { currentItem -> + outState.putInt(VIEWPAGER_ITEM_KEY, currentItem) + } + super.onSaveInstanceState(outState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.fixPaddingStatusbar(search_status_bar_padding) + + sort_fab?.setOnClickListener { + val methods = libraryViewModel.sortingMethods.map { + txt(it.stringRes).asString(view.context) + } + + activity?.showBottomDialog(methods, + libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod), + txt(R.string.sort_by).asString(view.context), + false, + {}, + { + val method = libraryViewModel.sortingMethods[it] + libraryViewModel.sort(method) + }) + } + + main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + libraryViewModel.sort(ListSorting.Query, query) + return true + } + + // This is required to prevent the first text change + // When this is attached it'll immediately send a onQueryTextChange("") + // Which we do not want + var hasInitialized = false + override fun onQueryTextChange(newText: String?): Boolean { + if (!hasInitialized) { + hasInitialized = true + return true + } + + libraryViewModel.sort(ListSorting.Query, newText) + return true + } + }) + + libraryViewModel.reloadPages(false) + + list_selector?.setOnClickListener { + val items = libraryViewModel.availableApiNames + val currentItem = libraryViewModel.currentApiName.value + + activity?.showBottomDialog(items, + items.indexOf(currentItem), + txt(R.string.select_library).asString(it.context), + false, + {}) { index -> + val selectedItem = items.getOrNull(index) ?: return@showBottomDialog + libraryViewModel.switchList(selectedItem) + } + } + + + /** + * Shows a plugin selection dialogue and saves the response + **/ + fun Activity.showPluginSelectionDialog( + key: String, + syncId: SyncIdName, + apiName: String? = null, + ) { + val availableProviders = allProviders.filter { + it.supportedSyncNames.contains(syncId) + }.map { it.name } + + // Add the api if it exists + (APIHolder.getApiFromNameNull(apiName)?.let { listOf(it.name) } ?: emptyList()) + + val baseOptions = listOf( + LibraryOpenerType.Default, + LibraryOpenerType.None, + LibraryOpenerType.Browser, + LibraryOpenerType.Search + ) + + val items = baseOptions.map { txt(it.stringRes).asString(this) } + availableProviders + + val savedSelection = getKey(LIBRARY_FOLDER, key) + val selectedIndex = + when { + savedSelection == null -> 0 + // If provider + savedSelection.openType == LibraryOpenerType.Provider + && savedSelection.providerData?.apiName != null -> { + availableProviders.indexOf(savedSelection.providerData.apiName) + .takeIf { it != -1 } + ?.plus(baseOptions.size) ?: 0 + } + // Else base option + else -> baseOptions.indexOf(savedSelection.openType) + } + + this.showBottomDialog( + items, + selectedIndex, + txt(R.string.open_with).asString(this), + false, + {}, + ) { + val savedData = if (it < baseOptions.size) { + LibraryOpener( + baseOptions[it], + null + ) + } else { + LibraryOpener( + LibraryOpenerType.Provider, + ProviderLibraryData(items[it]) + ) + } + + setKey( + LIBRARY_FOLDER, + key, + savedData, + ) + } + } + + provider_selector?.setOnClickListener { + val syncName = libraryViewModel.currentSyncApi?.syncIdName ?: return@setOnClickListener + activity?.showPluginSelectionDialog(syncName.name, syncName) + } + + viewpager?.setPageTransformer(LibraryScrollTransformer()) + viewpager?.adapter = + viewpager.adapter ?: ViewpagerAdapter(mutableListOf(), { isScrollingDown: Boolean -> + if (isScrollingDown) { + sort_fab?.shrink() + } else { + sort_fab?.extend() + } + }) callback@{ searchClickCallback -> + // To prevent future accidents + debugAssert({ + searchClickCallback.card !is SyncAPI.LibraryItem + }, { + "searchClickCallback ${searchClickCallback.card} is not a LibraryItem" + }) + + val syncId = (searchClickCallback.card as SyncAPI.LibraryItem).syncId + val syncName = + libraryViewModel.currentSyncApi?.syncIdName ?: return@callback + + when (searchClickCallback.action) { + SEARCH_ACTION_SHOW_METADATA -> { + activity?.showPluginSelectionDialog( + syncId, + syncName, + searchClickCallback.card.apiName + ) + } + + SEARCH_ACTION_LOAD -> { + // This basically first selects the individual opener and if that is default then + // selects the whole list opener + val savedListSelection = + getKey(LIBRARY_FOLDER, syncName.name) + val savedSelection = getKey(LIBRARY_FOLDER, syncId).takeIf { + it?.openType != LibraryOpenerType.Default + } ?: savedListSelection + + when (savedSelection?.openType) { + null, LibraryOpenerType.Default -> { + // Prevents opening MAL/AniList as a provider + if (APIHolder.getApiFromNameNull(searchClickCallback.card.apiName) != null) { + activity?.loadSearchResult( + searchClickCallback.card + ) + } else { + // Search when no provider can open + QuickSearchFragment.pushSearch( + activity, + searchClickCallback.card.name + ) + } + } + LibraryOpenerType.None -> {} + LibraryOpenerType.Provider -> + savedSelection.providerData?.apiName?.let { apiName -> + activity?.loadResult( + searchClickCallback.card.url, + apiName, + ) + } + LibraryOpenerType.Browser -> + openBrowser(searchClickCallback.card.url) + LibraryOpenerType.Search -> { + QuickSearchFragment.pushSearch( + activity, + searchClickCallback.card.name + ) + } + } + } + } + } + + viewpager?.offscreenPageLimit = 2 + viewpager?.reduceDragSensitivity() + + val startLoading = Runnable { + gridview?.numColumns = context?.getSpanCount() ?: 3 + gridview?.adapter = + context?.let { LoadingPosterAdapter(it, 6 * 3) } + library_loading_overlay?.isVisible = true + library_loading_shimmer?.startShimmer() + empty_list_textview?.isVisible = false + } + + val stopLoading = Runnable { + gridview?.adapter = null + library_loading_overlay?.isVisible = false + library_loading_shimmer?.stopShimmer() + } + + val handler = Handler(Looper.getMainLooper()) + + observe(libraryViewModel.pages) { resource -> + when (resource) { + is Resource.Success -> { + handler.removeCallbacks(startLoading) + val pages = resource.value + val showNotice = pages.all { it.items.isEmpty() } + empty_list_textview?.isVisible = showNotice + if (showNotice) { + if (libraryViewModel.availableApiNames.size > 1) { + empty_list_textview?.setText(R.string.empty_library_logged_in_message) + } else { + empty_list_textview?.setText(R.string.empty_library_no_accounts_message) + } + } + + (viewpager.adapter as? ViewpagerAdapter)?.pages = pages + // Using notifyItemRangeChanged keeps the animations when sorting + viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0) + + // Only stop loading after 300ms to hide the fade effect the viewpager produces when updating + // Without this there would be a flashing effect: + // loading -> show old viewpager -> black screen -> show new viewpager + handler.postDelayed(stopLoading, 300) + + savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> + viewpager?.setCurrentItem(currentPos, false) + savedInstanceState.remove(VIEWPAGER_ITEM_KEY) + } + + // Since the animation to scroll multiple items is so much its better to just hide + // the viewpager a bit while the fastest animation is running + fun hideViewpager(distance: Int) { + if (distance < 3) return + + val hideAnimation = AlphaAnimation(1f, 0f).apply { + duration = distance * 50L + fillAfter = true + } + val showAnimation = AlphaAnimation(0f, 1f).apply { + duration = distance * 50L + startOffset = distance * 100L + fillAfter = true + } + viewpager?.startAnimation(hideAnimation) + viewpager?.startAnimation(showAnimation) + } + + TabLayoutMediator( + library_tab_layout, + viewpager, + ) { tab, position -> + tab.text = pages.getOrNull(position)?.title?.asStringNull(context) + tab.view.setOnClickListener { + val currentItem = viewpager?.currentItem ?: return@setOnClickListener + val distance = abs(position - currentItem) + hideViewpager(distance) + } + }.attach() + } + is Resource.Loading -> { + // Only start loading after 200ms to prevent loading cached lists + handler.postDelayed(startLoading, 200) + } + is Resource.Failure -> { + stopLoading.run() + // No user indication it failed :( + // TODO + } + } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + (viewpager.adapter as? ViewpagerAdapter)?.rebind() + super.onConfigurationChanged(newConfig) + } +} + +class MenuSearchView(context: Context) : SearchView(context) { + override fun onActionViewCollapsed() { + super.onActionViewCollapsed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt new file mode 100644 index 00000000..8aafbdd6 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt @@ -0,0 +1,17 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 +import kotlinx.android.synthetic.main.library_viewpager_page.view.* +import kotlin.math.roundToInt + +class LibraryScrollTransformer : ViewPager2.PageTransformer { + override fun transformPage(page: View, position: Float) { + val padding = (-position * page.width).roundToInt() + page.page_recyclerview.setPadding( + padding, 0, + -padding, 0 + ) + } +} + diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt new file mode 100644 index 00000000..5f64880c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -0,0 +1,104 @@ +package com.lagradost.cloudstream3.ui.library + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import kotlinx.coroutines.delay + +enum class ListSorting(@StringRes val stringRes: Int) { + Query(R.string.none), + RatingHigh(R.string.sort_rating_desc), + RatingLow(R.string.sort_rating_asc), + UpdatedNew(R.string.sort_updated_new), + UpdatedOld(R.string.sort_updated_old), + AlphabeticalA(R.string.sort_alphabetical_a), + AlphabeticalZ(R.string.sort_alphabetical_z), +} + +const val LAST_SYNC_API_KEY = "last_sync_api" + +class LibraryViewModel : ViewModel() { + private val _pages: MutableLiveData>> = MutableLiveData(null) + val pages: LiveData>> = _pages + + private val _currentApiName: MutableLiveData = MutableLiveData("") + val currentApiName: LiveData = _currentApiName + + private val availableSyncApis + get() = SyncApis.filter { it.hasAccount() } + + var currentSyncApi = availableSyncApis.let { allApis -> + val lastSelection = getKey(LAST_SYNC_API_KEY) + availableSyncApis.firstOrNull { it.name == lastSelection } ?: allApis.firstOrNull() + } + private set(value) { + field = value + setKey(LAST_SYNC_API_KEY, field?.name) + } + + val availableApiNames: List + get() = availableSyncApis.map { it.name } + + var sortingMethods = emptyList() + private set + + var currentSortingMethod: ListSorting? = sortingMethods.firstOrNull() + private set + + fun switchList(name: String) { + currentSyncApi = availableSyncApis[availableApiNames.indexOf(name)] + _currentApiName.postValue(currentSyncApi?.name) + reloadPages(true) + } + + fun sort(method: ListSorting, query: String? = null) { + val currentList = pages.value ?: return + currentSortingMethod = method + (currentList as? Resource.Success)?.value?.forEachIndexed { _, page -> + page.sort(method, query) + } + _pages.postValue(currentList) + } + + fun reloadPages(forceReload: Boolean) { + // Only skip loading if its not forced and pages is not empty + if (!forceReload && (pages.value as? Resource.Success)?.value?.isNotEmpty() == true && + currentSyncApi?.requireLibraryRefresh != true + ) return + + ioSafe { + currentSyncApi?.let { repo -> + _currentApiName.postValue(repo.name) + _pages.postValue(Resource.Loading()) + val libraryResource = repo.getPersonalLibrary() + if (libraryResource is Resource.Failure) { + _pages.postValue(libraryResource) + return@let + } + val library = (libraryResource as? Resource.Success)?.value ?: return@let + + sortingMethods = library.supportedListSorting.toList() + currentSortingMethod = null + + repo.requireLibraryRefresh = false + + val pages = library.allLibraryLists.map { + SyncAPI.Page( + it.name, + it.items + ) + } + + _pages.postValue(Resource.Success(pages)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt new file mode 100644 index 00000000..a637133b --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt @@ -0,0 +1,37 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.ListPopupWindow.MATCH_PARENT +import android.widget.RelativeLayout +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.android.synthetic.main.loading_poster_dynamic.view.* +import kotlin.math.roundToInt +import kotlin.math.sqrt + +class LoadingPosterAdapter(context: Context, private val itemCount: Int) : + BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int { + return itemCount + } + + override fun getItem(position: Int): Any? { + return null + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return convertView ?: inflater.inflate(R.layout.loading_poster_dynamic, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt new file mode 100644 index 00000000..2435f8be --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -0,0 +1,130 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.AutofitRecyclerView +import com.lagradost.cloudstream3.ui.search.SearchClickCallback +import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* +import kotlin.math.roundToInt + + +class PageAdapter( + override val items: MutableList, + private val resView: AutofitRecyclerView, + val clickCallback: (SearchClickCallback) -> Unit +) : + AppUtils.DiffAdapter(items) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return LibraryItemViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.search_result_grid_expanded, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is LibraryItemViewHolder -> { + holder.bind(items[position], position) + } + } + } + + private fun isDark(color: Int): Boolean { + return ColorUtils.calculateLuminance(color) < 0.5 + } + + fun getDifferentColor(color: Int, ratio: Float = 0.7f): Int { + return if (isDark(color)) { + ColorUtils.blendARGB(color, Color.WHITE, ratio) + } else { + ColorUtils.blendARGB(color, Color.BLACK, ratio) + } + } + + inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val cardView: ImageView = itemView.imageView + + private val compactView = false//itemView.context.getGridIsCompact() + private val coverHeight: Int = + if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt() + + fun bind(item: SyncAPI.LibraryItem, position: Int) { + /** https://stackoverflow.com/questions/8817522/how-to-get-color-code-of-image-view */ + + SearchResultBuilder.bind( + this@PageAdapter.clickCallback, + item, + position, + itemView, + colorCallback = { palette -> + AcraApplication.context?.let { ctx -> + val defColor = ContextCompat.getColor(ctx, R.color.ratingColorBg) + var bg = palette.getDarkVibrantColor(defColor) + if (bg == defColor) { + bg = palette.getDarkMutedColor(defColor) + } + if (bg == defColor) { + bg = palette.getVibrantColor(defColor) + } + + val fg = + getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) + itemView.text_rating.apply { + setTextColor(ColorStateList.valueOf(fg)) + } + itemView.text_rating_holder?.backgroundTintList = ColorStateList.valueOf(bg) + itemView.watchProgress?.apply { + progressTintList = ColorStateList.valueOf(fg) + progressBackgroundTintList = ColorStateList.valueOf(bg) + } + } + } + ) + + // See searchAdaptor for this, it basically fixes the height + if (!compactView) { + cardView.apply { + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + coverHeight + ) + } + } + + val showProgress = item.episodesCompleted != null && item.episodesTotal != null + itemView.watchProgress.isVisible = showProgress + if (showProgress) { + itemView.watchProgress.max = item.episodesTotal!! + itemView.watchProgress.progress = item.episodesCompleted!! + } + + itemView.imageText.text = item.name + + val showRating = (item.personalRating ?: 0) != 0 + itemView.text_rating_holder.isVisible = showRating + if (showRating) { + // We want to show 8.5 but not 8.0 hence the replace + val rating = ((item.personalRating ?: 0).toDouble() / 10).toString() + .replace(".0", "") + + itemView.text_rating.text = "★ $rating" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt new file mode 100644 index 00000000..33a40386 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -0,0 +1,90 @@ +package com.lagradost.cloudstream3.ui.library + +import android.os.Build +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.doOnAttach +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnFlingListener +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.search.SearchClickCallback +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount +import kotlinx.android.synthetic.main.library_viewpager_page.view.* + +class ViewpagerAdapter( + var pages: List, + val scrollCallback: (isScrollingDown: Boolean) -> Unit, + val clickCallback: (SearchClickCallback) -> Unit +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return PageViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.library_viewpager_page, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is PageViewHolder -> { + holder.bind(pages[position], unbound.remove(position)) + } + } + } + + private val unbound = mutableSetOf() + /** + * Used to mark all pages for re-binding and forces all items to be refreshed + * Without this the pages will still use the same adapters + **/ + fun rebind() { + unbound.addAll(0..pages.size) + this.notifyItemRangeChanged(0, pages.size) + } + + inner class PageViewHolder(private val itemViewTest: View) : + RecyclerView.ViewHolder(itemViewTest) { + fun bind(page: SyncAPI.Page, rebind: Boolean) { + itemView.page_recyclerview?.spanCount = + this@PageViewHolder.itemView.context.getSpanCount() ?: 3 + + if (itemViewTest.page_recyclerview?.adapter == null || rebind) { + // Only add the items after it has been attached since the items rely on ItemWidth + // Which is only determined after the recyclerview is attached. + // If this fails then item height becomes 0 when there is only one item + itemViewTest.page_recyclerview?.doOnAttach { + itemViewTest.page_recyclerview?.adapter = PageAdapter( + page.items.toMutableList(), + itemViewTest.page_recyclerview, + clickCallback + ) + } + } else { + (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) + itemViewTest.page_recyclerview?.scrollToPosition(0) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> + val diff = scrollY - oldScrollY + if (diff == 0) return@setOnScrollChangeListener + + scrollCallback.invoke(diff > 0) + } + } else { + itemViewTest.page_recyclerview.onFlingListener = object : OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + scrollCallback.invoke(velocityY > 0) + return false + } + } + } + + } + } + + override fun getItemCount(): Int { + return pages.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index ad3d9eb8..ba57d2de 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -220,7 +220,7 @@ class QuickSearchFragment : Fragment() { when (it) { is Resource.Success -> { it.value.let { data -> - (quick_search_autofit_results?.adapter as? SearchAdapter?)?.updateList( + (quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList( context?.filterSearchResultByFilmQuality(data) ?: data ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 9cfbf45c..2e2e46b7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -277,7 +277,7 @@ open class ResultFragment : ResultTrailerPlayer() { private var downloadButton: EasyDownloadButton? = null override fun onDestroyView() { updateUIListener = null - (result_episodes?.adapter as EpisodeAdapter?)?.killAdapter() + (result_episodes?.adapter as? EpisodeAdapter)?.killAdapter() downloadButton?.dispose() super.onDestroyView() @@ -458,7 +458,7 @@ open class ResultFragment : ResultTrailerPlayer() { temporary_no_focus?.requestFocus() } - (result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) + (result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value) if (isTv && hasEpisodes) main { delay(500) @@ -687,7 +687,7 @@ open class ResultFragment : ResultTrailerPlayer() { val newList = list.filter { it.isSynced && it.hasAccount } result_mini_sync?.isVisible = newList.isNotEmpty() - (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.mapNotNull { it.icon }) + (result_mini_sync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon }) } var currentSyncProgress = 0 @@ -900,7 +900,7 @@ open class ResultFragment : ResultTrailerPlayer() { result_cast_items?.isVisible = d.actors != null - (result_cast_items?.adapter as ActorAdaptor?)?.apply { + (result_cast_items?.adapter as? ActorAdaptor)?.apply { updateList(d.actors ?: emptyList()) } 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 9bae8753..b38e1765 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 @@ -485,7 +485,7 @@ class ResultFragmentPhone : ResultFragment() { result_recommendations?.post { rec?.let { list -> - (result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst }) + (result_recommendations?.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst }) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index d5cab1a6..2bd8ff0f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -107,7 +107,7 @@ class ResultFragmentTv : ResultFragment() { result_recommendations?.isGone = isInvalid result_recommendations_holder?.isGone = isInvalid val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName - (result_recommendations?.adapter as SearchAdapter?)?.updateList(rec?.filter { it.apiName == matchAgainst } + (result_recommendations?.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst } ?: emptyList()) rec?.map { it.apiName }?.distinct()?.let { apiNames -> 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 6ed32b15..6817af6a 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 @@ -13,6 +13,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.AcraApplication.Companion.setKey @@ -1443,12 +1444,18 @@ class ResultViewModel2 : ViewModel() { val realRecommendations = ArrayList() // TODO: fix - //val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name) - // meta.recommendations?.forEach { rec -> - // apiNames.forEach { name -> - // realRecommendations.add(rec.copy(apiName = name)) - // } - // } + val apiNames = apis.filter { + it.name.contains("gogoanime", true) || + it.name.contains("9anime", true) + }.map { + it.name + } + + meta.recommendations?.forEach { rec -> + apiNames.forEach { name -> + realRecommendations.add(rec.copy(apiName = name)) + } + } recommendations = recommendations?.union(realRecommendations)?.toList() ?: realRecommendations @@ -2143,7 +2150,7 @@ class ResultViewModel2 : ViewModel() { val validUrlResource = safeApiCall { SyncRedirector.redirect( url, - api.mainUrl + api ) } // TODO: fix diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt index ddf559fc..649641c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt @@ -10,12 +10,16 @@ import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.ui.AutofitRecyclerView +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.UIHelper.IsBottomLayout import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.search_result_compact.view.* import kotlin.math.roundToInt +/** Click */ const val SEARCH_ACTION_LOAD = 0 + +/** Long press */ const val SEARCH_ACTION_SHOW_METADATA = 1 const val SEARCH_ACTION_PLAY_FILE = 2 const val SEARCH_ACTION_FOCUSED = 4 diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 4144a042..b4a38216 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -420,7 +420,7 @@ class SearchFragment : Fragment() { is Resource.Success -> { it.value.let { data -> if (data.isNotEmpty()) { - (search_autofit_results?.adapter as SearchAdapter?)?.updateList(data) + (search_autofit_results?.adapter as? SearchAdapter)?.updateList(data) } } searchExitIcon.alpha = 1f diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt index 3afbb8c0..3447ee32 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt @@ -1,12 +1,14 @@ package com.lagradost.cloudstream3.ui.search import android.content.Context +import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView import androidx.cardview.widget.CardView import androidx.core.view.isVisible +import androidx.palette.graphics.Palette import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings @@ -41,6 +43,7 @@ object SearchResultBuilder { nextFocusBehavior: Boolean? = null, nextFocusUp: Int? = null, nextFocusDown: Int? = null, + colorCallback : ((Palette) -> Unit)? = null ) { val cardView: ImageView = itemView.imageView val cardText: TextView? = itemView.imageText @@ -100,7 +103,7 @@ object SearchResultBuilder { cardText?.isVisible = showTitle cardView.isVisible = true - if (!cardView.setImage(card.posterUrl, card.posterHeaders)) { + if (!cardView.setImage(card.posterUrl, card.posterHeaders, colorCallback = colorCallback)) { cardView.setImageResource(R.drawable.default_cover) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index bd44a058..d328d226 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -143,7 +143,7 @@ class PluginsFragment : Fragment() { } observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) -> - (plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list) + (plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list) if (scrollToTop) plugin_recycler_view?.scrollToPosition(0) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index d563bffa..00dee9b2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -28,10 +28,12 @@ import androidx.core.text.toSpanned import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.tvprovider.media.tv.* import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor +import androidx.viewpager2.widget.ViewPager2 import com.fasterxml.jackson.module.kotlin.readValue import com.google.android.gms.cast.framework.CastContext import com.google.android.gms.cast.framework.CastState @@ -65,6 +67,7 @@ import okhttp3.Cache import java.io.* import java.net.URL import java.net.URLDecoder +import kotlin.system.measureTimeMillis object AppUtils { fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) { @@ -164,6 +167,18 @@ object AppUtils { return builder.build() } + // https://stackoverflow.com/a/67441735/13746422 + fun ViewPager2.reduceDragSensitivity(f: Int = 4) { + val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView") + recyclerViewField.isAccessible = true + val recyclerView = recyclerViewField.get(this) as RecyclerView + + val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop") + touchSlopField.isAccessible = true + val touchSlop = touchSlopField.get(recyclerView) as Int + touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally + } + @SuppressLint("RestrictedApi") fun getAllWatchNextPrograms(context: Context): Set { val COLUMN_WATCH_NEXT_ID_INDEX = 0 @@ -329,6 +344,46 @@ object AppUtils { } } + abstract class DiffAdapter( + open val items: MutableList, + val comparison: (first: T, second: T) -> Boolean = { first, second -> + first.hashCode() == second.hashCode() + } + ) : + RecyclerView.Adapter() { + override fun getItemCount(): Int { + return items.size + } + + fun updateList(newList: List) { + val diffResult = DiffUtil.calculateDiff( + GenericDiffCallback(this.items, newList) + ) + + items.clear() + items.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + + inner class GenericDiffCallback( + private val oldList: List, + private val newList: List + ) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + comparison(oldList[oldItemPosition], newList[newItemPosition]) + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] + } + } + + fun Activity.downloadAllPluginsDialog(repositoryUrl: String, repositoryName: String) { runOnUiThread { val context = this diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt index 80e5d64a..8d51e5ef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -18,13 +18,11 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.PLUGINS_KEY import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_CACHED_LIST -import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_REFRESH_TOKEN_KEY -import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY @@ -52,12 +50,10 @@ object BackupUtils { // When sharing backup we do not want to transfer what is essentially the password ANILIST_TOKEN_KEY, ANILIST_CACHED_LIST, - ANILIST_SHOULD_UPDATE_LIST, ANILIST_UNIXTIME_KEY, ANILIST_USER_KEY, MAL_TOKEN_KEY, MAL_REFRESH_TOKEN_KEY, - MAL_SHOULD_UPDATE_LIST, MAL_CACHED_LIST, MAL_UNIXTIME_KEY, MAL_USER_KEY, 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 9174c481..281c9c44 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.utils import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey @@ -10,6 +11,8 @@ import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.syncproviders.AccountManager +import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.result.VideoWatchState @@ -51,7 +54,20 @@ object DataStoreHelper { @JsonProperty("year") val year: Int?, @JsonProperty("quality") override var quality: SearchQuality? = null, @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, - ) : SearchResponse + ) : SearchResponse { + fun toLibraryItem(id: String): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + name, + url, + id, + null, + null, + null, + null, + apiName, type, posterUrl, posterHeaders, quality, this.id + ) + } + } data class ResumeWatchingResult( @JsonProperty("name") override val name: String, @@ -71,6 +87,9 @@ object DataStoreHelper { @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse + /** + * A datastore wide account for future implementations of a multiple account system + **/ private var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION fun getAllWatchStateIds(): List? { @@ -177,6 +196,7 @@ object DataStoreHelper { fun setBookmarkedData(id: Int?, data: BookmarkedData) { if (id == null) return setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data) + AccountManager.localListApi.requireLibraryRefresh = true } fun getBookmarkedData(id: Int?): BookmarkedData? { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt index 7dda3e18..e5f2f2dc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt @@ -4,6 +4,7 @@ package com.lagradost.cloudstream3.utils import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.apis //import com.lagradost.cloudstream3.animeproviders.AniflixProvider import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -78,17 +79,21 @@ object SyncUtil { return null } - suspend fun getUrlsFromId(id: String, type: String = "anilist") : List { - return arrayListOf() - // val url = - // "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" - // val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() - // val pages = response.pages ?: return emptyList() - // val current = pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url }.toMutableList() - // if(type == "anilist") { // TODO MAKE BETTER - // current.add("${AniflixProvider().mainUrl}/anime/$id") - // } - // return current + suspend fun getUrlsFromId(id: String, type: String = "anilist"): List { + val url = + "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" + val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() + val pages = response.pages ?: return emptyList() + val current = + pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values) + .mapNotNull { it.url }.toMutableList() + + if (type == "anilist") { // TODO MAKE BETTER + apis.filter { it.name.contains("Aniflix", ignoreCase = true) }.forEach { + current.add("${it.mainUrl}/anime/$id") + } + } + return current } data class SyncPage( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index 63b3623d..c300d615 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -9,7 +9,9 @@ import android.content.Context import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources +import android.graphics.Bitmap import android.graphics.Color +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.view.* @@ -28,15 +30,21 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.graphics.alpha import androidx.core.graphics.blue +import androidx.core.graphics.drawable.toBitmapOrNull import androidx.core.graphics.green import androidx.core.graphics.red import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.navigation.fragment.NavHostFragment +import androidx.palette.graphics.Palette import androidx.preference.PreferenceManager +import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings @@ -105,7 +113,7 @@ object UIHelper { listView.requestLayout() } - fun Activity?.getSpanCount(): Int? { + fun Context?.getSpanCount(): Int? { val compactView = false val spanCountLandscape = if (compactView) 2 else 6 val spanCountPortrait = if (compactView) 1 else 3 @@ -158,12 +166,27 @@ object UIHelper { return color } + var createPaletteAsyncCache: HashMap = hashMapOf() + fun createPaletteAsync(url: String, bitmap: Bitmap, callback: (Palette) -> Unit) { + createPaletteAsyncCache[url]?.let { palette -> + callback.invoke(palette) + return + } + Palette.from(bitmap).generate { paletteNull -> + paletteNull?.let { palette -> + createPaletteAsyncCache[url] = palette + callback(palette) + } + } + } + fun ImageView?.setImage( url: String?, headers: Map? = null, @DrawableRes errorImageDrawable: Int? = null, - fadeIn: Boolean = true + fadeIn: Boolean = true, + colorCallback: ((Palette) -> Unit)? = null ): Boolean { if (this == null || url.isNullOrBlank()) return false @@ -177,6 +200,33 @@ object UIHelper { else req } + if (colorCallback != null) { + builder.listener(object : RequestListener { + @SuppressLint("CheckResult") + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + resource?.toBitmapOrNull() + ?.let { bitmap -> createPaletteAsync(url, bitmap, colorCallback) } + return false + } + + @SuppressLint("CheckResult") + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + return false + } + }) + } + val res = if (errorImageDrawable != null) builder.error(errorImageDrawable).into(this) else diff --git a/app/src/main/res/color/item_select_color.xml b/app/src/main/res/color/item_select_color.xml index 0d2834dd..3d69c540 100644 --- a/app/src/main/res/color/item_select_color.xml +++ b/app/src/main/res/color/item_select_color.xml @@ -1,5 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml new file mode 100644 index 00000000..fc90e300 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_sort_24.xml b/app/src/main/res/drawable/ic_baseline_sort_24.xml new file mode 100644 index 00000000..96d46231 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_sort_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_star_24.xml b/app/src/main/res/drawable/ic_baseline_star_24.xml index ab099425..2dcadb7f 100644 --- a/app/src/main/res/drawable/ic_baseline_star_24.xml +++ b/app/src/main/res/drawable/ic_baseline_star_24.xml @@ -1,5 +1,5 @@ - + android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/drawable/ic_outline_account_circle_24.xml b/app/src/main/res/drawable/ic_outline_account_circle_24.xml new file mode 100644 index 00000000..cc564471 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_account_circle_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/indicator_background.xml b/app/src/main/res/drawable/indicator_background.xml new file mode 100644 index 00000000..ef44fb7c --- /dev/null +++ b/app/src/main/res/drawable/indicator_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/rating_bg_color.xml b/app/src/main/res/drawable/rating_bg_color.xml new file mode 100644 index 00000000..60e62bab --- /dev/null +++ b/app/src/main/res/drawable/rating_bg_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ad29d22a..b6290865 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,9 +35,9 @@ --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/library_viewpager_page.xml b/app/src/main/res/layout/library_viewpager_page.xml new file mode 100644 index 00000000..f69f68b5 --- /dev/null +++ b/app/src/main/res/layout/library_viewpager_page.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/layout/loading_poster_dynamic.xml b/app/src/main/res/layout/loading_poster_dynamic.xml new file mode 100644 index 00000000..11855acb --- /dev/null +++ b/app/src/main/res/layout/loading_poster_dynamic.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 25b3f67d..47fd7cd3 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -5,62 +5,109 @@ android:id="@+id/search_result_root" android:layout_width="match_parent" android:layout_height="wrap_content" - android:clickable="true" android:focusable="true" android:foreground="@drawable/outline_drawable" android:orientation="vertical"> - + android:layout_height="wrap_content"> - - android:layout_height="match_parent" - android:contentDescription="@string/search_poster_img_des" - android:duplicateParentState="true" - android:foreground="?android:attr/selectableItemBackgroundBorderless" - android:scaleType="centerCrop" - tools:src="@drawable/example_poster" /> - - - - + + android:id="@+id/text_quality" + style="@style/TypeButton" /> - - - + + + + + + + + + + + + + + + + - - + + - \ No newline at end of file + diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index 3a5e0929..cb620bb8 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -1,20 +1,23 @@ - + android:id="@+id/navigation_home" + android:icon="@drawable/home_alt" + android:title="@string/title_home" /> + android:id="@+id/navigation_search" + android:icon="@drawable/search_icon" + android:title="@string/title_search" /> + android:id="@+id/navigation_library" + android:icon="@drawable/ic_outline_account_circle_24" + android:title="@string/library" /> + android:id="@+id/navigation_downloads" + android:icon="@drawable/netflix_download" + android:title="@string/title_downloads" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/library_menu.xml b/app/src/main/res/menu/library_menu.xml new file mode 100644 index 00000000..f21d998d --- /dev/null +++ b/app/src/main/res/menu/library_menu.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 14d750a0..d71eeb06 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -144,6 +144,15 @@ app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + #F53B66 #BEC8FF ?attr/colorPrimaryDark + #4C3115 + #FFA662 #FF6F63 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c10d865..cee6bccc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,7 @@ Genres Share Open In Browser + Browser Skip Loading Loading… Watching @@ -229,6 +230,7 @@ Storage permissions missing. Please try again. Error backing up %s Search + Library Accounts Updates and backup Info @@ -616,5 +618,16 @@ Legacy PackageInstaller App will be updated upon exit - + Sort by + Sort + Rating (High to Low) + Rating (Low to High) + Updated (New to Old) + Updated (Old to New) + Alphabetical (A to Z) + Alphabetical (Z to A) + Select Library + Open with + Looks like your library is empty :(\nLogin to a library account or add shows to your local library + Looks like this list is empty, try switching to another one diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b14cd189..2540bf34 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -90,11 +90,13 @@ @font/google_sans 0dp + + + + + + From 2771dcb6124726ade7aec8a9e46655f5ff6987f9 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:38:55 +0000 Subject: [PATCH 030/104] update version code --- app/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 808c0cc3..93e386b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 33 - versionCode = 56 - versionName = "3.5.0" + versionCode = 57 + versionName = "4.0.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") @@ -253,4 +253,4 @@ tasks.withType().configureEach { } } } -} \ No newline at end of file +} From 9905618a47d9f3d47abe5a4f627fce0733e1f072 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:15:28 +0100 Subject: [PATCH 031/104] Added safe mode file as a last resort --- .../lagradost/cloudstream3/MainActivity.kt | 9 +++++++-- .../cloudstream3/plugins/PluginManager.kt | 19 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 5720b7a7..eddec15e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -347,7 +347,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } - var lastPopup : SearchResponse? = null + var lastPopup: SearchResponse? = null fun loadPopup(result: SearchResponse) { lastPopup = result viewModel.load( @@ -716,7 +716,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) - if (lastError == null) { + + if (PluginManager.checkSafeModeFile()) { + normalSafeApiCall { + showToast(this, R.string.safe_mode_file, Toast.LENGTH_LONG) + } + } else if (lastError == null) { ioSafe { getKey(USER_SELECTED_HOMEPAGE_API)?.let { homeApi -> mainPluginsLoadedEvent.invoke(loadSinglePlugin(this@MainActivity, homeApi)) 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 54fe5d75..28dfc092 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -144,8 +144,10 @@ object PluginManager { return getKey(PLUGINS_KEY_LOCAL) ?: emptyArray() } - private val LOCAL_PLUGINS_PATH = - Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/plugins" + private val CLOUD_STREAM_FOLDER = + Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/" + + private val LOCAL_PLUGINS_PATH = CLOUD_STREAM_FOLDER + "plugins" public var currentlyLoading: String? = null @@ -421,6 +423,19 @@ object PluginManager { afterPluginsLoadedEvent.invoke(forceReload) } + /** + * This can be used to override any extension loading to fix crashes! + * @return true if safe mode file is present + **/ + fun checkSafeModeFile(): Boolean { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + return files?.any() ?: false + } + /** * @return True if successful, false if not * */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cee6bccc..a4375ce3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -630,4 +630,5 @@ Open with Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one + Safe mode file found!\nNot loading any extensions on startup until file is removed. From fd2648df459e4cdc0089ea5119de6da349d211c5 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:31:16 +0100 Subject: [PATCH 032/104] made the checkSafeModeFile() crash-proof --- .../cloudstream3/plugins/PluginManager.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 28dfc092..3533d6a8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -428,12 +428,14 @@ object PluginManager { * @return true if safe mode file is present **/ fun checkSafeModeFile(): Boolean { - val folder = File(CLOUD_STREAM_FOLDER) - if (!folder.exists()) return false - val files = folder.listFiles { _, name -> - name.equals("safe", ignoreCase = true) - } - return files?.any() ?: false + return normalSafeApiCall { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return@normalSafeApiCall false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + files?.any() + } ?: false } /** From 6e9b1cb855fa298f0df3c8b015a4cd8583821ddc Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:51:25 +0100 Subject: [PATCH 033/104] Made source dialog fullscreen and added some Extractors --- .../cloudstream3/extractors/Filesim.kt | 41 ++++++++++++++----- .../cloudstream3/extractors/GuardareStream.kt | 5 +++ .../cloudstream3/ui/player/GeneratorPlayer.kt | 10 ++--- .../cloudstream3/utils/ExtractorApi.kt | 2 + .../layout/player_select_source_and_subs.xml | 2 + 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 8e3dc730..bc910a7e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -1,32 +1,51 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import java.net.URI + +class FileMoon : Filesim() { + override val mainUrl = "https://filemoon.to" + override val name = "FileMoon" +} open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" override val requiresReferer = false - override suspend fun getUrl(url: String, referer: String?): List { - val sources = mutableListOf() + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { with(app.get(url).document) { - this.select("script").map { script -> + this.select("script").forEach { script -> if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]") - tryParseJson>("[$data]")?.map { - M3u8Helper.generateM3u8( - name, - it.file, - "$mainUrl/", - ).forEach { m3uData -> sources.add(m3uData) } + val data = getAndUnpack(script.data()) + val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach + val fixedData = foundData.replace("file:", """"file":""") + + parseJson>("[$fixedData]").forEach { + callback.invoke( + ExtractorLink( + name, + name, + it.file, + "$mainUrl/", + Qualities.Unknown.value, + URI(it.file).path.endsWith(".m3u8") + ) + ) } } } } - return sources } private data class ResponseSource( diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt index f25cb5ba..2adc00d5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt @@ -6,6 +6,11 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +class Vanfem : GuardareStream() { + override var name = "Vanfem" + override var mainUrl = "https://vanfem.com/" +} + class CineGrabber : GuardareStream() { override var name = "CineGrabber" override var mainUrl = "https://cinegrabber.com" 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 e15dcee6..67f58195 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 @@ -11,9 +11,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.EditorInfo import android.widget.* -import android.widget.TextView.OnEditorActionListener import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.core.animation.addListener @@ -528,7 +526,7 @@ class GeneratorPlayer : FullScreenPlayer() { } } - var selectSourceDialog: AlertDialog? = null + var selectSourceDialog: Dialog? = null // var selectTracksDialog: AlertDialog? = null override fun showMirrorsDialogue() { @@ -540,10 +538,8 @@ class GeneratorPlayer : FullScreenPlayer() { player.handleEvent(CSPlayerEvent.Pause) val currentSubtitles = sortSubs(currentSubs) - val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) - .setView(R.layout.player_select_source_and_subs) - - val sourceDialog = sourceBuilder.create() + val sourceDialog = Dialog(ctx, R.style.AlertDialogCustomBlack) + sourceDialog.setContentView(R.layout.player_select_source_and_subs) selectSourceDialog = sourceDialog 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 bd4f8705..1ad3639b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -291,6 +291,7 @@ val extractorApis: MutableList = arrayListOf( Supervideo(), GuardareStream(), CineGrabber(), + Vanfem(), // StreamSB.kt works // SBPlay(), @@ -321,6 +322,7 @@ val extractorApis: MutableList = arrayListOf( DesuDrive(), Filesim(), + FileMoon(), Linkbox(), Acefile(), SpeedoStream(), diff --git a/app/src/main/res/layout/player_select_source_and_subs.xml b/app/src/main/res/layout/player_select_source_and_subs.xml index 369f6776..067e4ad5 100644 --- a/app/src/main/res/layout/player_select_source_and_subs.xml +++ b/app/src/main/res/layout/player_select_source_and_subs.xml @@ -44,6 +44,7 @@ android:nextFocusLeft="@id/sort_subtitles" android:nextFocusRight="@id/apply_btt" android:requiresFadingEdge="vertical" + tools:layout_height="100dp" tools:listitem="@layout/sort_bottom_single_choice" /> @@ -117,6 +118,7 @@ android:nextFocusLeft="@id/sort_providers" android:nextFocusRight="@id/cancel_btt" android:requiresFadingEdge="vertical" + tools:layout_height="200dp" tools:listfooter="@layout/sort_bottom_footer_add_choice" tools:listitem="@layout/sort_bottom_single_choice" /> From c7c5fa250e177dbf22a9ea463c721c63f3feb79a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 31 Jan 2023 09:12:25 +0100 Subject: [PATCH 034/104] Translated using Weblate (Polish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 96.5% (562 of 582 strings) Translated using Weblate (Russian) Currently translated at 91.4% (532 of 582 strings) Translated using Weblate (Greek) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 88.3% (514 of 582 strings) Translated using Weblate (Russian) Currently translated at 87.9% (512 of 582 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (German) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Italian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Polish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Italian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Co-authored-by: Aitor Salaberria Co-authored-by: Alex Georgiou Co-authored-by: Cliff Heraldo Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Hosted Weblate Co-authored-by: JL Pilgram Co-authored-by: Julian Co-authored-by: NickSkier Co-authored-by: Rex_sa Co-authored-by: Sdarfeesh Co-authored-by: Skrripy Co-authored-by: Translator-3000 Co-authored-by: eightyy8 Co-authored-by: gallegonovato Co-authored-by: gnu-ewm Co-authored-by: kaajjo Co-authored-by: tuan041 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 17 ++++ app/src/main/res/values-de/strings.xml | 17 ++++ app/src/main/res/values-el/strings.xml | 20 ++++- app/src/main/res/values-es/strings.xml | 17 ++++ app/src/main/res/values-in/strings.xml | 21 ++++- app/src/main/res/values-it/strings.xml | 17 ++++ app/src/main/res/values-pl/strings.xml | 17 ++++ app/src/main/res/values-ru/strings.xml | 107 ++++++++++++++++++++----- app/src/main/res/values-uk/strings.xml | 15 ++++ app/src/main/res/values-vi/strings.xml | 53 ++++++++---- app/src/main/res/values-zh/strings.xml | 17 ++++ 11 files changed, 278 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3a0c97e7..f318478e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -521,4 +521,21 @@ بدأ التحديث تم تنزيل الإضافة إزالة من المشاهدة + الترتيب الأبجدي (من الألف إلى الياء) + اختر المكتبة + المتصفح + محدث (من الأحدث إلى الأقدم) + يبدو أن هذه القائمة فارغة ، حاول التبديل إلى قائمة أخرى + التقييم (من الأعلى إلى الأدنى) + التقييم (من الأدنى إلى الأعلى) + الترتيب الأبجدي (من ي إلى أ) + يبدو أن مكتبتك فارغة :( +\nتسجيل الدخول إلى حساب مكتبة أو إضافة عروض إلى مكتبتك المحلية + محدث (من القديم إلى الجديد) + فرز حسب + افرز + فتح بواسطة + المكتبة + تم العثور على ملف الوضع الآمن! +\nلا يتم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف. \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 00aa5b97..63ed5444 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -489,4 +489,21 @@ Die Anwendung wird beim Beenden aktualisiert Das Plugin wurde heruntergeladen Von geschaut entfernen + Bibliothek + Browser + Sortieren nach + Sortieren + Bewertung (gut bis schlecht) + Bewertung (schlecht bis gut) + Aktualisiert (neu bis alt) + Aktualisiert (alt bis neu) + Alphabetisch (A bis Z) + Alphabetisch (Z bis A) + Bibliothek auswählen + Öffnen mit + Sieht aus, als wäre deine Bibliothek leer :( +\nMelde dich mit einem Bibliothekskonto an oder füge Titel zu deiner lokalen Bibliothek hinzu + Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln. + Datei für abgesicherten Modus gefunden! +\nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird. \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index dc7088cc..0d0b7fb2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -314,7 +314,7 @@ Αναφορά κατάρρευσης Τι θα θέλατε να δείτε Έγινε - Πρόσθετα + Extensions Προσθήκη αποθετηρίου Όνομα αποθετηρίου Σύνδεσμος αποθετηρίου @@ -490,4 +490,22 @@ Το πρόσθετο κατέβει Ενημέρωση ξεκίνησε Η εφαρμογή θα ενημερωθεί κατά την έξοδο + Αλφαβητικά (Ω προς Α) + Ταξινόμηση + Κριτική (Χαμηλή προς Υψηλή) + Ενημερωμένο (Καινούριο προς παλιό) + Ενημερωμένο (Παλιό προς Καινούργιο) + Βιβλιοθήκη + Κριτική (Υψηλή προς χαμηλή) + Ταξινόμηση με βάση + Αλφαβητικά (Α προς Ω) + Διάλεξε βιβλιοθήκη + Φαίνεται πως η λίστα είναι άδεια, δοκίμασε να μεταβείς σε μία άλλη + Αφαίρεση από παρακολουθημένα + Περιηγητής + Άνοιγμα με + Φαίνεται πως η βιβλιοθήκη σου είναι άδεια :( +\nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου + Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! +\nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο. \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 08ae5bf1..37e5c431 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -489,4 +489,21 @@ Actualización iniciada Complemento descargado Quitar de visto + Ordenar por + Ordenar + Valoración (más a menos) + Valoración (menos a más) + Actualizado (nuevo a viejo) + Actualizado (viejo a nuevo) + Alfabéticamente (A a Z) + Navegador + Biblioteca + Parece que esta lista está vacía, intenta cambiar a otra + Alfabéticamente (Z a A) + Seleccionar biblioteca + Abrir con + Parece que tu biblioteca está vacía :( +\nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local + ¡Se encontró un archivo en modo seguro! +\nNo cargar ninguna extensión al inicio hasta que se elimine el archivo. \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 83dc6ee9..96c5950b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -453,7 +453,7 @@ Semua fitur tambahkan dimatikan karena crash, untuk memudahkanmu mencari penyebab crash. Kode bahasa (en) Ambil dari internet - Putar vidio di bahasa ini + Putar video di bahasa ini Tambah Repositori Pilih ini untuk menghapus semua repositori plugin Lewati pengaturan @@ -483,7 +483,7 @@ Gerakan Beberapa perangkat tidak mendukung penginstal paket mode baru. Coba mode lama jika pembaruan tidak dapat diinstal. Aksi - Referensi + Referer Ya Pasang dulu fitur tambahan Semua Bahasa @@ -512,4 +512,21 @@ Aplikasi akan diperbaharui pada saat keluar Pembaharuan Dimulai Hapus dari tontonan + Browser + Pilih pustaka + Yahh daftar pustaka kamu kosong :( +\nMasuk ke akun pustaka atau tambah perlihatkan ke lokal pustaka kamu + Pustaka + Urutkan berdasar + Urutkan + Peringkat (Rendah ke Tinggi) + Update (Lama ke Terbaru) + Peringkat (Tinggi ke Rendah) + Update (Terbaru ke Lama) + Abjad (A ke Z) + Abjad (Z ke A) + Buka dengan + Yahh daftar ini kosong, coba ganti ke yang lain + Mode aman file ditemukan! +\nTidak memuat ekstensi pada startup sampai berkas dihapus. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b4ba292e..419818a2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -511,4 +511,21 @@ Aggiornamento avviato Plugin scaricato Rimuovi dai già visti + Browser + Ordina per + Punteggio (Decrescente) + Punteggio (Crescente) + Aggiornato (Da nuovo a vecchio) + Aggiornato (Da vecchio a nuovo) + Alfabetico (A - Z) + Alfabetico (Z - A) + Sembra che la tua libreria sia vuota :( +\nAccedi a un account di libreria o aggiungi degli show alla tua libreria locale + Seleziona libreria + Apri con + Libreria + Ordina + Sembra che questa lista sia vuota, prova a passare a un\'altra + File \"safe mode\" trovato! +\nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso. \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 244ae2e1..e4b74300 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -492,4 +492,21 @@ Rozpoczęto aktualizację Pobrano rozszerzenie Usuń z obejrzanych + Przeglądarka + Data aktualizacji (od nowego do starego) + Sortuj według + Sortuj + Otwórz za pomocą + Ocena (od najwyższej do najniższej) + Ocena (od najniższej do najwyższej) + Data aktualizacji (od starego do nowego) + Alfabetycznie (od A do Z) + Alfabetycznie (od Z do A) + Wybierz bibliotekę + Biblioteka + Wygląda na to, że twoja biblioteka jest pusta :( +\nZaloguj się na swoje konto lub dodaj programy do swojej lokalnej biblioteki + Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną + Znaleziono plik trybu bezpiecznego. +\nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty. \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 39e74794..537bdb7d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -25,12 +25,12 @@ Серия %d будет выпущен в Плакат \@нить/результат_плокат_картинка_ - Серия плакат - Главный плакат + Постер Эпизода + Главный постер Следующий случайный Вернуться Изменить поставщика - Фон предпросмотр + Предпросмотр фона Скорость (%.2fx) Оценили: %.1f Новое обновление найдено! @@ -48,31 +48,31 @@ Поиск %s… Нет данных Дополнительные опции - Следующий серия + Следующий эпизод Жанры Поделиться - Открыть в браузер + Открыть в браузере Пропустить загрузку Просмотр Приостановленно Завершено Брошенный - План по смотреть + План посмотреть Никто Пересмотрю Смотреть фильм - Проиграть трейлер + Воспроизвести трейлер Воспроизвести Livestream Источники Субтитры - Проиграть серия + Воспроизвести эпизод Повторная попытка подключение… Вернуться - Скачали + Скачано Скачивание Скачать приостановленный Скачать начатый - Скачать отменено + Скачать отменённый Скачать выполнено Инфо Обновление началось @@ -116,8 +116,8 @@ Удалить файл Проиграть файл Внутренняя память - Скачать резюме - Приостановить скачать + Продолжить Скачать + Приостановить скачивание Отключить автоматическое информирование об ошибках Импортируйте шрифты поместив их в %s Продолжить смотреть @@ -174,7 +174,7 @@ Э Эпизоды не найдены Удалить файл - Возобновить + Продолжить -30 +30 Это будет удалено безвозвратно%s @@ -193,7 +193,7 @@ Другое Ошибка загрузки, проверьте разрешения хранилища Копировать ссылку - Автоматическая загрузка + Автоскачивание Загрузка. Зеркало Сезон Аниме приложение от тех же разработчиков @@ -217,7 +217,7 @@ Торрент Документальный Азиатская драма - Общий + Основные Провайдеры Макет Расширения @@ -240,7 +240,7 @@ Обновление не найдено Изменить размер Источник - Проверьте наличие обновления + Проверить обновления Клон сайта DNS через HTTPS Удалить сайт @@ -248,7 +248,7 @@ Синхронизация субтитров Добавить клон существующего сайта с другим URL-адресом Используется для обхода блокировок интернет провайдера - Путь загрузки + Путь скачивания учитывая бенен Обновить Основной цвет @@ -304,7 +304,7 @@ Пропустить это обновление URL-адрес NGINX-сервера Создать учётную запись - Добавить трекинг + Добавить слежение Добавлено %s Синхронизировать Оценено @@ -393,7 +393,7 @@ Отключено: %d %s %s %s аутентифицировано - Не удалось перейти к %s + Не удается логин на %s Макс Минимум Очертание @@ -416,20 +416,83 @@ Главное Источник Случайный - Скоро будет… + Скоро… Этикетка Sub Фон - Вид + Oтoбpaжeниe Трейлер %s (отключено) Следующий В CloudStream по умолчанию не установлены сайты. Вам необходимо установить сайты из репозиториев. \n -\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем связать сайт репозитория в приложении. +\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем привязать сайт репозитория в приложении. \n \nПрисоединяйтесь к нашему Discord или ищите в интернете. Недопустимые данные Разрешение и название Предыдущий Разрешение + Браузер + Библиотека + Обновленный (старый - новый) + Алфавитный (А - Я) + Алфавитный (Я - А) + Выбрать библиотеку + Открыть с + Похоже, ваша библиотека пуста :( +\nВойдите в аккаунт с библиотекой или добавьте сериалы в локальную библиотеку + Сортировка + Открытый список + Рейтинг (высокий - низкий) + Рейтинг (низкий - высокий) + Обновленный (новый - старый) + Сортировать по + PackageInstaller + Кодировка субтитров + Загрузить из файла + Рейтинг: %s + Скачано %d %s + Все %s уже скачаны + Начата загрузка %d %s… + Не скачано: %d + Скачать все плагины из этого репозитория\? + Включен безопасный режим + Скачано: %d + Обновлено %d плагинов + Загрузить из интернета + Загрузка обновления приложения… + Недопустимый URL + Применить при перезапуске + Отчеты ошибках + Что вы хотите увидеть + Смотрите видео на этих языках + Скачано файл + Изображение постера + Пакетная загрузка + Скачайте список сайтов, который вы хотите использовать + Отображать Аниме с Дубляжом/Субтитрами + Включить NSFW на поддерживаемых провайдерах + Убрать скрытые субтитры из субтитров + Дополнительно + Изменить вид интерфейса, чтобы соответствовать устройству + Аудио дорожки + Это также удалит все плагины репозитория + Просмотреть репозитории сообщества + Видео дорожки + Все расширения были отключены из-за сбоя, чтобы помочь вам найти то, которое вызывает проблемы. + Повтор + Слишком много текста. Не удалось сохранить в буфер обмена. + Установка обновления приложения… + Не удалось установить новую версию приложения + Файл безопасного режима найден! +\nНе загружаются никакие расширения при запуске, пока файл не будет удален. + Приложение будет обновлено после выхода + Похоже, этот список пуст, попробуйте переключиться на другой + Все субтитры заглавными + Показывать всплывающие окна для пропуска вступления/заключения + Фильтровать по предпочитаемому языку медиа + Неверный ID + Ссылка на стрим + Отображать рандомную кнопку на Главной странице + Рандомная кнопка \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 93e51c84..821d062a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -489,4 +489,19 @@ Програму не знайдено Змішаний опенінг Видалити з переглянутого + За оновленням (від старого до нового) + За оновленням (від нового до старого) + Бібліотека + Сортувати + За рейтингом (від високого до низького) + Сортувати за + За алфавітом (від А до Я) + За рейтингом (від низького до високого) + Схоже, ваша бібліотека порожня :( +\nУвійдіть в обліковий запис бібліотеки або додайте серіали до вашої локальної бібліотеки + За алфавітом (від Я до А) + Виберіть бібліотеку + Відкрити з + Браузер + Схоже, цей список порожній, спробуйте перейти до іншого \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 3aa5cf69..db647b5d 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -66,7 +66,7 @@ Tải thành công Trực tiếp Đã có lỗi xảy ra - Bộ nhớ máy + Bộ nhớ trong Lồng Tiếng Phụ Đề Xóa Tệp @@ -118,7 +118,7 @@ Không tìm thấy thông tin Hiển thị Logcat 🐈 Chế độ cửa sổ nhỏ - Tiếp tục xem phim khi thoát app hoặc đang tìm kiếm + Tiếp tục xem phim khi thoát ứng dụng hoặc khi tìm kiếm Bật nút thu phóng khi xem Xóa khoảng đen của phim Phụ đề @@ -159,7 +159,7 @@ Không gửi dữ liệu Hiển thị tập phụ cho anime Hiển thị trailer - Hiển thị poster từ kitsu + Hiển thị poster từ Kitsu Ẩn chất lượng video khi tìm kiếm Tự động cập nhật plugin Hiển thị thông báo cập nhật App @@ -210,7 +210,7 @@ Không có phụ đề Mặc Định Còn trống - Đã dùng + Đã sử dụng App Phim Lẻ @@ -228,7 +228,7 @@ Phim Lẻ Phim Bộ Hoạt Hình - \@string/anime + Anime \@string/ova Torrent Phim Tài Liệu @@ -261,7 +261,7 @@ Khóa Thu Phóng Tuỳ chọn - Tập tiếp + Tua nhanh Không hiện lại Bỏ qua Cập nhật @@ -272,8 +272,8 @@ Thời lượng bộ nhớ đệm Dung lượng video cache Xoá hình ảnh và video - Sẽ gây lỗi nếu đặt quá cao. Không thay đổi nếu máy có dung lượng ram thấp, chẳng hạn như Android TV hoặc điện thoại cũ - Sẽ thể gây lỗi trên các máy có dung lượng lưu trữ thấp, chẳng hạn như thiết bị Android TV nếu bạn đặt nó quá cao + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng ram thấp như Android TV. + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. DNS over HTTPS Rất hữu ích để bỏ chặn ISP Sao chép trang web @@ -410,7 +410,7 @@ Đã xoá plugin Không tải được %s 18+ - Bắt đầu tải %d %s + Bắt đầu tải %d %s… Tải xuống %d %s thành công Toàn bộ %s đã được tải xuống Tải hàng loạt @@ -422,7 +422,11 @@ Đã tải: %d Đã vô hiệu: %d Không tải: %d - Thêm kho lưu trữ để cài tiện ích + CloudStream không có sẵn trang web nào. Bạn cần cài đặt các trang web từ kho lưu trữ. +\n +\nDo Sky UK Limited đã gỡ xuống theo DMCA một cách thiếu suy nghĩ 🤮 chúng tôi không thể cài sẵn trang web. +\n +\nHãy tham gia Discord của chúng tôi hoặc tìm kiếm trực tuyến. Xem kho lưu trữ của cộng đồng Danh sách công khai In hoa toàn bộ phụ đề @@ -437,18 +441,18 @@ Xem thông tin sự cố Lịch sử Đánh dấu là đã xem - Tự động tải plugin + Tự động tải xuống plugin Thiết lập lại Bộ cài APK Một số máy không hỗ trợ trình cài đặt gói mới. Hãy thử tùy chọn cũ nếu các bản cập nhật không cài đặt. %s %d%s - Xem Trailer - Tự động tải plugins còn thiếu. + Xem giới thiệu + Tự động tải plugin còn thiếu. Bắt đầu cập nhật Liên kết Danh sách HLS Trình phát ưu tiên - Trình phát mặc địng + Trình phát mặc định Đánh giá: %s Không Phiên bản @@ -487,7 +491,7 @@ Danh đề Giới thiệu Xoá lịch sử - Hiển thị cửa sổ tua cho mở đầu/kết thúc + Hiển thị nút tua nhanh cho mở đầu/kết thúc Văn bản quá dài. Không thể lưu vào bộ nhớ tạm. Xoá khỏi đã xem Bạn có chắc muốn thoát\? @@ -496,4 +500,23 @@ Đang cài bản cập nhật… Không thể cài đặt phiên bản mới Ứng dụng sẽ được cập nhật khi thoát + Thư viện + Trình duyệt + Plugin đã tải + Mặc định + Tải lên (Mới đến Cũ) + Tải lên (Cũ đến Mới) + Thư viện của bạn đang trống :( +\nHãy đăng nhập vào thư viện hoặc thêm phim vào thư viện cục bộ + Mở với + Siêu dữ liệu không có sẵn, video sẽ không được tải nếu nó không tồn tại trên trang web. + PackageInstaller + Sắp xếp + Xếp hạng (Cao đến Thấp) + Xếp hạng (Thấp đến Cao) + Chữ cái (Z đến A) + Sắp xếp + Có vẻ như danh sách này trống, hãy thử chuyển sang danh sách khác + Chữ cái (A đến Z) + Chọn Thư viện \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 97a48597..ece917d9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -537,4 +537,21 @@ 应用退出后将会更新 插件已下载 从已观看中移除 + 发现安全模式文件! +\n启动时不加载任何扩展,直到文件被删除。 + 浏览器 + + 排序方式 + 排序 + 评分(从高到低) + 评分(从低到高) + 更新(从新到旧) + 更新(从旧到新) + 字母排序(从 A 到 Z) + 字母排序(从 Z 到 A) + 选择库 + 打开方式 + 看来您的库是空的 :( +\n登录库账户或添加节目到您的本地库 + 看来此列表是空的,请尝试切换到另一个 \ No newline at end of file From b26a41bdaf8aeab10a7038c991327747bdd74cf5 Mon Sep 17 00:00:00 2001 From: hexated Date: Tue, 31 Jan 2023 14:26:34 +0700 Subject: [PATCH 035/104] fixed VidSrcExtractor --- .../lagradost/cloudstream3/extractors/VidSrcExtractor.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index b910f9dd..a27bf188 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -59,8 +59,8 @@ open class VidSrcExtractor : ExtractorApi() { if (datahash.isNotBlank()) { val links = try { app.get( - "$absoluteUrl/src/$datahash", - referer = "https://source.vidsrc.me/" + "$absoluteUrl/srcrcp/$datahash", + referer = "https://rcp.vidsrc.me/" ).url } catch (e: Exception) { "" @@ -71,7 +71,7 @@ open class VidSrcExtractor : ExtractorApi() { serverslist.amap { server -> val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/") - if (linkfixed.contains("/pro")) { + if (linkfixed.contains("/prorcp")) { val srcresponse = app.get(server, referer = absoluteUrl).text val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)") val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@amap From 490381451b28473fcfc41002856e5daa4c1ec131 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:57:11 +0100 Subject: [PATCH 036/104] [skip ci] label issues if provider mentioned --- .github/workflows/issue_action.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml index 28b737b3..2a54857c 100644 --- a/.github/workflows/issue_action.yml +++ b/.github/workflows/issue_action.yml @@ -53,6 +53,18 @@ jobs: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM). 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 + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["possible provider issue"] + }) - name: Add eyes reaction to all issues uses: actions-cool/emoji-helper@v1.0.0 with: From b0921161a3c31c46079c4791f575e538c0b1e153 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Tue, 31 Jan 2023 23:43:29 +0100 Subject: [PATCH 037/104] Nicehttp version bump --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 93e386b6..3c855d28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -190,7 +190,7 @@ dependencies { // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") - implementation("com.github.Blatzar:NiceHttp:0.4.1") + implementation("com.github.Blatzar:NiceHttp:0.4.2") // To fix SSL fuckery on android 9 implementation("org.conscrypt:conscrypt-android:2.2.1") // Util to skip the URI file fuckery 🙏 From 99887534327c86bcc653dfbc84a24917d34b0fe5 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Thu, 2 Feb 2023 01:15:24 +0100 Subject: [PATCH 038/104] Library and Light mode improvements. --- .../lagradost/cloudstream3/ui/library/LibraryFragment.kt | 4 +++- .../com/lagradost/cloudstream3/ui/result/ResultFragment.kt | 1 + app/src/main/res/layout/fragment_library.xml | 6 +++--- app/src/main/res/layout/search_result_grid_expanded.xml | 2 +- app/src/main/res/values/styles.xml | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) 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 1c6af447..d7c06c4e 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 @@ -333,8 +333,10 @@ class LibraryFragment : Fragment() { handler.postDelayed(stopLoading, 300) savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> + if (currentPos < 0) return@let viewpager?.setCurrentItem(currentPos, false) - savedInstanceState.remove(VIEWPAGER_ITEM_KEY) + // Using remove() sets the key to 0 instead of removing it + savedInstanceState.putInt(VIEWPAGER_ITEM_KEY, -1) } // Since the animation to scroll multiple items is so much its better to just hide diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 2e2e46b7..68dd1c0e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -974,6 +974,7 @@ open class ResultFragment : ResultTrailerPlayer() { chip.isCheckable = false chip.isFocusable = false chip.isClickable = false + chip.setTextColor(context.colorFromAttribute(R.attr.textColor)) addView(chip) } } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index f9012148..985d055d 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -166,12 +166,12 @@ app:layout_scrollFlags="noScroll" app:tabGravity="center" app:tabIndicator="@drawable/indicator_background" - app:tabIndicatorColor="@color/textColor" + app:tabIndicatorColor="?attr/white" app:tabIndicatorGravity="center" app:tabIndicatorHeight="30dp" app:tabMode="scrollable" - app:tabSelectedTextColor="@color/lightTextColor" + app:tabSelectedTextColor="?attr/primaryBlackBackground" app:tabTextAppearance="@style/TabNoCaps" - app:tabTextColor="@color/textColor" /> + app:tabTextColor="?attr/textColor" /> diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 47fd7cd3..cf6ab3b2 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -23,7 +23,7 @@ android:elevation="10dp" app:cardBackgroundColor="?attr/primaryGrayBackground" app:cardCornerRadius="@dimen/rounded_image_radius" - app:layout_constraintDimensionRatio="1:1.414" + app:layout_constraintDimensionRatio="1:1.5" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2540bf34..78c62c69 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -100,6 +100,7 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 61ff0c2b..7dd4c989 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -82,4 +82,7 @@ #515151 #FFFFFF #622C00 + + #48E484 + #ea596e \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 778f34c9..cb9d5508 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ fast_forward_button_time benene_count subtitle_settings_key + test_providers_key subtitle_settings_chromecast_key quality_pref_key player_pref_key @@ -195,6 +196,7 @@ No Plot Found No Description Found Show Logcat 🐈 + Log Picture-in-picture Continues playback in a miniature player on top of other apps Player resize button @@ -282,6 +284,9 @@ Delete @string/sort_cancel Pause + Start + Failed + Passed Resume -30 +30 @@ -423,6 +428,7 @@ Enable NSFW on supported providers Subtitle encoding Providers + Provider test Layout Auto TV layout @@ -579,6 +585,8 @@ Audio tracks Video tracks Apply on Restart + Restart + Stop Safe mode on All extensions were turned off due to a crash to help you find the one causing trouble. View crash info @@ -636,4 +644,6 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. + + Hello blank fragment \ No newline at end of file diff --git a/app/src/main/res/xml/settings_providers.xml b/app/src/main/res/xml/settings_providers.xml index a177865b..1ee58faf 100644 --- a/app/src/main/res/xml/settings_providers.xml +++ b/app/src/main/res/xml/settings_providers.xml @@ -1,24 +1,30 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:icon="@drawable/ic_baseline_language_24" + android:key="@string/provider_lang_key" + android:title="@string/provider_lang_settings" /> + android:icon="@drawable/ic_baseline_play_arrow_24" + android:key="@string/prefer_media_type_key" + android:title="@string/preferred_media_settings" /> + android:icon="@drawable/ic_outline_voice_over_off_24" + android:key="@string/display_sub_key" + android:title="@string/display_subbed_dubbed_settings" /> + android:icon="@drawable/ic_baseline_extension_24" + android:key="@string/enable_nsfw_on_providers_key" + android:summary="@string/apply_on_restart" + android:title="@string/enable_nsfw_on_providers" + app:defaultValue="false" /> + + + \ No newline at end of file From 9d0cce47a67472260e553ba8acd2e4d08fd43cc9 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:40:50 +0000 Subject: [PATCH 062/104] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e249948..354dc89c 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 @@ -68,12 +68,12 @@ val appLanguages = arrayListOf( Triple("", "español", "es"), Triple("", "فارسی", "fa"), Triple("", "français", "fr"), - Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), Triple("", "हिन्दी", "hi"), Triple("", "hrvatski", "hr"), Triple("", "magyar", "hu"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"), Triple("", "italiano", "it"), + Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), From 789cd14ef6fcb9cfdd5646d79af25f16916cf3d3 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Wed, 15 Feb 2023 21:41:20 +0100 Subject: [PATCH 063/104] remove placeholder --- app/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb9d5508..47517378 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -644,6 +644,4 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. - - Hello blank fragment \ No newline at end of file From 3dd0fc6c8e90e70eb413aba256da5cc4e3ab6f2e Mon Sep 17 00:00:00 2001 From: Lag <> Date: Wed, 15 Feb 2023 22:09:08 +0100 Subject: [PATCH 064/104] add view_test --- app/src/main/res/layout/view_test.xml | 138 ++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 app/src/main/res/layout/view_test.xml diff --git a/app/src/main/res/layout/view_test.xml b/app/src/main/res/layout/view_test.xml new file mode 100644 index 00000000..99300ce4 --- /dev/null +++ b/app/src/main/res/layout/view_test.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From aacd57cb5d2ae860fad11640d0ada1fe3fd55d2d Mon Sep 17 00:00:00 2001 From: Lag <> Date: Thu, 16 Feb 2023 01:15:30 +0100 Subject: [PATCH 065/104] Fixed scrolling up on bottom dialogs and removing stuff from AniList --- .../syncproviders/providers/AniListApi.kt | 53 +++++++++++++----- .../ui/result/ResultFragmentPhone.kt | 2 - .../ui/result/ResultFragmentTv.kt | 5 +- .../layout/bottom_selection_dialog_direct.xml | 54 +++++++++---------- 4 files changed, 69 insertions(+), 45 deletions(-) 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 7d9de43a..0010ce25 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 @@ -759,6 +759,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return data != "" } + /** Used to query a saved MediaItem on the list to get the id for removal */ + data class MediaListItemRoot(@JsonProperty("data") val data: MediaListItem? = null) + data class MediaListItem(@JsonProperty("MediaList") val MediaList: MediaListId? = null) + data class MediaListId(@JsonProperty("id") val id: Long? = null) + private suspend fun postDataAboutId( id: Int, type: AniListStatusType, @@ -766,19 +771,43 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { progress: Int? ): Boolean { val q = - """mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ - aniListStatusString[maxOf( - 0, - type.value - )] - }, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) { - SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) { - id - status - progress - score - } + // Delete item if status type is None + if (type == AniListStatusType.None) { + val userID = getKey(accountId, ANILIST_USER_KEY)?.id ?: return false + // Get list ID for deletion + val idQuery = """ + query MediaList(${'$'}userId: Int = $userID, ${'$'}mediaId: Int = $id) { + MediaList(userId: ${'$'}userId, mediaId: ${'$'}mediaId) { + id + } + } + """ + val response = postApi(idQuery) + val listId = + tryParseJson(response)?.data?.MediaList?.id ?: return false + """ + mutation(${'$'}id: Int = $listId) { + DeleteMediaListEntry(id: ${'$'}id) { + deleted + } + } + """ + } else { + """mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ + aniListStatusString[maxOf( + 0, + type.value + )] + }, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) { + SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) { + id + status + progress + score + } }""" + } + val data = postApi(q) return data != "" } 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 b38e1765..2f232995 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 @@ -322,9 +322,7 @@ class ResultFragmentPhone : ResultFragment() { // it?.dismiss() //} builder.setCanceledOnTouchOutside(true) - builder.show() - builder } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index 2bd8ff0f..71ecb0e9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -176,8 +176,7 @@ class ResultFragmentTv : ResultFragment() { loadingDialog = null } loadingDialog = loadingDialog ?: context?.let { ctx -> - val builder = - BottomSheetDialog(ctx) + val builder = BottomSheetDialog(ctx) builder.setContentView(R.layout.bottom_loading) builder.setOnDismissListener { loadingDialog = null @@ -187,9 +186,7 @@ class ResultFragmentTv : ResultFragment() { // it?.dismiss() //} builder.setCanceledOnTouchOutside(true) - builder.show() - builder } } diff --git a/app/src/main/res/layout/bottom_selection_dialog_direct.xml b/app/src/main/res/layout/bottom_selection_dialog_direct.xml index 0d179ebb..cf31ba1f 100644 --- a/app/src/main/res/layout/bottom_selection_dialog_direct.xml +++ b/app/src/main/res/layout/bottom_selection_dialog_direct.xml @@ -1,34 +1,34 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:id="@+id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_rowWeight="1" + android:layout_marginTop="20dp" + android:layout_marginBottom="10dp" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:textColor="?attr/textColor" + android:textSize="20sp" + android:textStyle="bold" + tools:text="Test" /> + android:id="@+id/listview1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_rowWeight="1" + android:layout_marginBottom="60dp" + android:nestedScrollingEnabled="true" + android:nextFocusLeft="@id/apply_btt" + android:nextFocusRight="@id/cancel_btt" + android:paddingTop="10dp" + android:requiresFadingEdge="vertical" + tools:listitem="@layout/sort_bottom_single_choice_no_checkmark" /> From b6ac155350cf6d4b070e79276efd6389b5858f05 Mon Sep 17 00:00:00 2001 From: MhmdIbrahim1 <107378571+MhmdIbrahim1@users.noreply.github.com> Date: Fri, 17 Feb 2023 23:42:20 +0200 Subject: [PATCH 066/104] update VideoDownloadService (#377) --- .../services/VideoDownloadService.kt | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt b/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt index be2fe75b..6151a0ed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt @@ -1,11 +1,22 @@ package com.lagradost.cloudstream3.services - -import android.app.IntentService +import android.app.Service import android.content.Intent +import android.os.IBinder import com.lagradost.cloudstream3.utils.VideoDownloadManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch -class VideoDownloadService : IntentService("VideoDownloadService") { - override fun onHandleIntent(intent: Intent?) { +class VideoDownloadService : Service() { + + private val downloadScope = CoroutineScope(Dispatchers.Default) + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (intent != null) { val id = intent.getIntExtra("id", -1) val type = intent.getStringExtra("type") @@ -14,10 +25,36 @@ class VideoDownloadService : IntentService("VideoDownloadService") { "resume" -> VideoDownloadManager.DownloadActionType.Resume "pause" -> VideoDownloadManager.DownloadActionType.Pause "stop" -> VideoDownloadManager.DownloadActionType.Stop - else -> return + else -> return START_NOT_STICKY + } + + downloadScope.launch { + VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) } - VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) } } + + return START_NOT_STICKY } -} \ No newline at end of file + + override fun onDestroy() { + downloadScope.coroutineContext.cancel() + super.onDestroy() + } +} +// override fun onHandleIntent(intent: Intent?) { +// if (intent != null) { +// val id = intent.getIntExtra("id", -1) +// val type = intent.getStringExtra("type") +// if (id != -1 && type != null) { +// val state = when (type) { +// "resume" -> VideoDownloadManager.DownloadActionType.Resume +// "pause" -> VideoDownloadManager.DownloadActionType.Pause +// "stop" -> VideoDownloadManager.DownloadActionType.Stop +// else -> return +// } +// VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) +// } +// } +// } +//} From b4065b69beeb6ab12298aba3ea74583fe5f7372f Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Fri, 17 Feb 2023 23:05:11 +0100 Subject: [PATCH 067/104] Added dropdown indicators Solves #375 --- app/src/main/res/layout/fragment_result.xml | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index afbf735d..a481ed6b 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -129,9 +129,9 @@ + android:paddingBottom="100dp"> @@ -516,8 +515,8 @@ android:visibility="gone" /> نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق - مُوصي به + مُوصى به تم تحميل %s إختيار ملف تحميل من الانترنت @@ -543,4 +543,16 @@ تلفزيون أندرويد مدة التقديم عنما يكون المشغل مرئيا مدة التقديم- المشغل المرئي + فشل + نجح + إختبار المزود + إعادة التشغيل + سجل + بَدأ + إيقاف + تحديث العروض التي تم الاشتراك فيها + إلغاء الاشتراك من %s + تم إصدار الحلقة %d! + مشترك + مشترك في %s \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index a78da8a4..966cd7d9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -535,4 +535,16 @@ Zobrazený přehrávač - doba hledání Android TV Množství vyhledávané doby při zobrazeném přehrávači + Protokol + Test poskytovatele + Neúspěšné + Úspěšné + Restart + Spustit + Zastavit + Aktualizace odebíraných pořadů + Přihlášeno k odběru %s + Odhlášen odběr od %s + Byla vydána epizoda %d! + Odebíráno \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e190aa9c..f6583c20 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -53,7 +53,7 @@ Abgebrochen Geplant Nichts - Erneut anschauen + Erneut schauen Film abspielen Livestream abspielen Torrent streamen @@ -212,7 +212,7 @@ Keine Untertitel Standard Frei - Benutzt + Belegt App Filme TV-Serien @@ -284,7 +284,7 @@ Strecken Vergrößern Haftungsausschluss - General + Allgemein Zufalls-Button Zufallsbutton auf der Startseite anzeigen Anbieter-Sprachen @@ -460,11 +460,11 @@ Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories. Einrichtungsvorgang wiederholen APK-Installer - Einige Telefone unterstützen das neue Installationsprogramm für Pakete nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen. + Einige Telefone unterstützen den neuen Package-Installer nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen. %s %d%s Links App-Updates - Back-Up + Sicherung Erweiterungen Wartung Cache @@ -506,4 +506,16 @@ Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln. Datei für abgesicherten Modus gefunden! \nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird. + Player ausgeblendet - Betrag zum vor- und zurückspulen + Der Betrag, welcher verwendet wird, wenn der Player eingeblendet ist + Der Betrag, welcher verwendet wird, wenn der Player ausgeblendet ist + Android-TV + Player eingeblendet - Betrag zum vor- und zurückspulen + Fehlgeschlagen + Erfolgreich + Anbieter-Test + Stopp + Log + Start + Neustarten \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0d0b7fb2..5e9dafd8 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -150,7 +150,7 @@ Επεισόδια %d-%d %d %s - Κ + Σ E Δεν βρέθηκαν επεισόδια Διαγραφή αρχείου diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 8366b294..2040169b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -511,4 +511,16 @@ Android TV La cantidad de búsqueda utilizada cuando la jugadora es visible La cantidad de búsqueda utilizada cuando el jugador está oculto + Parar + Falló + Registro + Empezar + Aprobado + Prueba del proveedor + Reiniciar + Suscrito + Suscrito a %s + Darse de baja de %s + Actualizando los programas suscritos + ¡Episodio %d publicado! \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f0e112a8..18255b3b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -6,12 +6,12 @@ Téléchargements Paramètres Rechercher… - Miniature + Affiche Aucune Donnée Plus d\'options Retour Épisode suivant - Miniature + Affiche Genres Partager Ouvrir dans le navigateur @@ -29,7 +29,7 @@ Sous-titres Réessayer la connection… Retour - Miniature de l\'Épisode + Affiche de l\'épisode Lire l\'Épisode Télécharger @@ -51,10 +51,10 @@ Désactiver le rapport de bug automatique Plus d\'informations Cacher - Poster principal + Affiche principale Lecture - Info - Suivant Aléatoire + Infos + Aléatoire suivant Changer le fournisseur Filtrer les marques-pages Marque-pages @@ -211,7 +211,7 @@ Arrière plan Source Aléatoire - À venir … + Bientôt disponible… Image de l\'affiche %s Connecté Définir le statut de visionage @@ -490,4 +490,22 @@ L\'application sera mise à jour dès la fin de la session Plugin Téléchargé Retirer de la vue + Bibliothèque + Navigateur + Trier + Note (basse à haute) + Note (haut à bas) + Alphabétique (A à Z) + On dirait que votre bibliothèque est vide :( +\nConnectez-vous à un compte ou ajoutez des séries à votre bibliothèque locale + Il semble que cette liste soit vide, essayez d\'en choisir une autre + Android TV + Trié par + Alphabétique (Z à A) + Sélectionnez la bibliothèque + Ouvrir avec + Mis à jour (Nouveau vers ancien) + Mis à jour (ancien vers nouveau) + Fichier du mode sans échec trouvé ! +\nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé. \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 0f3e36bc..926c7f57 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -531,4 +531,21 @@ Čini se da je ova lista prazna, pokušajte se prebaciti na drugu Pronađena datoteka sigurnog načina rada! \nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni. + Prikazan player- iznos preskakanja + Količina preskakanja koja se koristi kada je player vidljiv + Player skriven - Količina preskakanja + Količina preskakanja koja se koristi kada je player skriven + Android TV + Prošlo + Restart + Log + Početak + Neuspješno + Stop + Test pružatelja usluga + Ažuriram pretplaćene serije + Epizoda %d izbačena! + Pretplaćeno + Pretplaćen na %s + Otkazana pretplata sa %s \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index d54e4fa9..46d61e44 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -35,7 +35,7 @@ Skip Loading Loading… Sedang Menonton - Tertahan + Tertunda Selesai Dihentikan Rencana untuk Menonton @@ -387,7 +387,7 @@ %d %s 17+ Lainnya - Vidio + Video Duplikasi Website Duplikasi website yang telah ada, dengan alamat berbeda Tautan @@ -395,7 +395,7 @@ Cadangkan Fitur Tambahan Putar di CloudStream - Sembunyikan kualitas vidio terpilih di pencarian + Sembunyikan kualitas video terpilih di pencarian %s %d%s Siaran langsung Hapus Website @@ -444,7 +444,7 @@ Peringkat: %s Pembuat Bahasa - Pemutar vidio utama + Pemutar video utama Pemutar Bawaan VLC MPV @@ -475,7 +475,7 @@ Hapus teks tertutup dari subtitel Hapus karakter sampah dari subtitel Audio Trek - Vidio Trek + Video Trek Dukungan Daftar putar HLS Penginstal APK @@ -529,4 +529,21 @@ Yahh daftar ini kosong, coba ganti ke yang lain Mode aman file ditemukan! \nTidak memuat ekstensi pada startup sampai berkas dihapus. + Sembunyikan Pemutaran - Geser + Pemutar terlihat - Geser + Geser untuk menghilangkan + Geser untuk menghilangkan + Android TV + Log + Berhasil + Tes provider + Berhenti + Mulai + Mulai lagi + Gagal + Memperbarui acara langganan + Berlangganan + Berlangganan ke %s + Berhenti berlangganan di %s + Episode %d telah rilis! \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 9dbc627f..89f6b4ee 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -528,4 +528,16 @@ Sembra che questa lista sia vuota, prova a passare a un\'altra File \"safe mode\" trovato! \nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso. + Quantità di ricerca usata quando il player è nascosto + TV Android + Quantità di ricerca usata quando il player è visibile + Player visibile - Quantità di ricerca + Player nascosto - Quantità di ricerca + Registro + Avvia + Test del provider + Riavvia + Ferma + Superato + Fallito \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..a3d1d434 --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,185 @@ + + + %d分 + ダウンロード + 検索 + 設定 + シェア + 映画 + ホーム + ライブラリ + 再生 + %d日 %d時間%d分 + %d時間%d分 + 検索… + ダウンロード + 情報 + シーズン + 予告編 + シリーズ + エピソード + 再生速度 (%.2fx) + 次のエピソード + 適用 + アカウント + カートゥーン + TVシリーズ + トレント + ドキュメンタリー + OVA + アジアドラマ + ライブ配信 + 映画 + その他 + カートゥーン + トレント + ドキュメンタリー + アジアドラマ + ライブ配信 + NSFW + キャンセル + アニメ + ロック + ソース + NSFW + 履歴を削除 + 視聴中コンテンツ + 全般 + 動画 + プレーヤー + 懐う + 予告編を再生 + エピソード + 視聴 + ジャンル + 映画を再生 + 字幕 + CloudStream + CloudStreamで再生 + ブラウザ + 完成 + 放置 + 保留 + ローディング… + ブラウザで開く + シーズン + 残り +\n%d分 + 再生エピソード + ダウンロード済 + バックアップ + ソース + 履歴 + ポスター + なし + コピー + 閉じる + 保存 + 消去 + %sエピ%d + 出演者:%s + ポスター + エピソードポスター + 主要ポスター + 次のランダム + 戻り + 視聴率 %.1f + 新しいアップデートを発見! +\n%s -> %s + %d分 + %sを検索… + ソース + ろくごうきじ + 接続を再試行… + 戻り + 削除 + 詳細情報 + 閉じる + アップデート・バックアップ + アプリ言語 + GitHub(ギットハブ) + -30 + +30 + 免責 + 拡張機能 + アプリ更新 + 提供者 + 字幕 + 特徴 + デフォルト + 自動 + 任意 + 拡張機能 + リンク + Android TV + ログイン + ログアウト + 最大 + 最小 + なし + + 18+ + + で開く + エピソード + 時間 + 概要 + サイト + 使用 + アプリ + 詳細情報 + 削除 + ピクチャーインピクチャー + 字幕 + 情報 + 一時停止 + 再生エピソード + 削除 + 開始 + 状態 + + 再開 + 失敗 + 合格 + 空き + 完成 + 進行中 + デフォルト + ウェブブラウザ + VLC + MPV + 言語 + 作成者 + サイズ + 状態 + バージョン + 視聴率 %s + 視聴率 + デフォルト + ダウンロード失敗 + ダウンロード開始 + ダウンロード完了 + ダウンロード終了 + ストリーム + アップデート開始 + シーズンなし + 字幕なし + アスペクト比 + ロードをスキップする + その他のオプション + データなし + ダウンロード中 + ブックマーク + 内部記憶装置 + ダウンロードが一時停止 + メタデータはこのサイトでは提供されません。メタデータがサイト上に存在しない場合、ビデオの読み込みに失敗します。 + 記述 + Logcat 🐈を表示 + ログ + 検索 + Discordに参加 + アップデート + アップデートを確認 + 作品名 + アプリのアップデートをインストール中… + \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index efe0a1d8..c36459b7 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,3 +1,128 @@ - \ No newline at end of file + %sಎಪಿ%d + ಕ್ಯಾಸ್ಟ್:%s + ಹಿಂದೆ ಹೋಗು + ಫಿಲ್ಲರ್ + ಹುಡುಕು + ಡೌನ್ಲೋಡ್ + ಫಾಂಟ್ + ಪೂರೈಕೆದಾರರನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ + ಪ್ರಕಾರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ + ಯಾವುದೇ ಬೆನೆನ್ಸ್ ನೀಡಿಲ್ಲ + ಸ್ವಯಂ-ಆಯ್ಕೆ ಭಾಷೆ + ಹೆಚ್ಚಿನ ಮಾಹಿತಿ + \@ಸ್ಟ್ರಿಂಗ್/ಹೋಮ್_ಪ್ಲೇ + ಈ ಪೂರೈಕೆದಾರರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಲು VPN ಬೇಕಾಗಬಹುದು + ಕಪ್ಪು ಗಡಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ + ಸಂಚಿಕೆ%d ಬಿಡುಗಡೆಯಾಗಲಿದೆ + %dh %dm + ಪೋಸ್ಟರ್ + ಪೋಸ್ಟರ್ + ಸಂಚಿಕೆ ಪೋಸ್ಟರ್ + ಮೇನ್ ಪೋಸ್ಟರ್ + ಅಪ್ಡೇಟ್ ಪ್ರಾರಂಭವಾಗಿದೆ + ಲೋಡಿಂಗ್ ಲಿಂಕ್ ಎರರ್ ಬಂದಿದೆ + ಇಂಟರ್ನಲ್ ಸ್ಟೋರೇಜ್ + ಡಬ್ + ಸಬ್ + ಸ್ವಯಂಚಾಲಿತ ದೋಷ ವರದಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ + ಹೈಡ್ + ಪ್ಲೇ + ಮಾಹಿತಿ + ಸೆಟ್ ವಾಚ್ ಸ್ಟೇಟಸ್ + ಅನ್ವಯಿಸು + ರದ್ದುಮಾಡು + ಸಬ್ ಟೈಟಲ್ಸ್ ಎಲೆವಷನ್ + ಫಾಂಟ್ ಸೈಜ್ + ಸಬ್ ಟೈಟಲ್ಸ್ ಭಾಷೆ + ತೆಗೆದುಹಾಕಿ + ಈ ಪೂರೈಕೆದಾರರು ಟೊರೆಂಟ್ ಆಗಿದೆ, VPN ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ + ಯಾವುದೇ ಪ್ಲಾಟ್ ಕಂಡುಬಂದಿಲ್ಲ + ಲಾಗ್‌ಕ್ಯಾಟ್ 🐈 ತೋರಿಸಿ + ಲಾಗ್ + ಚಿತ್ರದಲ್ಲಿ-ಚಿತ್ರದಲ್ಲಿ + ಪ್ಲೇಯರ್ ಮರುಗಾತ್ರಗೊಳಿಸಿ ಬಟನ್ + ಸಬ್ ಟೈಟಲ್ಸ್ + ಪ್ಲೇಯರ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು + ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್ + ಹಿಂದೆ ಹೋಗು + ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಿ + ಬುಕ್‌ಮಾರ್ಕ್‌ + ಬ್ಯಾಕ್ ಗ್ರೌಂಡ್ ಕಲರ್ + %d ಡೇವ್‌ಗಳಿಗೆ ಬೆನೆನೆಸ್ ನೀಡಲಾಗಿದೆ + ಡೀಫಾಲ್ಟ್‌ಗೆ ಮರುಹೊಂದಿಸಲು ಹಿಡಿದುಕೊಳ್ಳಿ + ಸೈಟ್‌ನಿಂದ ಮೆಟಾಡೇಟಾವನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ, ಅದು ಸೈಟ್‌ನಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲದಿದ್ದರೆ ವೀಡಿಯೊ ಲೋಡಿಂಗ್ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ. + ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲೆ ಚಿಕಣಿ ಪ್ಲೇಯರ್‌ನಲ್ಲಿ ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಮುಂದುವರಿಸುತ್ತದೆ + ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ + ರೇಟೆಡ್:%.1f + ತೆಗೆದುಹಾಕಿ + ಡೌನ್‌ಲೋಡ್ ಅನ್ನು ಪುನರಾರಂಭಿಸಿ + ಕ್ಲೋಸ್ + ಕ್ಲಿಯರ್ + ಸೇವ್ + ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್ + ಫೈಲ್ ಪ್ಲೇ + ಟೆಕ್ಸ್ಟ್ ಕಲರ್ + ಔಟ್ ಲೈನ್ ಕಲರ್ + ವಿಂಡೋ ಕಲರ್ + ಎಡ್ಜ್ ಟೈಪ್ + ಪ್ರೊವೈಡರ್ ಬದಲಾಯಿಸಿ + %dಮಿನ + ವಿವರಣೆ + ಸ್ಪೀಡ್(%.2fx) + ಹೋಂ + ಸಬ್ ಟೈಟಲ್ಸ್ + ಸೆಟ್ಟಿಂಗ್ಸ್ + ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ + ಹುಡುಕು… + ಚಲನಚಿತ್ರವನ್ನು ಪ್ಲೇ ಮಾಡಿ + ಪ್ರಿವ್ಯೂ ಹಿನ್ನೆಲೆ + ಮುಂದಿನ ಸಂಚಿಕೆ + ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ + ಸ್ಟ್ರೀಮ್ + ಶೇರ್ + ಫೈಲ್ ಅಳಿಸಿ + ಹೆಚ್ಚಿನ ಮಾಹಿತಿ + ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ +\n%s-%s + ಲೋಡಿಂಗ್… + ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ + ಲೈವ್‌ಸ್ಟ್ರೀಮ್ ಪ್ಲೇ ಮಾಡಿ + ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ ಇದರೊಂದಿಗೆ ಪ್ಲೇ ಮಾಡಿ + ವೀಕ್ಷಿಸಲು ಯೋಜನೆ + ಸಂಚಿಕೆಯನ್ನು ಪ್ಲೇ ಮಾಡಿ + ಕಂಟಿನ್ಯೂ ವಾಟಚಿಂಗ್ + ಯಾವುದೇ ವಿವರಣೆ ಕಂಡುಬಂದಿಲ್ಲ + ಸ್ಟ್ರೀಮ್ ಟೊರೆಂಟ್ + ಡೌನ್‌ಲೋಡ್ + ಕಾಪಿ + ನೋ ಡೇಟಾ + ಪ್ಲೇಯರ್ ಸ್ಪೀಡ್ + %d %dh %dm + ಹುಡುಕು %s… + ಹೆಚ್ಚಿನ ಆಯ್ಕೆ + ಫಾಂಟ್‌ಗಳನ್ನು ಇರಿಸುವ ಮೂಲಕ ಆಮದು ಮಾಡಿ %s + %dm + ಪ್ರಕಾರಗಳು + ಬ್ರೌಸರ್ ತೆರೆಯಿರಿ + ಆನ್-ಹೋಲ್ಡ್ + ನನ್ + ಸಂಪರ್ಕವನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ… + ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ವಿಫಲವಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ಮುಗಿದಿದೆ + ಬ್ರೌಸರ್ + ಸ್ಕಿಪ್ ಲೋಡಿಂಗ್ + ವಾಚಿಂಗ್ + ಪೂರ್ಣಗೊಂಡಿದೆ + ಕೈಬಿಡಲಾಯಿತು + ಪುನಃ ವೀಕ್ಷಿಸುತ್ತಿದೆ + ಟ್ರೈಲರ್ ಪ್ಲೇ ಮಾಡಿ + ಮೂಲಗಳು + ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ + ಮುಂದಿನ ರಾಂಡಮ್ + \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index c709f124..411f0b45 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -373,7 +373,7 @@ Pomiń setup Dostosuj wygląd aplikacji do urządzenia Zgłaszanie błędów - Co chciałbyś obejrzeć\? + Co chciałbyś obejrzeć Gotowe Rozszerzenia Dodaj repozytorium @@ -509,4 +509,9 @@ Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną Znaleziono plik trybu bezpiecznego. \nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty. + Używana ilość przewijania, gdy widoczny jest odtwarzacz + Ukryty odtwarzacz - ilość przewijania + Android TV + Pokazany odtwarzacz — ilość przewijania + Używana ilość przewijania, gdy ukryty jest odtwarzacz \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 982546bc..42d9b7c8 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Poster Episod Poster Principal Următorul la Întâmplare @@ -142,7 +142,7 @@ Fișier de rezervă încărcat Imposibilitatea de a restaura datele din %s Date stocate - Permisiuni de arhivare lipsă, vă rugăm să încercați din nou + Permisiunea de arhivare lipșe, vă rugăm să încercați din nou. Eroare de backup %s Căutare Conturi și credite @@ -154,7 +154,7 @@ Nu trimiteți niciun fel de date Afișează etichetele [filler] pentru anime Arată trailerul - Arată posterele de la Kitsu + Arată afișele de la Kitsu Afișați actualizările aplicației Căutați automat noi actualizări la pornire Actualizați la prerelease @@ -384,4 +384,8 @@ Începe următorul episod când se termină episodul curent Ascundeți calitatea video selectată în rezultatele căutării Redare Livestream + Librărie + Log + Browser + Joacă cu CloudStream \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 6e9fb394..2812667a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -506,4 +506,16 @@ Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем Удалять лишнее из субтитров + Местоположение ползунка, когда игрок скрыт + Android TV + Второго планa + Смешанный опенинг + Смешанный конец + Тест провайдер + Журнал + Запустить + Выполнено + Неудачный + Прекратить + Перезапустить \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 97039233..66d8ada9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -17,7 +17,7 @@ %dd %dh %dm %dm %d min - \@string/result_poster_img_des + Plagát Plagát epizódy Hlavný plagát Prehrať s CloudStream diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 871e0a28..5330d3ec 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -507,4 +507,20 @@ Файл безпечного режиму знайдено! \nРозширеня не завантажуються під час запуску, доки файл не буде видалено. Android TV + Плеєр сховано - обсяг пошуку + Плеєр показано - обсяг пошуку + Обсяг пошуку, який використовується, коли плеєр видимий + Обсяг пошуку, який використовується, коли гравець прихований + Не вдалося + Пройдено + Перезапуск + Журнал + Старт + Стоп + Тест постачальника + Оновлення підписаних шоу + Підписано + Підписано на %s + Відписатися від %s + Епізод %d випущено! \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a8341d46..8a10208a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -19,7 +19,7 @@ %dm 封面 - \@string/result_poster_img_des + 封面 劇集封面 主封面 隨機下一個 @@ -533,4 +533,5 @@ 預設 外觀 功能 + 瀏覽器 \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index c57e3ca1..9e2d6137 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -554,4 +554,21 @@ 看来您的库是空的 :( \n登录库账户或添加节目到您的本地库 看来此列表是空的,请尝试切换到另一个 + 播放器显示 - 快进快退秒数 + 播放器可见时使用的快进快退秒数 + 播放器隐藏 - 快进快退秒数 + 播放器隐藏时使用的快进快退秒数 + Android TV + 失败 + 片源测试 + 重启 + 停止 + 正在更新订阅节目 + 已订阅 + 已订阅 %s + 已取消订阅 %s + 开始 + 第 %d 集已发布! + 成功 + 日志 \ No newline at end of file From 51137701f2c6228660720fba94d9dfc53cefb582 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:43:35 +0100 Subject: [PATCH 071/104] add proxy to raw.githubusercontent.com (#368) --- .../lagradost/cloudstream3/MainActivity.kt | 34 ++++++++++++++++++ .../cloudstream3/plugins/RepositoryManager.kt | 30 ++++++++++++---- .../ui/settings/SettingsGeneral.kt | 13 ++++--- .../lagradost/cloudstream3/utils/AppUtils.kt | 8 ++++- app/src/main/res/values/strings.xml | 6 ++++ app/src/main/res/xml/settins_general.xml | 36 ++++++++++++------- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 28419e7a..e626dcd6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -32,7 +32,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.google.android.gms.cast.framework.* import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.navigationrail.NavigationRailView +import com.google.android.material.snackbar.Snackbar import com.jaredrummler.android.colorpicker.ColorPickerDialogListener +import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings @@ -79,6 +81,7 @@ import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable +import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadResult @@ -86,6 +89,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching @@ -717,6 +721,28 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) + // Automatically enable jsdelivr if cant connect to raw.githubusercontent.com + if (this.getKey(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) { + main { + if (checkGithubConnectivity()) { + this.setKey(getString(R.string.jsdelivr_proxy_key), false) + } else { + this.setKey(getString(R.string.jsdelivr_proxy_key), true) + val parentView: View = findViewById(android.R.id.content) + Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG).let { snackbar -> + snackbar.setAction(R.string.revert) { + setKey(getString(R.string.jsdelivr_proxy_key), false) + } + snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) + snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) + snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) + snackbar.show() + } + } + + } + } + if (PluginManager.checkSafeModeFile()) { normalSafeApiCall { @@ -1090,4 +1116,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // } } + + suspend fun checkGithubConnectivity(): Boolean { + return try { + app.get("https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", timeout = 5).text.trim() == "ok" + } catch (t: Throwable) { + false + } + } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt index e77b2d54..742bf308 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt @@ -2,8 +2,10 @@ package com.lagradost.cloudstream3.plugins import android.content.Context import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -71,6 +73,15 @@ object RepositoryManager { val PREBUILT_REPOSITORIES: Array by lazy { getKey("PREBUILT_REPOSITORIES") ?: emptyArray() } + val GH_REGEX = Regex("^https://raw.githubusercontent.com/([A-Za-z0-9-]+)/([A-Za-z0-9_.-]+)/(.*)$") + + /* Convert raw.githubusercontent.com urls to cdn.jsdelivr.net if enabled in settings */ + fun convertRawGitUrl(url: String): String { + if (getKey(context!!.getString(R.string.jsdelivr_proxy_key)) != true) return url + val match = GH_REGEX.find(url) ?: return url + val (user, repo, rest) = match.destructured + return "https://cdn.jsdelivr.net/gh/$user/$repo@$rest" + } suspend fun parseRepoUrl(url: String): String? { val fixedUrl = url.trim() @@ -84,10 +95,15 @@ object RepositoryManager { } } else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) { suspendSafeApiCall { - app.get("https://l.cloudstream.cf/${fixedUrl}").let { - return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url - else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 -> - return@let2 if (it2.isSuccessful) it2.url else null + app.get("https://l.cloudstream.cf/${fixedUrl}", allowRedirects = false).let { + it.headers["Location"]?.let { url -> + return@suspendSafeApiCall if (!url.startsWith("https://cutt.ly/branded-domains")) url + else null + } + app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 -> + it2.headers["Location"]?.let { url -> + return@suspendSafeApiCall if (url.startsWith("https://cutt.ly/404")) url else null + } } } } @@ -97,14 +113,14 @@ object RepositoryManager { suspend fun parseRepository(url: String): Repository? { return suspendSafeApiCall { // Take manifestVersion and such into account later - app.get(url).parsedSafe() + app.get(convertRawGitUrl(url)).parsedSafe() } } private suspend fun parsePlugins(pluginUrls: String): List { // Take manifestVersion and such into account later return try { - val response = app.get(pluginUrls) + val response = app.get(convertRawGitUrl(pluginUrls)) // Normal parsed function not working? // return response.parsedSafe() tryParseJson>(response.text)?.toList() ?: emptyList() @@ -139,7 +155,7 @@ object RepositoryManager { } file.createNewFile() - val body = app.get(pluginUrl).okhttpResponse.body + val body = app.get(convertRawGitUrl(pluginUrl)).okhttpResponse.body write(body.byteStream(), file.outputStream()) file } 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 354dc89c..c5a11cce 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 @@ -82,7 +82,7 @@ val appLanguages = arrayListOf( Triple("", "norsk bokmål", "no"), Triple("", "polski", "pl"), Triple("\uD83C\uDDF5\uD83C\uDDF9", "português", "pt"), - Triple("🦍", "mmmm... monke", "qt"), + Triple("\uD83E\uDD8D", "mmmm... monke", "qt"), Triple("", "română", "ro"), Triple("", "русский", "ru"), Triple("", "slovenčina", "sk"), @@ -97,7 +97,7 @@ val appLanguages = arrayListOf( Triple("", "中文", "zh"), Triple("\uD83C\uDDF9\uD83C\uDDFC", "文言", "zh-rTW"), /* end language list */ -).sortedBy { it.second?.toLowerCase() } //ye, we go alphabetical, so ppl don't put their lang on top +).sortedBy { it.second.lowercase() } //ye, we go alphabetical, so ppl don't put their lang on top class SettingsGeneral : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -157,9 +157,6 @@ class SettingsGeneral : PreferenceFragmentCompat() { getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> val tempLangs = appLanguages.toMutableList() - //if (beneneCount > 100) { - // tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo")) - //} val current = getCurrentLocale(pref.context) val languageCodes = tempLangs.map { (_, _, iso) -> iso } val languageNames = tempLangs.map { (emoji, name, iso) -> @@ -316,6 +313,12 @@ class SettingsGeneral : PreferenceFragmentCompat() { } ?: emptyList() } + settingsManager.edit().putBoolean(getString(R.string.jsdelivr_proxy_key), getKey(getString(R.string.jsdelivr_proxy_key), false) ?: false).apply() + getPref(R.string.jsdelivr_proxy_key)?.setOnPreferenceChangeListener { _, newValue -> + setKey(getString(R.string.jsdelivr_proxy_key), newValue) + return@setOnPreferenceChangeListener true + } + getPref(R.string.download_path_key)?.setOnPreferenceClickListener { val dirs = getDownloadDirs() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 860144ee..205f0a6b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -491,6 +491,12 @@ object AppUtils { } } + fun Context.isNetworkAvailable(): Boolean { + val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = manager.activeNetworkInfo + return activeNetworkInfo != null && activeNetworkInfo.isConnected || manager.allNetworkInfo?.any { it.isConnected } ?: false + } + fun splitQuery(url: URL): Map { val queryPairs: MutableMap = LinkedHashMap() val query: String = url.query @@ -815,4 +821,4 @@ object AppUtils { } return currentAudioFocusRequest } -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0858fdfa..2d46a70d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ random_button_key provider_lang_key dns_key + jsdelivr_proxy_key download_path_key Cloudstream app_layout_key @@ -378,6 +379,9 @@ Causes problems if set too high on devices with low storage space, such as Android TV. DNS over HTTPS Useful for bypassing ISP blocks + raw.githubusercontent.com Proxy + Failed to reach GitHub, enabling jsdelivr proxy. + Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -405,6 +409,7 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. + ISP Bypasses Links App updates Backup @@ -644,6 +649,7 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. + Revert Updating subscribed shows Subscribed Subscribed to %s diff --git a/app/src/main/res/xml/settins_general.xml b/app/src/main/res/xml/settins_general.xml index 726f3fd0..c4900bca 100644 --- a/app/src/main/res/xml/settins_general.xml +++ b/app/src/main/res/xml/settins_general.xml @@ -6,18 +6,6 @@ android:title="@string/app_language" android:icon="@drawable/ic_baseline_language_24" /> - - - - + + + + + + + + + + From 1da6a925692de08cb8e8ec00d93d1e5d1f76b8c6 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:07:04 +0000 Subject: [PATCH 072/104] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 1 + 1 file changed, 1 insertion(+) 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 c5a11cce..078419e2 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 @@ -74,6 +74,7 @@ val appLanguages = arrayListOf( Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"), Triple("", "italiano", "it"), Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), + Triple("", "日本語 (にほんご)", "ja"), Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), From aeab423d2929d1de6dd2681592e2e832f261a935 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 24 Feb 2023 18:47:54 +0100 Subject: [PATCH 073/104] Excluded the referer header when empty --- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 4772a7f1..cd384c6f 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 @@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import java.io.File +import java.time.Duration import javax.net.ssl.HttpsURLConnection import javax.net.ssl.SSLContext import javax.net.ssl.SSLSession @@ -535,15 +536,16 @@ class CS3IPlayer : IPlayer { OkHttpDataSource.Factory(client).setUserAgent(USER_AGENT) } + // Do no include empty referer, if the provider wants those they can use the header map. + val refererMap = if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) val headers = mapOf( - "referer" to link.referer, "accept" to "*/*", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", "sec-ch-ua-mobile" to "?0", "sec-fetch-user" to "?1", "sec-fetch-mode" to "navigate", "sec-fetch-dest" to "video" - ) + link.headers // Adds the headers from the provider, e.g Authorization + ) + refererMap + link.headers // Adds the headers from the provider, e.g Authorization return source.apply { setDefaultRequestProperties(headers) From f722785a379547fb9f28893e0661eeff186b0a3d Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:49:53 +0700 Subject: [PATCH 074/104] fixed Linkbox (#390) --- .../cloudstream3/extractors/Linkbox.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt index c28a8900..6a4945bb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt @@ -18,31 +18,36 @@ open class Linkbox : ExtractorApi() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val id = Regex("""(/file/|id=)(\S+)[&/?]""").find(url)?.groupValues?.get(2) - app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe()?.data?.rList?.map { link -> - callback.invoke( - ExtractorLink( - name, - name, - link.url, - url, - getQualityFromName(link.resolution) + val id = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1) + app.get("$mainUrl/api/file/detail?itemId=$id", referer = url) + .parsedSafe()?.data?.itemInfo?.resolutionList?.map { link -> + callback.invoke( + ExtractorLink( + name, + name, + link.url ?: return@map null, + url, + getQualityFromName(link.resolution) + ) ) - ) - } + } } - data class RList( - @JsonProperty("url") val url: String, - @JsonProperty("resolution") val resolution: String?, + data class Resolutions( + @JsonProperty("url") val url: String? = null, + @JsonProperty("resolution") val resolution: String? = null, + ) + + data class ItemInfo( + @JsonProperty("resolutionList") val resolutionList: ArrayList? = arrayListOf(), ) data class Data( - @JsonProperty("rList") val rList: List?, + @JsonProperty("itemInfo") val itemInfo: ItemInfo? = null, ) data class Responses( - @JsonProperty("data") val data: Data?, + @JsonProperty("data") val data: Data? = null, ) } \ No newline at end of file From 2926dc6c8eea53a35006b4188f9a03a9a9bb5216 Mon Sep 17 00:00:00 2001 From: Allen Baby <64322605+allenbaby@users.noreply.github.com> Date: Sat, 25 Feb 2023 00:21:03 +0530 Subject: [PATCH 075/104] Issue #376: Added new feature for separate watch quality on mobile data. (#391) * Issue #376: Added new feature for separate watch quality on mobile data. --- .../cloudstream3/ui/player/CS3IPlayer.kt | 3 ++- .../ui/player/FullScreenPlayer.kt | 4 ++-- .../ui/settings/SettingsPlayer.kt | 24 +++++++++++++++++++ .../lagradost/cloudstream3/utils/AppUtils.kt | 7 +++++- app/src/main/res/values/strings.xml | 6 +++-- app/src/main/res/xml/settings_player.xml | 4 ++++ 6 files changed, 42 insertions(+), 6 deletions(-) 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 cd384c6f..782e3fa4 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 @@ -35,6 +35,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.utils.EpisodeSkip +import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorUri @@ -849,7 +850,7 @@ class CS3IPlayer : IPlayer { Log.i(TAG, "loadExo") val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val maxVideoHeight = settingsManager.getInt( - context.getString(com.lagradost.cloudstream3.R.string.quality_pref_key), + context.getString(if (context.isUsingMobileData()) com.lagradost.cloudstream3.R.string.quality_pref_mobile_data_key else com.lagradost.cloudstream3.R.string.quality_pref_key), Int.MAX_VALUE ) 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 8d28fd9d..d1b2814d 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 @@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe @@ -1246,9 +1247,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() { ctx.getString(R.string.double_tap_pause_enabled_key), false ) - currentPrefQuality = settingsManager.getInt( - ctx.getString(R.string.quality_pref_key), + ctx.getString(if (ctx.isUsingMobileData()) R.string.quality_pref_mobile_data_key else R.string.quality_pref_key), currentPrefQuality ) // useSystemBrightness = diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt index 33d41934..e10a5a1a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt @@ -113,6 +113,30 @@ class SettingsPlayer : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.quality_pref_mobile_data_key)?.setOnPreferenceClickListener { + val prefValues = Qualities.values().map { it.value }.reversed().toMutableList() + prefValues.remove(Qualities.Unknown.value) + + val prefNames = prefValues.map { Qualities.getStringByInt(it) } + + val currentQuality = + settingsManager.getInt( + getString(R.string.quality_pref_mobile_data_key), + Qualities.values().last().value + ) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentQuality), + getString(R.string.watch_quality_pref_data), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.quality_pref_mobile_data_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + getPref(R.string.player_pref_key)?.setOnPreferenceClickListener { val prefNames = resources.getStringArray(R.array.player_pref_names) val prefValues = resources.getIntArray(R.array.player_pref_values) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 205f0a6b..a76b62fd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -776,8 +776,13 @@ object AppUtils { return networkInfo.any { conManager.getNetworkCapabilities(it) ?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true + } && + !networkInfo.any { + conManager.getNetworkCapabilities(it) + ?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true + } } - } + private fun Activity?.cacheClass(clazz: String?) { clazz?.let { c -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2d46a70d..49380b5e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ test_providers_key subtitle_settings_chromecast_key quality_pref_key + quality_pref_mobile_data_key player_pref_key prefer_limit_title_key prefer_limit_title_rez_key @@ -364,7 +365,8 @@ Don\'t show again Skip this Update Update - Preferred watch quality + Preferred watch quality (WiFi) + Preferred watch quality (Mobile Data) Video player title max chars Video player resolution Video buffer size @@ -655,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - \ No newline at end of file + diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml index 0e5bd84f..2d2905ea 100644 --- a/app/src/main/res/xml/settings_player.xml +++ b/app/src/main/res/xml/settings_player.xml @@ -15,6 +15,10 @@ android:icon="@drawable/ic_baseline_hd_24" android:key="@string/quality_pref_key" android:title="@string/watch_quality_pref" /> + Date: Sat, 25 Feb 2023 21:18:48 +0000 Subject: [PATCH 076/104] Added some extractors mirrors and added Vido Extractor (#393) * Added some mirrors and fixed some extractors * Fixed Vido extractor (for MesFilms and Wiflix) --- .../cloudstream3/extractors/DoodExtractor.kt | 3 ++ .../cloudstream3/extractors/Evolaod.kt | 25 ++------------ .../cloudstream3/extractors/Filesim.kt | 5 +++ .../cloudstream3/extractors/StreamSB.kt | 4 +++ .../cloudstream3/extractors/Uqload.kt | 26 ++++---------- .../lagradost/cloudstream3/extractors/Vido.kt | 34 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 5 +++ 7 files changed, 61 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt index 7ec1fb22..0d94eb08 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt @@ -38,6 +38,9 @@ class DoodWsExtractor : DoodLaExtractor() { override var mainUrl = "https://dood.ws" } +class DoodYtExtractor : DoodLaExtractor() { + override var mainUrl = "https://dood.yt" +} open class DoodLaExtractor : ExtractorApi() { override var name = "DoodStream" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt index eddbf6df..3e38b446 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt @@ -16,26 +16,7 @@ open class Evoload : ExtractorApi() { override suspend fun getUrl(url: String, referer: String?): List { - val lang = url.substring(0, 2) - val flag = - if (lang == "vo") { - " \uD83C\uDDEC\uD83C\uDDE7" - } - else if (lang == "vf"){ - " \uD83C\uDDE8\uD83C\uDDF5" - } else { - "" - } - - val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// - url - } else { - url.substring(2, url.length) - } - //println(lang) - //println(cleaned_url) - - val id = cleaned_url.replace("https://evoload.io/e/", "") // wanted media id + val id = url.replace("https://evoload.io/e/", "") // wanted media id val csrv_token = app.get("https://csrv.evosrv.com/captcha?m412548=").text // whatever that is val captchaPass = app.get("https://cd2.evosrv.com/html/jsx/e.jsx").text.take(300).split("captcha_pass = '")[1].split("\'")[0] //extract the captcha pass from the js response (located in the 300 first chars) val payload = mapOf("code" to id, "csrv_token" to csrv_token, "pass" to captchaPass) @@ -44,9 +25,9 @@ open class Evoload : ExtractorApi() { return listOf( ExtractorLink( name, - name + flag, + name, link, - cleaned_url, + url, Qualities.Unknown.value, ) ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index bc910a7e..382ca756 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -13,6 +13,11 @@ class FileMoon : Filesim() { override val name = "FileMoon" } +class FileMoonSx : Filesim() { + override val mainUrl = "https://filemoon.sx" + override val name = "FileMoonSx" +} + open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index b77617c2..b7477242 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -77,6 +77,10 @@ class StreamSB10 : StreamSB() { override var mainUrl = "https://sbplay2.xyz" } +class StreamSB11 : StreamSB() { + override var mainUrl = "https://sbbrisk.com" +} + // This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt // The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE open class StreamSB : ExtractorApi() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt index 5109acc3..86bd9e0b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt @@ -7,6 +7,10 @@ class Uqload1 : Uqload() { override var mainUrl = "https://uqload.com" } +class Uqload2 : Uqload() { + override var mainUrl = "https://uqload.co" +} + open class Uqload : ExtractorApi() { override val name: String = "Uqload" override val mainUrl: String = "https://www.uqload.com" @@ -15,30 +19,14 @@ open class Uqload : ExtractorApi() { override suspend fun getUrl(url: String, referer: String?): List? { - val lang = url.substring(0, 2) - val flag = - if (lang == "vo") { - " \uD83C\uDDEC\uD83C\uDDE7" - } - else if (lang == "vf"){ - " \uD83C\uDDE8\uD83C\uDDF5" - } else { - "" - } - - val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// - url - } else { - url.substring(2, url.length) - } - with(app.get(cleaned_url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" + with(app.get(url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" srcRegex.find(this.text)?.groupValues?.get(1)?.replace("\"", "")?.let { link -> return listOf( ExtractorLink( name, - name + flag, + name, link, - cleaned_url, + url, Qualities.Unknown.value, ) ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt new file mode 100644 index 00000000..67e59281 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt @@ -0,0 +1,34 @@ +package com.lagradost.cloudstream3.extractors +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getAndUnpack + +class Vido : ExtractorApi() { + override var name = "Vido" + override var mainUrl = "https://vido.lol" + private val srcRegex = Regex("""sources:\s*\["(.*?)"\]""") + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?): List? { + val methode = app.get(url.replace("/e/", "/embed-")) // fix wiflix and mesfilms + with(methode) { + if (!methode.isSuccessful) return null + //val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull() + srcRegex.find(this.text)?.groupValues?.get(1)?.let { link -> + return listOf( + ExtractorLink( + name, + name, + link, + url, + Qualities.Unknown.value, + true, + ) + ) + } + } + return null + } +} \ No newline at end of file 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 b0dba9ff..6540b8c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -229,6 +229,7 @@ val extractorApis: MutableList = arrayListOf( StreamSB8(), StreamSB9(), StreamSB10(), + StreamSB11(), SBfull(), // Streamhub(), cause Streamhub2() works Streamhub2(), @@ -254,6 +255,7 @@ val extractorApis: MutableList = arrayListOf( // WatchSB(), 'cause StreamSB.kt works Uqload(), Uqload1(), + Uqload2(), Evoload(), Evoload1(), VoeExtractor(), @@ -277,6 +279,7 @@ val extractorApis: MutableList = arrayListOf( DoodShExtractor(), DoodWatchExtractor(), DoodWfExtractor(), + DoodYtExtractor(), AsianLoad(), @@ -324,6 +327,8 @@ val extractorApis: MutableList = arrayListOf( Filesim(), FileMoon(), + FileMoonSx(), + Vido(), Linkbox(), Acefile(), SpeedoStream(), From e5834d485b1447a3687891d698f776e7922289d8 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 25 Feb 2023 20:37:54 +0100 Subject: [PATCH 077/104] Translated using Weblate (German) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Polish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Portuguese) Currently translated at 81.0% (493 of 608 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 99.5% (605 of 608 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Italian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Polish) Currently translated at 97.3% (592 of 608 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Japanese) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (qt (generated) (qt)) Currently translated at 50.4% (304 of 602 strings) Translated using Weblate (Slovak) Currently translated at 31.7% (191 of 602 strings) Translated using Weblate (Portuguese) Currently translated at 76.9% (463 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Persian) Currently translated at 20.0% (121 of 602 strings) Translated using Weblate (Hungarian) Currently translated at 55.6% (335 of 602 strings) Translated using Weblate (German) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Russian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Urdu) Currently translated at 72.2% (435 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Hebrew) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 94.1% (567 of 602 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (583 of 602 strings) Translated using Weblate (Turkish) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Polish) Currently translated at 98.0% (590 of 602 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 88.3% (532 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Italian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (French) Currently translated at 97.3% (586 of 602 strings) Translated using Weblate (Greek) Currently translated at 97.0% (584 of 602 strings) Translated using Weblate (Czech) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (602 of 602 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Japanese) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (qt (generated) (qt)) Currently translated at 50.4% (304 of 602 strings) Translated using Weblate (Slovak) Currently translated at 31.7% (191 of 602 strings) Translated using Weblate (Portuguese) Currently translated at 76.9% (463 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Persian) Currently translated at 20.0% (121 of 602 strings) Translated using Weblate (Hungarian) Currently translated at 55.6% (335 of 602 strings) Translated using Weblate (German) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Russian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Urdu) Currently translated at 72.2% (435 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Hebrew) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 94.1% (567 of 602 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (583 of 602 strings) Translated using Weblate (Turkish) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Polish) Currently translated at 98.0% (590 of 602 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 88.3% (532 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Italian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (French) Currently translated at 97.3% (586 of 602 strings) Translated using Weblate (Greek) Currently translated at 97.0% (584 of 602 strings) Translated using Weblate (Czech) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (602 of 602 strings) Co-authored-by: Aitor Salaberria Co-authored-by: Allan Nordhøy Co-authored-by: Anarchydr Co-authored-by: Anonymous Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Fjuro Co-authored-by: Geovani Amaral Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: MedRAM Co-authored-by: Prathap Rathod Co-authored-by: Rex_sa Co-authored-by: Sandyran Co-authored-by: Sdarfeesh Co-authored-by: Translator-3000 Co-authored-by: Walter H Co-authored-by: gallegonovato Co-authored-by: gnu-ewm Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bp/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/eo/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ur/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 8 ++++++- app/src/main/res/values-cs/strings.xml | 8 ++++++- app/src/main/res/values-de/strings.xml | 13 ++++++++++- app/src/main/res/values-es/strings.xml | 8 ++++++- app/src/main/res/values-hr/strings.xml | 5 +++++ app/src/main/res/values-in/strings.xml | 5 +++++ app/src/main/res/values-it/strings.xml | 10 +++++++++ app/src/main/res/values-kn/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 17 ++++++++++++++ app/src/main/res/values-pt/strings.xml | 31 ++++++++++++++++++++++++-- app/src/main/res/values-qt/strings.xml | 23 ++++++++++--------- app/src/main/res/values-uk/strings.xml | 9 +++++++- app/src/main/res/values-zh/strings.xml | 5 +++++ 13 files changed, 125 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 1e9bcfcc..cfd761e3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -259,7 +259,7 @@ لا تظهر مرة أخرى تخطي هذا التحديث تحديث - جودة المشاهدة المفضلة + جودة المشاهدة المفضلة (WiFi) أقصى عدد لحروف عنوان مُشغل الفيديو أبعاد مُشغل الفيديو حجم ذاكرة التخزين المؤقت للفيديو @@ -555,4 +555,10 @@ تم إصدار الحلقة %d! مشترك مشترك في %s + تجاوز مزود خدمة الإنترنت + استرجاع + فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. + تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + وكيل raw.githubusercontent.com + جودة المشاهدة المفضلة (بيانات الجوال) \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 966cd7d9..e99e1010 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -245,7 +245,7 @@ Již nezobrazovat Přeskočit tuto aktualizace Aktualizovat - Upřednostněná kvalita sledování + Upřednostněná kvalita sledování (WiFi) Maximální počet znaků v názvu přehrávače Rozlišení přehrávače Velikost vyrovnávací paměti videa @@ -547,4 +547,10 @@ Odhlášen odběr od %s Byla vydána epizoda %d! Odebíráno + Proxy raw.githubusercontent.com + Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. + Upřednostněná kvalita sledování (mobilní data) + Vrátit zpět + Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Obcházení ISP \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f6583c20..c5e74a60 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -263,7 +263,7 @@ Nicht mehr anzeigen Update ignorieren Update - Bevorzugte Auflösung + Bevorzugte Videoqualität (WLAN) Videoplayertitel max. Zeichen Videoplayer Auflösung Videopuffergröße @@ -518,4 +518,15 @@ Log Start Neustarten + Bevorzugte Videoqualität (mobile Daten) + Umgehung der GitHub Sperre mit jsdelivr, kann zu einigen Tagen Verzögerung bei Updates führen. + %s abonniert + %s deabonniert + Episode %d erschienen! + raw.githubusercontent.com Proxy + GitHub kann nicht erreicht werden, der jsdelivr-Proxy wird aktiviert. + Aktualisierung abonnierter Sendungen + Rückgängig + Abonniert + ISP-Umgehungen \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 2040169b..18647ef8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -24,7 +24,7 @@ Ocultar la calidad de video en los resultados de búsqueda Diseño Diseño - Calidad de visualización preferida + Calidad de visualización preferida (WiFi) Reproductor de video preferido Diseño para emulador Diseño de la aplicación @@ -523,4 +523,10 @@ Darse de baja de %s Actualizando los programas suscritos ¡Episodio %d publicado! + Proxy raw.githubusercontent.com + No se ha podido acceder a GitHub, activando el proxy jsdelivr. + Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Revertir + ISP Bypasses + Calidad de visualización preferida (Datos móviles) \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 926c7f57..b623ec5d 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -548,4 +548,9 @@ Pretplaćeno Pretplaćen na %s Otkazana pretplata sa %s + Vraćanje + ISP zaobilaznice + raw.githubusercontent.com Proxy + Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. + Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 46d61e44..84179352 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -546,4 +546,9 @@ Berlangganan ke %s Berhenti berlangganan di %s Episode %d telah rilis! + raw.githubusercontent.com Proksi + Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. + Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Bypass ISP + Pulihkan \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 89f6b4ee..d6bdc204 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -540,4 +540,14 @@ Ferma Superato Fallito + Proxy raw.githubusercontent.com + Disiscritto da %s + Iscritto + Iscritto a %s + Impossibile contattare GitHub, abilitazione proxy jsdelivr avviata. + Bypassa il blocco di GitHub utilizzando jsdelivr, potrebbe causare un ritardo di alcuni giorni. + Baypass ISP + Ripristina + Aggiornando shows a cui sei iscritto + L\'episodio %d è stato rilasciato! \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index c36459b7..242653be 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -85,7 +85,7 @@ ಶೇರ್ ಫೈಲ್ ಅಳಿಸಿ ಹೆಚ್ಚಿನ ಮಾಹಿತಿ - ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ + ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ \n%s-%s ಲೋಡಿಂಗ್… ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 411f0b45..bbaaec57 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -514,4 +514,21 @@ Android TV Pokazany odtwarzacz — ilość przewijania Używana ilość przewijania, gdy ukryty jest odtwarzacz + Dziennik + Uruchom ponownie + Rozpocznij + Nie powiodło się + Ukończone powodzeniem + Serwer pośredniczący raw.githubusercontent.com + Obejścia ISP + Test dostawcy + Zatrzymaj + Przywróć + Aktualizowanie subskrybowanych programów + Zasubskrybowano + Zasubskrybowano %s + Anulowano subskrypcję %s + Został wydany odcinek %d! + Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. + Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 9353664e..3754de8b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -6,12 +6,12 @@ Episódio %d será lançado em Poster Capa do Episódio - \@string/result_poster_img_des + Poster Capa Principal Próximo Aleatório Voltar Trocar Provedor - %dd %dh %dm + %d dia(s), %d hora(s) e %d mese(s) Fonte Resolução Extras @@ -381,4 +381,31 @@ Todas as legendas em maiúsculas Transferir todos os plugins deste repositório\? %s (Desativado) + Instalador APK + %d minuto(s) + Reproduzir trailer + Marcar como visto/não visto + Reproduzir + Instalar automaticamente todas as extensões dos repositórios cadastrados. + Baixar extensões automaticamente + Refazer o processo de configuração + -30 + Vídeo + +30 + %s %d%s + Elenco: %s + Atualização em andamento + Log + Alguns aparelhos não possuem suporte para o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. + %d-%d + %d %s + Iniciar + Falha + Sucesso + Biblioteca + Navegar + Aplicativo de Anime pelos mesmos desenvolvedores + Ova + Anime + Player visível - Procurar valor \ No newline at end of file diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index b36f3b16..c1119bfc 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -172,30 +172,31 @@ oouuh haa oohahaha hahha ooooohaha oohahaha hahha ooooohaha haaoou - u ahhu uuuh hau uaohuau + u ahhu uuuh hau uaohuau aahuuouhh ouh hhhah hhaohuhha a auoo ohauh - uhaauauau ahuuouaha + uhaauauau ahuuouaha auuuha h a ahuhaaaa - uaoh uhu uahaaaaoo - uauhah u aao u oah - h u ahahh aoou ha + uaoh uhu uahaaaaoo + uauhah u aao u oah + h u ahahh aoou ha haoooo aaoou uou ah oahuouooaouoa ouuhh o ouou uhauuuoaah h ou aouhouo aaooao hh - hhauhohhuu au aaohu - uhuoh o a ohahuhohoa hah + hhauhohhuu au aaohu + uhuoh o a ohahuhohoa hah ua hu ouo o aoau hah ah - ah huu oouhhau aoaoaaohoo ha - a ahu uoo uoahuo uo + ah huu oouhhau aoaoaaohoo ha + a ahu uoo uoahuo uo uo u ohouao uuoouhh hhuhuuh ouhoaao hau aouo - uha uh huo uooaah u + uha uh huo uooaah u u ooah uo ahauao huhuu hauu h a ou oh ouhuouhoaaha aaooohhouhhha hauauuu - aaaaaaa uuuuuu\n%s -> %s + aaaaaaa uuuuuu +\n%s -> %s %s aaou %d oouaaahh %s aaaaaaugh ouh %d uuoogahaaah ooua-h-ha diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5330d3ec..a676b583 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -217,7 +217,7 @@ Пропустити OP Не показувати знову Оновити - Бажана якість перегляду + Бажана якість перегляду (WiFi) Заголовок Перемикання елементів інтерфейсу на плакаті Оновлення не знайдено @@ -523,4 +523,11 @@ Підписано на %s Відписатися від %s Епізод %d випущено! + Повернути + raw.githubusercontent.com +\nProxy + Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. + Обходи ISP + Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9e2d6137..626cc0fe 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -571,4 +571,9 @@ 第 %d 集已发布! 成功 日志 + raw.githubusercontent.com 代理 + 连接 Github 失败,正在启用 jsdelivr 代理。 + 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + ISP 绕过 + 还原 \ No newline at end of file From d6df24eff2d425fc79e52b94a1fe600f857ec19a Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:05:42 -0600 Subject: [PATCH 078/104] Fixes on filesim and added filemoon, ztreamhub (#397) * fix fastream, tomatomatela, and added okrulink * forgot this * sendvid extractor * sendvid extractor * fixes * Filesim fix, added filemoon and ztreamhub --- .../cloudstream3/extractors/Filesim.kt | 45 +++++++++---------- .../cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 382ca756..84fd0552 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -1,13 +1,15 @@ package com.lagradost.cloudstream3.extractors -import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import java.net.URI +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 + +class Ztreamhub : Filesim() { + override val mainUrl: String = "https://ztreamhub.com" //Here 'cause works + override val name = "Zstreamhub" +} class FileMoon : Filesim() { override val mainUrl = "https://filemoon.to" override val name = "FileMoon" @@ -29,34 +31,27 @@ open class Filesim : ExtractorApi() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - with(app.get(url).document) { - this.select("script").forEach { script -> - if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val data = getAndUnpack(script.data()) - val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach - val fixedData = foundData.replace("file:", """"file":""") - - parseJson>("[$fixedData]").forEach { - callback.invoke( - ExtractorLink( - name, - name, - it.file, - "$mainUrl/", - Qualities.Unknown.value, - URI(it.file).path.endsWith(".m3u8") - ) - ) - } + val response = app.get(url, referer = mainUrl).document + response.select("script[type=text/javascript]").map { script -> + if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) { + val unpackedscript = getAndUnpack(script.data()) + val m3u8Regex = Regex("file.\\\"(.*?m3u8.*?)\\\"") + val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: "" + if (m3u8.isNotEmpty()) { + generateM3u8( + name, + m3u8, + mainUrl + ).forEach(callback) } } } } - private data class ResponseSource( + /* private data class ResponseSource( @JsonProperty("file") val file: String, @JsonProperty("type") val type: String?, @JsonProperty("label") val label: String? - ) + ) */ } \ No newline at end of file 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 6540b8c4..0bced6b2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -370,6 +370,7 @@ val extractorApis: MutableList = arrayListOf( Cda(), Dailymotion(), ByteShare(), + Ztreamhub() ) From ab324b93e89b2e955436d7ec2099b3256cb0d005 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Tue, 28 Feb 2023 01:19:59 +0100 Subject: [PATCH 079/104] Small fixes to Intents and Subscriptions --- .../lagradost/cloudstream3/MainActivity.kt | 6 ++++- .../services/SubscriptionWorkManager.kt | 22 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index e626dcd6..a7449255 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -319,7 +319,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } else if (safeURI(str)?.scheme == appStringSearch) { nextSearchQuery = URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8") - nav_view.selectedItemId = R.id.navigation_search + + // Use both navigation views to support both layouts. + // It might be better to use the QuickSearch. + nav_view?.selectedItemId = R.id.navigation_search + nav_rail_view?.selectedItemId = R.id.navigation_search } else if (safeURI(str)?.scheme == appStringResumeWatching) { val id = str.substringAfter("$appStringResumeWatching://").toIntOrNull() diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt index d1b1b660..adf5abfa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt @@ -105,14 +105,12 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete SUBSCRIPTION_CHANNEL_DESCRIPTION ) - safeApiCall { - setForeground( - ForegroundInfo( - SUBSCRIPTION_NOTIFICATION_ID, - progressNotificationBuilder.build() - ) + setForeground( + ForegroundInfo( + SUBSCRIPTION_NOTIFICATION_ID, + progressNotificationBuilder.build() ) - } + ) val subscriptions = getAllSubscriptions() @@ -196,7 +194,15 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete PendingIntent.getActivity(context, 0, intent, 0) } - val poster = ioWork { savedData.posterUrl?.let { url -> context.getImageBitmapFromUrl(url, savedData.posterHeaders) } } + val poster = ioWork { + savedData.posterUrl?.let { url -> + context.getImageBitmapFromUrl( + url, + savedData.posterHeaders + ) + } + } + val updateNotification = updateNotificationBuilder.setContentTitle(updateHeader) .setContentText(updateDescription) From f0515c4dc9e38bcf80f9766e7dff9f9ffd802f72 Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Fri, 3 Mar 2023 09:24:02 +0000 Subject: [PATCH 080/104] Support qualities for Dailymotion (#407) * Dailymotion qualities --- .../cloudstream3/extractors/Dailymotion.kt | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt index 125e4bcf..4b7cb19f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt @@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 import com.lagradost.cloudstream3.utils.Qualities import java.net.URL @@ -42,18 +43,9 @@ open class Dailymotion : ExtractorApi() { ) val metaData = app.get(metaDataUrl, referer = embedUrl, cookies = cookies) .parsedSafe() ?: return - metaData.qualities.forEach { (key, video) -> + metaData.qualities.forEach { (_, video) -> video.forEach { - callback.invoke( - ExtractorLink( - name, - "$name $key", - it.url, - "", - Qualities.Unknown.value, - true - ) - ) + getStream(it.url, this.name, callback) } } } @@ -75,6 +67,17 @@ open class Dailymotion : ExtractorApi() { return null } + private suspend fun getStream( + streamLink: String, + name: String, + callback: (ExtractorLink) -> Unit + ) { + return generateM3u8( + name, + streamLink, + "", + ).forEach(callback) + } data class Config( val context: Context, val dmInternalData: InternalData From 76545f55c3efeb66e14c6a4c62d2c980adf51830 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Fri, 3 Mar 2023 17:45:26 +0100 Subject: [PATCH 081/104] Standardized some home screen padding and made subtitle delay persistent. Fixes #405 --- .../ui/player/FullScreenPlayer.kt | 10 +++++++ .../cloudstream3/ui/player/GeneratorPlayer.kt | 29 +++++++++---------- .../main/res/layout/fragment_home_head.xml | 28 +++++++----------- app/src/main/res/layout/homepage_parent.xml | 2 -- 4 files changed, 34 insertions(+), 35 deletions(-) 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 d1b2814d..86e21fd6 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 @@ -84,6 +84,7 @@ const val HORIZONTAL_MULTIPLIER = 2.0f const val DOUBLE_TAB_MAXIMUM_HOLD_TIME = 200L const val DOUBLE_TAB_MINIMUM_TIME_BETWEEN = 200L // this also affects the UI show response time const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15 // in both directions +private const val SUBTITLE_DELAY_BUNDLE_KEY = "subtitle_delay" // All the UI Logic for the player open class FullScreenPlayer : AbstractPlayerFragment() { @@ -1120,11 +1121,20 @@ open class FullScreenPlayer : AbstractPlayerFragment() { resetRewindText() } + override fun onSaveInstanceState(outState: Bundle) { + // As this is video specific it is better to not do any setKey/getKey + outState.putLong(SUBTITLE_DELAY_BUNDLE_KEY, subtitleDelay) + super.onSaveInstanceState(outState) + } + @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // init variables setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) + savedInstanceState?.getLong(SUBTITLE_DELAY_BUNDLE_KEY)?.let { + subtitleDelay = it + } // handle tv controls playerEventListener = { eventType -> 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 67f58195..46f2bca9 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 @@ -13,7 +13,6 @@ import android.view.View import android.view.ViewGroup import android.widget.* import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.view.isGone @@ -734,19 +733,17 @@ class GeneratorPlayer : FullScreenPlayer() { } val currentAudioTracks = tracks.allAudioTracks - val trackBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) - .setView(R.layout.player_select_tracks) - - val tracksDialog = trackBuilder.create() + val trackDialog = Dialog(ctx, R.style.AlertDialogCustomBlack) + trackDialog.setContentView(R.layout.player_select_tracks) + trackDialog.show() // selectTracksDialog = tracksDialog - tracksDialog.show() - val videosList = tracksDialog.video_tracks_list - val audioList = tracksDialog.auto_tracks_list + val videosList = trackDialog.video_tracks_list + val audioList = trackDialog.auto_tracks_list - tracksDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1 - tracksDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1 + trackDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1 + trackDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1 fun dismiss() { if (isPlaying) { @@ -781,7 +778,7 @@ class GeneratorPlayer : FullScreenPlayer() { videosList.setItemChecked(which, true) } - tracksDialog.setOnDismissListener { + trackDialog.setOnDismissListener { dismiss() // selectTracksDialog = null } @@ -811,11 +808,11 @@ class GeneratorPlayer : FullScreenPlayer() { audioList.setItemChecked(which, true) } - tracksDialog.cancel_btt?.setOnClickListener { - tracksDialog.dismissSafe(activity) + trackDialog.cancel_btt?.setOnClickListener { + trackDialog.dismissSafe(activity) } - tracksDialog.apply_btt?.setOnClickListener { + trackDialog.apply_btt?.setOnClickListener { val currentTrack = currentAudioTracks.getOrNull(audioIndexStart) player.setPreferredAudioTrack( currentTrack?.language, currentTrack?.id @@ -828,7 +825,7 @@ class GeneratorPlayer : FullScreenPlayer() { player.setMaxVideoSize(width, height, currentVideo?.id) } - tracksDialog.dismissSafe(activity) + trackDialog.dismissSafe(activity) } } } catch (e: Exception) { @@ -1145,7 +1142,7 @@ class GeneratorPlayer : FullScreenPlayer() { val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL" - val title = when (titleRez) { + val title = when (titleRez) { 0 -> "" 1 -> extra 2 -> source diff --git a/app/src/main/res/layout/fragment_home_head.xml b/app/src/main/res/layout/fragment_home_head.xml index 0ee50042..603621f7 100644 --- a/app/src/main/res/layout/fragment_home_head.xml +++ b/app/src/main/res/layout/fragment_home_head.xml @@ -1,7 +1,6 @@ - @@ -148,17 +145,16 @@ + app:drawableTint="?attr/white" /> @@ -184,9 +180,9 @@ + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground"> + app:drawableTint="?attr/white" /> diff --git a/app/src/main/res/layout/homepage_parent.xml b/app/src/main/res/layout/homepage_parent.xml index b2f3e0a7..9c5dc84d 100644 --- a/app/src/main/res/layout/homepage_parent.xml +++ b/app/src/main/res/layout/homepage_parent.xml @@ -23,9 +23,7 @@ android:nextFocusUp="@id/home_child_more_info" android:paddingHorizontal="5dp" android:clipToPadding="false" - android:descendantFocusability="afterDescendants" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:id="@+id/home_child_recyclerview" android:orientation="horizontal" From 1eaa4620dc7ec00d34486d1e6ce6a7a1c38afe77 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 2 Mar 2023 05:38:39 +0100 Subject: [PATCH 082/104] Translated using Weblate (qt (generated) (qt)) Currently translated at 54.5% (333 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (French) Currently translated at 98.8% (603 of 610 strings) Translated using Weblate (Italian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (German) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 99.6% (608 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 85.0% (519 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (591 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Polish) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Anarchydr Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Duc Nguyen Tien Co-authored-by: Felipe Nogueira Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: Massimo Pissarello Co-authored-by: Samuel Gadiel Co-authored-by: Sdarfeesh Co-authored-by: Walter H Co-authored-by: eightyy8 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 8 +- app/src/main/res/values-fr/strings.xml | 17 +- app/src/main/res/values-hr/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 3 +- app/src/main/res/values-it/strings.xml | 3 +- app/src/main/res/values-pl/strings.xml | 3 +- app/src/main/res/values-pt/strings.xml | 217 +++++++++++++++++++------ app/src/main/res/values-qt/strings.xml | 29 ++++ app/src/main/res/values-ru/strings.xml | 13 +- app/src/main/res/values-vi/strings.xml | 23 ++- app/src/main/res/values-zh/strings.xml | 3 +- 12 files changed, 254 insertions(+), 68 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c5e74a60..7cf49de1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -246,7 +246,7 @@ In Browser wiedergeben Link kopieren Auto-Download - Download-Mirror + Alternativer Download Links neu laden Untertitel herunterladen Qualitätsanzeige diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 18647ef8..0b195275 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -506,11 +506,11 @@ \nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local ¡Se encontró un archivo en modo seguro! \nNo cargar ninguna extensión al inicio hasta que se elimine el archivo. - Jugadora mostrada - buscar cantidad - Jugadora oculta - buscar cantidad + Reproductor visible - buscar cantidad + Reproductor oculto - buscar cantidad Android TV - La cantidad de búsqueda utilizada cuando la jugadora es visible - La cantidad de búsqueda utilizada cuando el jugador está oculto + Tiempo de búsqueda usado (en segundos) cuando el reproductor está visible + Tiempo de búsqueda usado (en segundos) cuando el reproductor está oculto Parar Falló Registro diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 18255b3b..f3d35c19 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -130,7 +130,7 @@ Nouvelle mise à jour trouvée ! \n%s -> %s Épisode spécial - Qualité de visionnage préférée + Qualité de visionnage préférée (WiFi) Taille de la mémoire cache Étendre Non-responsabilité @@ -508,4 +508,19 @@ Mis à jour (ancien vers nouveau) Fichier du mode sans échec trouvé ! \nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé. + Arrêter + Revenir à + Enregistrer + Qualité de visionnage préférée (données mobiles) + Abonné à %s + Démarrer + Test des fournisseurs + Réussi + Désabonné de %s + Redémarrer + Abonné + raw.githubusercontent.com Proxy + Contournements de FAI + L\'épisode %d est sorti ! + Échouer \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index b623ec5d..159542cc 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -553,4 +553,5 @@ raw.githubusercontent.com Proxy Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. + Preferirana kvaliteta gledanja (podatkovna mobilna mreža) \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 84179352..0e383562 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -243,7 +243,7 @@ Jangan tunjukkan lagi Skip Update ini Update - Kualitas tontonan yang lebih diinginkan + Kualitas tontonan yang lebih diinginkan (WIFI) Karakter maksimal judul pemutar video Resolusi pemutar video Ukuran buffer video @@ -551,4 +551,5 @@ Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. Bypass ISP Pulihkan + Nonton dengan kualitas yang di inginkan (Data Seluler) \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d6bdc204..b8e7eb20 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -265,7 +265,7 @@ Non mostrare di nuovo Salta questo aggiornamento Aggiorna - Risoluzione preferita + Qualità di visualizzazione preferita (WiFi) Limita i caratteri del titolo nel player Risoluzione video player Dimensione cache video @@ -550,4 +550,5 @@ Ripristina Aggiornando shows a cui sei iscritto L\'episodio %d è stato rilasciato! + Qualità di visualizzazione preferita (Dati mobili) \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index bbaaec57..558a46ed 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -254,7 +254,7 @@ Nie pokazuj ponownie Pomiń tę aktualizację Aktualizacja - Domyślna jakość + Domyślna jakość (WiFi) Maksymalna ilość znaków w tytule odtwarzacza Rozdzielczość odtwarzacza wideo Rozmiar bufora wideo @@ -531,4 +531,5 @@ Został wydany odcinek %d! Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. + Domyślna jakość (dane mobilne) \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3754de8b..0c846361 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -3,63 +3,63 @@ %s Ep %d %dh %dm %dm - Episódio %d será lançado em + O episódio %d será lançado em Poster - Capa do Episódio + Pôster do episódio Poster - Capa Principal + Pôster Principal Próximo Aleatório Voltar - Trocar Provedor - %d dia(s), %d hora(s) e %d mese(s) + Alterar Provedor + %dd %dh %dm Fonte Resolução Extras - Preview Background + Visualizar plano de fundo Velocidade (%.2fx) Classificado: %.1f Nova atualização encontrada! \n%s -> %s - Enchimento + Preenchimento CloudStream - Reproduzir com CloudStream + Assistir com o CloudStream Início - Pesquisa - Transferências - Opções + Pesquisar + Downloads + Configurações Procurar… - Procurar em %s… - Sem Dados - Mais Opções + Pesquisar %s… + Sem dados + Mais opções Próximo episódio - Géneros - Partilhar - Abrir no Navegador - Saltar Carga + Gêneros + Compartilhar + Abrir no navegador + Pular carregamento Carregando… Assistindo - Em Espera + Em espera Concluído - Abandonado - Planeio Assistir - Nenhuma - Assistindo de Novo - Reproduzir Filme - Reproduzir Livestream + Desistido + Pretendo assistir + Nenhum + Reassistindo + Reproduzir filme + Reproduzir transmissão ao vivo Transmitir Torrent Fontes Legendas - Voltar a tentar ligação… - Voltar Atrás - Reproduzir Episódio - Transferir - Transferido - A Transferir - Transferência em Pausa - Transferência Iniciada - Transferência Falhou - Transferência Cancelada - Transferência Completa + Tentar conexão novamente… + Voltar + Reproduzir episódio + Download + Baixado + Baixando + Download Pausado + Download Iniciado + Falha no Download + Download cancelado + Download concluído Stream Erro a Carregar Links Armazenamento Interno @@ -142,7 +142,7 @@ Arquivo de backup carregado Falha ao restaurar dados do ficheiro %s Dados guardados com sucesso - Permissões de armazenamento em falta, por favor tente de novo + Permissão de armazenamento não encontrada, por favor tente novamente. Erro no backup de %s Procurar Contas @@ -250,15 +250,15 @@ Não mostrar de novo Saltar esta Atualização Atualizar - Qualidade Preferida - Máximo de caracteres do título de vídeos + Qualidade Preferida (WiFi) + Máximo de caracteres do título no player de video Resolução do player de vídeo Tamanho do buffer do vídeo Comprimento do buffer do vídeo Cache do vídeo em disco Limpar cache de vídeo e imagem - Causará travamentos aleatórios se definido muito alto. Não mude se tiver pouca memória RAM, como um Android TV ou um telefone antigo - Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV + Causará travamentos em dispositivos com pouca memória se definido muito alto , como uma Android TV. + Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como uma Android TV. DNS sobre HTTPS Útil para contornar bloqueios do fornecedor de internet Clonar site @@ -363,7 +363,7 @@ Plugin Carregado Plugin Apagado Falha ao carregar %s - Iniciada a transferência %d %s + Download iniciado %d %s… Transferido %d %s com sucesso Tudo %s já transferido Transferência em batch @@ -375,18 +375,22 @@ Transferido: %d Desativado: %d Não transferido: %d - Adicionar um repositório para instalar extensões de sites + O CloudStream não possui sites instalados por padrão. Você precisa instalar os sites a partir de repositórios. +\n +\nDevido a uma restrição sem sentido de direitos autorais (DMCA) pela Sky UK Limited 🤮 não podemos vincular o site do repositório no aplicativo. +\n +\nJunte-se ao nosso Discord ou pesquise online. Ver repositórios da comunidade Lista pública Todas as legendas em maiúsculas Transferir todos os plugins deste repositório\? %s (Desativado) Instalador APK - %d minuto(s) - Reproduzir trailer + %d min + Assistir Trailer Marcar como visto/não visto Reproduzir - Instalar automaticamente todas as extensões dos repositórios cadastrados. + Instalar automaticamente todos os plugins ainda não instalados dos repositórios adicionados. Baixar extensões automaticamente Refazer o processo de configuração -30 @@ -394,9 +398,9 @@ +30 %s %d%s Elenco: %s - Atualização em andamento + Atualização iniciada Log - Alguns aparelhos não possuem suporte para o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. + 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 Iniciar @@ -408,4 +412,121 @@ Ova Anime Player visível - Procurar valor + Instalando atualização do app… + Você tem certeza que deseja sair\? + Versão + Encerramento + Limpar histórico + Abertura + Não + Ordenar por + Sim + Baixando atualização do app… + Episódio %d lançado! + Créditos + Descrição + Tamanho + Parar + Modo seguro ligado + Histórico + Ordenar + Player interno + Autores + Suportado + Idioma + Instalar a extensão primeiro + Playlist HLS + Player de vídeo preferido + Estado + Gestos + Faixas + WP + Cam + Abertura + Selecionar Biblioteca + Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns dias. + VLC + Todas as linguagens + Atualizado (Novo para Antigo) + Inscrito + HDR + Reiniciar + Navegador Web + Atualizado (Antigo para Novo) + Web Video Cast + DVD + Instalador de pacotes + MPV + Remover dos assistidos + Não foi possível instalar a nova versão do aplicativo + Inscrição cancelada em %s + Final misto + Avaliações (Decrescente) + Aplicar ao reiniciar + Referente + Player oculto - Quantidade de Busca + raw.githubusercontent.com Proxy + Blu-ray + Aparência + 1000 ms + SDR + 18+ + Abrir com + Teste de provedor + UHD + Ver informações sobre falha + Aplicativo não encontrado + Reverter + Link para transmitir + Plugins baixados + %d plugins atualizados + Pular %s + Abertura mista + Alfabético (Z a A) + Parece que esta lista está vazia, tente trocar para outra + Inscrito em %s + 4K + Faixas de vídeo + O aplicativo será atualizado ao sair + Atualizando shows inscritos + Alfabético (A a Z) + Avaliações (Crescente) + Parece que a sua biblioteca está vazia :( +\nFaça login em uma conta de biblioteca ou adicione shows à sua biblioteca local + Arquivo de modo de segurança encontrado! +\nNenhuma extensão será carregada na inicialização do app até que o arquivo seja removido. + Contorno do provedor de serviço de internet (ISP) + Links + Recursos do Player + Recursos + Atualizações de aplicativos + Qualidade Preferida (Dados Móveis) + Quantidade de busca (em segundos) usada quando o player de video está visível + Quantidade de busca (em segundos) usada quando o player de video está oculto + Falha ao conectar com GitHub, ativando proxy jsdelivr. + Cache + Android TV + Legendas + %s %s + TS + Cam + Cam + HQ + HD + TC + Web + Nota: %s + Legado + Todas as extensões foram desativadas devido a uma falha para ajudá-lo a encontrar a que está causando o problema. + Recapitular + Mostrar pop-ups para pular abertura/encerramento + Muito texto. Não é possível salvar na área de transferência. + Marcar como assistido + Backup + Extensões + Ações + Layout + Configurações padrão + SD + Faixas de áudio \ No newline at end of file diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index c1119bfc..76852ca4 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -219,4 +219,33 @@ uuuuhhhoouuooog ooaaahhhh uuu ugggg ooo guggg ooh + auuuooohaaaaagh + uuuuuuuh aaaoo o + ooooooouuuua aa aaagh agh + AAAAUUUOH + aoughoooaaaa + oooouuuh + ahaough aaouuuuh-h + auughooo + ooooooa aauoh + aaaaagh oouoo aaaaaaa + aaaaaagh uuohuoh + aaaaaauo agghhhhhhaoouu + uuuuuuuuh + ouaaahh + ooough aaoough aooou %s aaaa + ouooooouuuu oooooo + aaaaaaaaaaahhhgh-aooohoooo + aau aooooghaao + aagh aaaaaaaaaaaa oooh, aaough, ooga oguuu aaaaaaaaaaa ooooooohghh a-a-aaauo + %dmmmmmm.. +\naaaaooughugh + aooohuohaaaa ooooagh + oooooogh-aaaaaogh + guuuaaaahhhhhhhaaa + woooaaahh ahahaaaauu 🦍 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOGGAGHAGHAAA + aoaaaaaoooghhh + oooooh uuaagh + \@string/home_play \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 2812667a..e613cee4 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -318,7 +318,7 @@ Титры Отметить как просмотренное Разрешение видеоплеера - Желаемое качество видео + Предпочтительное качество видео (WiFi) Максимум символов Длинна буфера Кеш видео на диске @@ -518,4 +518,15 @@ Неудачный Прекратить Перезапустить + Вернуться + Подписался на %s + Предпочтительное качество видео (Мобильный интернет) + raw.githubusercontent.com Прокси-сервер + Не удалось подключиться к GitHub. Будет выполнен прокси jsdelivr. + Эпизод %d выпущен! + Обходы провайдера + Обновление подписки на фильмы и сериалы + Обход ограничения доступа к GitHub с помощью jsdelivr может задержать обновления на несколько дней. + Подписные + Отказались от подписки на %s \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index db647b5d..59c65916 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -10,7 +10,7 @@ %dm Poster - \@string/result_poster_img_des + Ảnh bìa Episode Poster Main Poster Next Random @@ -260,18 +260,18 @@ Kiểm tra cập nhật Khóa Thu Phóng - Tuỳ chọn - Tua nhanh + Nguồn + Bỏ qua OP Không hiện lại - Bỏ qua + Bỏ qua bản cập nhật này Cập nhật - Tự động chọn chất lượng phim + Chất lượng xem ưu tiên (WiFi) Kí tự tối đa trên tiêu đề - Định dạng trình phát - Dung lượng video cache + Độ phân giải trình phát video + Kích thước bộ nhớ đệm video Thời lượng bộ nhớ đệm - Dung lượng video cache - Xoá hình ảnh và video + Lưu bộ nhớ đệm video trên ổ cứng + Xoá bộ nhớ đệm hình ảnh và video Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng ram thấp như Android TV. Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. DNS over HTTPS @@ -519,4 +519,9 @@ Có vẻ như danh sách này trống, hãy thử chuyển sang danh sách khác Chữ cái (A đến Z) Chọn Thư viện + Nhật ký + Chất lượng xem ưu tiên (Dữ liệu di động) + Thất bại + Thành công + Bắt đầu \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 626cc0fe..72d62a04 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -286,7 +286,7 @@ 不再显示 跳过此更新 更新 - 首选播放画质 + 首选播放画质(WiFi) 视频播放器标题最多字符 视频播放器标题 视频缓冲大小 @@ -576,4 +576,5 @@ 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 ISP 绕过 还原 + 首选播放画质(移动数据) \ No newline at end of file From e85b31c35ddac86e03cfbd495e01a69cedfe56ff Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 7 Mar 2023 17:36:53 +0100 Subject: [PATCH 083/104] Fixing rouge pixels in settings --- app/src/main/res/values/styles.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 78c62c69..b9648162 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -40,7 +40,6 @@ ?attr/textColor ?attr/grayTextColor - ?attr/grayTextColor ?attr/grayTextColor ?attr/textColor From 37244ab0f74392af6861a4f894ebb1a3de77806a Mon Sep 17 00:00:00 2001 From: PokerFace <117321707+pokerface-bad@users.noreply.github.com> Date: Sat, 11 Mar 2023 02:45:11 +0700 Subject: [PATCH 084/104] Intertal Player: Added MPD support (#402) * added isDash in ExtractorLink --- .../cloudstream3/ui/player/CS3IPlayer.kt | 10 +++++----- .../cloudstream3/utils/ExtractorApi.kt | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) 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 782e3fa4..cb8efe92 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 @@ -1196,10 +1196,10 @@ class CS3IPlayer : IPlayer { HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) } - val mime = if (link.isM3u8) { - MimeTypes.APPLICATION_M3U8 - } else { - MimeTypes.VIDEO_MP4 + val mime = when { + link.isM3u8 -> MimeTypes.APPLICATION_M3U8 + link.isDash -> MimeTypes.APPLICATION_MPD + else -> MimeTypes.VIDEO_MP4 } val mediaItems = if (link is ExtractorLinkPlayList) { @@ -1249,4 +1249,4 @@ class CS3IPlayer : IPlayer { loadOfflinePlayer(context, it) } } -} \ No newline at end of file +} 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 0bced6b2..b03c9fb7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -52,7 +52,7 @@ data class ExtractorLinkPlayList( ) -open class ExtractorLink( +open class ExtractorLink constructor( open val source: String, open val name: String, override val url: String, @@ -62,7 +62,24 @@ open class ExtractorLink( override val headers: Map = mapOf(), /** Used for getExtractorVerifierJob() */ open val extractorData: String? = null, + open val isDash: Boolean = false, ) : VideoDownloadManager.IDownloadableMinimum { + /** + * Old constructor without isDash, allows for backwards compatibility with extensions. + * Should be removed after all extensions have updated their cloudstream.jar + **/ + constructor( + source: String, + name: String, + url: String, + referer: String, + quality: Int, + isM3u8: Boolean = false, + headers: Map = mapOf(), + /** Used for getExtractorVerifierJob() */ + extractorData: String? = null + ) : this(source, name, url, referer, quality, isM3u8, headers, extractorData, false) + override fun toString(): String { return "ExtractorLink(name=$name, url=$url, referer=$referer, isM3u8=$isM3u8)" } From 8b2881f5f64fad7eb29c93af0a3f696798b93d39 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 10 Mar 2023 20:45:19 +0100 Subject: [PATCH 085/104] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 18 +-- app/src/main/res/values-cs/strings.xml | 18 +-- app/src/main/res/values-es/strings.xml | 18 +-- app/src/main/res/values-in/strings.xml | 18 +-- app/src/main/res/values-ms/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 11 +- app/src/main/res/values-pt/strings.xml | 14 +- app/src/main/res/values-ru/strings.xml | 16 +-- app/src/main/res/values-tr/strings.xml | 173 ++++++++++++++----------- app/src/main/res/values-uk/strings.xml | 101 +++++++-------- app/src/main/res/values-zh/strings.xml | 14 +- app/src/main/res/values/strings.xml | 20 +-- 12 files changed, 225 insertions(+), 198 deletions(-) create mode 100644 app/src/main/res/values-ms/strings.xml diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index cfd761e3..ae45465b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -119,16 +119,16 @@ وضع إيغنغرافي يضيف خيار السرعة في المُشغل السحب لتقديم - إسحب إلى اليسار أو اليمين للتحكم في الوقت في مُشغل الفيديو + اسحب من جانب إلى آخر للتحكم في موضعك في مقطع فيديو السحب لتغيير الإعدادات - إسحب على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت + مرر لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت تشغيل الحلقة التالية تلقائيًا تبدأ الحلقة التالية عندما تنتهي الحالية النقر مرتان للتقديم للأمام أو للخلف الضغط مرتان لإيقاف مؤقت - التحكم في مدى تقديم المُشغل + التحكم في مدى تقديم المُشغل(ثوان) إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف - إضغط في الوسط لإيقاف مؤقت + اضغط مرتين في المنتصف للتوقف استخدم سطوع النظام استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن تحديث تقدم المشاهدة @@ -155,7 +155,7 @@ تحديث الإضافات تلقائيًا تنزيل الإضافات تلقائيًا التحديث التلقائي - البحث تلقائيًا عن التحديثات الجديدة عند البداية + ابحث تلقائيا عن التحديثات الجديدة بعد بدء التطبيق. التحديث إلى الاصدارات التجريبية (بيتا) البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط غيت هاب @@ -218,8 +218,8 @@ فيلم مسلسل كرتون - أنمي - اوفا + أنيمي + أوفا تورنت وثائقي دراما آسيوية @@ -284,7 +284,7 @@ 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. عام زر العشوائي - إظهار زر العشوائي على الصفحة الرئيسية + يظهر الزر على الصفحة الرئيسية والذي يمكنه اختيار فيلم عشوائي أو مسلسل تلفزيوني من الصفحة الرئيسية لغات المزود واجهة التطبيق المحتوى المفضل @@ -558,7 +558,7 @@ تجاوز مزود خدمة الإنترنت استرجاع فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. - تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e99e1010..67179b46 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -122,14 +122,14 @@ Rychlostní režim Přidá do přehrávače možnost rychlosti Přejet pro posun - Přejeďte prstem vlevo nebo vpravo pro ovládání času v přehrávači + Přejeďte prstem ze strany na stranu pro ovládání své pozice ve videu Přejet pro změnu nastavení - Přejeďte prstem na levé nebo pravé straně pro změnu jasu nebo hlasitosti + Přejeďte prstem nahoru nebo dolů na levé nebo pravé straně pro změnu jasu nebo hlasitosti Dvojité klepnutí pro posun Dvojité klepnutí pro pozastavení - Množství času k posunu + Množství času k posunu (sekundy) Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad - Klepněte doprostřed pro pozastavení + Klepněte dvakrát doprostřed pro pozastavení Použít systémový jas V přehrávači použít systémov překrytí Aktualizovat postup sledování @@ -151,7 +151,7 @@ Nebude odesílat žádná data Zobrazit výplňové epizody u anime Zobrazit aktualizace aplikace - Při spuštění automaticky zkontrolovat nové aktualizace + Při spuštění aplikace automaticky zkontrolovat nové aktualizace. Aktualizovat na předběžná vydání Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání GitHub @@ -211,8 +211,8 @@ Film Seriál Animovaný - \@string/anime - \@string/ova + Anime + OVA Torrent Dokument Asijské drama @@ -266,7 +266,7 @@ Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí. Obecné Náhodné tlačítko - Zobrazit na domovské stránce náhodné tlačítko + Zobrazit na domovské stránce tlačítko, kterým lze vybrat náhodný film nebo seriál z domovské stránky Jazyk poskytovatelů Rozložení aplikace Preferovaná média @@ -551,6 +551,6 @@ Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. Upřednostněná kvalita sledování (mobilní data) Vrátit zpět - Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 0b195275..5c8ac532 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -51,10 +51,10 @@ Elevado Use esto si los subtítulos se muestran %d ms muy pronto Use esto si los subtítulos se muestran %d ms tarde - Desliza el dedo hacia la izquierda o hacia la derecha para controlar el tiempo en el reproductor de video + Desliza el dedo de lado a lado para controlar la posición en un video Filtrar por idioma de medios preferido Eliminar Closed Captions (CC) de los subtítulos - Cantidad de tiempo de búsqueda en el reproductor (en segundos) + Cantidad de búsquedas del reproductor (segundos) Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura Resolución del reproductor de video MPV @@ -205,16 +205,16 @@ Modo Eigengravy Deslice para avanzar/retroceder Deslice para cambiar la configuración - Deslice el dedo hacia la izquierda o hacia la derecha para cambiar el brillo o el volumen + Deslice hacia arriba o hacia abajo en el lado izquierdo o derecho para cambiar el brillo o el volumen Toca dos veces para buscar Tocar dos veces para pausar Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás - Toque en el medio para pausar + Toque dos veces en el medio para hacer una pausa Usar brillo del sistema Restaurar datos desde el backup Hacer copia de los datos (backup) Archivo de backup cargado - Buscar automáticamente nuevas actualizaciones al inicio + Busque automáticamente nuevas actualizaciones después de iniciar la aplicación. Rehacer el proceso de configuración inicial Mostrar episodio de relleno para Anime Reproducir Episodio @@ -306,7 +306,7 @@ Aspecto Características Botón de Al azar - Muestra un botón de reproducción \"al azar\" en la página de inicio + Muestra un botón de reproducción \"al azar\" en la página de inicio para poelículas y series cuenta Cerrar sesión Cambiar cuenta @@ -363,8 +363,8 @@ Película Serie Dibujo animado - \@string/anime - \@string/ova + Anime + OVA Torrent Documental Drama asiático @@ -525,7 +525,7 @@ ¡Episodio %d publicado! Proxy raw.githubusercontent.com No se ha podido acceder a GitHub, activando el proxy jsdelivr. - Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Con jsdelivr, se puede omitir el bloqueo de GitHub. Puede retrasar las actualizaciones unos días. Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 0e383562..1913868a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -120,14 +120,14 @@ Mode Eigengravy Menambahkan opsi kecepatan di pemutar Geser untuk mengubah waktu - Geser ke kiri atau kanan untuk mengontrol waktu di pemutar video + Geser dari sisi ke sisi untuk mengontrol posisi dalam video Geser untuk mengubah pengaturan - Geser ke sisi kiri atau kanan untuk mengubah pencerahan atau volume + Geser ke atas atau ke bawah di sisi kiri atau kanan untuk mengubah kecerahan atau volume Tekan dua kali untuk mengubah waktu Tekan dua kali untuk menjeda - Jumlah pengubah waktu pemutar + Jumlah pengubah waktu pemutar (Detik) Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang - Tekan di tengah untuk menjeda + Tekan dua kali di tengah untuk menjeda Gunakan pencerahan sistem Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap Update progres tontonan @@ -149,7 +149,7 @@ Tidak mengirim data Tampilkan episode filler untuk anime Tampilkan update aplikasi - Secara otomatis mencari update terbaru saat aplikasi dibuka + Secara otomatis mencari update terbaru setelah aplikasi dibuka. Update ke prarilis Hanya mencari update prarilis daripada rilis penuh Github @@ -209,8 +209,8 @@ Movie Seri Kartun - \@string/anime - \@string/ova + Anime + OVA Torrent Film Dokumenter Drama Asia @@ -264,7 +264,7 @@ 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. Umum Tombol Acak - Tampilkan tombol acak di Beranda + Tampilkan tombol di halaman utama yang dapat memilih seri film atau TV acak dari halaman utama Bahasa provider Tata Letak Aplikasi Media yang lebih diinginkan @@ -548,7 +548,7 @@ Episode %d telah rilis! raw.githubusercontent.com Proksi Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. - Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Mengunakan jsdelivers, bisa melewati pemblokiran GitHub. Mungkin dapat menyebabkan pembaruan tertunda dalam beberapa hari. Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/app/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index c2561914..dd89c34a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Aflevering Poster Hoofdposter Volgende willekeurig @@ -128,14 +128,14 @@ Eigengravy Modus Voegt een snelheidsoptie toe in de speler Swipe to seek - Veeg naar links of rechts om de tijd in de videoplayer te regelen + Veeg naar links of rechts om de tijd in de videospeler te regelen Veeg om instellingen te wijzigen Veeg naar links of rechts om de helderheid of het volume te wijzigen Dubbeltik om te zien Dubbeltik om te pauzeren - Speler zoeken bedrag + Videospeler aantal zoeken Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken - Tik in het midden om te pauzeren + Tik twee keer in het midden om te pauzeren Systeemhelderheid gebruiken Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay Kijkvoortgang bijwerken @@ -405,4 +405,7 @@ Start de volgende episode wanneer deze afgelopen is Volgende episode automatisch afspelen De update is gestart + Bibliotheek + Browser + Logboek \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 0c846361..64ccb903 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -123,16 +123,16 @@ Modo Eigengravy Acrescenta uma opção de velocidade no player Deslize para andar - Deslize para a esq. ou dir. para controlar o tempo no player + Deslize para os lados para controlar a posição em um vídeo Deslize para mudar as configurações - Deslize do lado esq. ou dir. para ajustar brilho ou volume + Deslize para cima ou para baixo, no lado esquerdo ou direito, para ajustar brilho ou volume Reproduzir automaticamente próximo episódio Começa o próximo episódio quando o atual termina Toque duplo para avançar Toque duplo para pôr em pausa - Segundos avançados no player + Tempo de busca no player (Segundos) Toque duplo no lado esq. ou dir. para andar para trás ou para a frente - Toque no meio para pôr em pausa + Toque duas vezes no meio para pausar Usar brilho da sistema Usar brilho do sistema no player em vez de uma sobreposição escura Atualizar progresso @@ -158,7 +158,7 @@ Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa Atualizações de plugin automáticas Mostrar atualizações da app - Procurar novas atualizações automaticamente ao iniciar + Procurar automaticamente por novas atualizações depois de iniciar o app. Atualizar para pré-lançamentos Procura atualizações de pré-lançamento em vez de só lançamentos oficiais Github @@ -273,7 +273,7 @@ Aviso Legal Geral Botão Aleatório - Mostra o botão Aleatório na página inicial + Mostra o botão Aleatório na página inicial, que pode escolher aleatoriamente um filme ou série Idioma dos fornecedores Layout da App Mídia preferida @@ -444,7 +444,7 @@ Cam Abertura Selecionar Biblioteca - Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns dias. + Usando jsdelivr o bloqueio do GitHub pode ser contornado. Pode atrasar atualizações em alguns dias. VLC Todas as linguagens Atualizado (Novo para Antigo) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e613cee4..5295bd35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -142,10 +142,10 @@ Добавляет опцию скорости в проигрывателе Проведите пальцем для поиска Проведите пальцем для изменения настроек - Проведите пальцем по левой или правой стороне для изменения яркости или громкости + Проведите вверх или вниз по левой или правой стороне, чтобы изменить яркость или громкость Автопроиграть следующего серия Поток торрент - Проведите пальцем влево или вправо, чтобы управлять временем в видеоплеере + Проведите пальцем из стороны в сторону, чтобы управлять свое место в видеоролике Начните следующий серию, когда закончится текущий Загружена резервная копия Не удалось восстановить данные из %s @@ -159,7 +159,7 @@ Автоматическое обновление плагинов Автоматическая загрузка плагинов Показать обновления приложения - Автоматически проверять обновления при старте + Автоматически проверять обновления при старте приложения. Обновится до пре-релиза APK установщик Github @@ -227,7 +227,7 @@ Использовано Двойное нажатие для паузы Коснитесь дважды правой или левой стороны для поиска вперед или назад - Нажмите в центре для паузы + Нажмите дважды в центре, чтобы сделать паузу Использовать системную яркость Автоматически синхронизировать текущий прогресс эпизода Ошибка резервного копирования %s @@ -408,8 +408,8 @@ Съешь ещё этих мягких французских булок, да выпей же чаю Рекомендуется Загружено %s - \@нить/аниме - \@нить/ova + Аниме + OVA Этикетка Dub Сайт Функции @@ -493,7 +493,7 @@ Фильтровать по предпочитаемому языку медиа Неверный ID Ссылка на стрим - Отображать рандомную кнопку на Главной странице + Показывает кнопку на главной странице, с помощью которой можно выбрать случайный фильм или сериал с главной страницы Рандомная кнопка Legacy (старый) Веб видеокаст @@ -501,7 +501,7 @@ Перезагрузить ссылки Предпочтительные медиа Опущенные - Объем перемотки плеера + Объем перемотки плеера (секундах) Объем перемотка, используемый, когда плеер виден Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 807716d8..f53bb69d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -117,47 +117,47 @@ Hiç muz verilmedi Otomatik seçilecek dil İndirilecek diller - Alt yazı dili - Varsayılana döndürmek için basılı tut + Altyazı dili + Sıfırlamak için basılı tut Fontları içe aktarmak için %s konumuna yerleştirin İzlemeye devam et Kaldır Daha fazla bilgi \@string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir - Bu sağlayıcı bir torrent. VPN önerilir + Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. Açıklama Konu bulunamadı Açıklama bulunamadı - Logcat\'i göster 🐈 - Resim-içinde-resim - Diğer uygulamaların üzerinde minyatür bir oynatıcıda oynatmaya devam eder + Logcat\'i görüntüle 🐈 + Görüntü içinde görüntü + İçerik diğer uygulamaların üzerinde küçük bir pencerede oynatılmaya devam eder Oynatıcı yeniden boyutlandırma butonu - Siyah sınırları kaldır - Alt yazı + Siyah sınır çizgilerini kaldır + Alt yazılar Oynatıcı alt yazı ayarları - Chromecast alt yazı + Chromecast alt yazıları Chromecast alt yazı ayarları - Eigengravy modu - Oynatıcıya bir hız seçeneği ekle - Gözlemek için kaydır - Zamanı ayarlamak için sağa veya sola kaydır + Eigengrau modu + Oynatıcıya hız seçeneği ekler + Atlamak için kaydır + Zamanı ayarlamak için yanlardan kaydır Ayarları değiştirmek için kaydır - Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla + Sol ve sağ taraftan yukarı kaydırarak ekran parlaklığı ve sesi ayarla Sonraki bölümü otomatik oynat Mevcut bölüm bittiğinde sonraki bölüme başla - Gözlemek için çift tıkla - Durdurmak için çift tıkla - Oynatıcı gözleme miktarı - İleri ve geri atlamak için sağa ve sola çift tıkla - Durdurmak için ortaya tıkla + Çift dokunarak atla + İki kez dokunarak duraklat + Atlanacak süre (Saniye) + İleri ve geri atlamak için sağa ve sola iki kez dokun + Durdurmak için ekranın ortasına çift dokun Sistem parlaklığını kullan - Oynatıcıda karanlık kaplama yerine sistem parlaklığını kullan + Oynatıcıyı karartmak yerine sistem parlaklığını kullan İzleme ilerlemesini güncelle Mevcut bölüm ilerlemesini otomatik güncelle - Yedekten geri yükle - Verileri yedekleyin + Verileri yedekten geri yükle + Verileri yedekle Yedek dosyası yüklendi Geri yükleme başarısız oldu: %s Başarıyla yedeklendi @@ -165,21 +165,21 @@ %s yedeklenirken hata Ara Hesaplar - Güncellemeler ve yedek + Güncellemeler ve yedekleme Bilgi Gelişmiş arama - Sağlayıcılara göre ayrılmış arama sonuçlarını ver + Arama sonuçlarını sağlayıcıya göre ayırır Yalnızca çökmelerle ilgili verileri gönderir - Hiç veri göndermez - Anime için filler bölümleri gösterir + Veri göndermez + Anime için filler bölümleri göster Fragmanları göster Kitsu\'dan posterleri göster - Seçilen video kalitelerini arama sonuçlarında gizle + Seçilen video kalitelerini arama sonuçlarında gösterme Otomatik eklenti güncellemeleri Uygulama güncellemelerini göster - Başlangıçta yeni güncellemeleri otomatik olarak ara - Ön sürümlere güncelle - Sadece tam sürümler yerine ön sürüm güncellemelerini de ara + Uygulama başlatıldıktan sonra güncellemeleri otomatik olarak kontrol et. + Deneysel sürümlere güncelle + Yalnızca tam sürümler yerine deneysel güncellemeleri de ara GitHub Aynı geliştiriciler tarafından LightNovel uygulaması Aynı geliştiriciler tarafından anime uygulaması @@ -191,8 +191,8 @@ Bağlantı bulunamadı Bağlantı panoya kopyalandı Bölümü oynat - Varsayılana sıfırla - Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek + Varsayılan değere sıfırla + Üzgünüz, uygulama çöktü. Geliştiricilere anonim bir hata raporu gönderilecek Sezon %s %d%s Sezon yok @@ -210,8 +210,8 @@ Sürdür -30 +30 - %s dosyası tamamen silinecek -\nEmins misiniz\? + %s tamamen silinecek +\nEmin misiniz\? %dm \nkaldı Devam ediyor @@ -236,9 +236,9 @@ Torrentler Belgeseller OVA - Asya dramaları + Asya dizileri Canlı yayınlar - NSFW + +18 Diğerleri Film @@ -248,9 +248,9 @@ \@string/ova Torrent Belgesel - Asya draması + Asya dizisi Canlı yayın - NSFW + +18 Video Kaynak hatası Sunucu hatası @@ -259,10 +259,10 @@ İndirme hatası, depolama izinlerini kontrol edin Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla - Uygulamada oynat - %s\'deda oynat + Burada oynat + %s üzerinden oynat Tarayıcıda oynat - Linki kopyala + Bağlantıyı kopyala Otomatik indir Şu kaynaktan indir Bağlantıları yenile @@ -281,22 +281,22 @@ Kilitle Yeniden boyutlandır Kaynak - OP\'yi geç + Jeneriği geç Bir daha gösterme Bu güncellemeyi atla Güncelle - Tercih edilen izleme kalitesi - Oynatıcıdaki maksimum başlık karakter sayısı - Oynatıcının üst tarafındaki öğeler + Tercih edilen görüntü kalitesi (WiFi) + Video oynatıcı başlığı karakter üst sınırı + Oynatıcının çözünürlüğü Video arabelleği boyutu Video arabelleği uzunluğu - Diskteki video önbelleği + Hafızadaki video önbelleği Video ve resim önbelleğini temizle - Android TV gibi düşük belleğe sahip cihazlarda çok yükseğe ayarlanırsa çökmelere neden olur. - Çok yükseğe ayarlanırsa, Android TV cihazları gibi düşük depolama alanına sahip sistemlerde sorunlara neden olabilir. - HTTPS üzerinden DNS - ISP bloklarını atlatmak için kullanışlıdır - Klon site + Çok yükseğe ayarlanırsa düşük belleğe sahip cihazlarda çökmelere neden olur (örn. Android TV). + Çok yükseğe ayarlanırsa düşük depolama alanına sahip sistemlerde sorunlara neden olur (örn. Android TV). + HTTPS üzerinden DNS (DoH) + İnternet Servis Sağlayıcısı (İSS) kısıtlamalarını aşmak için kullanışlıdır + Siteyi kopyala Siteyi kaldır Farklı bir URL ile mevcut bir sitenin klonunu ekleyin İndirme konumu @@ -305,16 +305,16 @@ Ekrana sığdır Uzat Yakınlaştır - Disclaimer + Yasal Uyarı legal_notice_key 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. Genel - Rastgele butonu - Ana sayfada rastgele butonunu göster + Rastgele İçerik + Ana sayfada rastgele bir film veya dizi seçen bir tuş gösterir Sağlayıcı dilleri Uygulama düzeni Tercih edilen medya - Desteklenen sağlayıcılarda NSFW\'yi etkinleştir + Desteklenen sağlayıcılarda +18 içeriği etkinleştir Alt yazı kodlaması Sağlayıcılar Düzen @@ -336,7 +336,7 @@ hello@world.com 127.0.0.1 MyCoolSite - example.com + ornek.com Dil kodu (tr) Hiçbiri Normal @@ -376,7 +376,7 @@ Alt yazı senkronu 1000 ms Alt yazı gecikmesi - Alt yazılar %d ms erken gözüküyorsa bunu kullanın + Alt yazılar %d ms erken görüntüleniyorsa bunu kullanın Alt yazılar %d ms geç gözüküyorsa bunu kullanın Alt yazı gecikmesi yok Pijamalı hasta yağız şoföre çabucak güvendi Önerilen - %s yüklendi + %s eklendi Dosyadan yükle İnternetten yükle İndirilen dosya @@ -422,10 +422,10 @@ Geçersiz veri Geçersiz URL Hata - Alt yazılardan seçmeli alt yazıyı kaldır + Alt yazılardan seçmeli alt yazıyı (CC) kaldır Alt yazılardaki şişkinliği kaldır Tercih edilen medya diline göre filtrele - Ekstralar + Ek içerikler Fragman Yayına bağlan Yönlendiren @@ -433,7 +433,7 @@ Videoları bu dillerde izle Geri Kurulumu atla - Cihazınıza uygun görünümü seçin + Cihazınıza uygun uygulama görünümünü seçin Çökme raporları Ne izlemek istiyorsunuz Bitti @@ -445,7 +445,7 @@ Eklenti silindi %s yüklenemedi +18 - %d %s … indirilmeye başlandı + %d %s indirilmeye başlandı… %d %s indirildi %s\'nin tamamı zaten indirildi Toplu indir @@ -477,7 +477,7 @@ Çökme bilgisini göster Puan: %s Açıklama - Versiyon + Sürüm Durum Boyut Geliştiriciler @@ -499,14 +499,14 @@ Fragmanı oynat Eklenen depolardan henüz yüklenmemiş tüm eklentileri otomatik olarak yükleyin. Güncelleme başladı - Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemele yüklenmezse eski seçeneği deneyin. + Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemeler yüklenmezse eski seçeneği deneyin. Eklentileri otomatik olarak indir APK indirici - Linkler + Bağlantılar Uygulama güncellemeleri Yedek Oynatıcı özellikleri - Altyazılar + Alt yazılar Düzen Varsayılanlar Eklentiler @@ -531,22 +531,22 @@ İzlenenlerden kaldır Karışık son Karışık başlangıç - Kredi + Katkıda Bulunanlar Giriş Eklenti İndirildi - Aksiyonlar - Açma/bitiş için atlama açılır pencerelerini göster + Eylemler + Açılış/bitiş için atlama açılır pencerelerini göster Çok fazla metin. Panoya kaydedilemiyor. Kütüphane Tarayıcı Görünüşe göre kütüphaneniz boş :( -\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize gösteri ekleyin +\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize içerik ekleyin Güvenli mod dosyası bulundu! \nDosya kaldırılana kadar başlangıçta herhangi bir uzantı yüklenmiyor. Sırala Sırala - Güncel (Yeniden Eskiye) - Güncel (Eskiden Yeniye) + Güncellenme (Yeniden Eskiye) + Güncellenme (Eskiden Yeniye) Alfabetik (A\'dan Z’ye) Alfabetik (Z - A) Kütüphane Seçin @@ -554,4 +554,27 @@ Görünüşe göre bu liste boş, başka bir listeye geçmeyi deneyin Derecelendirme (Yüksekten Düşüğe) Derecelendirme (Düşükten Yükseğe) + Yeniden başlat + Oynatıcı gizlenmişken atlanacak süre + İSS Kısıtlamaları + GitHub\'a ulaşılamadı, jsdelivr vekil sunucusu etkinleştiriliyor. + Başlat + Başarılı oldu + raw.githubusercontent.com vekil sunucusu (proxy) + Tercih edilen görüntü kalitesi (Mobil veri) + Oynatıcı görünürken atlanacak süre + Oynatıcı gizli durumdayken atlanacak süre miktarı + jsdelivr kullanarak GitHub kısıtlamasını aşar. Güncellemeler birkaç gün gecikebilir. + Android TV + Yeni bölüm %d yayınlandı! + Sağlayıcıyı kontrol et + Başarısız oldu + Durdur + Geri al + Abone olunan gösteriler güncelleniyor + Abone olunan + %s kanalına abone olundu + %s kanalı aboneliğinden çıkıldı + Günlük + Oynatıcı görünür durumdayken atlanacak süre miktarı \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a676b583..dc7a452e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,13 +1,13 @@ Постер - Постер епізоду + Постер до епізоду Завантаження скасовано - Змінити постачальника + Змінити провайдера Назад Рейтинг: %.1f Актори: %s - Епізод %d буде випущено через + Епізод %d вийде через Poster %s Еп. %d %dд %dгод %dхв @@ -15,14 +15,14 @@ %dхв Головний постер Наступний випадковий - Перегляд фону + Попередній перегляд фону Швидкість (%.2fx) - Нове оновлення знайдено! + Знайдено нове оновлення! \n%s -> %s Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук %s… Дані відсутні @@ -37,7 +37,7 @@ Покинуто Переглянути фільм Переглянути трейлер - Переглянути торрент + Трансляція через торрент Повторити підключення… Назад Переглянути епізод @@ -64,7 +64,7 @@ Тип контуру Шрифт Розмір шрифту - Пошук за допомогою постачальників + Пошук за допомогою провайдерів Пошук за типами Бананів немає Автовибір мови @@ -75,25 +75,25 @@ Продовжити перегляд Вилучити Детальніше - Цей постачальник є торрентом, рекомендується VPN + Цей провайдер є торрентом, рекомендується VPN Опис Сюжет не знайдено Опис не знайдено Показати Logcat 🐈 - Продовження відтворення в мініатюрному плеєрі поверх інших програм + Продовження відтворення в мініатюрному плеєрі поверх інших застосунків Прибирає чорні рамки Субтитри Субтитри Chromecast Налаштування субтитрів Chromecast Режим Eigengravy Проведіть пальцем, щоб змінити налаштування - Проведіть пальцем ліворуч або праворуч, щоб змінити яскравість або гучність + Проведіть пальцем вгору або вниз ліворуч або праворуч, щоб змінити яскравість або гучність Відтворення наступного епізоду після закінчення поточного Головна CloudStream Філер Програти в CloudStream - Потік + Трансляція Переглядаю Поділитися Відкладено @@ -121,11 +121,11 @@ Колір тексту Колір контуру Автовідтворення наступного епізоду - Проведіть пальцем ліворуч або праворуч, щоб керувати часом у відеоплеєрі + Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play - Для коректної роботи цього постачальника може знадобитися VPN + Для коректної роботи цього провайдера може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці Налаштування субтитрів плеєра @@ -133,8 +133,8 @@ Проведіть пальцем, щоб перемотати Двічі торкніться, щоб перемотати Двічі торкніться для паузи - Крок перемотки - Натисніть посередині, щоб поставити на паузу + Крок перемотки (Секунди) + Натисніть двічі посередині, щоб призупинити Використовувати яскравість системи Оновити прогрес перегляду Відновлення даних з резервної копії @@ -147,23 +147,23 @@ Оновлення та резервне копіювання Інформація Розширений пошук - Надає результати пошуку, розділені за постачальниками + Надає результати пошуку, розділені за провайдерами Надсилає дані лише про збої Не надсилає даних - Показати заповнюючий епізод для аніме + Показати філерний епізод для аніме Показати трейлери Приховати вибрану якість відео в результатах пошуку Автоматичне завантаження плагінів - Показати оновлення програми + Показати оновлення застосунку Повторний процес налаштування - Пошук лише попередніх оновлень, а не повних релізів + Пошук лише бета-оновлень, а не повних релізів Встановлювач APK Github Застосунок для легких новел від тих же розробників Застосунок для аніме від тих же розробників Дайте бананів розробникам - Мова програми - Цей постачальник не має підтримки Chromecast + Мова застосунку + Цей провайдер не має підтримки Chromecast Посилань не знайдено Переглянути епізод Скинути до значення за замовчуванням @@ -180,7 +180,7 @@ \nВи впевнені\? %dхв \nзалишилося - Триває + Виходить Завершено Рейтинг Тривалість @@ -189,7 +189,7 @@ За замовчуванням Вільно Зайнято - Програма + Застосунок Телесеріали Мультфільми Аніме @@ -208,7 +208,7 @@ Віддалена помилка Помилка рендеринга Дзеркало Chromecast - Переглянути в програмі + Переглянути в застосунку Переглянути в %s Автозавантаження Завантажити дзеркало @@ -230,7 +230,7 @@ Показати постери від Kitsu Автоматичне оновлення плагінів Автоматично встановлювати всі ще не встановлені плагіни з доданих репозиторіїв. - Автоматичний пошук нових оновлень при запуску + Автоматично шукати нові оновлення після запуску застосунку. Оновлення до бета-версій Посилання скопійовано в буфер обміну Деякі телефони не підтримують новий інсталятор пакетів. Спробуйте стару версію, якщо оновлення не встановлюються. @@ -255,7 +255,7 @@ Документальні фільми NSFW Фільм - \@string/ova + OVA Торрент Мітка якості NSFW @@ -273,7 +273,7 @@ Заповнити Збільшити Доріжки - Оновлення програми + Оновлення застосунку Кеш Жести Особливості плеєра @@ -283,16 +283,16 @@ Особливості Загальне Випадкова кнопка - Показати випадкову кнопку на Головній сторінці - Мови постачальника - Макет програми + Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці + Мови провайдера + Макет застосунку Бажані медіа Авто Макет телевізора Макет телефону Макет емулятора Основний колір - Тема програми + Тема застосунку Розташування назви постера Розмістіть назву під постером пароль123 @@ -363,7 +363,7 @@ Кодування субтитрів Включити NSFW на підтримуваних постачальників Макет - Постачальники + Провайдери example.com %s %s Депресивний @@ -429,7 +429,7 @@ Оновлено %d плагіни За замовчуванням в CloudStream не встановлені сайти. Вам потрібно встановити сайти з репозиторіїв. \n -\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунку. +\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунок. \n \nПриєднуйтесь до нашого Discord або шукайте в інтернеті. Переглянути репозиторії спільноти @@ -451,28 +451,28 @@ Вбудований плеєр VLC MPV - Відтворення веб-відео - Веб-браузер - Кінець + Відтворення вебвідео + Веббраузер + Ендінґ Коротке повторення Пропустити %s - Змішаний кінець + Змішаний ендінґ Подяки - Опенінг + Опенінґ Вступ Очистити історію Історія - Показувати спливаючі вікна для опенінгу/кінця + Показувати спливаючі вікна для опенінґу/ендінґу Забагато тексту. Не вдалося зберегти в буфер обміну. Позначити як переглянуте Ви впевнені що хочете вийти\? Так Ні - Установлення оновлення програми… - Не вдалося встановити нову версію програми + Встановлення оновлення застосунку… + Не вдалося встановити нову версію застосунку Старий - Інсталятор пакетів - Програму буде оновлено після виходу + Встановлювач пакетів + Застосунок буде оновлено після виходу Це також призведе до видалення всіх плагінів репозиторію Всі мови Назад @@ -484,10 +484,10 @@ Бажаний відеоплеєр Увімкнено безпечний режим Автори - Завантаження оновлення програми… + Завантаження оновлення застосунку… Усі розширення вимкнено через збій, щоб допомогти вам знайти те, що спричиняє проблеми. - Програму не знайдено - Змішаний опенінг + Застосунок не знайдено + Змішаний опенінґ Видалити з переглянутого За оновленням (від старого до нового) За оновленням (від нового до старого) @@ -517,17 +517,16 @@ Журнал Старт Стоп - Тест постачальника + Тест провайдер Оновлення підписаних шоу Підписано Підписано на %s Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com -\nProxy + raw.githubusercontent.com Проксі Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP - Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 72d62a04..a14b87cc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -142,14 +142,14 @@ 倍速模式 在播放器中添加播放速度选项 滑动控制进度 - 左右滑动控制播放进度 + 左右滑动以控制视频中的位置 滑动更改设置 上下滑动修改亮度或音量 自动播放下一集 播放完毕后播放下一集 双击控制进度 双击暂停 - 双击控制进度时间 + 双击控制进度时间 (秒) 在左右侧双击快进或快退 双击中间暂停 使用系统亮度 @@ -178,7 +178,7 @@ 自动更新插件 自动下载插件 显示应用更新 - 启动时自动搜索更新 + 启动应用后自动搜索更新。 更新至预览版 搜索预览版更新替代仅搜索完整版本 Github @@ -245,8 +245,8 @@ 电影 电视剧 卡通 - \@string/anime - \@string/ova + 动漫 + OVA 种子 纪录片 亚洲剧 @@ -311,7 +311,7 @@ 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. 通用 随机按钮 - 在主页中显示随机按钮 + 在主页上显示按钮,可以从主页上随机选择电影或电视剧 片源语言 应用布局 首选类型 @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49380b5e..911c0d07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,17 +210,17 @@ Eigengravy Mode Adds a speed option in the player Swipe to seek - Swipe left or right to control time in the videoplayer + Swipe from side to side to control your position in a video Swipe to change settings - Swipe on the left or right side to change brightness or volume + Slide up or down on the left or right side to change brightness or volume Autoplay next episode Start the next episode when the current one ends Double tap to seek Double tap to pause - Player seek amount + Player seek amount (Seconds) Tap twice on the right or left side to seek forwards or backwards - Tap in the middle to pause + Tap twice in the middle to pause Use system brightness Use system brightness in the app player instead of a dark overlay @@ -251,7 +251,7 @@ Automatically download plugins Automatically install all not yet installed plugins from added repositories. Show app updates - Automatically search for new updates on start + Automatically search for new updates after starting the app. Redo setup process Update to prereleases Search for prerelease updates instead of full releases only @@ -324,8 +324,8 @@ Movie Series Cartoon - @string/anime - @string/ova + Anime + OVA Torrent Documentary Asian Drama @@ -383,7 +383,7 @@ Useful for bypassing ISP blocks raw.githubusercontent.com Proxy Failed to reach GitHub, enabling jsdelivr proxy. - Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. + Using jsdelivr, GitHub blocking can be bypassed. May delay updates by a few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -428,7 +428,7 @@ Features General Random Button - Show random button on Homepage + Shows button on Homepage which can choose a random movie or TV series from the Homepage Provider languages App Layout Preferred media @@ -657,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - + \ No newline at end of file From fab55d82c480c2de7a630b567c94e44fbc30ac41 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 9 Mar 2023 22:35:44 +0100 Subject: [PATCH 086/104] Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 18 +-- app/src/main/res/values-cs/strings.xml | 18 +-- app/src/main/res/values-es/strings.xml | 18 +-- app/src/main/res/values-in/strings.xml | 18 +-- app/src/main/res/values-ms/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 11 +- app/src/main/res/values-pt/strings.xml | 14 +- app/src/main/res/values-ru/strings.xml | 16 +-- app/src/main/res/values-tr/strings.xml | 173 ++++++++++++++----------- app/src/main/res/values-uk/strings.xml | 33 +++-- app/src/main/res/values-zh/strings.xml | 14 +- app/src/main/res/values/strings.xml | 20 +-- 12 files changed, 191 insertions(+), 164 deletions(-) create mode 100644 app/src/main/res/values-ms/strings.xml diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index cfd761e3..ae45465b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -119,16 +119,16 @@ وضع إيغنغرافي يضيف خيار السرعة في المُشغل السحب لتقديم - إسحب إلى اليسار أو اليمين للتحكم في الوقت في مُشغل الفيديو + اسحب من جانب إلى آخر للتحكم في موضعك في مقطع فيديو السحب لتغيير الإعدادات - إسحب على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت + مرر لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت تشغيل الحلقة التالية تلقائيًا تبدأ الحلقة التالية عندما تنتهي الحالية النقر مرتان للتقديم للأمام أو للخلف الضغط مرتان لإيقاف مؤقت - التحكم في مدى تقديم المُشغل + التحكم في مدى تقديم المُشغل(ثوان) إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف - إضغط في الوسط لإيقاف مؤقت + اضغط مرتين في المنتصف للتوقف استخدم سطوع النظام استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن تحديث تقدم المشاهدة @@ -155,7 +155,7 @@ تحديث الإضافات تلقائيًا تنزيل الإضافات تلقائيًا التحديث التلقائي - البحث تلقائيًا عن التحديثات الجديدة عند البداية + ابحث تلقائيا عن التحديثات الجديدة بعد بدء التطبيق. التحديث إلى الاصدارات التجريبية (بيتا) البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط غيت هاب @@ -218,8 +218,8 @@ فيلم مسلسل كرتون - أنمي - اوفا + أنيمي + أوفا تورنت وثائقي دراما آسيوية @@ -284,7 +284,7 @@ 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. عام زر العشوائي - إظهار زر العشوائي على الصفحة الرئيسية + يظهر الزر على الصفحة الرئيسية والذي يمكنه اختيار فيلم عشوائي أو مسلسل تلفزيوني من الصفحة الرئيسية لغات المزود واجهة التطبيق المحتوى المفضل @@ -558,7 +558,7 @@ تجاوز مزود خدمة الإنترنت استرجاع فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. - تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) \ No newline at end of file diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index e99e1010..67179b46 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -122,14 +122,14 @@ Rychlostní režim Přidá do přehrávače možnost rychlosti Přejet pro posun - Přejeďte prstem vlevo nebo vpravo pro ovládání času v přehrávači + Přejeďte prstem ze strany na stranu pro ovládání své pozice ve videu Přejet pro změnu nastavení - Přejeďte prstem na levé nebo pravé straně pro změnu jasu nebo hlasitosti + Přejeďte prstem nahoru nebo dolů na levé nebo pravé straně pro změnu jasu nebo hlasitosti Dvojité klepnutí pro posun Dvojité klepnutí pro pozastavení - Množství času k posunu + Množství času k posunu (sekundy) Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad - Klepněte doprostřed pro pozastavení + Klepněte dvakrát doprostřed pro pozastavení Použít systémový jas V přehrávači použít systémov překrytí Aktualizovat postup sledování @@ -151,7 +151,7 @@ Nebude odesílat žádná data Zobrazit výplňové epizody u anime Zobrazit aktualizace aplikace - Při spuštění automaticky zkontrolovat nové aktualizace + Při spuštění aplikace automaticky zkontrolovat nové aktualizace. Aktualizovat na předběžná vydání Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání GitHub @@ -211,8 +211,8 @@ Film Seriál Animovaný - \@string/anime - \@string/ova + Anime + OVA Torrent Dokument Asijské drama @@ -266,7 +266,7 @@ Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí. Obecné Náhodné tlačítko - Zobrazit na domovské stránce náhodné tlačítko + Zobrazit na domovské stránce tlačítko, kterým lze vybrat náhodný film nebo seriál z domovské stránky Jazyk poskytovatelů Rozložení aplikace Preferovaná média @@ -551,6 +551,6 @@ Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. Upřednostněná kvalita sledování (mobilní data) Vrátit zpět - Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 0b195275..5c8ac532 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -51,10 +51,10 @@ Elevado Use esto si los subtítulos se muestran %d ms muy pronto Use esto si los subtítulos se muestran %d ms tarde - Desliza el dedo hacia la izquierda o hacia la derecha para controlar el tiempo en el reproductor de video + Desliza el dedo de lado a lado para controlar la posición en un video Filtrar por idioma de medios preferido Eliminar Closed Captions (CC) de los subtítulos - Cantidad de tiempo de búsqueda en el reproductor (en segundos) + Cantidad de búsquedas del reproductor (segundos) Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura Resolución del reproductor de video MPV @@ -205,16 +205,16 @@ Modo Eigengravy Deslice para avanzar/retroceder Deslice para cambiar la configuración - Deslice el dedo hacia la izquierda o hacia la derecha para cambiar el brillo o el volumen + Deslice hacia arriba o hacia abajo en el lado izquierdo o derecho para cambiar el brillo o el volumen Toca dos veces para buscar Tocar dos veces para pausar Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás - Toque en el medio para pausar + Toque dos veces en el medio para hacer una pausa Usar brillo del sistema Restaurar datos desde el backup Hacer copia de los datos (backup) Archivo de backup cargado - Buscar automáticamente nuevas actualizaciones al inicio + Busque automáticamente nuevas actualizaciones después de iniciar la aplicación. Rehacer el proceso de configuración inicial Mostrar episodio de relleno para Anime Reproducir Episodio @@ -306,7 +306,7 @@ Aspecto Características Botón de Al azar - Muestra un botón de reproducción \"al azar\" en la página de inicio + Muestra un botón de reproducción \"al azar\" en la página de inicio para poelículas y series cuenta Cerrar sesión Cambiar cuenta @@ -363,8 +363,8 @@ Película Serie Dibujo animado - \@string/anime - \@string/ova + Anime + OVA Torrent Documental Drama asiático @@ -525,7 +525,7 @@ ¡Episodio %d publicado! Proxy raw.githubusercontent.com No se ha podido acceder a GitHub, activando el proxy jsdelivr. - Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Con jsdelivr, se puede omitir el bloqueo de GitHub. Puede retrasar las actualizaciones unos días. Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 0e383562..1913868a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -120,14 +120,14 @@ Mode Eigengravy Menambahkan opsi kecepatan di pemutar Geser untuk mengubah waktu - Geser ke kiri atau kanan untuk mengontrol waktu di pemutar video + Geser dari sisi ke sisi untuk mengontrol posisi dalam video Geser untuk mengubah pengaturan - Geser ke sisi kiri atau kanan untuk mengubah pencerahan atau volume + Geser ke atas atau ke bawah di sisi kiri atau kanan untuk mengubah kecerahan atau volume Tekan dua kali untuk mengubah waktu Tekan dua kali untuk menjeda - Jumlah pengubah waktu pemutar + Jumlah pengubah waktu pemutar (Detik) Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang - Tekan di tengah untuk menjeda + Tekan dua kali di tengah untuk menjeda Gunakan pencerahan sistem Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap Update progres tontonan @@ -149,7 +149,7 @@ Tidak mengirim data Tampilkan episode filler untuk anime Tampilkan update aplikasi - Secara otomatis mencari update terbaru saat aplikasi dibuka + Secara otomatis mencari update terbaru setelah aplikasi dibuka. Update ke prarilis Hanya mencari update prarilis daripada rilis penuh Github @@ -209,8 +209,8 @@ Movie Seri Kartun - \@string/anime - \@string/ova + Anime + OVA Torrent Film Dokumenter Drama Asia @@ -264,7 +264,7 @@ 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. Umum Tombol Acak - Tampilkan tombol acak di Beranda + Tampilkan tombol di halaman utama yang dapat memilih seri film atau TV acak dari halaman utama Bahasa provider Tata Letak Aplikasi Media yang lebih diinginkan @@ -548,7 +548,7 @@ Episode %d telah rilis! raw.githubusercontent.com Proksi Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. - Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Mengunakan jsdelivers, bisa melewati pemblokiran GitHub. Mungkin dapat menyebabkan pembaruan tertunda dalam beberapa hari. Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/app/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index c2561914..dd89c34a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Aflevering Poster Hoofdposter Volgende willekeurig @@ -128,14 +128,14 @@ Eigengravy Modus Voegt een snelheidsoptie toe in de speler Swipe to seek - Veeg naar links of rechts om de tijd in de videoplayer te regelen + Veeg naar links of rechts om de tijd in de videospeler te regelen Veeg om instellingen te wijzigen Veeg naar links of rechts om de helderheid of het volume te wijzigen Dubbeltik om te zien Dubbeltik om te pauzeren - Speler zoeken bedrag + Videospeler aantal zoeken Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken - Tik in het midden om te pauzeren + Tik twee keer in het midden om te pauzeren Systeemhelderheid gebruiken Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay Kijkvoortgang bijwerken @@ -405,4 +405,7 @@ Start de volgende episode wanneer deze afgelopen is Volgende episode automatisch afspelen De update is gestart + Bibliotheek + Browser + Logboek \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 0c846361..64ccb903 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -123,16 +123,16 @@ Modo Eigengravy Acrescenta uma opção de velocidade no player Deslize para andar - Deslize para a esq. ou dir. para controlar o tempo no player + Deslize para os lados para controlar a posição em um vídeo Deslize para mudar as configurações - Deslize do lado esq. ou dir. para ajustar brilho ou volume + Deslize para cima ou para baixo, no lado esquerdo ou direito, para ajustar brilho ou volume Reproduzir automaticamente próximo episódio Começa o próximo episódio quando o atual termina Toque duplo para avançar Toque duplo para pôr em pausa - Segundos avançados no player + Tempo de busca no player (Segundos) Toque duplo no lado esq. ou dir. para andar para trás ou para a frente - Toque no meio para pôr em pausa + Toque duas vezes no meio para pausar Usar brilho da sistema Usar brilho do sistema no player em vez de uma sobreposição escura Atualizar progresso @@ -158,7 +158,7 @@ Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa Atualizações de plugin automáticas Mostrar atualizações da app - Procurar novas atualizações automaticamente ao iniciar + Procurar automaticamente por novas atualizações depois de iniciar o app. Atualizar para pré-lançamentos Procura atualizações de pré-lançamento em vez de só lançamentos oficiais Github @@ -273,7 +273,7 @@ Aviso Legal Geral Botão Aleatório - Mostra o botão Aleatório na página inicial + Mostra o botão Aleatório na página inicial, que pode escolher aleatoriamente um filme ou série Idioma dos fornecedores Layout da App Mídia preferida @@ -444,7 +444,7 @@ Cam Abertura Selecionar Biblioteca - Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns dias. + Usando jsdelivr o bloqueio do GitHub pode ser contornado. Pode atrasar atualizações em alguns dias. VLC Todas as linguagens Atualizado (Novo para Antigo) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e613cee4..5295bd35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -142,10 +142,10 @@ Добавляет опцию скорости в проигрывателе Проведите пальцем для поиска Проведите пальцем для изменения настроек - Проведите пальцем по левой или правой стороне для изменения яркости или громкости + Проведите вверх или вниз по левой или правой стороне, чтобы изменить яркость или громкость Автопроиграть следующего серия Поток торрент - Проведите пальцем влево или вправо, чтобы управлять временем в видеоплеере + Проведите пальцем из стороны в сторону, чтобы управлять свое место в видеоролике Начните следующий серию, когда закончится текущий Загружена резервная копия Не удалось восстановить данные из %s @@ -159,7 +159,7 @@ Автоматическое обновление плагинов Автоматическая загрузка плагинов Показать обновления приложения - Автоматически проверять обновления при старте + Автоматически проверять обновления при старте приложения. Обновится до пре-релиза APK установщик Github @@ -227,7 +227,7 @@ Использовано Двойное нажатие для паузы Коснитесь дважды правой или левой стороны для поиска вперед или назад - Нажмите в центре для паузы + Нажмите дважды в центре, чтобы сделать паузу Использовать системную яркость Автоматически синхронизировать текущий прогресс эпизода Ошибка резервного копирования %s @@ -408,8 +408,8 @@ Съешь ещё этих мягких французских булок, да выпей же чаю Рекомендуется Загружено %s - \@нить/аниме - \@нить/ova + Аниме + OVA Этикетка Dub Сайт Функции @@ -493,7 +493,7 @@ Фильтровать по предпочитаемому языку медиа Неверный ID Ссылка на стрим - Отображать рандомную кнопку на Главной странице + Показывает кнопку на главной странице, с помощью которой можно выбрать случайный фильм или сериал с главной страницы Рандомная кнопка Legacy (старый) Веб видеокаст @@ -501,7 +501,7 @@ Перезагрузить ссылки Предпочтительные медиа Опущенные - Объем перемотки плеера + Объем перемотки плеера (секундах) Объем перемотка, используемый, когда плеер виден Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 807716d8..f53bb69d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -117,47 +117,47 @@ Hiç muz verilmedi Otomatik seçilecek dil İndirilecek diller - Alt yazı dili - Varsayılana döndürmek için basılı tut + Altyazı dili + Sıfırlamak için basılı tut Fontları içe aktarmak için %s konumuna yerleştirin İzlemeye devam et Kaldır Daha fazla bilgi \@string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir - Bu sağlayıcı bir torrent. VPN önerilir + Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. Açıklama Konu bulunamadı Açıklama bulunamadı - Logcat\'i göster 🐈 - Resim-içinde-resim - Diğer uygulamaların üzerinde minyatür bir oynatıcıda oynatmaya devam eder + Logcat\'i görüntüle 🐈 + Görüntü içinde görüntü + İçerik diğer uygulamaların üzerinde küçük bir pencerede oynatılmaya devam eder Oynatıcı yeniden boyutlandırma butonu - Siyah sınırları kaldır - Alt yazı + Siyah sınır çizgilerini kaldır + Alt yazılar Oynatıcı alt yazı ayarları - Chromecast alt yazı + Chromecast alt yazıları Chromecast alt yazı ayarları - Eigengravy modu - Oynatıcıya bir hız seçeneği ekle - Gözlemek için kaydır - Zamanı ayarlamak için sağa veya sola kaydır + Eigengrau modu + Oynatıcıya hız seçeneği ekler + Atlamak için kaydır + Zamanı ayarlamak için yanlardan kaydır Ayarları değiştirmek için kaydır - Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla + Sol ve sağ taraftan yukarı kaydırarak ekran parlaklığı ve sesi ayarla Sonraki bölümü otomatik oynat Mevcut bölüm bittiğinde sonraki bölüme başla - Gözlemek için çift tıkla - Durdurmak için çift tıkla - Oynatıcı gözleme miktarı - İleri ve geri atlamak için sağa ve sola çift tıkla - Durdurmak için ortaya tıkla + Çift dokunarak atla + İki kez dokunarak duraklat + Atlanacak süre (Saniye) + İleri ve geri atlamak için sağa ve sola iki kez dokun + Durdurmak için ekranın ortasına çift dokun Sistem parlaklığını kullan - Oynatıcıda karanlık kaplama yerine sistem parlaklığını kullan + Oynatıcıyı karartmak yerine sistem parlaklığını kullan İzleme ilerlemesini güncelle Mevcut bölüm ilerlemesini otomatik güncelle - Yedekten geri yükle - Verileri yedekleyin + Verileri yedekten geri yükle + Verileri yedekle Yedek dosyası yüklendi Geri yükleme başarısız oldu: %s Başarıyla yedeklendi @@ -165,21 +165,21 @@ %s yedeklenirken hata Ara Hesaplar - Güncellemeler ve yedek + Güncellemeler ve yedekleme Bilgi Gelişmiş arama - Sağlayıcılara göre ayrılmış arama sonuçlarını ver + Arama sonuçlarını sağlayıcıya göre ayırır Yalnızca çökmelerle ilgili verileri gönderir - Hiç veri göndermez - Anime için filler bölümleri gösterir + Veri göndermez + Anime için filler bölümleri göster Fragmanları göster Kitsu\'dan posterleri göster - Seçilen video kalitelerini arama sonuçlarında gizle + Seçilen video kalitelerini arama sonuçlarında gösterme Otomatik eklenti güncellemeleri Uygulama güncellemelerini göster - Başlangıçta yeni güncellemeleri otomatik olarak ara - Ön sürümlere güncelle - Sadece tam sürümler yerine ön sürüm güncellemelerini de ara + Uygulama başlatıldıktan sonra güncellemeleri otomatik olarak kontrol et. + Deneysel sürümlere güncelle + Yalnızca tam sürümler yerine deneysel güncellemeleri de ara GitHub Aynı geliştiriciler tarafından LightNovel uygulaması Aynı geliştiriciler tarafından anime uygulaması @@ -191,8 +191,8 @@ Bağlantı bulunamadı Bağlantı panoya kopyalandı Bölümü oynat - Varsayılana sıfırla - Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek + Varsayılan değere sıfırla + Üzgünüz, uygulama çöktü. Geliştiricilere anonim bir hata raporu gönderilecek Sezon %s %d%s Sezon yok @@ -210,8 +210,8 @@ Sürdür -30 +30 - %s dosyası tamamen silinecek -\nEmins misiniz\? + %s tamamen silinecek +\nEmin misiniz\? %dm \nkaldı Devam ediyor @@ -236,9 +236,9 @@ Torrentler Belgeseller OVA - Asya dramaları + Asya dizileri Canlı yayınlar - NSFW + +18 Diğerleri Film @@ -248,9 +248,9 @@ \@string/ova Torrent Belgesel - Asya draması + Asya dizisi Canlı yayın - NSFW + +18 Video Kaynak hatası Sunucu hatası @@ -259,10 +259,10 @@ İndirme hatası, depolama izinlerini kontrol edin Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla - Uygulamada oynat - %s\'deda oynat + Burada oynat + %s üzerinden oynat Tarayıcıda oynat - Linki kopyala + Bağlantıyı kopyala Otomatik indir Şu kaynaktan indir Bağlantıları yenile @@ -281,22 +281,22 @@ Kilitle Yeniden boyutlandır Kaynak - OP\'yi geç + Jeneriği geç Bir daha gösterme Bu güncellemeyi atla Güncelle - Tercih edilen izleme kalitesi - Oynatıcıdaki maksimum başlık karakter sayısı - Oynatıcının üst tarafındaki öğeler + Tercih edilen görüntü kalitesi (WiFi) + Video oynatıcı başlığı karakter üst sınırı + Oynatıcının çözünürlüğü Video arabelleği boyutu Video arabelleği uzunluğu - Diskteki video önbelleği + Hafızadaki video önbelleği Video ve resim önbelleğini temizle - Android TV gibi düşük belleğe sahip cihazlarda çok yükseğe ayarlanırsa çökmelere neden olur. - Çok yükseğe ayarlanırsa, Android TV cihazları gibi düşük depolama alanına sahip sistemlerde sorunlara neden olabilir. - HTTPS üzerinden DNS - ISP bloklarını atlatmak için kullanışlıdır - Klon site + Çok yükseğe ayarlanırsa düşük belleğe sahip cihazlarda çökmelere neden olur (örn. Android TV). + Çok yükseğe ayarlanırsa düşük depolama alanına sahip sistemlerde sorunlara neden olur (örn. Android TV). + HTTPS üzerinden DNS (DoH) + İnternet Servis Sağlayıcısı (İSS) kısıtlamalarını aşmak için kullanışlıdır + Siteyi kopyala Siteyi kaldır Farklı bir URL ile mevcut bir sitenin klonunu ekleyin İndirme konumu @@ -305,16 +305,16 @@ Ekrana sığdır Uzat Yakınlaştır - Disclaimer + Yasal Uyarı legal_notice_key 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. Genel - Rastgele butonu - Ana sayfada rastgele butonunu göster + Rastgele İçerik + Ana sayfada rastgele bir film veya dizi seçen bir tuş gösterir Sağlayıcı dilleri Uygulama düzeni Tercih edilen medya - Desteklenen sağlayıcılarda NSFW\'yi etkinleştir + Desteklenen sağlayıcılarda +18 içeriği etkinleştir Alt yazı kodlaması Sağlayıcılar Düzen @@ -336,7 +336,7 @@ hello@world.com 127.0.0.1 MyCoolSite - example.com + ornek.com Dil kodu (tr) Hiçbiri Normal @@ -376,7 +376,7 @@ Alt yazı senkronu 1000 ms Alt yazı gecikmesi - Alt yazılar %d ms erken gözüküyorsa bunu kullanın + Alt yazılar %d ms erken görüntüleniyorsa bunu kullanın Alt yazılar %d ms geç gözüküyorsa bunu kullanın Alt yazı gecikmesi yok Pijamalı hasta yağız şoföre çabucak güvendi Önerilen - %s yüklendi + %s eklendi Dosyadan yükle İnternetten yükle İndirilen dosya @@ -422,10 +422,10 @@ Geçersiz veri Geçersiz URL Hata - Alt yazılardan seçmeli alt yazıyı kaldır + Alt yazılardan seçmeli alt yazıyı (CC) kaldır Alt yazılardaki şişkinliği kaldır Tercih edilen medya diline göre filtrele - Ekstralar + Ek içerikler Fragman Yayına bağlan Yönlendiren @@ -433,7 +433,7 @@ Videoları bu dillerde izle Geri Kurulumu atla - Cihazınıza uygun görünümü seçin + Cihazınıza uygun uygulama görünümünü seçin Çökme raporları Ne izlemek istiyorsunuz Bitti @@ -445,7 +445,7 @@ Eklenti silindi %s yüklenemedi +18 - %d %s … indirilmeye başlandı + %d %s indirilmeye başlandı… %d %s indirildi %s\'nin tamamı zaten indirildi Toplu indir @@ -477,7 +477,7 @@ Çökme bilgisini göster Puan: %s Açıklama - Versiyon + Sürüm Durum Boyut Geliştiriciler @@ -499,14 +499,14 @@ Fragmanı oynat Eklenen depolardan henüz yüklenmemiş tüm eklentileri otomatik olarak yükleyin. Güncelleme başladı - Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemele yüklenmezse eski seçeneği deneyin. + Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemeler yüklenmezse eski seçeneği deneyin. Eklentileri otomatik olarak indir APK indirici - Linkler + Bağlantılar Uygulama güncellemeleri Yedek Oynatıcı özellikleri - Altyazılar + Alt yazılar Düzen Varsayılanlar Eklentiler @@ -531,22 +531,22 @@ İzlenenlerden kaldır Karışık son Karışık başlangıç - Kredi + Katkıda Bulunanlar Giriş Eklenti İndirildi - Aksiyonlar - Açma/bitiş için atlama açılır pencerelerini göster + Eylemler + Açılış/bitiş için atlama açılır pencerelerini göster Çok fazla metin. Panoya kaydedilemiyor. Kütüphane Tarayıcı Görünüşe göre kütüphaneniz boş :( -\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize gösteri ekleyin +\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize içerik ekleyin Güvenli mod dosyası bulundu! \nDosya kaldırılana kadar başlangıçta herhangi bir uzantı yüklenmiyor. Sırala Sırala - Güncel (Yeniden Eskiye) - Güncel (Eskiden Yeniye) + Güncellenme (Yeniden Eskiye) + Güncellenme (Eskiden Yeniye) Alfabetik (A\'dan Z’ye) Alfabetik (Z - A) Kütüphane Seçin @@ -554,4 +554,27 @@ Görünüşe göre bu liste boş, başka bir listeye geçmeyi deneyin Derecelendirme (Yüksekten Düşüğe) Derecelendirme (Düşükten Yükseğe) + Yeniden başlat + Oynatıcı gizlenmişken atlanacak süre + İSS Kısıtlamaları + GitHub\'a ulaşılamadı, jsdelivr vekil sunucusu etkinleştiriliyor. + Başlat + Başarılı oldu + raw.githubusercontent.com vekil sunucusu (proxy) + Tercih edilen görüntü kalitesi (Mobil veri) + Oynatıcı görünürken atlanacak süre + Oynatıcı gizli durumdayken atlanacak süre miktarı + jsdelivr kullanarak GitHub kısıtlamasını aşar. Güncellemeler birkaç gün gecikebilir. + Android TV + Yeni bölüm %d yayınlandı! + Sağlayıcıyı kontrol et + Başarısız oldu + Durdur + Geri al + Abone olunan gösteriler güncelleniyor + Abone olunan + %s kanalına abone olundu + %s kanalı aboneliğinden çıkıldı + Günlük + Oynatıcı görünür durumdayken atlanacak süre miktarı \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a676b583..6dca29b4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,13 +1,13 @@ Постер - Постер епізоду + Постер до епізоду Завантаження скасовано Змінити постачальника Назад Рейтинг: %.1f Актори: %s - Епізод %d буде випущено через + Епізод %d вийде через Poster %s Еп. %d %dд %dгод %dхв @@ -15,16 +15,16 @@ %dхв Головний постер Наступний випадковий - Перегляд фону + Попередній перегляд фону Швидкість (%.2fx) - Нове оновлення знайдено! + Знайдено нове оновлення! \n%s -> %s Пошук Завантаження %d хв Параметри Пошук… - Пошук %s… + Пошук на %s… Дані відсутні Більше опцій Наступний епізод @@ -37,7 +37,7 @@ Покинуто Переглянути фільм Переглянути трейлер - Переглянути торрент + Трансляція через торрент Повторити підключення… Назад Переглянути епізод @@ -87,13 +87,13 @@ Налаштування субтитрів Chromecast Режим Eigengravy Проведіть пальцем, щоб змінити налаштування - Проведіть пальцем ліворуч або праворуч, щоб змінити яскравість або гучність + Проведіть пальцем вгору або вниз ліворуч або праворуч, щоб змінити яскравість або гучність Відтворення наступного епізоду після закінчення поточного Головна CloudStream Філер Програти в CloudStream - Потік + Трансляція Переглядаю Поділитися Відкладено @@ -121,7 +121,7 @@ Колір тексту Колір контуру Автовідтворення наступного епізоду - Проведіть пальцем ліворуч або праворуч, щоб керувати часом у відеоплеєрі + Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play @@ -133,8 +133,8 @@ Проведіть пальцем, щоб перемотати Двічі торкніться, щоб перемотати Двічі торкніться для паузи - Крок перемотки - Натисніть посередині, щоб поставити на паузу + Крок перемотки (Секунди) + Натисніть двічі посередині, щоб призупинити Використовувати яскравість системи Оновити прогрес перегляду Відновлення даних з резервної копії @@ -230,7 +230,7 @@ Показати постери від Kitsu Автоматичне оновлення плагінів Автоматично встановлювати всі ще не встановлені плагіни з доданих репозиторіїв. - Автоматичний пошук нових оновлень при запуску + Автоматично шукати нові оновлення після запуску застосунку. Оновлення до бета-версій Посилання скопійовано в буфер обміну Деякі телефони не підтримують новий інсталятор пакетів. Спробуйте стару версію, якщо оновлення не встановлюються. @@ -255,7 +255,7 @@ Документальні фільми NSFW Фільм - \@string/ova + OVA Торрент Мітка якості NSFW @@ -283,7 +283,7 @@ Особливості Загальне Випадкова кнопка - Показати випадкову кнопку на Головній сторінці + Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці Мови постачальника Макет програми Бажані медіа @@ -524,10 +524,9 @@ Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com -\nProxy + raw.githubusercontent.com Proxy Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP - Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 72d62a04..a14b87cc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -142,14 +142,14 @@ 倍速模式 在播放器中添加播放速度选项 滑动控制进度 - 左右滑动控制播放进度 + 左右滑动以控制视频中的位置 滑动更改设置 上下滑动修改亮度或音量 自动播放下一集 播放完毕后播放下一集 双击控制进度 双击暂停 - 双击控制进度时间 + 双击控制进度时间 (秒) 在左右侧双击快进或快退 双击中间暂停 使用系统亮度 @@ -178,7 +178,7 @@ 自动更新插件 自动下载插件 显示应用更新 - 启动时自动搜索更新 + 启动应用后自动搜索更新。 更新至预览版 搜索预览版更新替代仅搜索完整版本 Github @@ -245,8 +245,8 @@ 电影 电视剧 卡通 - \@string/anime - \@string/ova + 动漫 + OVA 种子 纪录片 亚洲剧 @@ -311,7 +311,7 @@ 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. 通用 随机按钮 - 在主页中显示随机按钮 + 在主页上显示按钮,可以从主页上随机选择电影或电视剧 片源语言 应用布局 首选类型 @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49380b5e..911c0d07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,17 +210,17 @@ Eigengravy Mode Adds a speed option in the player Swipe to seek - Swipe left or right to control time in the videoplayer + Swipe from side to side to control your position in a video Swipe to change settings - Swipe on the left or right side to change brightness or volume + Slide up or down on the left or right side to change brightness or volume Autoplay next episode Start the next episode when the current one ends Double tap to seek Double tap to pause - Player seek amount + Player seek amount (Seconds) Tap twice on the right or left side to seek forwards or backwards - Tap in the middle to pause + Tap twice in the middle to pause Use system brightness Use system brightness in the app player instead of a dark overlay @@ -251,7 +251,7 @@ Automatically download plugins Automatically install all not yet installed plugins from added repositories. Show app updates - Automatically search for new updates on start + Automatically search for new updates after starting the app. Redo setup process Update to prereleases Search for prerelease updates instead of full releases only @@ -324,8 +324,8 @@ Movie Series Cartoon - @string/anime - @string/ova + Anime + OVA Torrent Documentary Asian Drama @@ -383,7 +383,7 @@ Useful for bypassing ISP blocks raw.githubusercontent.com Proxy Failed to reach GitHub, enabling jsdelivr proxy. - Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. + Using jsdelivr, GitHub blocking can be bypassed. May delay updates by a few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -428,7 +428,7 @@ Features General Random Button - Show random button on Homepage + Shows button on Homepage which can choose a random movie or TV series from the Homepage Provider languages App Layout Preferred media @@ -657,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - + \ No newline at end of file From 3a5d8725459a33e9f58a8923e172ef2cae85f0e9 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 20:01:20 +0000 Subject: [PATCH 087/104] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 1 + 1 file changed, 1 insertion(+) 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 078419e2..4aa859aa 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 @@ -78,6 +78,7 @@ val appLanguages = arrayListOf( Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), + Triple("", "bahasa Melayu", "ms"), Triple("", "Nederlands", "nl"), Triple("", "norsk nynorsk", "nn"), Triple("", "norsk bokmål", "no"), From 13ee8e21d06c34a5f01e476ee133a4acc4b854ea Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 10 Mar 2023 21:33:13 +0100 Subject: [PATCH 088/104] Semi-unfucked VLC on A13+ --- .../lagradost/cloudstream3/MainActivity.kt | 31 ++++++++++++------- .../ui/result/ResultViewModel2.kt | 8 ++++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index a7449255..7818e357 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.os.Build import android.os.Bundle import android.util.AttributeSet import android.util.Log @@ -34,7 +35,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.snackbar.Snackbar import com.jaredrummler.android.colorpicker.ColorPickerDialogListener -import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings @@ -170,7 +170,12 @@ open class ResultResume( val VLC = object : ResultResume( VLC_PACKAGE, - "org.videolan.vlc.player.result", + // Android 13 intent restrictions fucks up specifically launching the VLC player + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + "org.videolan.vlc.player.result" + } else { + Intent.ACTION_VIEW + }, "extra_position", "extra_duration", ) { @@ -733,15 +738,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } else { this.setKey(getString(R.string.jsdelivr_proxy_key), true) val parentView: View = findViewById(android.R.id.content) - Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG).let { snackbar -> - snackbar.setAction(R.string.revert) { - setKey(getString(R.string.jsdelivr_proxy_key), false) + Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG) + .let { snackbar -> + snackbar.setAction(R.string.revert) { + setKey(getString(R.string.jsdelivr_proxy_key), false) + } + snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) + snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) + snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) + snackbar.show() } - snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) - snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) - snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) - snackbar.show() - } } } @@ -1123,7 +1129,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { suspend fun checkGithubConnectivity(): Boolean { return try { - app.get("https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", timeout = 5).text.trim() == "ok" + app.get( + "https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", + timeout = 5 + ).text.trim() == "ok" } catch (t: Throwable) { false } 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 2983b41d..46a8c9f6 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 @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.result import android.app.Activity import android.content.* import android.net.Uri +import android.os.Build import android.os.Bundle import android.util.Log import android.widget.Toast @@ -1125,7 +1126,12 @@ class ResultViewModel2 : ViewModel() { 1L } - component = VLC_COMPONENT + // Component no longer safe to use in A13 for VLC + // https://code.videolan.org/videolan/vlc-android/-/issues/2776 + // This will likely need to be updated once VLC fixes their documentation. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + component = VLC_COMPONENT + } putExtra("from_start", !resume) putExtra("position", position) From 29174dbb30a2713844199206cc0b2e5723283f6c Mon Sep 17 00:00:00 2001 From: LikDev-256 <81100289+LikDev-256@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:41:35 +0530 Subject: [PATCH 089/104] Feat: fix Streamsb (#417) * Fix Streamsb * feat(StreamSB) stream break: support audiotracks * Revert "feat(StreamSB) stream break: support audiotracks" This reverts commit 078caf9f88dc92bb7416f51458b1bbea73bfb9bf. * Feat: fix Streamsb They normally update source numbers like 50, 51 but instead of 52 they totally dumped everything and just flipped the number into 15 --- .../main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index b7477242..cac31328 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -134,7 +134,7 @@ open class StreamSB : ExtractorApi() { it.value.replace(Regex("(embed-|/e/)"), "") }.first() // val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" - val master = "$mainUrl/sources51/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" + val master = "$mainUrl/sources15/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" val headers = mapOf( "watchsb" to "sbstream", ) From 3e2b0f2a17243abbdfddd929544058b7977bc32a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 10 Mar 2023 20:45:19 +0100 Subject: [PATCH 090/104] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-uk/strings.xml | 72 +++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6dca29b4..48856dbb 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -3,7 +3,7 @@ Постер Постер до епізоду Завантаження скасовано - Змінити постачальника + Змінити провайдера Назад Рейтинг: %.1f Актори: %s @@ -22,7 +22,7 @@ Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук на %s… Дані відсутні @@ -64,7 +64,7 @@ Тип контуру Шрифт Розмір шрифту - Пошук за допомогою постачальників + Пошук за допомогою провайдерів Пошук за типами Бананів немає Автовибір мови @@ -75,12 +75,12 @@ Продовжити перегляд Вилучити Детальніше - Цей постачальник є торрентом, рекомендується VPN + Цей провайдер є торрентом, рекомендується VPN Опис Сюжет не знайдено Опис не знайдено Показати Logcat 🐈 - Продовження відтворення в мініатюрному плеєрі поверх інших програм + Продовження відтворення в мініатюрному плеєрі поверх інших застосунків Прибирає чорні рамки Субтитри Субтитри Chromecast @@ -125,7 +125,7 @@ %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play - Для коректної роботи цього постачальника може знадобитися VPN + Для коректної роботи цього провайдера може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці Налаштування субтитрів плеєра @@ -147,23 +147,23 @@ Оновлення та резервне копіювання Інформація Розширений пошук - Надає результати пошуку, розділені за постачальниками + Надає результати пошуку, розділені за провайдерами Надсилає дані лише про збої Не надсилає даних - Показати заповнюючий епізод для аніме + Показати філерний епізод для аніме Показати трейлери Приховати вибрану якість відео в результатах пошуку Автоматичне завантаження плагінів - Показати оновлення програми + Показати оновлення застосунку Повторний процес налаштування - Пошук лише попередніх оновлень, а не повних релізів + Пошук лише бета-оновлень, а не повних релізів Встановлювач APK Github Застосунок для легких новел від тих же розробників Застосунок для аніме від тих же розробників Дайте бананів розробникам - Мова програми - Цей постачальник не має підтримки Chromecast + Мова застосунку + Цей провайдер не має підтримки Chromecast Посилань не знайдено Переглянути епізод Скинути до значення за замовчуванням @@ -180,7 +180,7 @@ \nВи впевнені\? %dхв \nзалишилося - Триває + Виходить Завершено Рейтинг Тривалість @@ -189,7 +189,7 @@ За замовчуванням Вільно Зайнято - Програма + Застосунок Телесеріали Мультфільми Аніме @@ -208,7 +208,7 @@ Віддалена помилка Помилка рендеринга Дзеркало Chromecast - Переглянути в програмі + Переглянути в застосунку Переглянути в %s Автозавантаження Завантажити дзеркало @@ -273,7 +273,7 @@ Заповнити Збільшити Доріжки - Оновлення програми + Оновлення застосунку Кеш Жести Особливості плеєра @@ -284,15 +284,15 @@ Загальне Випадкова кнопка Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці - Мови постачальника - Макет програми + Мови провайдера + Макет застосунку Бажані медіа Авто Макет телевізора Макет телефону Макет емулятора Основний колір - Тема програми + Тема застосунку Розташування назви постера Розмістіть назву під постером пароль123 @@ -363,7 +363,7 @@ Кодування субтитрів Включити NSFW на підтримуваних постачальників Макет - Постачальники + Провайдери example.com %s %s Депресивний @@ -429,7 +429,7 @@ Оновлено %d плагіни За замовчуванням в CloudStream не встановлені сайти. Вам потрібно встановити сайти з репозиторіїв. \n -\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунку. +\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунок. \n \nПриєднуйтесь до нашого Discord або шукайте в інтернеті. Переглянути репозиторії спільноти @@ -451,28 +451,28 @@ Вбудований плеєр VLC MPV - Відтворення веб-відео - Веб-браузер - Кінець + Відтворення вебвідео + Веббраузер + Ендінґ Коротке повторення Пропустити %s - Змішаний кінець + Змішаний ендінґ Подяки - Опенінг + Опенінґ Вступ Очистити історію Історія - Показувати спливаючі вікна для опенінгу/кінця + Показувати спливаючі вікна для опенінґу/ендінґу Забагато тексту. Не вдалося зберегти в буфер обміну. Позначити як переглянуте Ви впевнені що хочете вийти\? Так Ні - Установлення оновлення програми… - Не вдалося встановити нову версію програми + Встановлення оновлення застосунку… + Не вдалося встановити нову версію застосунку Старий - Інсталятор пакетів - Програму буде оновлено після виходу + Встановлювач пакетів + Застосунок буде оновлено після виходу Це також призведе до видалення всіх плагінів репозиторію Всі мови Назад @@ -484,10 +484,10 @@ Бажаний відеоплеєр Увімкнено безпечний режим Автори - Завантаження оновлення програми… + Завантаження оновлення застосунку… Усі розширення вимкнено через збій, щоб допомогти вам знайти те, що спричиняє проблеми. - Програму не знайдено - Змішаний опенінг + Застосунок не знайдено + Змішаний опенінґ Видалити з переглянутого За оновленням (від старого до нового) За оновленням (від нового до старого) @@ -517,14 +517,14 @@ Журнал Старт Стоп - Тест постачальника + Тест провайдер Оновлення підписаних шоу Підписано Підписано на %s Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com Proxy + raw.githubusercontent.com Проксі Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. From 19dc1a2456b658e85bbd2123e85a5cafdcdc651f Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 14 Mar 2023 12:59:32 +0100 Subject: [PATCH 091/104] Un-bruh-momented some translations --- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-bg/strings.xml | 6 +++--- app/src/main/res/values-bn/strings.xml | 4 ++-- app/src/main/res/values-bp/strings.xml | 8 ++++---- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 6 +++--- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 6 +++--- app/src/main/res/values-hr/strings.xml | 6 +++--- app/src/main/res/values-hu/strings.xml | 8 ++++---- app/src/main/res/values-in/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-iw/strings.xml | 6 +++--- app/src/main/res/values-nl/strings.xml | 6 +++--- app/src/main/res/values-no/strings.xml | 8 ++++---- app/src/main/res/values-pl/strings.xml | 6 +++--- app/src/main/res/values-qt/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-sk/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 10 +++++----- app/src/main/res/values-tl/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 16 ++++++++-------- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-ur/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 4 ++-- app/src/main/res/values-zh-rTW/strings.xml | 16 ++++++++-------- app/src/main/res/values-zh/strings.xml | 12 ++++++------ 29 files changed, 77 insertions(+), 77 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ae45465b..84934288 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -342,7 +342,7 @@ الكل الحد الاقصي الحد الأدنى - \@string/none + @string/none الخطوط المحيطة النمط المنخفض ظل diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index f1f512a1..496512f7 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -105,7 +105,7 @@ Продължете да гледате Премахване Повече информация - \@string/home_play + @string/home_play Може да е необходим VPN, за да работи правилно този доставчик Този доставчик е торент, препоръчва се VPN Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта. @@ -223,8 +223,8 @@ Филм Серия Анимационен филм - \@string/anime - \@string/ova + @string/anime + @string/ova Торент Документален филм Азиатска драма diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 7e0448d6..7c37e291 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -143,8 +143,8 @@ হালনাগাদ ও ব্যাকআপ অ্যাপ এর হালনাগাদ দেখান খুঁজতে সোয়াইপ করুন - \@string/result_poster_img_des - \@string/home_play + @string/result_poster_img_des + @string/home_play আগাতে ডবল ট্যাপ করুন আইজেনগ্রাভি মোড আপডেট শুরু হয়েছে diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml index 2c2e1303..acdf0ae0 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -10,7 +10,7 @@ %dm Poster - \@string/result_poster_img_des + @string/result_poster_img_des Episode Poster Main Poster Next Random @@ -108,7 +108,7 @@ Continue Assistindo Remover Mais Info - \@string/home_play + @string/home_play Uma VPN pode ser necessária para esse fornecedor funcionar corretamente Esse fornecedor é um torrent, uma VPN é recomendada Metadados não são oferecidas pelo site, o carregamento do video pode falhar se ele não existir no site. @@ -222,8 +222,8 @@ Filme Série Desenho Animado - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Documentário Drama Asiático diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 67179b46..1a139511 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -103,7 +103,7 @@ Pokračovat ve sledování Odebrat Další informace - \@string/home_play + @string/home_play Aby tento poskytovatel fungoval správně, budete možná potřebovat VPN Tento poskytovatel je torrent, je doporučená VPN Web neposkytnul žádná metadata, načítání videa selže, pokud na webu neexistuje. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7cf49de1..e1093e05 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -115,7 +115,7 @@ Weiterschauen Entfernen Mehr Infos - \@string/home_play + @string/home_play Damit dieser Anbieter korrekt funktioniert, ist möglicherweise ein VPN erforderlich Dieser Anbieter bietet Torrents an, ein VPN wird dringend empfohlen Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie auf der Website nicht vorhanden sind. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 5e9dafd8..0d45b2c1 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -387,7 +387,7 @@ Κλείσιμο Εκκαθάριση Γλώσσα υποτίτλων - \@string/home_play + @string/home_play Δεν έχουν παρασχεθεί μεταδεδομένα από τον ιστότοπο, η φόρτωση του βίντεο θα αποτύχει αν δεν υπάρχει στον ιστότοπο. Διπλό πάτημα για παύση Μέγεθος αναζήτησης στο πρόγραμμα αναπαραγωγής @@ -452,7 +452,7 @@ Ανάμεικτοι τίτλοι τέλους -30 Κριτική - \@string/ova + @string/ova Ενημερώσεις εφαρμογής Αντίγραφο ασφαλείας Extensions @@ -464,7 +464,7 @@ Προεπιλεγμένα %s %s Μέγεθος γραμματοσειράς - \@string/anime + @string/anime Σύνδεσμοι Εμφάνιση Χαρακτηριστικά diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5c8ac532..f036653f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -194,7 +194,7 @@ Continuar Viendo Remover Más info - \@string/home_play + @string/home_play Una VPN puede ser necesaria para que este proveedor funcione correctamente Este proveedor es un torrent, se recomienda una VPN El sitio no proporciona los metadatos, la carga del video fallará si no existe en el sitio. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f3d35c19..9fee8c3c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -240,7 +240,7 @@ Continuer à regarder Retirer Plus d\'informations - \@string/home_play + @string/home_play Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement Ce fournisseur est un torrent, un VPN est recommandé Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site. @@ -385,8 +385,8 @@ 4K Web -30 - \@string/anime - \@string/ova + @string/anime + @string/ova NSFW %s %s Filtrez par langue préférée diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 159542cc..23fd9624 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -119,7 +119,7 @@ Nastavite s gledanjem Makni Više informacija - \@string/home_play + @string/home_play Za ispravan rad ovog pružatelja usluga može biti potreban VPN Ovaj pružatelj usluga je torrent, preporučuje se VPN Stranica ne daje metapodatke, učitavanje videozapisa neće uspjeti ako ne postoji na stranici. @@ -238,8 +238,8 @@ Film Serija Crtić - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Dokumentarac Azijska drama diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5b42fd6a..66526821 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -57,7 +57,7 @@ Megnyitás böngészőben Betöltés kihagyása Poster - \@string/result_poster_img_des + @string/result_poster_img_des Nézés Befejezve Később megnézés @@ -111,7 +111,7 @@ Betűtípusok importálása %s Eltávolítás Több információ - \@string/home_play + @string/home_play VPN szükséges lehet ehhez a szolgáltató megfelelő működéséhez Ez a szolgáltató torrent, VPN ajánlott Leírás @@ -172,11 +172,11 @@ OVA Egyebek Sorozat - \@string/anime + @string/anime Forráshiba NSFW Rajzfilm - \@string/ova + @string/ova Élőadás NSFW Videó diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 1913868a..f5af3877 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -101,7 +101,7 @@ Lanjutkan Menonton Hapus Info lebih lanjut - \@string/home_play + @string/home_play Sebuah VPN mungkin diperlukan agar provider ini bisa bekerja dengan benar Provider ini adalah sebuah torrent, VPN direkomendasikan Metadata tidak disediakan oleh situs, loading video akan gagal jika tidak ada di situs. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b8e7eb20..4476b4a0 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -108,7 +108,7 @@ Continua a guardare Rimuovi Più info - \@string/home_play + @string/home_play Potrebbe essere necessaria una VPN per far funzionare correttamente questo provider Questo provider è un torrent, si raccomanda una VPN I metadati non sono forniti dal sito, il caricamento del video fallirà se non esiste sul sito. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 11cf77ce..4ed5ddc0 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -116,7 +116,7 @@ כתוביות כרומקאסט ממשיך ניגון בנגן מינימלי מעל ישומים אחרים כתוביות - \@string/home_play + @string/home_play היסטוריה מורשת לא @@ -164,8 +164,8 @@ משומש סדרת טלוויזיה סדרות/סרטים מצוירים - \@string/אנימה - \@string/אנימציית וידאו מקורית + @string/אנימה + @string/אנימציית וידאו מקורית דרמה אסייתית כרומקאסט את הפרק כרומקאסט את המראה diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index dd89c34a..3595a24a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -109,7 +109,7 @@ Doorgaan met kijken Verwijder Meer Info - \@string/home_play + @string/home_play Een VPN kan nodig zijn om deze provider correct te laten werken Deze provider is een torrent, een VPN wordt aanbevolen Metadata wordt niet geleverd door de site, het laden van video\'s zal mislukken als deze niet op de site bestaat. @@ -222,8 +222,8 @@ Film Serie Tekenfilm - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Documentaire Aziatisch drama diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 41bf704d..d9feb60c 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -2,7 +2,7 @@ Plakat - \@string/result_poster_img_des + @string/result_poster_img_des Episode Plakat Main Plakat Neste tilfeldig @@ -412,7 +412,7 @@ Slå av/på grensesnittselementer på plakat Hopp over denne oppdateringen Forårsaker tilfeldige krasj hvis satt for høyt. Ikke endre dette hvis du ikke har lite minne. - \@string/home_play + @string/home_play Sikkerhetskopier data Data lagret Kunne ikke logge inn på %s @@ -422,11 +422,11 @@ Sensurerbart Vev Lenke til strøm - \@string/anime + @string/anime Skjul valgt videokvalitet i søkeresultater Lastet inn sikkerhetkopifil Oppdateringer og sikkerhetskopi - \@string/ova + @string/ova Avslutt\? Sensurerbart Alle %s er allerede nedlastet diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 558a46ed..7fc0c887 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -455,7 +455,7 @@ Instalator APK Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb legacy, jeśli aktualizacje nie zostaną zainstalowane. password123 - \@string/ova + @string/ova MojaFajnaWitryna MyCoolUsername 127.0.0.1 @@ -463,9 +463,9 @@ przyklad.pl /\?\? Instalator pakietów - \@string/home_play + @string/home_play hello@world.com - \@string/anime + @string/anime Opening Ending Mixed opening diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index 76852ca4..aee3de91 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -247,5 +247,5 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOGGAGHAGHAAA aoaaaaaoooghhh oooooh uuaagh - \@string/home_play + @string/home_play \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 42d9b7c8..8cd24a3b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -106,7 +106,7 @@ Continuați să urmăriți Eliminați Mai multe informații - \@string/home_play + @string/home_play Există probabilitatea necesitații unui VPN pentru ca acest furnizor să funcționeze corespunzător Acest furnizor este un torrent, se recomandă un VPN Metadatele nu sunt furnizate de către site, există posibilitatea ca încărcarea videoclipului să eșueze. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5295bd35..e9494040 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -363,7 +363,7 @@ Расширения URL репозитория Плагин загружен - \@string/home_play + @string/home_play Перемотка двойным нажатием /\?\? /%d diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 66d8ada9..96fbaff1 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -99,7 +99,7 @@ Tento poskytovateľ je torrent, odporúča sa VPN Importovať písma ich umiestnením do %s Viac informácií - \@string/home_play + @string/home_play Pokračovať v sledovaní Na správne fungovanie tohto poskytovateľa môže byť potrebná VPN Stránka neposkytla žiadne metadáta, načítanie videa zlyhá, ak na stránke neexistuje. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 32336b66..25066d7b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -33,7 +33,7 @@ Undertexter Försök ansluta igen… Gå tillbaka - \@string/result_poster_img_des + @string/result_poster_img_des Spela Avsnitt Ladda ner Intern lagring @@ -44,7 +44,7 @@ Inaktivera automatisk felrapportering Mer information Hide - \@string/result_poster_img_des + @string/result_poster_img_des Spela upp Info Nästa @@ -235,7 +235,7 @@ Episod %d kommer släppas om %d min Visa trailers - \@string/home_play + @string/home_play OVA %d-%d %d %s @@ -244,7 +244,7 @@ %dm \nåterstår NSFW - \@string/ova + @string/ova Torrent NSFW +30 @@ -273,7 +273,7 @@ Asiatiska draman Andra Tecknade serier - \@string/anime + @string/anime Dokumentär Asiatisk drama Video diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 9e5b29d4..721c421c 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -5,7 +5,7 @@ %s Ep %d Poster - \@string/result_poster_img_des + @string/result_poster_img_des Episode Poster Main Poster Next Random diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f53bb69d..975242b2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -23,9 +23,9 @@ Bölüm Posteri Ana Poster Sonraki Rastgele - \@string/play_episode + @string/play_episode Geri git - \@string/home_change_provider_img_des + @string/home_change_provider_img_des Change Provider Preview Background @@ -46,7 +46,7 @@ Veri yok Daha fazla seçenek Sonraki bölüm - \@string/synopsis + @string/synopsis Türler Paylaş Tarayıcıda aç @@ -123,7 +123,7 @@ İzlemeye devam et Kaldır Daha fazla bilgi - \@string/home_play + @string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. @@ -205,7 +205,7 @@ Bölüm bulunamadı Dosyayı sil Sil - \@string/sort_cancel + @string/sort_cancel Durdur Sürdür -30 @@ -244,8 +244,8 @@ Film Dizi Çizgi film - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Belgesel Asya dizisi @@ -368,7 +368,7 @@ Hepsi Maksimum Minimum - \@string/none + @string/none Dış hat Çökmüş Gölge diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6dca29b4..648de819 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -124,7 +124,7 @@ Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра - \@string/home_play + @string/home_play Для коректної роботи цього постачальника може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index f733addc..80081215 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -230,7 +230,7 @@ سلسلہ کارٹون انیمی - \@string/اووا + @string/اووا ٹورینٹ دستاویزی فلم ایشیائی ڈرامے diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 59c65916..74e748a3 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -110,7 +110,7 @@ Tiếp tục xem Loại bỏ Thông tin thêm - \@string/home_play + @string/home_play Bạn có thể sẽ cần sử dụng VPN để xem phim này Phim này được chiếu dưới dạng Torrent. Hãy sử dụng VPN để xem Thông tin phim @@ -229,7 +229,7 @@ Phim Bộ Hoạt Hình Anime - \@string/ova + @string/ova Torrent Phim Tài Liệu Truyền Hình Châu Á diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8a10208a..6aa41ff3 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -23,9 +23,9 @@ 劇集封面 主封面 隨機下一個 - \@string/play_episode + @string/play_episode 返回 - \@string/home_change_provider_img_des + @string/home_change_provider_img_des 更改片源 預覽背景 @@ -46,7 +46,7 @@ 無資料 更多選項 下一集 - \@string/synopsis + @string/synopsis 類型 分享 在瀏覽器中打開 @@ -123,7 +123,7 @@ 繼續觀看 移除 更多資訊 - \@string/home_play + @string/home_play 此片源可能需要 VPN 才能正常使用 此片源是種子,建議使用 VPN 站點不提供元數據,如果站點上不存在元數據,影片載入將失敗。 @@ -205,7 +205,7 @@ 未找到劇集 刪除文件 刪除 - \@string/sort_cancel + @string/sort_cancel 暫停 繼續 -30 @@ -244,8 +244,8 @@ 電影 電視劇 卡通 - \@string/anime - \@string/ova + @string/anime + @string/ova 種子 紀錄片 亞洲劇 @@ -368,7 +368,7 @@ 全部 最大 最小 - \@string/none + @string/none 輪廓 凹陷 陰影 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a14b87cc..574624bc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -23,9 +23,9 @@ 剧集封面 主封面 随机下一个 - \@string/play_episode + @string/play_episode 返回 - \@string/home_change_provider_img_des + @string/home_change_provider_img_des 更改片源 预览背景 @@ -46,7 +46,7 @@ 无数据 更多选项 下一集 - \@string/synopsis + @string/synopsis 类型 分享 在浏览器中打开 @@ -123,7 +123,7 @@ 继续观看 移除 更多信息 - \@string/home_play + @string/home_play 此片源可能需要 VPN 才能正常使用 此片源为种子文件,建议使用 VPN 站点不提供元数据,如果站点上不存在元数据,视频加载将失败。 @@ -206,7 +206,7 @@ 未找到剧集 删除文件 删除 - \@string/sort_cancel + @string/sort_cancel 暂停 继续 -30 @@ -369,7 +369,7 @@ 全部 最大 最小 - \@string/none + @string/none 轮廓 凹陷 阴影 From 2d7126d71f3946d072652c4d6e63c938198bdafe Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 14 Mar 2023 13:12:34 +0100 Subject: [PATCH 092/104] Fix for fix for translations --- app/src/main/res/values-iw/strings.xml | 4 ++-- app/src/main/res/values-ur/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 4ed5ddc0..645724fd 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -164,8 +164,8 @@ משומש סדרת טלוויזיה סדרות/סרטים מצוירים - @string/אנימה - @string/אנימציית וידאו מקורית + @string/anime + @string/ova דרמה אסייתית כרומקאסט את הפרק כרומקאסט את המראה diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 80081215..4a8bbf11 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -230,7 +230,7 @@ سلسلہ کارٹون انیمی - @string/اووا + اووا ٹورینٹ دستاویزی فلم ایشیائی ڈرامے From 7bfcf25df4738741c8553cfce5e96fe711cddea6 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 14 Mar 2023 18:50:13 +0000 Subject: [PATCH 093/104] add a way to autofix weblate's issue with @string --- .github/locales.py | 15 ++++++++++++++- .github/workflows/update_locales.yml | 9 ++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 1c79c093..04d9cd13 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -1,6 +1,7 @@ import re import glob import requests +import lxml.etree as ET # builtin library doesn't preserve comments SETTINGS_PATH = "app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt" @@ -45,4 +46,16 @@ open(SETTINGS_PATH, "w+",encoding='utf-8').write( "\n" + END_MARKER + after_src -) \ No newline at end of file +) + +# Go through each values.xml file and fix escaped \@string +for file in glob.glob(f"{XML_NAME}*/strings.xml"): + try: + tree = ET.parse(file) + for child in tree.getroot(): + if child.text.startswith("\\@string/"): + print(f"[{file}] fixing {child.attrib['name']}") + child.text = child.text.replace("\\@string/", "@string/") + tree.write(file, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=True) + except ET.ParseError as ex: + print(f"[{file}] {ex}") \ No newline at end of file diff --git a/.github/workflows/update_locales.yml b/.github/workflows/update_locales.yml index 93cdca44..628e9bc9 100644 --- a/.github/workflows/update_locales.yml +++ b/.github/workflows/update_locales.yml @@ -1,4 +1,4 @@ -name: Update locale lists +name: Fix locale issues on: workflow_dispatch: @@ -9,7 +9,7 @@ on: - master concurrency: - group: "locale-list" + group: "locale" cancel-in-progress: true jobs: @@ -26,6 +26,9 @@ jobs: - uses: actions/checkout@v2 with: token: ${{ steps.generate_token.outputs.token }} + - name: Install dependencies + run: | + pip3 install lxml - name: Edit files run: | python3 .github/locales.py @@ -35,5 +38,5 @@ jobs: git config --local user.name "recloudstream[bot]" git add . # "echo" returns true so the build succeeds, even if no changed files - git commit -m 'update list of locales' || echo + git commit -m 'chore(locales): fix locale issues' || echo git push From 8ebf5185a3fe95db8adabedf483e34ccda1fbdcb Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 15:46:11 +0100 Subject: [PATCH 094/104] Add ffmpeg audio decoding --- app/build.gradle.kts | 2 + .../cloudstream3/ui/player/CS3IPlayer.kt | 44 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9cbccbe5..f70a575f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -159,6 +159,8 @@ dependencies { implementation("com.google.android.exoplayer:extension-cast:2.18.2") implementation("com.google.android.exoplayer:extension-mediasession:2.18.2") implementation("com.google.android.exoplayer:extension-okhttp:2.18.2") + // Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3 + implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 cb8efe92..2aaa3619 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 @@ -9,8 +9,11 @@ import android.widget.FrameLayout import androidx.preference.PreferenceManager import com.google.android.exoplayer2.* import com.google.android.exoplayer2.C.* +import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON +import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource +import com.google.android.exoplayer2.mediacodec.MediaCodecSelector import com.google.android.exoplayer2.source.* import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.trackselection.DefaultTrackSelector @@ -538,7 +541,8 @@ class CS3IPlayer : IPlayer { } // Do no include empty referer, if the provider wants those they can use the header map. - val refererMap = if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) + val refererMap = + if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) val headers = mapOf( "accept" to "*/*", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", @@ -669,23 +673,27 @@ class CS3IPlayer : IPlayer { val exoPlayerBuilder = ExoPlayer.Builder(context) .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> - DefaultRenderersFactory(context).createRenderers( - eventHandler, - videoRendererEventListener, - audioRendererEventListener, - textRendererOutput, - metadataRendererOutput - ).map { - if (it is TextRenderer) { - currentTextRenderer = CustomTextRenderer( - subtitleOffset, - textRendererOutput, - eventHandler.looper, - CustomSubtitleDecoderFactory() - ) - currentTextRenderer!! - } else it - }.toTypedArray() + DefaultRenderersFactory(context).apply { + setEnableDecoderFallback(true) + // Enable Ffmpeg extension + setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) + }.createRenderers( + eventHandler, + videoRendererEventListener, + audioRendererEventListener, + textRendererOutput, + metadataRendererOutput + ).map { + if (it is TextRenderer) { + currentTextRenderer = CustomTextRenderer( + subtitleOffset, + textRendererOutput, + eventHandler.looper, + CustomSubtitleDecoderFactory() + ) + currentTextRenderer!! + } else it + }.toTypedArray() } .setTrackSelector( trackSelector ?: getTrackSelector( From 288c5ffa39d60e0285cef573b20fe6ad4ecf7c29 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 16 Mar 2023 13:00:08 +0100 Subject: [PATCH 095/104] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (German) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Anarchydr Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: Sdarfeesh Co-authored-by: Skrripy Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-de/strings.xml | 16 ++++++++-------- app/src/main/res/values-hr/strings.xml | 18 +++++++++--------- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e1093e05..911705d5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -41,7 +41,7 @@ Suche %s… Keine Daten vorhanden Mehr Optionen - Nächste Epsisode + Nächste Episode Genres Teilen In Browser öffnen @@ -136,14 +136,14 @@ Wischen zum vor- und zurückspulen Nach links oder rechts wischen, um die Zeit im Videoplayer zu steuern Wischen, um Einstellungen zu ändern - Links oder rechts wischen, um die Helligkeit oder Lautstärke zu ändern + Links oder rechts nach oben oder unten wischen, um die Helligkeit oder Lautstärke zu ändern Nächste Episode automatisch abspielen Nächste Episode wird gestartet, sobald die aktuelle Episode endet Doppeltippen zum vor- und zurückspulen Doppeltippen zum Pausieren - Zeit für vor- und zurückspulen im Player + Zeit für vor- und zurückspulen im Player (Sekunden) Zweimal auf die rechte oder linke Seite tippen, um vor- oder zurückzuspulen - In die Mitte tippen, um zu pausieren + Doppelt in die Mitte tippen, um zu pausieren Systemhelligkeit verwenden Systemhelligkeit anstelle eines dunklen Overlay im Player verwenden Episodenfortschritt aktualisieren @@ -166,7 +166,7 @@ Ausgewählte Videoqualität bei Suchergebnissen ausblenden Automatische Plugin-Updates App-Updates anzeigen - Automatisches Suchen nach neuen Updates beim Start + Automatisches Suchen nach neuen Updates nach dem Start Auf Vorabversionen updaten Suche nach Vorabversionen statt nur nach Vollversionen Github @@ -286,7 +286,7 @@ Haftungsausschluss Allgemein Zufalls-Button - Zufallsbutton auf der Startseite anzeigen + Zeigt einen Zufallsbutton auf der Startseite an, mit welchem eine Serie oder ein Film von der Website zufällig ausgewählt wird Anbieter-Sprachen App-Layout Bevorzugte Medien @@ -519,13 +519,13 @@ Start Neustarten Bevorzugte Videoqualität (mobile Daten) - Umgehung der GitHub Sperre mit jsdelivr, kann zu einigen Tagen Verzögerung bei Updates führen. + Umgehung der GitHub Sperre mit jsdelivr. Kann zu einigen Tagen Verzögerung bei Updates führen. %s abonniert %s deabonniert Episode %d erschienen! raw.githubusercontent.com Proxy GitHub kann nicht erreicht werden, der jsdelivr-Proxy wird aktiviert. - Aktualisierung abonnierter Sendungen + Abonnierte Serien werden aktualisiert Rückgängig Abonniert ISP-Umgehungen diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 23fd9624..5366fe34 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -138,16 +138,16 @@ Eigengravy način Dodaje opciju brzine u playeru Prijeđi prstom za traženje - Prijeđi prstom ulijevo ili udesno za kontrolu vremena u videoplayeru + Prijeđite prstom ulijevo ili udesno kako biste kontrolirali player Klizni za promjenu postavki - Prijeđi prstom ulijevo ili udesno za promjenu svjetline ili glasnoće + Kliznite prstom ulijevo ili udesno za promjenu svjetline ili glasnoće Automatski započni sljedeću epizodu Započne sljedeću epizodu kad trenutna završi Dodirni dvaput za traženje Dodirni dvaput za pauziranje - Iznos preskakanja u playeru + Iznos preskakanja u playeru (Sekunde) Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag - Dodirni u sredinu zaslona za pauziranje + Dodirnite dvaput u sredinu zaslona za pauziranje Koristi svijetlinu u sustavu Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog preklopa Ažuriraj napredak gledanja @@ -173,7 +173,7 @@ Sakrij odabranu kvalitetu videozapisa u rezultatima pretraživanja Automatsko ažuriranje dodataka Prikaži ažuriranja aplikacije - Automatski traži nova ažuriranja pri pokretanju aplikacije + Automatski traži nova ažuriranja nakon pokretanja aplikacije Ažuriranje na predizdanja Tražite ažuriranja prije izdanja umjesto samo potpunih izdanja Github @@ -238,8 +238,8 @@ Film Serija Crtić - @string/anime - @string/ova + Anime + OVA Torrent Dokumentarac Azijska drama @@ -299,7 +299,7 @@ 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. Općenito Random gumb - Prikaži random gumb na početnoj stranici + Prikazuje gumb na početnoj stranici koji može odabrati nasumični film ili TV seriju s početne stranice Jezici pružatelja usluga Izgled aplikacije Preferirani mediji @@ -552,6 +552,6 @@ ISP zaobilaznice raw.githubusercontent.com Proxy Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. - Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. + Koristeći jsdelivr, GitHub blokiranje se može zaobići. Može odgoditi ažuriranja za nekoliko dana. Preferirana kvaliteta gledanja (podatkovna mobilna mreža) \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 648de819..d9ec76bb 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -22,7 +22,7 @@ Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук на %s… Дані відсутні diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 574624bc..47807259 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 + 使用 jsdelivr,可以绕过 GitHub 的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) From 67318a62a37673f1acef39dc60684a0b9e005def Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:04:00 +0000 Subject: [PATCH 096/104] chore(locales): fix locale issues --- app/src/main/res/values-ar/strings.xml | 4 ++-- app/src/main/res/values-bg/strings.xml | 7 ++++--- app/src/main/res/values-bn/strings.xml | 4 ++-- app/src/main/res/values-bp/strings.xml | 7 ++++--- app/src/main/res/values-cs/strings.xml | 7 ++++--- app/src/main/res/values-de/strings.xml | 4 ++-- app/src/main/res/values-el/strings.xml | 7 ++++--- app/src/main/res/values-eo/strings.xml | 4 ++-- app/src/main/res/values-es/strings.xml | 4 ++-- app/src/main/res/values-fa/strings.xml | 4 ++-- app/src/main/res/values-fr/strings.xml | 7 ++++--- app/src/main/res/values-hi/strings.xml | 7 ++++--- app/src/main/res/values-hr/strings.xml | 7 ++++--- app/src/main/res/values-hu/strings.xml | 4 ++-- app/src/main/res/values-in/strings.xml | 7 ++++--- app/src/main/res/values-it/strings.xml | 7 ++++--- app/src/main/res/values-iw/strings.xml | 4 ++-- app/src/main/res/values-ja/strings.xml | 4 ++-- app/src/main/res/values-kn/strings.xml | 4 ++-- app/src/main/res/values-mk/strings.xml | 7 ++++--- app/src/main/res/values-ml/strings.xml | 7 ++++--- app/src/main/res/values-ms/strings.xml | 4 ++-- app/src/main/res/values-nl/strings.xml | 7 ++++--- app/src/main/res/values-nn/strings.xml | 4 ++-- app/src/main/res/values-no/strings.xml | 4 ++-- app/src/main/res/values-pl/strings.xml | 7 ++++--- app/src/main/res/values-pt/strings.xml | 4 ++-- app/src/main/res/values-qt/strings.xml | 4 ++-- app/src/main/res/values-ro/strings.xml | 7 ++++--- app/src/main/res/values-ru/strings.xml | 4 ++-- app/src/main/res/values-sk/strings.xml | 4 ++-- app/src/main/res/values-so/strings.xml | 4 ++-- app/src/main/res/values-sv/strings.xml | 7 ++++--- app/src/main/res/values-ta/strings.xml | 4 ++-- app/src/main/res/values-tl/strings.xml | 7 ++++--- app/src/main/res/values-tr/strings.xml | 7 ++++--- app/src/main/res/values-uk/strings.xml | 4 ++-- app/src/main/res/values-ur/strings.xml | 4 ++-- app/src/main/res/values-vi/strings.xml | 7 ++++--- app/src/main/res/values-zh-rTW/strings.xml | 7 ++++--- app/src/main/res/values-zh/strings.xml | 7 ++++--- 41 files changed, 122 insertions(+), 102 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 84934288..2a356812 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,4 +1,4 @@ - + ملصق @@ -561,4 +561,4 @@ باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) - \ No newline at end of file + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 496512f7..301242cd 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,5 +1,6 @@ - - + + + %s еп. %d Актьори: %s @@ -497,4 +498,4 @@ Приложението ще се актуализира при изход от него Започна Актуализация Премахване от гледани - \ No newline at end of file + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 7c37e291..71d5d6d0 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1,4 +1,4 @@ - + পোস্টার ক্লাউডস্ট্রিম দিয়ে চালান @@ -148,4 +148,4 @@ আগাতে ডবল ট্যাপ করুন আইজেনগ্রাভি মোড আপডেট শুরু হয়েছে - \ No newline at end of file + diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml index acdf0ae0..13b34872 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -428,4 +429,4 @@ Começa o próximo episódio quando o atual termina Ativar NSFW em fornecedores compatíveis Fornecedores - \ No newline at end of file + diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 1a139511..1501a5d9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -553,4 +554,4 @@ Vrátit zpět Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP - \ No newline at end of file + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 911705d5..8fbcc2d0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,4 +1,4 @@ - + %s Ep %d Besetzung: %s @@ -529,4 +529,4 @@ Rückgängig Abonniert ISP-Umgehungen - \ No newline at end of file + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0d45b2c1..f07ce43c 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1,5 +1,6 @@ - - + + + CloudStream Αρχική Αναζήτηση @@ -508,4 +509,4 @@ \nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! \nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο. - \ No newline at end of file + diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 09e6941d..5eac8686 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -1,4 +1,4 @@ - + Reen Hejmo @@ -78,4 +78,4 @@ Rapido (%.2fx) Serĉi… Elŝuti - \ No newline at end of file + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f036653f..06c20aa5 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,4 +1,4 @@ - + Extensiones Descargue la lista de sitios que quiera utilizar @@ -529,4 +529,4 @@ Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) - \ No newline at end of file + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 81853674..e4c23628 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1,4 +1,4 @@ - + حذف مکث @@ -33,4 +33,4 @@ %dساعت %dدقیقه %dدقیقه پوستر اصلی - \ No newline at end of file + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9fee8c3c..b96ff0cd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,5 +1,6 @@ - - + + + CloudStream Accueil Rechercher @@ -523,4 +524,4 @@ Contournements de FAI L\'épisode %d est sorti ! Échouer - \ No newline at end of file + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index f33a2336..833b76f4 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1,5 +1,6 @@ - - + + + रफ्तार (%.2fx) नया अपडेट आया है! @@ -146,4 +147,4 @@ %dh %dm %dm विज्ञापन - \ No newline at end of file + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 5366fe34..b4931377 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -554,4 +555,4 @@ Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. Koristeći jsdelivr, GitHub blokiranje se može zaobići. Može odgoditi ažuriranja za nekoliko dana. Preferirana kvaliteta gledanja (podatkovna mobilna mreža) - \ No newline at end of file + diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 66526821..1389dff0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,4 +1,4 @@ - + Stáblista: %s %dn %dó%dp @@ -275,4 +275,4 @@ Minőségi jelzés Szinkroncímke Alcímke - \ No newline at end of file + diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index f5af3877..02234c49 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Pemeran: %s @@ -552,4 +553,4 @@ Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) - \ No newline at end of file + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4476b4a0..eca60da1 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Cast: %s @@ -551,4 +552,4 @@ Aggiornando shows a cui sei iscritto L\'episodio %d è stato rilasciato! Qualità di visualizzazione preferita (Dati mobili) - \ No newline at end of file + diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 645724fd..b24f0c60 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1,4 +1,4 @@ - + הרקע של ההצגה לפני צוות שחקנים: %s @@ -506,4 +506,4 @@ אלפביתי (ת\' עד א\') פתח עם נראה שהרשימה הזו ריקה, נסו לעבור לרשימה אחרת - \ No newline at end of file + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a3d1d434..20641b20 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,4 +1,4 @@ - + %d分 ダウンロード @@ -182,4 +182,4 @@ アップデートを確認 作品名 アプリのアップデートをインストール中… - \ No newline at end of file + diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 242653be..4b7b6869 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,4 +1,4 @@ - + %sಎಪಿ%d ಕ್ಯಾಸ್ಟ್:%s @@ -125,4 +125,4 @@ ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ ಮುಂದಿನ ರಾಂಡಮ್ - \ No newline at end of file + diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 7251d0d7..811a09c5 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1,5 +1,6 @@ - - + + + Брзина (%.2fx) Оценето: %.1f @@ -213,4 +214,4 @@ Сенка Подигнат Историја - \ No newline at end of file + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index b6ad3a80..d430d7cc 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -1,5 +1,6 @@ - - + + + വേഗം (%.2fx) റേറ്റിംഗ്: %.1f @@ -169,4 +170,4 @@ ഔചിത്യ വീഡിയോ ക്വാളിറ്റി ചരിത്രം കണ്ടതാണെന്ന് അടയാളപ്പെടുത്തുക - \ No newline at end of file + diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index a6b3daec..c757504a 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -1,2 +1,2 @@ - - \ No newline at end of file + + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 3595a24a..766bcdc7 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Cast: %s @@ -408,4 +409,4 @@ Bibliotheek Browser Logboek - \ No newline at end of file + diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index b5132028..43738665 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -1,4 +1,4 @@ - + Fleire val Heim @@ -183,4 +183,4 @@ Varigheit Direktesendingar Programoppdateringar - \ No newline at end of file + diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index d9feb60c..fddd4919 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -1,4 +1,4 @@ - + Plakat @@ -492,4 +492,4 @@ Oppdatering startet Programtillegg nedlastet Programmet vil oppgraderes når du avslutter det - \ No newline at end of file + diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 7fc0c887..a2a07dd7 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,5 +1,6 @@ - - + + + Prędkość (%.2fx) Ocena: %.1f Znaleziono nową aktualizację! @@ -532,4 +533,4 @@ Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. Domyślna jakość (dane mobilne) - \ No newline at end of file + diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 64ccb903..dd722f62 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,4 +1,4 @@ - + %s Ep %d %dh %dm @@ -529,4 +529,4 @@ Configurações padrão SD Faixas de áudio - \ No newline at end of file + diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index aee3de91..eee28785 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -1,4 +1,4 @@ - + aauugghhaauuh @@ -248,4 +248,4 @@ aoaaaaaoooghhh oooooh uuaagh @string/home_play - \ No newline at end of file + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 8cd24a3b..aa443783 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Distribuție: %s @@ -388,4 +389,4 @@ Log Browser Joacă cu CloudStream - \ No newline at end of file + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e9494040..9d8f6895 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,4 +1,4 @@ - + История Нет @@ -529,4 +529,4 @@ Обход ограничения доступа к GitHub с помощью jsdelivr может задержать обновления на несколько дней. Подписные Отказались от подписки на %s - \ No newline at end of file + diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 96fbaff1..a1afd6d9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -1,4 +1,4 @@ - + Našla sa nová aktualizácia! \n%s -> %s @@ -104,4 +104,4 @@ Na správne fungovanie tohto poskytovateľa môže byť potrebná VPN Stránka neposkytla žiadne metadáta, načítanie videa zlyhá, ak na stránke neexistuje. Popis - \ No newline at end of file + diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index b944b6b3..ce7d557a 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -1,4 +1,4 @@ - + Metalaya: %s %dm %ds %dd @@ -487,4 +487,4 @@ Bilowga Bilow isku qasan Qoraalka dhamaadka - \ No newline at end of file + diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 25066d7b..0b7ba89e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1,5 +1,6 @@ - - + + + Betygsatt: %.1f Hastighet (%.2fx) Ny uppdatering hittad! @@ -368,4 +369,4 @@ Titta på videor på dessa språk Föregående Spår - \ No newline at end of file + diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index b2334c5f..4370e760 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1,4 +1,4 @@ - + தேடுக தேடல் %s… @@ -107,4 +107,4 @@ இடைநிறுத்துவதற்கு இருமுறை தட்டவும் Chromecast வசன அமைப்புகள் இருண்ட மேலடுக்குக்குப் பதிலாக ஆப் பிளேயரில் சிஸ்டம் பிரகாசத்தைப் பயன்படுத்தவும் - \ No newline at end of file + diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 721c421c..cf3b1263 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -263,4 +264,4 @@ Magdagdag ng Account Kasaysayan I-tanda bilang napanood na - \ No newline at end of file + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 975242b2..74754008 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -577,4 +578,4 @@ %s kanalı aboneliğinden çıkıldı Günlük Oynatıcı görünür durumdayken atlanacak süre miktarı - \ No newline at end of file + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d9ec76bb..bd062394 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,4 +1,4 @@ - + Постер Постер до епізоду @@ -529,4 +529,4 @@ Обходи ISP За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) - \ No newline at end of file + diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 4a8bbf11..c19c6472 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1,4 +1,4 @@ - + کاسٹ: %s قسط %d جاری کیا جائے گا @@ -356,4 +356,4 @@ %d / 10 اٹھایا اگر سب ٹائٹلز %d ms بہت جلد دکھائے جائیں تو اسے استعمال کریں - \ No newline at end of file + diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 74e748a3..520cfaa4 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Tập %d @@ -524,4 +525,4 @@ Thất bại Thành công Bắt đầu - \ No newline at end of file + diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 6aa41ff3..3364ea86 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -534,4 +535,4 @@ 外觀 功能 瀏覽器 - \ No newline at end of file + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 47807259..44b93430 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -577,4 +578,4 @@ ISP 绕过 还原 首选播放画质(移动数据) - \ No newline at end of file + From 8fff809b792dc8f9885f71509bdde11427d9e378 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 16:07:28 +0100 Subject: [PATCH 097/104] Revert ffmpeg as it causes issues with subtitles :( --- app/build.gradle.kts | 2 +- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f70a575f..0bd56fe7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -160,7 +160,7 @@ dependencies { implementation("com.google.android.exoplayer:extension-mediasession:2.18.2") implementation("com.google.android.exoplayer:extension-okhttp:2.18.2") // Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3 - implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") +// implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 2aaa3619..e0885671 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 @@ -674,9 +674,9 @@ class CS3IPlayer : IPlayer { ExoPlayer.Builder(context) .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> DefaultRenderersFactory(context).apply { - setEnableDecoderFallback(true) +// setEnableDecoderFallback(true) // Enable Ffmpeg extension - setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) +// setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) }.createRenderers( eventHandler, videoRendererEventListener, From 019399952f4516a1478875c0ca1c3918e55f0788 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 16:23:03 +0100 Subject: [PATCH 098/104] Better subtitle decoding :) --- .../ui/player/CustomSubtitleDecoderFactory.kt | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt index 690d3706..974a5d26 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt @@ -4,13 +4,16 @@ import android.content.Context import android.util.Log import androidx.preference.PreferenceManager import com.google.android.exoplayer2.Format -import com.google.android.exoplayer2.text.SubtitleDecoder -import com.google.android.exoplayer2.text.SubtitleDecoderFactory -import com.google.android.exoplayer2.text.SubtitleInputBuffer -import com.google.android.exoplayer2.text.SubtitleOutputBuffer +import com.google.android.exoplayer2.text.* +import com.google.android.exoplayer2.text.cea.Cea608Decoder +import com.google.android.exoplayer2.text.cea.Cea708Decoder +import com.google.android.exoplayer2.text.dvb.DvbDecoder +import com.google.android.exoplayer2.text.pgs.PgsDecoder import com.google.android.exoplayer2.text.ssa.SsaDecoder import com.google.android.exoplayer2.text.subrip.SubripDecoder import com.google.android.exoplayer2.text.ttml.TtmlDecoder +import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder +import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder import com.google.android.exoplayer2.text.webvtt.WebvttDecoder import com.google.android.exoplayer2.util.MimeTypes import com.lagradost.cloudstream3.R @@ -19,7 +22,11 @@ import org.mozilla.universalchardet.UniversalDetector import java.nio.ByteBuffer import java.nio.charset.Charset -class CustomDecoder : SubtitleDecoder { +/** + * @param fallbackFormat used to create a decoder based on mimetype if the subtitle string is not + * enough to identify the subtitle format. + **/ +class CustomDecoder(private val fallbackFormat: Format?) : SubtitleDecoder { companion object { fun updateForcedEncoding(context: Context) { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) @@ -139,7 +146,7 @@ class CustomDecoder : SubtitleDecoder { val inputString = getStr(inputBuffer) if (realDecoder == null && !inputString.isNullOrBlank()) { var str: String = inputString - // this way we read the subtitle file and decide what decoder to use instead of relying on mimetype + // this way we read the subtitle file and decide what decoder to use instead of relying fully on mimetype Log.i(TAG, "Got data from queueInputBuffer") //https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388 realDecoder = when { @@ -148,8 +155,31 @@ class CustomDecoder : SubtitleDecoder { (str.startsWith( "[Script Info]", ignoreCase = true - ) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder() + ) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder(fallbackFormat?.initializationData) str.startsWith("1", ignoreCase = true) -> SubripDecoder() + fallbackFormat != null -> { + when (val mimeType = fallbackFormat.sampleMimeType) { + MimeTypes.TEXT_VTT -> WebvttDecoder() + MimeTypes.TEXT_SSA -> SsaDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() + MimeTypes.APPLICATION_TTML -> TtmlDecoder() + MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() + MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> Cea608Decoder( + mimeType, + fallbackFormat.accessibilityChannel, + Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS + ) + MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( + fallbackFormat.accessibilityChannel, + fallbackFormat.initializationData + ) + MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_PGS -> PgsDecoder() + MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() + else -> null + } + } else -> null } Log.i( @@ -246,28 +276,6 @@ class CustomSubtitleDecoderFactory : SubtitleDecoderFactory { } override fun createDecoder(format: Format): SubtitleDecoder { - return CustomDecoder() - //return when (val mimeType = format.sampleMimeType) { - // MimeTypes.TEXT_VTT -> WebvttDecoder() - // MimeTypes.TEXT_SSA -> SsaDecoder(format.initializationData) - // MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() - // MimeTypes.APPLICATION_TTML -> TtmlDecoder() - // MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() - // MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(format.initializationData) - // MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> return Cea608Decoder( - // mimeType, - // format.accessibilityChannel, - // Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS - // ) - // MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( - // format.accessibilityChannel, - // format.initializationData - // ) - // MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(format.initializationData) - // MimeTypes.APPLICATION_PGS -> PgsDecoder() - // MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() - // // Default WebVttDecoder - // else -> WebvttDecoder() - //} + return CustomDecoder(format) } } \ No newline at end of file From 9c40abc4d32f2003d84361828435683b031dc0e0 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 22:15:25 +0100 Subject: [PATCH 099/104] Added player intent --- app/src/main/AndroidManifest.xml | 10 +++++++++ .../lagradost/cloudstream3/MainActivity.kt | 21 +++++++++++++++++++ .../syncproviders/AccountManager.kt | 1 + .../ui/download/DownloadFragment.kt | 4 ++-- .../ui/player/DownloadedPlayerActivity.kt | 2 +- .../cloudstream3/ui/player/LinkGenerator.kt | 17 ++++++++++----- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 871c4f69..563c82f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,6 +98,16 @@ + + + + + + + + + + diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 7818e357..d054f504 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.net.Uri import android.os.Build import android.os.Bundle import android.util.AttributeSet @@ -57,6 +58,7 @@ import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringPlayer import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch @@ -65,6 +67,9 @@ import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.home.HomeViewModel +import com.lagradost.cloudstream3.ui.player.BasicLink +import com.lagradost.cloudstream3.ui.player.GeneratorPlayer +import com.lagradost.cloudstream3.ui.player.LinkGenerator import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setImage @@ -274,6 +279,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { isWebview: Boolean ): Boolean = with(activity) { + // TODO MUCH BETTER HANDLING + // Invalid URIs can crash fun safeURI(uri: String) = normalSafeApiCall { URI(uri) } @@ -329,6 +336,20 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // It might be better to use the QuickSearch. nav_view?.selectedItemId = R.id.navigation_search nav_rail_view?.selectedItemId = R.id.navigation_search + } else if (safeURI(str)?.scheme == appStringPlayer) { + val uri = Uri.parse(str) + val name = uri.getQueryParameter("name") + val url = URLDecoder.decode(uri.authority, "UTF-8") + + navigate( + R.id.global_to_navigation_player, + GeneratorPlayer.newInstance( + LinkGenerator( + listOf(BasicLink(url, name)), + extract = true, + ) + ) + ) } else if (safeURI(str)?.scheme == appStringResumeWatching) { val id = str.substringAfter("$appStringResumeWatching://").toIntOrNull() diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index f17086c1..8ce6bae2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -45,6 +45,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { const val appString = "cloudstreamapp" const val appStringRepo = "cloudstreamrepo" + const val appStringPlayer = "cloudstreamplayer" // Instantly start the search given a query const val appStringSearch = "cloudstreamsearch" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index f0340845..e80a8fa5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -24,7 +24,6 @@ import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.LinkGenerator -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE @@ -40,6 +39,7 @@ import kotlinx.android.synthetic.main.stream_input.* import android.text.format.Formatter.formatShortFileSize import androidx.core.widget.doOnTextChanged import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.ui.player.BasicLink import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import java.net.URI @@ -225,7 +225,7 @@ class DownloadFragment : Fragment() { R.id.global_to_navigation_player, GeneratorPlayer.newInstance( LinkGenerator( - listOf(url), + listOf(BasicLink(url)), extract = true, referer = referer, isM3u8 = dialog.hls_switch?.isChecked diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt index dc1bbba3..6f40e145 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt @@ -42,7 +42,7 @@ class DownloadedPlayerActivity : AppCompatActivity() { R.id.global_to_navigation_player, GeneratorPlayer.newInstance( LinkGenerator( listOf( - url + BasicLink(url) ) ) ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt index 1f242481..0b560857 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt @@ -5,8 +5,15 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.utils.* import java.net.URI +/** + * Used to open the player more easily with the LinkGenerator + **/ +data class BasicLink( + val url: String, + val name: String? = null, +) class LinkGenerator( - private val links: List, + private val links: List, private val extract: Boolean = true, private val referer: String? = null, private val isM3u8: Boolean? = null @@ -47,7 +54,7 @@ class LinkGenerator( offset: Int ): Boolean { links.amap { link -> - if (!extract || !loadExtractor(link, referer, { + if (!extract || !loadExtractor(link.url, referer, { subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it)) }) { callback(it to null) @@ -57,11 +64,11 @@ class LinkGenerator( callback( ExtractorLink( "", - link, - unshortenLinkSafe(link), // unshorten because it might be a raw link + link.name ?: link.url, + unshortenLinkSafe(link.url), // unshorten because it might be a raw link referer ?: "", Qualities.Unknown.value, isM3u8 ?: normalSafeApiCall { - URI(link).path?.substringAfterLast(".")?.contains("m3u") + URI(link.url).path?.substringAfterLast(".")?.contains("m3u") } ?: false ) to null ) From 5245eff6e12a781bb2e072e75d7e610252c4135d Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sat, 18 Mar 2023 09:22:07 +0100 Subject: [PATCH 100/104] [skip ci] fix xml header being slightly wrong --- .github/locales.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 04d9cd13..9ab272b9 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -56,6 +56,8 @@ for file in glob.glob(f"{XML_NAME}*/strings.xml"): if child.text.startswith("\\@string/"): print(f"[{file}] fixing {child.attrib['name']}") child.text = child.text.replace("\\@string/", "@string/") - tree.write(file, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=True) + with open(file, 'w') as fp: + fp.write('\n') + tree.write(fp, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=False) except ET.ParseError as ex: - print(f"[{file}] {ex}") \ No newline at end of file + print(f"[{file}] {ex}") From 4235c826a5f150c69af0f601e76855bbf12e9971 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Sat, 18 Mar 2023 23:55:58 +0100 Subject: [PATCH 101/104] Better focus on Android TV (Thank you ocean for reporting) --- .../ui/home/HomeParentItemAdapter.kt | 2 +- .../ui/result/LinearListLayout.kt | 18 +++++++++---- .../cloudstream3/ui/result/ResultFragment.kt | 20 ++++++++++++++ .../ui/settings/SettingsAccount.kt | 22 +++++++++++++++ .../main/res/layout/fragment_result_tv.xml | 9 +++---- .../main/res/layout/homepage_parent_tv.xml | 27 +++++++++---------- 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt index e6999c9e..58c6dbe0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt @@ -185,7 +185,7 @@ open class ParentItemAdapter( ) : RecyclerView.ViewHolder(itemView) { val title: TextView = itemView.home_child_more_info - val recyclerView: RecyclerView = itemView.home_child_recyclerview + private val recyclerView: RecyclerView = itemView.home_child_recyclerview fun update(expand: HomeViewModel.ExpandableHomepageList) { val info = expand.list diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt index 59a46264..affbcbb4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt @@ -7,13 +7,13 @@ import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.mvvm.logError fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { - if(this == null) return + if (this == null) return this.layoutManager = this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } ?: this.layoutManager } -class LinearListLayout(context: Context?) : +open class LinearListLayout(context: Context?) : LinearLayoutManager(context) { fun setHorizontal() { @@ -24,7 +24,8 @@ class LinearListLayout(context: Context?) : orientation = VERTICAL } - private fun getCorrectParent(focused: View): View? { + private fun getCorrectParent(focused: View?): View? { + if (focused == null) return null var current: View? = focused val last: ArrayList = arrayListOf(focused) while (current != null && current !is RecyclerView) { @@ -54,10 +55,17 @@ class LinearListLayout(context: Context?) : linearSmoothScroller.targetPosition = position startSmoothScroll(linearSmoothScroller) }*/ - override fun onInterceptFocusSearch(focused: View, direction: Int): View? { val dir = if (orientation == HORIZONTAL) { - if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) return null + if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) { + // This scrolls the recyclerview before doing focus search, which + // allows the focus search to work better. + + // Without this the recyclerview focus location on the screen + // would change when scrolling between recyclerviews. + (focused.parent as? RecyclerView)?.focusSearch(direction) + return null + } if (direction == View.FOCUS_RIGHT) 1 else -1 } else { if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index bdef14b5..5a3e28b4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -22,6 +22,7 @@ import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.RecyclerView import com.discord.panels.OverlappingPanelsLayout import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable @@ -531,6 +532,25 @@ open class ResultFragment : ResultTrailerPlayer() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + result_cast_items?.layoutManager = object : LinearListLayout(view.context) { + override fun onRequestChildFocus( + parent: RecyclerView, + state: RecyclerView.State, + child: View, + focused: View? + ): Boolean { + // Make the cast always focus the first visible item when focused + // from somewhere else. Otherwise it jumps to the last item. + return if (parent.focusedChild == null) { + scrollToPosition(this.findFirstCompletelyVisibleItemPosition()) + true + } else { + super.onRequestChildFocus(parent, state, child, focused) + } + } + }.apply { + this.orientation = RecyclerView.HORIZONTAL + } result_cast_items?.adapter = ActorAdaptor() updateUIListener = ::updateUI diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index f9627e46..1ef3cb55 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -157,6 +157,28 @@ class SettingsAccount : PreferenceFragmentCompat() { ) dialog.dismissSafe() } + + val displayedItems = listOf( + dialog.login_username_input, + dialog.login_email_input, + dialog.login_server_input, + dialog.login_password_input + ).filter { it.isVisible } + + displayedItems.foldRight(displayedItems.firstOrNull()) { item, previous -> + item?.id?.let { previous?.nextFocusDownId = it } + previous?.id?.let { item?.nextFocusUpId = it } + item + } + + displayedItems.firstOrNull()?.let { + dialog.create_account?.nextFocusDownId = it.id + it.nextFocusUpId = dialog.create_account.id + } + dialog.apply_btt?.id?.let { + displayedItems.lastOrNull()?.nextFocusDownId = it + } + dialog.text1?.text = api.name if (api.storesPasswordInPlainText) { diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index a29dc192..5eacdbe2 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -199,17 +199,13 @@ android:id="@+id/result_back" android:layout_width="30dp" android:layout_height="30dp" - android:layout_gravity="center_vertical" android:layout_marginEnd="10dp" - android:background="?android:attr/selectableItemBackgroundBorderless" android:clickable="true" android:contentDescription="@string/go_back" - android:focusable="true" android:gravity="center_vertical" - android:nextFocusDown="@id/result_description" android:src="@drawable/ic_baseline_arrow_back_24" app:tint="?attr/white" /> @@ -385,8 +381,8 @@ @@ -423,11 +419,11 @@ @@ -568,6 +564,7 @@ android:layout_weight="1" android:minWidth="250dp" android:nextFocusLeft="@id/result_movie_progress_downloaded_holder" + android:nextFocusRight="@id/result_bookmark_button" android:nextFocusDown="@id/result_resume_series_button_play" android:text="@string/type_none" android:visibility="visible" /> diff --git a/app/src/main/res/layout/homepage_parent_tv.xml b/app/src/main/res/layout/homepage_parent_tv.xml index d0c88c39..9dcf0bae 100644 --- a/app/src/main/res/layout/homepage_parent_tv.xml +++ b/app/src/main/res/layout/homepage_parent_tv.xml @@ -2,33 +2,30 @@ + android:layout_height="wrap_content" + android:orientation="vertical"> \ No newline at end of file From 0cbee7068326a8f215f53c45d9c85d3601eac468 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 19 Mar 2023 12:51:54 +0100 Subject: [PATCH 102/104] [skip ci] Update locales.py --- .github/locales.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 9ab272b9..7d6d6b90 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -56,8 +56,8 @@ for file in glob.glob(f"{XML_NAME}*/strings.xml"): if child.text.startswith("\\@string/"): print(f"[{file}] fixing {child.attrib['name']}") child.text = child.text.replace("\\@string/", "@string/") - with open(file, 'w') as fp: - fp.write('\n') + with open(file, 'wb') as fp: + fp.write(b'\n') tree.write(fp, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=False) except ET.ParseError as ex: print(f"[{file}] {ex}") From 52d495f425fa3a305a2c4018c36b93e9542751b5 Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Tue, 21 Mar 2023 20:50:13 +0000 Subject: [PATCH 103/104] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 3430d626..e3d033ba 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,7 @@ + Download and stream movies, tv-shows and anime + Chromecast -### Screenshots: - - - - ### Supported languages: Translation status - \ No newline at end of file + From 67b0549fd2a3fe4b94d0a6f03f490bfa8956258e Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Tue, 21 Mar 2023 21:01:47 +0000 Subject: [PATCH 104/104] remove images --- .github/downloads.jpg | Bin 59461 -> 0 bytes .github/home.jpg | Bin 139384 -> 0 bytes .github/player.jpg | Bin 49418 -> 0 bytes .github/results.jpg | Bin 98562 -> 0 bytes .github/search.jpg | Bin 152135 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/downloads.jpg delete mode 100644 .github/home.jpg delete mode 100644 .github/player.jpg delete mode 100644 .github/results.jpg delete mode 100644 .github/search.jpg diff --git a/.github/downloads.jpg b/.github/downloads.jpg deleted file mode 100644 index ca14a664a2ce2b07cdc366343d54c690d9bacc01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59461 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ℜ5F2>W2mS%`!tt>Y2ggw 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

6R?OyVHq`Nv6g|; zs#O!(uJN|6l0_}3C=G$3Q6+PEABvDX)Y%`&aD*hJjNJ)cjlfNT;Z;*FvyJgEoBVoA z5lkp}ZGtAg`d|rKphsQ5{LG!qAw1Fsv3Y0lL_SstJ;IGt^k6E7Fhh5ou4UMCA;~J` z=e5Kr?&7cSgcA>gMrMFENSd>g8Tl!&i)twfMzG7ypOaJyT4w#5&_wXp%S}U+IVWoD zrna!Nv4d*oB|6AvDBh|^9NrDlM{SG~Qy+y>&d%XuyJNk-d~Bc|$Gm38Qo+T_>eJ>i zd)&N`mVPToRMldANB~P-LV8D@xDH-BCu!=8h zY9w+DD@%n~Qtn)4Pq0vP!icc6iMdNsr{t256%0$KpNg0DDV_zRFi$d|L&W&^El1m(Vr-4&c*xm6RnGMxyIW?B7Cf+@I^u`tGZl zWHp{RDT$py?z!+xloLbRbyLuR*rv_REfto*?6PGBlkypVMUx7Rz#lLNR2-KSp)oc#|RdlezF%LVD25a*5)@BS{cZ zp!QM;2dUv8(XsAtS}AfDVq+tC_6N%4KwveWvTJqExIBKt8QPVjzz86$>e`^_mxp` zHQk!sjXMNQBS8Yey-9Fr+_iDH;1GfY2{cZy;4Y23TX2^k!8N!$K?4ba;r(Q0*4(?^ zyXMEtkGuAd)BALH)l>VFoK?H_emqB8qxuOotM!(;9RwQS2NU&J)`dl08Z_1r{PV&4YC;}yj`8Vsb z4b?FgJw}VWx-lK3YkuY+a-LP&NqW+)mRIY=RkfHV{?i-ZJ9fxkjv_X$cB_8;!2Vi%@oQ@yOHwrGPo{q<6i%mI}JW6r{R3&`a0zB`yl_UoU}=yOH?TlfU%4c z>|)6K9?j#YGwaQ~-yXhB2IM7MGtpJWDf|29^KARXbDx}nxE+oLjk4_CuMl30A2b88 zzT-nFCNPgV@`z)4q1u)L{OW}`zNikbg0#AavrLfYe-niP>sUE#; zbTRO>qnBF2Wp*NTl?l0qGg$_=^XeU)dhq;Brd{($ls+N3A&9C{H=Pj-he5ekl4K z9kVmy#73fuwhjY5Ki6N0P(kQM)Tx~4?Cb%)P`Fpg0qJZ+CLeqNuOYQ?3C8`#2 zG>Z#Y^Lmzh^n^$sG{27PlS*TD5cn}V_{Ka205W9yxS$ln*#(4y z;OB%E*wxe<1o`G4Q+7abQZZxmD^wBZI#26FIy%+IWV^(3w!0+(e{w zRB8*4W51vrEQhi8$5bv@r2)%)We?jl(TD(opOOF!Tf;_Jxb?3KN?L3AqWG*L5P*y> zRy5Q>9NnH!a+y=!6L}g9gB|WOpju^Imue-e!C9Nys@Aag$g+&5t+D|&18w9^05fZA z5_h7S$>bn5YfN$ca|8mEx!;3-U_yaIefsj#1yeP#a36=)m+2r-Qcie_g?pZs5U2x7 zo|-@b=X?mim_14}GcLaF$wi&-k@OqX57*o_)%r2a{#pvhbqcg>Qbs*%p7#^%UVvXx z>5}S^N-XeiEL+c&;#Z9}1-pWS<0$eO*$nldYYXU=xj7eTs9dqk>LnpiN^EN9EG>!f!D#f~VPl5YYhF#*b2J47-mUGc&u+4mZDX~l?6#W#97_!1QZkpU>UVrQ4(bZCipF^wG#dJm|kAg6%5#iVdR_t4O(!K8I` zn?$%yRY;T`v*8%2tvENLCpf@d=0Dt25!<$m8A+!F50}_~DgzXHOpex>;y6H~yLX2L z*vg&Q2)+QEZOD2c2|yfMkZGvp!)%M>zVI_y*{#p{A80Io1cpj<^-|(V#z|rry%=-G zcSTm8ZPQ^sELE#uUKbn*m<=Ye*k}A}hkw5uKFpm{dJ;dpg(b{CE)2Kyb_EOFnGefT z8Ny=10m37G^BUc}2;t!DrT&k_u|c};!OEI~%RFTUIS5=wR4M82KyBwWYmIGrwrGUk05!Ai6{6Dh+gfubWt(yO2%R>8XM*$hoi=X#N{f8h7_(Y z#F+}EL^Z`is19xAVGgv4u{vu^QtEDe^Yj5Go{$y`ZPO#hm*k6HHMR*`j(-7Ql~3x7 z=x=Hf@kohlw#{1oATLcKieh+Ud!@Gx>k0h91%fE_YXE8&DxR^nSrq8&f+6A+n_B1) zZ9I7#_KFGph1L|kP_yH~#>BT-)FONYVSFHs-tU}oRyIs%DO;^=<{Tx3SEkY7LkUNycmJJI_=mV)E5NXI$3IUkjpOHH`l!0nM3u0ouPHZmA|1G z#5^huY+iWWjpb26&KL;P`#5WCUT|=U^=q#LE&gw_wpF{Yg8)COf74;B13XBcxV+gY z_Zsf_Zl~K63=k-FCQ{|Rw%oTo1d&r z#i^}LBBDqQMjy?|C)TXrzN?~WIM~w^kze=FU-?{4yEnq7SNg5)5Z@D`Yg2#cF?LV6 zH$(l4{bJhlXOO|IWr>H*oVqWZ39MtGIQ90I|L0S1))f(Gj%!Cp5a|)n#A~b$bP4r^ z?T}_7b8FiI7f{Y3<%vj|G4*Cc$>RwKmDp896#e920CFIF$(t1Stx%NK6heeR0s689 zxv*5HM4jFii5A@QwIsh;h9$X_!SQ7LFH|_-vTt#_zkiQ06#&9Huc#9I#kphVz5JH7 z1}p=UaT@fG(4&*eqY%W>qvzNxfym7mSJ0(3STk#t$yNa-mO(I^ZEw`quup}_j4v2? zGcYi)l$b-f`h@Xc1Klqm%9%S5UtWIX&$6utzlIHTlRRFJ@2tJPaL@XeD=4KRsP`2v zxozVdti3;>ehl|YXxYfAi9=0{@^EDx5loHRmnY}=t&*NVhI=eR%DneAi(@8+mM5cg z#hH!sqRlSAx(z&;=w;?@cao9i{+abA`-T5fmGJVvJV+urXy+bD>HW_Jv^X0e-eK7( zAH=Mml>m_SdB>A|*1A_YwW@U*8=Cg*u*I&S-H!OWU#KQT80w_y`43mT-%4FmlQMej z7^!(qNYJ|@DB(u8gK(?d;ELEcQdFbdz17C8VvwMP!W`prpK#+j8%ul6` ze;4!6IY;n*C#eGV*7#C$VSurX8Y_oZnv4<|8DF9TN+5oS$>ePLknRX_e}SAEr0@Lt zx78eiqzpm$bc9vF1UE_?OY$>ySM0aM-wk-EOv}>2P2}%lJhV5;D=cnX1(@f*;2J z*T-M!WHnMmYEsc=@{ydew5|d{C92e7K{Q0FCf&&#J>Ruzx$l6^&wfj27t=3lgK6tA zCpcnJ`@wZ^XBV03qYH8kBjx%j5XlgxlkW-gUW_32FDtAHEn*VD3(znr;Kxd!4JKiq)Z*64{}^cX zs|6nyRrHk&vxsR_7?!-sOR@{EbQzNo=5xPgF*h-hDcIrjQe6rWvnTpKK7~UpJEoi! zz}s;->sU=gme$;lvE|Sc)HdOOyr{xFwR#X&Dhn?iSxr@xG77S!w?i)E?0Z!ft%Bfvq+l%R`ds6o))CF>E3xbcr_$dfbmv~tCoIq~{v`??BL||Ld*4PfV zKwDSP%krv=u&T8yr3lhK+eF+z5FS8S90{?HT#Uc3YVvequ=iKoh;1!#tBJLRXZ5=a z6$1cGKmGRT$iBC0$TC1sQ`9;RH%1kDaui2ZZ);EfNYi$@z6eC4>U2AGsM}lNI%S_zq z3%?_cr2< z??wi-x^pjS$$dtI7-H$m)XhRzNjTZSCbQ8G&U7#2uw&H|$u}2xTJQ^MZv)X!FR!}7 zC{&FoMkwe7(iB{RCi>6yXt@AH{U48}w7#Ejkn6T^_$IHTh;?@;EB$>?m`Yy$#1z2hAo`Pm_6R^=di-8o?yB)iq+S5dn;Cll`M@n9AiFTP>RZiQRgv)2OGt74`EM zzzTpou3v%X=kRQbfOSPBO5a zF<35HXO6Oh!z=S!sNfNU$DH&?7tW90T)X&#efy>kP6|h^6+lDv_iAjMhkmZKaN`D+ z?nMloP5)H%)Z7;Wb6V-L`2cLhP&?JsK1l{It$i_S6S)3-B)7ce84m@enq+vUAh@?wpf zr13!BpPdK^ z0Z+_JtKFiv%0$gvzA>9iTn-z+j#m00Y^Crr5hHgc%`qf67Fs&*ubqTx4{1G}*o>b^ z71uWlzX^#-BjOCAn6i_0+b1h#847KA_WN#DI*L?Z@hm?xI*69L6!zIxVQI%560i2b zzoj3h^y~t?zZ|$|&~l}dN(co{6~2*t>4+^yyc@>bxIgXP&xq(}_xMKY7DaP+X)T!N zA*%!Fbt4ThgzDVnw^y>N6RYW9AL>t~gb5m~vpQ#Dp3dwl}gWi ztKV@##Uw13K-|b!Wvxv_Ub6p@gLpaGXlk?VxbRksRKlTJ?gnqQ^mBDD7Li(HF+RQc zL`!q zOJl+r-$+hyiC;}7rxW<9A^8}xnyZDXugbCD&NHbG&q3X^Fs1*beP-szgLR-@78qE= zg5QW1k4Y6nLNXP#i|Dv!>kp)>l{_zJe3Nshu+>z`N39vpBDhoY0-sqfao_JDEEGjg zkWlS#>7pMGN=3>-@{LgPxTliE%t-FOPf~NKQX4_`%i;}tUJX}*#xXrwj>siGW|?_b zLwzfwGB-zYvWErTBuX>ddY-73|jt6}QPH$mliejGQIB)XFm2sjf ze_^(k5(b`#h7I>3v-xlLgyt)KHqZ4e=4hA9O*q%nUkpFCtH(_7g)c;Z<-h}A=CI<( z@O0=CoP+XHb=QDnSU?Ioxh0e_q$lbJSu~MsT&&__C1EVsQ*9DCtZd;tV@jCyS0|NDRcv(3Z*XtPhgU#;6ok;MN2Nc#3>-`in{E{eu= zgYYi%V&QWb@yp?dn~h2P>abqlg6>f9!xk@G4Gsl~0l>9PsISd#^jh{5_KF^#gcNwl zhN3pFl&|CL)Ty$WEjNKs?S#|dJ(hgE$?(C0%TLbJEStR_Uni*dg-P;fQyHoz^Awmq z%Z0WGpPzkp_0C(NjP}7u@l9~zqn_9q59R#GxN0w+Gm@Gj@~>NANr$BBQQvXFET0#4 zFiisbt=R*cpn0?G15_q{RA|roW&jKCgJ4<}>H*1(LZV1z(%_%dy*BD5L*4QQBIElc zr8Se=K)Y{+un)7PKPFk+@KLuV#gtGInC^37|I*?e+} zbn81Ma1fEX$)#xJWlP?0&*>mKoM8qN|H<4w$b)84l?(96-3A>?koZV4l7U@{>d<88?H=hl{Iufl7RRlW<=w`dm3K7^WkKaK6I*^dJCx&|5uIs z!LiDyV(au|=aEr@}`QcS&T-+4_Ij zl*3rFYdB&j?0?sUX*(^!U z`j@D{4!+?vTd(ftg>k?Sxby`T?M!(-(?);T-tvR&*Lbz-)zV+TB#qrg2O`!mjjmPX zT5KjyTrua_?nVztOK{Szipm)g#j%nv1R-HUoJ)5fr}wzkxAz^2gc{gYF-f_ZhZl_+ zrvJ`vwt8IRCOdUrGTz_CB%VwE1IBhm8=drBVf!y-tb#HP6U}c+`{GsQ2%Nz`Im3c< zItLwS^EcWLGr0TzTaS2BBw8q^$Eo&L^+^#|*ycJvWuE4~ry1jNu)}j}jp}YCb8+QK zlW`jVDMXo=UK69knVf61<}}KcPHMfrh{sKUGqjF!?U0$6URy`n!+;0}FRPqV)H|+V zmK|%CYX>8N%N~#p$P6bnw7+o+$JxL0`piF=Rr}O4yeyYC9s$qOX|x$M7R|-F9eG=Z zx-}&^z^K<=)fmQ(fpfn_55dd&q;%7Go9x`D#wBOB@P&hiuyU+Q|XQ zj`Amhsc3FKwO%^(AE6Ph9Q%MX>H6tsj34vdr(Yqm*ml-@LPtmg8%7=Zg z-;JSYvI*ozi>S=eYEI6BKSxjrHsPbB?sB8 z{z&-zIm;s;u)*brH-R4L%U~_WNUDR!TPFK%JHf#Ljgb- za&pP(fq`p`z4Mw@UUPvY$Lhj0N!b)?jCFw65O>Uj5F~_RgS)@J__mBfxYG zL!c|LTjYbx`>yVnlxRKo=3|q)hks>XFWJa`ZN5rNUuJh7R$TyX@jIptLy`(?u8g_OZx{h!Ic2M?+*_p_|Hu(-6I*O`BooZp{%9X zbxIo&uh`{V&cKXXd`rQlHSdZfAuzKoYy>Ws(KPc)9 ziI)6rm!g*^%f7uehOTR zm=*j(bu5^(f_u03*)L$u=g%vh-pPQybn=g3>&ZR$EdAfT9$M7CIs#oL1tUHwHv!5z zs*7OmaK@HbZq6DZ>!=DR=(cqQOkPsUZ!ja(t#|RTf;nMUO2no`&-XTXq8i4FSlNmbvTYM~5%&vals4|PCP z9qO1GfC3BpxF}q{NjV3Gd`*YBN5Kl?l5jkfQCz}tW1BQpM^?$h*U@m|Sfh&cx!4kYjF}SED~Z_ zE0@xzv3ycF`7569A2i_q@$v|`(a`jj{QdH8C4UBK^be(&&!?YqH-95J96VRzdRQjE zyube4&;G-AvdMp9iIKe@0Vz%}aGfGE)`TaPuMGXo9IQ@+^GJt^wo|25qIsofN zwnLlpi+YXP8Osmc*7JP>wy#0a$*$dRn#TP|KhC=V|BP;Bge62YjI`ml7{hXd19$^> zq4xf$0Bise+PDbEkqHELjeEN!^^FumHWwaHelSO0bU(O#NK6F6W@p4 z1%+?GRzEFb+EOJV`PVFtAi**ocm*j!1GDiqI-CEf>}M}Oq~1B5^|8s%n?zwc#PWyS z;sQ`6zM`}ggZ)#w*$B{=kt>$kOi}FQd7A`}>;;E8Q{J}(0tX7~bP%7dW69fXlnUo_ z%1O);)fqkb&ht@HgXO}j7Y}N(4C#WilI=!aZGNGB)V|LY#{1L3a1YNhe6}UNC!M=Z z^^Giz9vK^K@Fv#XyJ*>KxxA{a)P!w1Q*iYZ$ahI{atHj!-XCai`$A{)%foYCWJWmA$f-uANk}w)Sz*W%_q$OpXJi znz-%U){`TbnuruS|F%Fx%{)oWphlUvmYHK%+;IsDlO>%w+e%Ot1!baPF=!D9JNylR zO}F5wz$rS}EDMFZqSSl`9SuDpd7s=tR(P{n28)HJaYDdKJqeLEpLXx!uBB(bewxhN z=5zC`{ILnOmn}8K-?cZjp3PQvVPe5POy0jW)jAAO_s5=&VxmfuCr`Q}|6>Ix9TOhN zvF9IfLv)O~5XdX!+cnR!4>hjyz9xJtOnyAb1VFTqw2Dx}Z5!~Bl#>YCUt~SJv4yH6WOS!9U~4k6nvyN>D@7 zVwj|Ab!39Go3_xus*GlWxJW3eOaqwV0MyfTc4%6z7x87j^y(khi`9c>gq|GX+(R@$ z(tUrq#>3SO=?sV7Pm;+!gKPGhc_-<(Mo8I6;JrBegB2Q}Rfs=ZcqcuFDpB@0-iGwrl{i?9Ax z=zbe)U{7XtN1=trgNt_~_imbpKgRu?u)#0;D9|^`@aA#WaAt~rN9&y)kKt2E(>wxP z|8_O{Pu40GUYDHAr6>&d54<>d7yA`-Lt{X%T_4cjiv!)aP8l024-w$s3B*Z(0M?-0 zx$jFzM*8qyEKC1k*+M4g+luV#|S%l$10++J)jD7Ad_r|kD{-0k&jTQX? zn>pTUgn9_|D9Jy6w6m~<1OWqzzCYX3bc%L_O~WU z^?3w{I|>5Ml6RI zF{d=9ord~0ULKXxky+kD2f6wVKG}FmgyG>4Sme%pwc3f@blbP&qHev_b@md-4X_wT z)X_Rhf1yvS%elQ%lS^l2BCLJ)2p}+xAaxJc4|qjECQzYJ$4&`9g`Ht_%~+$BxB1b- z!9^KcxV9P9n&+3nviTJLi%GGu6Yj`kVbk0Fr85&VK>=qQ5_YJG#Ms%#P2{=k4|2u+`Xiv~qcPsrwmDHN92*1S@VtW+y;7@eZ*JB7C-&`d9<*1XYYOJW zIHpaF+3Extms#3mT;~3r&~a{(yCrsq;tR!{f_IT26>V+f0UDMVMU+ItdTE>;#0vT$ zT0Sc{oxHQlp*w+<-nB|j=9H6ShvL+!HB!bOFavw)tUucS8Zgix3HTGtQP1l`wolad zoidDdb6-rADqeptfx2t+XVH+v0Z!p&#uBk}YRlYM4wSesFJeNUPf2CFNI7S&x)9;w zE^KQAL-f1g;F-_*cyo715(l>2WSudwysNM?%37ll?DmFa{o+X(=kaJWNnlIFWTX?b z*izc16l>y3YEj^x^it7+^0hp9%V`rDro*iwXYFy z@9QRjCCl{4AYd}_w6VkQPs=~XjL;Aq&FL6)K|{&Kf|M(m{VnXEL=^|N@F_&h6Jw!&@KSba=XF<|)=k-kjf$F6T?9U?E zEaS2kEVWb`TAqqIo|XpFO}?RPRah>jZxeJl(urMgi+n>9!WpY*hCQWIJK$IZLyv%{ ze>4o`!yZ3qW(LR}Qc3hulE^1a(_d5U!t&|vv;G-;+n%G|s;T1wFyd>cg2~W6xi0fE zYb6xioEMlQrIobmM2HecSR6I{nTyNi5g?%*HJ*j;ty}{kuJWkWlPsDc&{~w+p#aVf zVm@dFp;7zd640I(g6%EMyo0tLu7k1NZfdlwo%NLMFjLbsYO5!vjQd!?7B=-5@ zUH^h-tZNn(6JEjAw#v`ai$X-~c*}ThyyYsYRPrDe-LB+Zk;0?R`_naXXX3O{g{ipX znzg-dR`L03TMi*9x?l)EEoQ;S>Aeb8S5Be29`1;ZW&`W8BM2??mqf&K>EBKI5-rDr zs!>F=Sr%o(FAa8qdi}|cY~NCn7QU=Q?AQZtNp!mQ&|hsw#Cl$M-1dF@79|~44o8uQ7ZGrCsKa6R*mKNi1&r`jqGo5^hOXJDA^Fm`8H9JTbg6gk2|$3resn-@9G5()}R<3vaj)5lS{GqM~#g5hTK zBv_#ACClexs5j}{X8W0g&N||hlh123D967;=p^y(1Qli!7pO^Wrg?D019fh z6@9-R-lqq8f1dNpL1ixrZ`#&R$l5op+JQrUd{4VT(pCQfkrymfw4&0I?Iv>gaprf) zPmR<6j;fS4^AolFBj9NUe;r^K+}g#9R&>Br{E;C=zkY1}!4Zz7gVOwvT@4!bvkt>l z*=K0XT`92Esb*QYzx3oU z9fVVhq1PIy0Fn*(1Y`C=OUx?GkAUsF1s;>JfDlY#pjLpNWO@D564EgtNl^3jK1f0I z{jK^`CY(eqy4$m(XdUQl8+ji>7X;`A&x0+)u(?AN(xgkO$E;1allvS+waGkS97t%RGPA!GSza z?h#=9!gxwT)oYBa$tL0{6X~5t{lj+f&a9_-Le$7S8%hP3CyMG=zE{_psQm~}W%(tS zbeMN*d*m-G)(+$CxYhII#z(+x!gHU_uk^y}802mei;Ks5)1qIXU(F{-wcvI9Kgw%I zo}0!p>&qU=JbxfU3ks*w0PZ2b?Z3JQC)-|y-7AW*aZUX0P@6TdC1h^t{30kHtlmBB ziL{qrAqVF(-BYV#2f&E+jdJ!QDwnR2n{C2y0wdM$Z`Exy>nED=xP z;YVE#{p06!>uB5yduF;cj3eU4H#$gr7j#`VX*p_WGWOzfP#c?95$+`1Gw&0C0G)>nKj_RGCXIhK!%fQyEH9dzOH&5wNq zWbXZiVPn>_4DQfThB}cSh?jI}@7ea@#X3(Q)@Uh^m8Av^cturov7 zlVk6M;jb-ZSgV(Iy=sn}rBD;{yR1HQc>%89A9}57zsK~X79pMPUf)=$`sd9W)y14v zz33oZOoifpmindO*vstL_PH*7a>TK-X|YBN#6x0v;{$=g#69?slN$UuHbTfDiAOq^ zC#`c^FGLV&oz30zn8@p>?acOMO=o{Z32}X_B|`kKJC`$XCl!{n8J==rZm*sCr2iR@ T_iJ8Mtj&4^JWxLkp~e3JSo6U! diff --git a/.github/search.jpg b/.github/search.jpg deleted file mode 100644 index 784bec892550387282d3551e7ed8d830aa0a1cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152135 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 !Xcj4QAvn?KT(Ckvd+4 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ξ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

g!fC#;0+fen{k3i*HT`@Uz5%PpHG z!Cm^L3(OC+m%K|#bEPy-lJhBM~Nhg5ex48|bhgu~Jm{m@;L+cIq0UAwAc;aHLIwomF`!k@2Q^ugeMy&f_ zSc31DsJVrLY4A)4TZaQ{h#6An-F;==kvWO^ZZmP+N=1%o*ha+CLfQ1=T^X2wx{H6w)U8^Z5N0pAp7j5l9wy3OOan2%J6d} zl&_->J=#&70snd4wW``5qWzLV#728($1=`Xv+UT{$+|DKhr?Bw^o ziyqac0<8MrlZ`W7sYB!4;4yUB12uQzun$b+$y99s7~VP+H&oro~fG@pt7`nHdZ7LZ*ie zJ$h!QC~YEqbX2!?ElvKqY7N2J3pevcj>0B_%{xQSr6HdCXfibHKaW94gS@PHm9>3h z!+mV{v)$Q3A8iM;1a!Va=1ZQu1Ma%Oc^hBxXDUja_cB~~JV?T{OEt-K&mM7Dx)Ua4 zlYeP$b=9XiKAkr8MR^8aQeS%ID^{i3p(nQ^_aoUIj}p^7mRa=~CkkN(5=j(%=!yyt zmAyR(oG0!+%TAG$MkdzM-X9MrZ@on@RbW#~obSkI-oBzL>xe5R35q7|4^d><3Mtx;=x0*<9KreEZ4I#e#E=E@tWQp| z!o-!AjKMrW@iLLlPuME-0$fqGRKI@vej@Jx*}(ovkhgKjzY_7@$*aNg;IeW1F~w9xi-ZXDsAa%y zusdGbGh&f#w(onos@&|`!at21S&oOgga`#djFxy-Cg|-d$>ZH`YF!EUCnT$TLod0$ zpT%8K9W12#^09$Cc`ifu-s7IoXga zUp`v%@Q#T}cG#J3rmZ`ZODfdT+Zba$ob#Bw%+m8&%Jdx@NTS0lN3^@m?(9#*7BKl@ zN>I&7o8g*~g>9!l3z1zKkX{<(FYM+cKfV_dzp!NL@TF56(c;ELiNILo4E16!&-fU=wyAYk^R}=pQoQXPN diff --git a/.github/player.jpg b/.github/player.jpg deleted file mode 100644 index f6959cf31d27e5debc98e7d69b54688c589803a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49418 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�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