mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						0ea243ebb6
					
				
					 11 changed files with 565 additions and 52 deletions
				
			
		|  | @ -3,6 +3,7 @@ package com.lagradost.cloudstream3.subtitles | |||
| import androidx.annotation.WorkerThread | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleEntity | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleSearch | ||||
| import com.lagradost.cloudstream3.syncproviders.AuthAPI | ||||
| 
 | ||||
| interface AbstractSubProvider { | ||||
|     @WorkerThread | ||||
|  | @ -15,3 +16,5 @@ interface AbstractSubProvider { | |||
|         throw NotImplementedError() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| interface AbstractSubApi : AbstractSubProvider, AuthAPI | ||||
|  | @ -9,9 +9,11 @@ class AbstractSubtitleEntities { | |||
|         var lang: String = "en", | ||||
|         var data: String = "", //Id or link, depends on provider how to process | ||||
|         var type: TvType = TvType.Movie, //Movie, TV series, etc.. | ||||
|         var source: String, | ||||
|         var epNumber: Int? = null, | ||||
|         var seasonNumber: Int? = null, | ||||
|         var year: Int? = null | ||||
|         var year: Int? = null, | ||||
|         var isHearingImpaired: Boolean = false | ||||
|     ) | ||||
| 
 | ||||
|     data class SubtitleSearch( | ||||
|  |  | |||
|  | @ -3,10 +3,7 @@ package com.lagradost.cloudstream3.syncproviders | |||
| import com.lagradost.cloudstream3.AcraApplication.Companion.getKey | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.setKey | ||||
| import com.lagradost.cloudstream3.syncproviders.providers.AniListApi | ||||
| import com.lagradost.cloudstream3.syncproviders.providers.MALApi | ||||
| import com.lagradost.cloudstream3.syncproviders.providers.NginxApi | ||||
| import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi | ||||
| import com.lagradost.cloudstream3.syncproviders.providers.* | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| abstract class AccountManager(private val defIndex: Int) : AuthAPI { | ||||
|  | @ -14,6 +11,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { | |||
|         val malApi = MALApi(0) | ||||
|         val aniListApi = AniListApi(0) | ||||
|         val openSubtitlesApi = OpenSubtitlesApi(0) | ||||
|         // Removed because of cloudflare | ||||
| //        val indexSubtitlesApi = IndexSubtitleApi() | ||||
|         val nginxApi = NginxApi(0) | ||||
| 
 | ||||
|         // used to login via app intent | ||||
|  | @ -39,7 +38,9 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { | |||
| 
 | ||||
|         val subtitleProviders | ||||
|             get() = listOf( | ||||
|                 openSubtitlesApi | ||||
|                 openSubtitlesApi, | ||||
|                 // Removed because of cloudflare | ||||
| //                indexSubtitlesApi | ||||
|             ) | ||||
| 
 | ||||
|         const val appString = "cloudstreamapp" | ||||
|  |  | |||
|  | @ -1,13 +1,14 @@ | |||
| package com.lagradost.cloudstream3.syncproviders.providers | ||||
| 
 | ||||
| import android.util.Log | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubProvider | ||||
| import com.lagradost.cloudstream3.TvType | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.imdbUrlToIdNullable | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubApi | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities | ||||
| import com.lagradost.cloudstream3.syncproviders.AuthAPI | ||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper | ||||
| 
 | ||||
| class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | ||||
| class IndexSubtitleApi : AbstractSubApi { | ||||
|     override val name = "IndexSubtitle" | ||||
|     override val idPrefix = "indexsubtitle" | ||||
|     override val requiresLogin = false | ||||
|  | @ -42,7 +43,7 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getOrdinal(num: Int?) : String? { | ||||
|     private fun getOrdinal(num: Int?): String? { | ||||
|         return when (num) { | ||||
|             1 -> "First" | ||||
|             2 -> "Second" | ||||
|  | @ -83,13 +84,15 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?) : Boolean { | ||||
|         val FILTER_EPS_REGEX = Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))") | ||||
|     private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?): Boolean { | ||||
|         val FILTER_EPS_REGEX = | ||||
|             Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))") | ||||
|         return text.contains(FILTER_EPS_REGEX) | ||||
|     } | ||||
| 
 | ||||
|     private fun haveEps(text: String) : Boolean { | ||||
|         val HAVE_EPS_REGEX = Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))") | ||||
|     private fun haveEps(text: String): Boolean { | ||||
|         val HAVE_EPS_REGEX = | ||||
|             Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))") | ||||
|         return text.contains(HAVE_EPS_REGEX) | ||||
|     } | ||||
| 
 | ||||
|  | @ -104,13 +107,18 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
| 
 | ||||
|         val urlItems = ArrayList<String>() | ||||
| 
 | ||||
|         fun cleanResources(results: MutableList<AbstractSubtitleEntities.SubtitleEntity>, name: String, link: String) { | ||||
|         fun cleanResources( | ||||
|             results: MutableList<AbstractSubtitleEntities.SubtitleEntity>, | ||||
|             name: String, | ||||
|             link: String | ||||
|         ) { | ||||
|             results.add( | ||||
|                 AbstractSubtitleEntities.SubtitleEntity( | ||||
|                     idPrefix = idPrefix, | ||||
|                     name = name, | ||||
|                     lang = queryLang.toString(), | ||||
|                     data = link, | ||||
|                     source = this.name, | ||||
|                     type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie, | ||||
|                     epNumber = epNum, | ||||
|                     seasonNumber = seasonNum, | ||||
|  | @ -152,11 +160,13 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
|                                 it.selectFirst("a")!!.attr("href") | ||||
|                             ) | ||||
|                             val itemDoc = app.get(urlItem).document | ||||
|                             val id = imdbUrlToIdNullable(itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() | ||||
|                                     ?.attr("href"))?.toLongOrNull() | ||||
|                             val id = imdbUrlToIdNullable( | ||||
|                                 itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() | ||||
|                                     ?.attr("href") | ||||
|                             )?.toLongOrNull() | ||||
|                             val year = itemDoc.selectFirst("div.d-flex span.badge.badge-success") | ||||
|                                     ?.ownText() | ||||
|                                     ?.trim().toString() | ||||
|                                 ?.ownText() | ||||
|                                 ?.trim().toString() | ||||
|                             Log.i(TAG, "id => $id \nyear => $year||$yearNum") | ||||
|                             if (imdbId > 0) { | ||||
|                                 if (id == imdbId) { | ||||
|  | @ -191,12 +201,13 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
|             val request = app.get(url) | ||||
|             if (request.isSuccessful) { | ||||
|                 request.document.select("div.my-3.p-3 div.media").map { block -> | ||||
|                     if (block.select("span.d-block span[data-original-title=Language]").text().trim() | ||||
|                     if (block.select("span.d-block span[data-original-title=Language]").text() | ||||
|                             .trim() | ||||
|                             .contains("$queryLang") | ||||
|                     ) { | ||||
|                         var name = block.select("strong.text-primary").text().trim() | ||||
|                         val link = fixUrl(block.selectFirst("a")!!.attr("href")) | ||||
|                         if(seasonNum > 0) { | ||||
|                         if (seasonNum > 0) { | ||||
|                             when { | ||||
|                                 isRightEps(name, seasonNum, epNum) -> { | ||||
|                                     cleanResources(results, name, link) | ||||
|  | @ -222,7 +233,7 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
| 
 | ||||
|         val req = app.get(data.data) | ||||
| 
 | ||||
|         if(req.isSuccessful) { | ||||
|         if (req.isSuccessful) { | ||||
|             val document = req.document | ||||
|             val link = if (document.select("div.my-3.p-3 div.media").size == 1) { | ||||
|                 fixUrl( | ||||
|  | @ -230,7 +241,8 @@ class IndexSubtitleApi : AuthAPI, AbstractSubProvider { | |||
|                 ) | ||||
|             } else { | ||||
|                 document.select("div.my-3.p-3 div.media").mapNotNull { block -> | ||||
|                     val name = block.selectFirst("strong.d-block.text-primary")?.text()?.trim().toString() | ||||
|                     val name = | ||||
|                         block.selectFirst("strong.d-block.text-primary")?.text()?.trim().toString() | ||||
|                     if (seasonNum!! > 0) { | ||||
|                         if (isRightEps(name, seasonNum, epNum)) { | ||||
|                             fixUrl(block.selectFirst("a")!!.attr("href")) | ||||
|  |  | |||
|  | @ -2,22 +2,19 @@ package com.lagradost.cloudstream3.syncproviders.providers | |||
| 
 | ||||
| import android.util.Log | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.getKey | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.setKey | ||||
| import com.lagradost.cloudstream3.ErrorLoadingException | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.TvType | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubProvider | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubApi | ||||
| import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities | ||||
| import com.lagradost.cloudstream3.syncproviders.AuthAPI | ||||
| import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI | ||||
| import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager | ||||
| import com.lagradost.cloudstream3.utils.AppUtils | ||||
| 
 | ||||
| class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProvider { | ||||
| class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi { | ||||
|     override val idPrefix = "opensubtitles" | ||||
|     override val name = "OpenSubtitles" | ||||
|     override val icon = R.drawable.open_subtitles_icon | ||||
|  | @ -190,6 +187,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProv | |||
|                 val resSeasonNum = featureDetails?.seasonNumber ?: query.seasonNumber | ||||
|                 val year = featureDetails?.year ?: query.year | ||||
|                 val type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie | ||||
|                 val isHearingImpaired = attr.hearing_impaired ?: false | ||||
|                 //Log.i(TAG, "Result id/name => ${item.id} / $name") | ||||
|                 item.attributes?.files?.forEach { file -> | ||||
|                     val resultData = file.fileId?.toString() ?: "" | ||||
|  | @ -201,9 +199,11 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProv | |||
|                             lang = lang, | ||||
|                             data = resultData, | ||||
|                             type = type, | ||||
|                             source = this.name, | ||||
|                             epNumber = resEpNum, | ||||
|                             seasonNumber = resSeasonNum, | ||||
|                             year = year | ||||
|                             year = year, | ||||
|                             isHearingImpaired = isHearingImpaired | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|  | @ -277,7 +277,8 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProv | |||
|         @JsonProperty("release") var release: String? = null, | ||||
|         @JsonProperty("url") var url: String? = null, | ||||
|         @JsonProperty("files") var files: List<ResultFiles>? = listOf(), | ||||
|         @JsonProperty("feature_details") var featDetails: ResultFeatureDetails? = ResultFeatureDetails() | ||||
|         @JsonProperty("feature_details") var featDetails: ResultFeatureDetails? = ResultFeatureDetails(), | ||||
|         @JsonProperty("hearing_impaired") var hearing_impaired: Boolean? = null, | ||||
|     ) | ||||
| 
 | ||||
|     data class ResultFiles( | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ import android.view.ViewGroup | |||
| import android.widget.* | ||||
| import androidx.activity.result.contract.ActivityResultContracts | ||||
| import androidx.appcompat.app.AlertDialog | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.view.isGone | ||||
| import androidx.core.view.isVisible | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
|  | @ -216,25 +217,80 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|         dismissCallback: (() -> Unit) | ||||
|     ) { | ||||
|         val providers = subsProviders | ||||
|         val isSingleProvider = subsProviders.size == 1 | ||||
| 
 | ||||
|         val dialog = Dialog(context, R.style.AlertDialogCustomBlack) | ||||
|         dialog.setContentView(R.layout.dialog_online_subtitles) | ||||
| 
 | ||||
|         var currentSubtitles: List<AbstractSubtitleEntities.SubtitleEntity> = emptyList() | ||||
|         var currentSubtitle: AbstractSubtitleEntities.SubtitleEntity? = null | ||||
| 
 | ||||
|         fun getName(entry: AbstractSubtitleEntities.SubtitleEntity, withLanguage: Boolean): String { | ||||
|             if (entry.lang.isBlank() || !withLanguage) { | ||||
|                 return entry.name | ||||
|             } | ||||
|             val language = fromTwoLettersToLanguage(entry.lang.trim()) ?: entry.lang | ||||
|             return "$language ${entry.name}" | ||||
|         } | ||||
| 
 | ||||
|         val layout = R.layout.sort_bottom_single_choice_double_text | ||||
|         val arrayAdapter = | ||||
|             ArrayAdapter<String>(dialog.context, R.layout.sort_bottom_single_choice) | ||||
|             object : ArrayAdapter<AbstractSubtitleEntities.SubtitleEntity>(dialog.context, layout) { | ||||
|                 fun setHearingImpairedIcon( | ||||
|                     imageViewEnd: ImageView?, | ||||
|                     position: Int | ||||
|                 ) { | ||||
|                     if (imageViewEnd == null) return | ||||
|                     val isHearingImpaired = | ||||
|                         currentSubtitles.getOrNull(position)?.isHearingImpaired ?: false | ||||
| 
 | ||||
|                     val drawableEnd = if (isHearingImpaired) { | ||||
|                         ContextCompat.getDrawable( | ||||
|                             context, | ||||
|                             R.drawable.ic_baseline_hearing_24 | ||||
|                         )?.apply { | ||||
|                             setTint( | ||||
|                                 ContextCompat.getColor( | ||||
|                                     context, | ||||
|                                     R.color.textColor | ||||
|                                 ) | ||||
|                             ) | ||||
|                         } | ||||
|                     } else null | ||||
| 
 | ||||
|                     imageViewEnd.setImageDrawable(drawableEnd) | ||||
|                 } | ||||
| 
 | ||||
|                 override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { | ||||
|                     val view = convertView ?: LayoutInflater.from(context) | ||||
|                         .inflate(layout, null) | ||||
| 
 | ||||
|                     val item = getItem(position) | ||||
| 
 | ||||
|                     val mainTextView = view.findViewById<TextView>(R.id.main_text) | ||||
|                     val secondaryTextView = view.findViewById<TextView>(R.id.secondary_text) | ||||
|                     val drawableEnd = view.findViewById<ImageView>(R.id.drawable_end) | ||||
| 
 | ||||
|                     mainTextView?.text = item?.let { getName(it, false) } | ||||
| 
 | ||||
|                     val language = item?.let { fromTwoLettersToLanguage(it.lang.trim()) ?: it.lang } ?: "" | ||||
|                     val providerSuffix = if (isSingleProvider || item == null) "" else " · ${item.source}" | ||||
|                     secondaryTextView?.text = language + providerSuffix | ||||
| 
 | ||||
|                     setHearingImpairedIcon(drawableEnd, position) | ||||
|                     return view | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         dialog.show() | ||||
| 
 | ||||
|         dialog.cancel_btt.setOnClickListener { | ||||
|             dialog.dismissSafe() | ||||
|         } | ||||
| 
 | ||||
|         dialog.subtitle_adapter.choiceMode = AbsListView.CHOICE_MODE_SINGLE | ||||
|         dialog.subtitle_adapter.adapter = arrayAdapter | ||||
|         val adapter = dialog.subtitle_adapter.adapter as? ArrayAdapter<String> | ||||
| 
 | ||||
|         var currentSubtitles: List<AbstractSubtitleEntities.SubtitleEntity> = emptyList() | ||||
|         var currentSubtitle: AbstractSubtitleEntities.SubtitleEntity? = null | ||||
|         val adapter = | ||||
|             dialog.subtitle_adapter.adapter as? ArrayAdapter<AbstractSubtitleEntities.SubtitleEntity> | ||||
| 
 | ||||
|         dialog.subtitle_adapter.setOnItemClickListener { _, _, position, _ -> | ||||
|             currentSubtitle = currentSubtitles.getOrNull(position) ?: return@setOnItemClickListener | ||||
|  | @ -242,19 +298,11 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
| 
 | ||||
|         var currentLanguageTwoLetters: String = getAutoSelectLanguageISO639_1() | ||||
| 
 | ||||
|         fun getName(entry: AbstractSubtitleEntities.SubtitleEntity): String { | ||||
|             return if (entry.lang.isBlank()) { | ||||
|                 entry.name | ||||
|             } else { | ||||
|                 val language = fromTwoLettersToLanguage(entry.lang.trim()) ?: entry.lang | ||||
|                 return "$language ${entry.name}" | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fun setSubtitlesList(list: List<AbstractSubtitleEntities.SubtitleEntity>) { | ||||
|             currentSubtitles = list | ||||
|             adapter?.clear() | ||||
|             adapter?.addAll(currentSubtitles.map { getName(it) }) | ||||
|             adapter?.addAll(currentSubtitles) | ||||
|         } | ||||
| 
 | ||||
|         val currentTempMeta = getMetaData() | ||||
|  | @ -328,7 +376,7 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|                     ioSafe { | ||||
|                         val url = api.load(currentSubtitle) ?: return@ioSafe | ||||
|                         val subtitle = SubtitleData( | ||||
|                             name = getName(currentSubtitle), | ||||
|                             name = getName(currentSubtitle, true), | ||||
|                             url = url, | ||||
|                             origin = SubtitleOrigin.URL, | ||||
|                             mimeType = url.toSubtitleMimeType() | ||||
|  | @ -846,7 +894,11 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|                             if (season == null) | ||||
|                                 " - ${ctx.getString(R.string.episode)} $episode" | ||||
|                             else | ||||
|                                 " \"${ctx.getString(R.string.season_short)}${season}:${ctx.getString(R.string.episode_short)}${episode}\"" | ||||
|                                 " \"${ctx.getString(R.string.season_short)}${season}:${ | ||||
|                                     ctx.getString( | ||||
|                                         R.string.episode_short | ||||
|                                     ) | ||||
|                                 }${episode}\"" | ||||
|                         else "") + if (subName.isNullOrBlank() || subName == headerName) "" else " - $subName" | ||||
|             } else { | ||||
|                 "" | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| <vector android:height="24dp" android:tint="@color/check_selection_color" | ||||
|     android:viewportHeight="24" android:viewportWidth="24" | ||||
|     android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/> | ||||
| </vector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/ic_baseline_hearing_24.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/ic_baseline_hearing_24.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| <vector android:height="24dp" android:tint="?attr/white" | ||||
|     android:viewportHeight="24" android:viewportWidth="24" | ||||
|     android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/> | ||||
| </vector> | ||||
|  | @ -0,0 +1,69 @@ | |||
| <!--<CheckedTextView | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" | ||||
|         android:id="@android:id/text1" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="?android:attr/listPreferredItemHeightSmall" | ||||
|         android:textAppearance="?android:attr/textAppearanceListItemSmall" | ||||
|         android:gravity="center_vertical" | ||||
|         android:textColor="?attr/textColor" | ||||
|         tools:text="Example Text" | ||||
|         android:background="?attr/bitDarkerGrayBackground" | ||||
|         android:checkMark="?android:attr/listChoiceIndicatorSingle" | ||||
|         android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||
|         android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/> | ||||
| --> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="wrap_content" | ||||
|     android:minHeight="?android:attr/listPreferredItemHeightSmall" | ||||
|     android:paddingVertical="10dp" | ||||
|     android:orientation="horizontal"> | ||||
| 
 | ||||
|     <ImageView | ||||
|         android:id="@+id/drawable_start" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_gravity="center" | ||||
|         android:paddingHorizontal="10dp" | ||||
|         android:src="@drawable/ic_baseline_check_24_listview" | ||||
|         tools:ignore="ContentDescription" /> | ||||
| 
 | ||||
|     <LinearLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_weight="1" | ||||
|         android:gravity="center" | ||||
|         android:orientation="vertical"> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/main_text" | ||||
|             style="@style/CheckLabel" | ||||
|             android:foreground="@null" | ||||
|             android:minHeight="@null" | ||||
|             app:drawableStartCompat="@null" | ||||
|             tools:text="The Boys - S01E02 Cherry" /> | ||||
| 
 | ||||
|         <TextView | ||||
|             android:id="@+id/secondary_text" | ||||
|             style="@style/CheckLabel" | ||||
|             android:foreground="@null" | ||||
|             android:minHeight="@null" | ||||
|             android:padding="0dp" | ||||
|             android:textSize="12sp" | ||||
|             app:drawableStartCompat="@null" | ||||
|             tools:text="English · OpenSubtitles" /> | ||||
|     </LinearLayout> | ||||
| 
 | ||||
|     <ImageView | ||||
|         tools:src="@drawable/ic_baseline_hearing_24" | ||||
|         android:id="@+id/drawable_end" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_gravity="center" | ||||
|         android:layout_weight="0" | ||||
|         android:paddingHorizontal="10dp" | ||||
|         tools:ignore="ContentDescription" /> | ||||
| </LinearLayout> | ||||
| 
 | ||||
							
								
								
									
										362
									
								
								app/src/main/res/values-vi/array.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								app/src/main/res/values-vi/array.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,362 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources xmlns:tools="http://schemas.android.com/tools"> | ||||
|     <array name="cast_mini_controller_control_buttons"> | ||||
|         <item>@id/cast_button_type_rewind_30_seconds</item> | ||||
|         <item>@id/cast_button_type_play_pause_toggle</item> | ||||
|         <item>@id/cast_button_type_forward_30_seconds</item> | ||||
|     </array> | ||||
|     <array name="cast_expanded_controller_control_buttons"> | ||||
|         <!-- Fake sources button --> | ||||
|         <item>@id/cast_button_type_rewind_30_seconds</item> | ||||
|         <item>@id/cast_button_type_rewind_30_seconds</item> | ||||
|         <item>@id/cast_button_type_forward_30_seconds</item> | ||||
|         <!-- Actually fake to make the skip op button the same style --> | ||||
|         <item>@id/cast_button_type_forward_30_seconds</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="dns_pref"> | ||||
|         <item>@string/none</item> | ||||
|         <item>Google</item> | ||||
|         <item>Cloudflare</item> | ||||
|         <!--        <item>OpenDns</item>--> | ||||
|         <item>AdGuard</item> | ||||
|     </array> | ||||
|     <array name="dns_pref_values"> | ||||
|         <item>0</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|         <!--        <item>3</item>--> | ||||
|         <item>4</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="media_type_pref"> | ||||
|         <item>Tất cả</item> | ||||
|         <item>Phim lẻ và Phim bộ</item> | ||||
|         <item>Anime</item> | ||||
|         <item>Phim tài liệu</item> | ||||
|     </array> | ||||
|     <array name="media_type_pref_values"> | ||||
|         <item>0</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|         <item>3</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="limit_title_rez_pref_names"> | ||||
|         <item>@string/resolution_and_title</item> | ||||
|         <item>@string/title</item> | ||||
|         <item>@string/resolution</item> | ||||
|         <item>@string/none</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="limit_title_rez_pref_values"> | ||||
|         <item>3</item> | ||||
|         <item>2</item> | ||||
|         <item>1</item> | ||||
|         <item>0</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="limit_title_pref_names"> | ||||
|         <item>@string/none</item> | ||||
|         <item>16 ký tự</item> | ||||
|         <item>32 ký tự</item> | ||||
|         <item>64 ký tự</item> | ||||
|         <item>128 ký tự</item> | ||||
|         <item>Ẩn Tiêu đề</item> | ||||
|     </array> | ||||
|     <array name="limit_title_pref_values"> | ||||
|         <item>0</item> | ||||
|         <item>16</item> | ||||
|         <item>32</item> | ||||
|         <item>64</item> | ||||
|         <item>128</item> | ||||
|         <item>-1</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="skip_sec_values"> | ||||
|         <item>5</item> | ||||
|         <item>10</item> | ||||
|         <item>15</item> | ||||
|         <item>20</item> | ||||
|         <item>25</item> | ||||
|         <item>30</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="video_buffer_length_names"> | ||||
|         <item>@string/automatic</item> | ||||
|         <item>1 phút</item> | ||||
|         <item>1 phút 30 giây</item> | ||||
|         <item>2 phút</item> | ||||
|         <item>2 phút 30 giây</item> | ||||
|         <item>3 phút</item> | ||||
|         <item>3 phút 30 giây</item> | ||||
|         <item>4 phút</item> | ||||
|         <item>5 phút</item> | ||||
|         <item>6 phút</item> | ||||
|         <item>7 phút</item> | ||||
|         <item>8 phút</item> | ||||
|         <item>9 phút</item> | ||||
|         <item>10 phút</item> | ||||
|         <item>15 phút</item> | ||||
|         <item>20 phút</item> | ||||
|         <item>30 phút</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="video_buffer_length_values"> | ||||
|         <item>0</item> | ||||
|         <item>60</item> | ||||
|         <item>90</item> | ||||
|         <item>120</item> | ||||
|         <item>150</item> | ||||
|         <item>180</item> | ||||
|         <item>210</item> | ||||
|         <item>240</item> | ||||
|         <item>300</item> | ||||
|         <item>360</item> | ||||
|         <item>420</item> | ||||
|         <item>480</item> | ||||
|         <item>540</item> | ||||
|         <item>600</item> | ||||
|         <item>900</item> | ||||
|         <item>1200</item> | ||||
|         <item>1800</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="video_buffer_size_names"> | ||||
|         <item>@string/automatic</item> | ||||
|         <item>10MB</item> | ||||
|         <item>20MB</item> | ||||
|         <item>30MB</item> | ||||
|         <item>40MB</item> | ||||
|         <item>50MB</item> | ||||
|         <item>60MB</item> | ||||
|         <item>70MB</item> | ||||
|         <item>80MB</item> | ||||
|         <item>90MB</item> | ||||
|         <item>100MB</item> | ||||
|         <item>150MB</item> | ||||
|         <item>200MB</item> | ||||
|         <item>250MB</item> | ||||
|         <item>300MB</item> | ||||
|         <item>350MB</item> | ||||
|         <item>400MB</item> | ||||
|         <item>450MB</item> | ||||
|         <item>500MB</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="video_buffer_size_values"> | ||||
|         <item>0</item> | ||||
|         <item>10</item> | ||||
|         <item>20</item> | ||||
|         <item>30</item> | ||||
|         <item>40</item> | ||||
|         <item>50</item> | ||||
|         <item>60</item> | ||||
|         <item>70</item> | ||||
|         <item>80</item> | ||||
|         <item>90</item> | ||||
|         <item>100</item> | ||||
|         <item>150</item> | ||||
|         <item>200</item> | ||||
|         <item>250</item> | ||||
|         <item>300</item> | ||||
|         <item>350</item> | ||||
|         <item>400</item> | ||||
|         <item>450</item> | ||||
|         <item>500</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="poster_ui_options"> | ||||
|         <item>@string/show_hd</item> | ||||
|         <item>@string/show_dub</item> | ||||
|         <item>@string/show_sub</item> | ||||
|         <item>@string/show_title</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="poster_ui_options_values"> | ||||
|         <item>@string/show_hd_key</item> | ||||
|         <item>@string/show_dub_key</item> | ||||
|         <item>@string/show_sub_key</item> | ||||
|         <item>@string/show_title_key</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="episode_long_click_options"> | ||||
|         <item>@string/episode_action_chromecast_episode</item> | ||||
|         <item>@string/episode_action_chromecast_mirror</item> | ||||
|         <item>@string/episode_action_play_in_app</item> | ||||
|         <item>@string/episode_action_play_in_vlc</item> | ||||
|         <item>@string/episode_action_play_in_browser</item> | ||||
|         <item>@string/episode_action_copy_link</item> | ||||
|         <item>@string/episode_action_auto_download</item> | ||||
|         <item>@string/episode_action_download_mirror</item> | ||||
|         <item>@string/episode_action_download_subtitle</item> | ||||
|         <item>@string/episode_action_reload_links</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="episode_long_click_options_values"> | ||||
|         <item>4</item> | ||||
|         <item>5</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|         <item>3</item> | ||||
|         <item>9</item> | ||||
|         <item>6</item> | ||||
|         <item>7</item> | ||||
|         <item>13</item> | ||||
|         <item>8</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="app_layout"> | ||||
|         <item>@string/automatic</item> | ||||
|         <item>@string/phone_layout</item> | ||||
|         <item>@string/tv_layout</item> | ||||
|         <item>@string/emulator_layout</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <array name="app_layout_values"> | ||||
|         <item>-1</item> | ||||
|         <item>0</item> | ||||
|         <item>1</item> | ||||
|         <item>2</item> | ||||
|     </array> | ||||
| 
 | ||||
|     <string-array name="themes_overlay_names"> | ||||
|         <item>Mặc định</item> | ||||
|         <item>Hồng nhạt</item> | ||||
|         <item>Xanh lam đậm</item> | ||||
|         <item>Nâu sẫm</item> | ||||
|         <item>Xanh lục</item> | ||||
|         <item>Xám</item> | ||||
|         <item>Trắng</item> | ||||
|         <item>Nâu</item> | ||||
|         <item>Xanh lục nhạt</item> | ||||
|         <item>Đỏ</item> | ||||
|         <item>Tím</item> | ||||
|         <item>Xanh lam</item> | ||||
|         <item>Xanh lam nhạt</item> | ||||
|         <item>Vàng</item> | ||||
|         <item>Hồng</item> | ||||
|         <item>Hồng đậm</item> | ||||
|     </string-array> | ||||
|     <string-array name="themes_overlay_names_values"> | ||||
|         <item>Normal</item> | ||||
|         <item>CarnationPink</item> | ||||
|         <item>DarkGreen</item> | ||||
|         <item>Maroon</item> | ||||
|         <item>NavyBlue</item> | ||||
|         <item>Grey</item> | ||||
|         <item>White</item> | ||||
|         <item>Brown</item> | ||||
|         <item>Blue</item> | ||||
|         <item>Red</item> | ||||
|         <item>Purple</item> | ||||
|         <item>Green</item> | ||||
|         <item>GreenApple</item> | ||||
|         <item>Banana</item> | ||||
|         <item>Party</item> | ||||
|         <item>Pink</item> | ||||
|     </string-array> | ||||
| 
 | ||||
|     <string-array name="themes_names"> | ||||
|         <item>Tối</item> | ||||
|         <item>Xám</item> | ||||
|         <item>Amoled</item> | ||||
|         <item>Flashbang</item> | ||||
|     </string-array> | ||||
|     <string-array name="themes_names_values"> | ||||
|         <item>AmoledLight</item> | ||||
|         <item>Black</item> | ||||
|         <item>Amoled</item> | ||||
|         <item>Light</item> | ||||
|     </string-array> | ||||
| 
 | ||||
|     <!--https://github.com/videolan/vlc-android/blob/72ccfb93db027b49855760001d1a930fa657c5a8/application/resources/src/main/res/values/arrays.xml#L266--> | ||||
|     <string-array name="subtitles_encoding_list" tools:ignore="TypographyDashes"> | ||||
|         <item>@string/automatic</item> | ||||
|         <item>Universal (UTF-8)</item> | ||||
|         <item>Universal (UTF-16)</item> | ||||
|         <item>Universal (big endian UTF-16)</item> | ||||
|         <item>Universal (little endian UTF-16)</item> | ||||
|         <item>Universal, Chinese (GB18030)</item> | ||||
|         <item>Western European (Latin-9)</item> | ||||
|         <item>Western European (Windows-1252)</item> | ||||
|         <item>Western European (IBM 00850)</item> | ||||
|         <item>Eastern European (Latin-2)</item> | ||||
|         <item>Eastern European (Windows-1250)</item> | ||||
|         <item>Esperanto (Latin-3)</item> | ||||
|         <item>Nordic (Latin-6)</item> | ||||
|         <item>Cyrillic (Windows-1251)</item> | ||||
|         <item>Russian (KOI8-R)</item> | ||||
|         <item>Ukrainian (KOI8-U)</item> | ||||
|         <item>Arabic (ISO 8859-6)</item> | ||||
|         <item>Arabic (Windows-1256)</item> | ||||
|         <item>Greek (ISO 8859-7)</item> | ||||
|         <item>Greek (Windows-1253)</item> | ||||
|         <item>Hebrew (ISO 8859-8)</item> | ||||
|         <item>Hebrew (Windows-1255)</item> | ||||
|         <item>Turkish (ISO 8859-9)</item> | ||||
|         <item>Turkish (Windows-1254)</item> | ||||
|         <item>Thai (TIS 620-2533/ISO 8859-11)</item> | ||||
|         <item>Thai (Windows-874)</item> | ||||
|         <item>Baltic (Latin-7)</item> | ||||
|         <item>Baltic (Windows-1257)</item> | ||||
|         <item>Celtic (Latin-8)</item> | ||||
|         <item>South-Eastern European (Latin-10)</item> | ||||
|         <item>Simplified Chinese (ISO-2022-CN-EXT)</item> | ||||
|         <item>Simplified Chinese Unix (EUC-CN)</item> | ||||
|         <item>Japanese (7-bits JIS/ISO-2022-JP-2)</item> | ||||
|         <item>Japanese Unix (EUC-JP)</item> | ||||
|         <item>Japanese (Shift JIS)</item> | ||||
|         <item>Korean (EUC-KR/CP949)</item> | ||||
|         <item>Korean (ISO-2022-KR)</item> | ||||
|         <item>Traditional Chinese (Big5)</item> | ||||
|         <item>Traditional Chinese Unix (EUC-TW)</item> | ||||
|         <item>Hong-Kong Supplementary (HKSCS)</item> | ||||
|         <item>Vietnamese (VISCII)</item> | ||||
|         <item>Vietnamese (Windows-1258)</item> | ||||
|     </string-array> | ||||
|     <string-array name="subtitles_encoding_values" translatable="false" tools:ignore="TypographyDashes"> | ||||
|         <item></item> | ||||
|         <item>UTF-8</item> | ||||
|         <item>UTF-16</item> | ||||
|         <item>UTF-16BE</item> | ||||
|         <item>UTF-16LE</item> | ||||
|         <item>GB18030</item> | ||||
|         <item>ISO-8859-15</item> | ||||
|         <item>Windows-1252</item> | ||||
|         <item>IBM850</item> | ||||
|         <item>ISO-8859-2</item> | ||||
|         <item>Windows-1250</item> | ||||
|         <item>ISO-8859-3</item> | ||||
|         <item>ISO-8859-10</item> | ||||
|         <item>Windows-1251</item> | ||||
|         <item>KOI8-R</item> | ||||
|         <item>KOI8-U</item> | ||||
|         <item>ISO-8859-6</item> | ||||
|         <item>Windows-1256</item> | ||||
|         <item>ISO-8859-7</item> | ||||
|         <item>Windows-1253</item> | ||||
|         <item>ISO-8859-8</item> | ||||
|         <item>Windows-1255</item> | ||||
|         <item>ISO-8859-9</item> | ||||
|         <item>Windows-1254</item> | ||||
|         <item>ISO-8859-11</item> | ||||
|         <item>Windows-874</item> | ||||
|         <item>ISO-8859-13</item> | ||||
|         <item>Windows-1257</item> | ||||
|         <item>ISO-8859-14</item> | ||||
|         <item>ISO-8859-16</item> | ||||
|         <item>ISO-2022-CN-EXT</item> | ||||
|         <item>EUC-CN</item> | ||||
|         <item>ISO-2022-JP-2</item> | ||||
|         <item>EUC-JP</item> | ||||
|         <item>Shift_JIS</item> | ||||
|         <item>CP949</item> | ||||
|         <item>ISO-2022-KR</item> | ||||
|         <item>Big5</item> | ||||
|         <item>ISO-2022-TW</item> | ||||
|         <item>Big5-HKSCS</item> | ||||
|         <item>VISCII</item> | ||||
|         <item>Windows-1258</item> | ||||
|     </string-array> | ||||
| </resources> | ||||
|  | @ -462,8 +462,9 @@ | |||
|         <item name="android:ellipsize">marquee</item> | ||||
|         <item name="android:foreground">?attr/selectableItemBackgroundBorderless</item> | ||||
|         <item name="android:drawablePadding">20dp</item> | ||||
|         <item name="drawableTint">@color/check_selection_color</item> | ||||
|         <item name="drawableStartCompat">@drawable/ic_baseline_check_24</item> | ||||
| <!--        <item name="drawableTint">@color/check_selection_color</item>--> | ||||
| <!--        Set color in the drawable instead of tint to allow multiple drawables--> | ||||
|         <item name="drawableStartCompat">@drawable/ic_baseline_check_24_listview</item> | ||||
|     </style> | ||||
| 
 | ||||
|     <style name="BlackButton" parent="NiceButton"> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue