Merge remote-tracking branch 'origin/master'

This commit is contained in:
Blatzar 2022-05-30 15:19:48 +02:00
commit ab958187f6
9 changed files with 300 additions and 67 deletions

View File

@ -171,4 +171,7 @@ dependencies {
implementation 'com.facebook.shimmer:shimmer:0.5.0' implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation "androidx.tvprovider:tvprovider:1.0.0" implementation "androidx.tvprovider:tvprovider:1.0.0"
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
} }

View File

@ -394,6 +394,7 @@ abstract class AbstractPlayerFragment(
override fun onDestroy() { override fun onDestroy() {
playerEventListener = null playerEventListener = null
keyEventListener = null keyEventListener = null
canEnterPipMode = false
SubtitlesFragment.applyStyleEvent -= ::onSubStyleChanged SubtitlesFragment.applyStyleEvent -= ::onSubStyleChanged
keepScreenOn(false) keepScreenOn(false)

View File

@ -1,6 +1,8 @@
package com.lagradost.cloudstream3.ui.player package com.lagradost.cloudstream3.ui.player
import android.content.Context
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager
import com.google.android.exoplayer2.Format import com.google.android.exoplayer2.Format
import com.google.android.exoplayer2.text.SubtitleDecoder import com.google.android.exoplayer2.text.SubtitleDecoder
import com.google.android.exoplayer2.text.SubtitleDecoderFactory import com.google.android.exoplayer2.text.SubtitleDecoderFactory
@ -11,13 +13,29 @@ import com.google.android.exoplayer2.text.subrip.SubripDecoder
import com.google.android.exoplayer2.text.ttml.TtmlDecoder import com.google.android.exoplayer2.text.ttml.TtmlDecoder
import com.google.android.exoplayer2.text.webvtt.WebvttDecoder import com.google.android.exoplayer2.text.webvtt.WebvttDecoder
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import org.mozilla.universalchardet.UniversalDetector
import java.nio.ByteBuffer import java.nio.ByteBuffer
class CustomDecoder : SubtitleDecoder { class CustomDecoder : SubtitleDecoder {
companion object { companion object {
fun updateForcedEncoding(context: Context) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
val value = settingsManager.getString(
context.getString(R.string.subtitles_encoding_key),
null
)
overrideEncoding = if (value.isNullOrBlank()) {
null
} else {
value
}
}
private const val UTF_8 = "UTF-8"
private const val TAG = "CustomDecoder" private const val TAG = "CustomDecoder"
private var overrideEncoding: String? = null
var regexSubtitlesToRemoveCaptions = false var regexSubtitlesToRemoveCaptions = false
val bloatRegex = val bloatRegex =
listOf( listOf(
@ -65,18 +83,67 @@ class CustomDecoder : SubtitleDecoder {
if (realDecoder == null) { if (realDecoder == null) {
inputBuffer.data?.let { data -> inputBuffer.data?.let { data ->
// 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 on mimetype
Log.i(TAG, "Got data from queueInputBuffer")
val pos = data.position() var (str, charset) = try {
data.position(0) data.position(0)
val arr = ByteArray(minOf(data.remaining(), 100)) val fullDataArr = ByteArray(data.remaining())
data.get(arr) data.get(fullDataArr)
data.position(pos) val encoding = try {
val encoding = overrideEncoding ?: run {
val detector = UniversalDetector()
detector.handleData(fullDataArr, 0, fullDataArr.size)
detector.dataEnd()
detector.detectedCharset // "windows-1256"
}
Log.i(
TAG,
"Detected encoding with charset $encoding and override = $overrideEncoding"
)
encoding ?: UTF_8
} catch (e: Exception) {
Log.e(TAG, "Failed to detect encoding throwing error")
logError(e)
UTF_8
}
var (fullStr, charset) = try {
val set = charset(encoding)
Pair(String(fullDataArr, set), set)
} catch (e: Exception) {
Log.e(TAG, "Failed to parse using encoding $encoding")
logError(e)
Pair(fullDataArr.decodeToString(), charset(UTF_8))
}
bloatRegex.forEach { rgx ->
fullStr = fullStr.replace(rgx, "\n")
}
fullStr.replace(Regex("(\r\n|\r|\n){2,}"), "\n")
// fullStr = "1\n00:00:01,616 --> 00:00:40,200\n" +
// "تــــرجــمة"
Log.i(
TAG,
"Encoded Text start: " + fullStr.substring(
0,
minOf(fullStr.length, 300)
)
)
Pair(fullStr, charset)
} catch (e: Exception) {
Log.e(TAG, "Failed to parse text returning plain data")
logError(e)
return
}
//https://emptycharacter.com/ //https://emptycharacter.com/
//https://www.fileformat.info/info/unicode/char/200b/index.htm //https://www.fileformat.info/info/unicode/char/200b/index.htm
val str = trimStr(arr.decodeToString()) //val str = trimStr(arr.decodeToString())
Log.i(TAG, "Got data from queueInputBuffer") //Log.i(TAG, "first string is >>>$str<<<")
Log.i(TAG, "first string is >>>$str<<<")
if (str.isNotEmpty()) { if (str.isNotEmpty()) {
//https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388 //https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388
realDecoder = when { realDecoder = when {
@ -93,33 +160,21 @@ class CustomDecoder : SubtitleDecoder {
TAG, TAG,
"Decoder selected: $realDecoder" "Decoder selected: $realDecoder"
) )
val decoder = realDecoder realDecoder?.let { decoder ->
if (decoder != null) {
decoder.dequeueInputBuffer()?.let { buff -> decoder.dequeueInputBuffer()?.let { buff ->
if (regexSubtitlesToRemoveCaptions && decoder::class.java != SsaDecoder::class.java) { if (regexSubtitlesToRemoveCaptions && decoder::class.java != SsaDecoder::class.java) {
try { captionRegex.forEach { rgx ->
data.position(0) str = str.replace(rgx, "\n")
val fullDataArr = ByteArray(data.remaining())
data.get(fullDataArr)
var fullStr = trimStr(fullDataArr.decodeToString())
bloatRegex.forEach { rgx ->
fullStr = fullStr.replace(rgx, "\n")
}
captionRegex.forEach { rgx ->
fullStr = fullStr.replace(rgx, "\n")
}
fullStr.replace(Regex("(\r\n|\r|\n){2,}"), "\n")
buff.data = ByteBuffer.wrap(fullStr.toByteArray())
} catch (e: Exception) {
data.position(pos)
buff.data = data
} }
} else {
buff.data = data
} }
buff.data = ByteBuffer.wrap(str.toByteArray(charset = charset))
decoder.queueInputBuffer(buff) decoder.queueInputBuffer(buff)
Log.i(
TAG,
"Decoder queueInputBuffer successfully"
)
} }
CS3IPlayer.requestSubtitleUpdate?.invoke() CS3IPlayer.requestSubtitleUpdate?.invoke()
} }

View File

@ -15,7 +15,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.material.button.MaterialButton
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -24,6 +23,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.player.CustomDecoder.Companion.updateForcedEncoding
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
import com.lagradost.cloudstream3.ui.result.ResultEpisode import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.ResultFragment import com.lagradost.cloudstream3.ui.result.ResultFragment
@ -32,11 +32,13 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSet
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import kotlinx.android.synthetic.main.fragment_player.* import kotlinx.android.synthetic.main.fragment_player.*
import kotlinx.android.synthetic.main.player_custom_layout.* import kotlinx.android.synthetic.main.player_custom_layout.*
import kotlinx.android.synthetic.main.player_select_source_and_subs.*
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
class GeneratorPlayer : FullScreenPlayer() { class GeneratorPlayer : FullScreenPlayer() {
@ -241,14 +243,8 @@ class GeneratorPlayer : FullScreenPlayer() {
val sourceDialog = sourceBuilder.create() val sourceDialog = sourceBuilder.create()
selectSourceDialog = sourceDialog selectSourceDialog = sourceDialog
sourceDialog.show() sourceDialog.show()
val providerList = val providerList = sourceDialog.sort_providers
sourceDialog.findViewById<ListView>(R.id.sort_providers)!! val subtitleList = sourceDialog.sort_subtitles
val subtitleList =
sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!!
val applyButton =
sourceDialog.findViewById<MaterialButton>(R.id.apply_btt)!!
val cancelButton =
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
val footer: TextView = val footer: TextView =
layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView
@ -314,11 +310,56 @@ class GeneratorPlayer : FullScreenPlayer() {
subtitleList.setItemChecked(which, true) subtitleList.setItemChecked(which, true)
} }
cancelButton.setOnClickListener { sourceDialog.cancel_btt?.setOnClickListener {
sourceDialog.dismissSafe(activity) sourceDialog.dismissSafe(activity)
} }
applyButton.setOnClickListener { sourceDialog.subtitles_encoding_format?.apply {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
val value = settingsManager.getString(
ctx.getString(R.string.subtitles_encoding_key),
null
)
val index = prefValues.indexOf(value)
text = prefNames[if(index == -1) 0 else index]
}
sourceDialog.subtitles_click_settings?.setOnClickListener {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val prefNames = ctx.resources.getStringArray(R.array.subtitles_encoding_list)
val prefValues = ctx.resources.getStringArray(R.array.subtitles_encoding_values)
val currentPrefMedia =
settingsManager.getString(
getString(R.string.subtitles_encoding_key),
null
)
val index = prefValues.indexOf(currentPrefMedia)
sourceDialog.dismissSafe(activity)
activity?.showDialog(
prefNames.toList(),
if(index == -1) 0 else index,
ctx.getString(R.string.subtitles_encoding),
true,
{}) {
settingsManager.edit()
.putString(
ctx.getString(R.string.subtitles_encoding_key),
prefValues[it]
)
.apply()
println("FORCED ENCODING: ${prefValues[it]}")
updateForcedEncoding(ctx)
}
}
sourceDialog.apply_btt?.setOnClickListener {
var init = false var init = false
if (sourceIndex != startSource) { if (sourceIndex != startSource) {
init = true init = true
@ -477,15 +518,24 @@ class GeneratorPlayer : FullScreenPlayer() {
): SubtitleData? { ): SubtitleData? {
val langCode = preferredAutoSelectSubtitles ?: return null val langCode = preferredAutoSelectSubtitles ?: return null
val lang = SubtitleHelper.fromTwoLettersToLanguage(langCode) ?: return null val lang = SubtitleHelper.fromTwoLettersToLanguage(langCode) ?: return null
if (downloads) {
if (settings) return subtitles.firstOrNull { sub ->
subtitles.firstOrNull { sub -> (sub.origin == SubtitleOrigin.DOWNLOADED_FILE && sub.name == context?.getString(
val t = sub.name.replace(Regex("[^A-Za-z]"), " ").trim() R.string.default_subtitles
t == lang || t.startsWith("$lang ") ))
|| t == langCode
}?.let { sub ->
return sub
} }
}
sortSubs(subtitles).firstOrNull { sub ->
val t = sub.name.replace(Regex("[^A-Za-z]"), " ").trim()
(settings || (downloads && sub.origin == SubtitleOrigin.DOWNLOADED_FILE)) && t == lang || t.startsWith(
"$lang "
) || t == langCode
}?.let { sub ->
return sub
}
// post check in case both did not catch anything
if (downloads) { if (downloads) {
return subtitles.firstOrNull { sub -> return subtitles.firstOrNull { sub ->
(sub.origin == SubtitleOrigin.DOWNLOADED_FILE || sub.name == context?.getString( (sub.origin == SubtitleOrigin.DOWNLOADED_FILE || sub.name == context?.getString(
@ -493,10 +543,11 @@ class GeneratorPlayer : FullScreenPlayer() {
)) ))
} }
} }
return null return null
} }
private fun autoSelectFromSettings() { private fun autoSelectFromSettings(): Boolean {
// auto select subtitle based of settings // auto select subtitle based of settings
val langCode = preferredAutoSelectSubtitles val langCode = preferredAutoSelectSubtitles
@ -506,29 +557,34 @@ class GeneratorPlayer : FullScreenPlayer() {
if (setSubtitles(sub)) { if (setSubtitles(sub)) {
player.reloadPlayer(ctx) player.reloadPlayer(ctx)
player.handleEvent(CSPlayerEvent.Play) player.handleEvent(CSPlayerEvent.Play)
return true
} }
} }
} }
} }
return false
} }
private fun autoSelectFromDownloads() { private fun autoSelectFromDownloads(): Boolean {
if (player.getCurrentPreferredSubtitle() == null) { if (player.getCurrentPreferredSubtitle() == null) {
getAutoSelectSubtitle(currentSubs, settings = false, downloads = true)?.let { sub -> getAutoSelectSubtitle(currentSubs, settings = false, downloads = true)?.let { sub ->
context?.let { ctx -> context?.let { ctx ->
if (setSubtitles(sub)) { if (setSubtitles(sub)) {
player.reloadPlayer(ctx) player.reloadPlayer(ctx)
player.handleEvent(CSPlayerEvent.Play) player.handleEvent(CSPlayerEvent.Play)
return true
} }
} }
} }
} }
return false
} }
private fun autoSelectSubtitles() { private fun autoSelectSubtitles() {
normalSafeApiCall { normalSafeApiCall {
autoSelectFromSettings() if (!autoSelectFromSettings()) {
autoSelectFromDownloads() autoSelectFromDownloads()
}
} }
} }
@ -567,7 +623,11 @@ class GeneratorPlayer : FullScreenPlayer() {
if (season == null) if (season == null)
" - ${ctx.getString(R.string.episode)} $episode" " - ${ctx.getString(R.string.episode)} $episode"
else 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 "") + if (subName.isNullOrBlank() || subName == headerName) "" else " - $subName"
} else { } else {
"" ""
@ -649,6 +709,7 @@ class GeneratorPlayer : FullScreenPlayer() {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
titleRez = settingsManager.getInt(getString(R.string.prefer_limit_title_rez_key), 3) titleRez = settingsManager.getInt(getString(R.string.prefer_limit_title_rez_key), 3)
limitTitle = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) limitTitle = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0)
updateForcedEncoding(ctx)
} }
unwrapBundle(savedInstanceState) unwrapBundle(savedInstanceState)

View File

@ -61,6 +61,7 @@ enum class Qualities(var value: Int) {
0 -> "Auto" 0 -> "Auto"
Unknown.value -> "" Unknown.value -> ""
P2160.value -> "4K" P2160.value -> "4K"
null -> ""
else -> "${qual}p" else -> "${qual}p"
} }
} }

View File

@ -62,19 +62,37 @@
android:orientation="horizontal" android:orientation="horizontal"
tools:ignore="UseCompoundDrawables"> tools:ignore="UseCompoundDrawables">
<TextView <LinearLayout
android:layout_width="match_parent" android:foreground="?attr/selectableItemBackgroundBorderless"
android:layout_height="wrap_content" android:id="@+id/subtitles_click_settings"
android:layout_rowWeight="1" android:layout_rowWeight="1"
android:layout_marginTop="10dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingTop="10dp" android:paddingTop="10dp"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingBottom="10dp" android:paddingBottom="10dp"
android:text="@string/pick_subtitle" android:layout_marginTop="10dp"
android:textColor="?attr/textColor" android:orientation="horizontal"
android:textSize="20sp" android:layout_width="match_parent"
android:textStyle="bold" /> android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:text="@string/pick_subtitle"
android:textColor="?attr/textColor"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:textSize="15sp"
android:id="@+id/subtitles_encoding_format"
android:textColor="?attr/textColor"
android:layout_gravity="center"
android:gravity="center"
tools:text="Thai (TIS 620-2533/ISO 8859-11)"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ImageView <ImageView
android:layout_width="25dp" android:layout_width="25dp"
@ -112,6 +130,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp"> android:layout_height="60dp">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
style="@style/WhiteButton" style="@style/WhiteButton"
android:layout_gravity="center_vertical|end" android:layout_gravity="center_vertical|end"

View File

@ -4,4 +4,5 @@
style="@style/CheckLabel" style="@style/CheckLabel"
android:id="@android:id/text1" android:id="@android:id/text1"
tools:text="hello" tools:text="hello"
app:drawableStartCompat="@drawable/ic_baseline_add_24" /> app:drawableStartCompat="@drawable/ic_baseline_add_24"
app:drawableTint="?attr/textColor" />

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources xmlns:tools="http://schemas.android.com/tools">
<array name="cast_mini_controller_control_buttons"> <array name="cast_mini_controller_control_buttons">
<item>@id/cast_button_type_rewind_30_seconds</item> <item>@id/cast_button_type_rewind_30_seconds</item>
<item>@id/cast_button_type_play_pause_toggle</item> <item>@id/cast_button_type_play_pause_toggle</item>
@ -269,4 +269,94 @@
<item>Amoled</item> <item>Amoled</item>
<item>Light</item> <item>Light</item>
</string-array> </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> </resources>

View File

@ -51,6 +51,7 @@
<string name="show_logcat_key" translatable="false">show_logcat_key</string> <string name="show_logcat_key" translatable="false">show_logcat_key</string>
<string name="bottom_title_key" translatable="false">bottom_title_key</string> <string name="bottom_title_key" translatable="false">bottom_title_key</string>
<string name="poster_ui_key" translatable="false">poster_ui_key</string> <string name="poster_ui_key" translatable="false">poster_ui_key</string>
<string name="subtitles_encoding_key" translatable="false">subtitles_encoding_key</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string> <string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
@ -418,6 +419,7 @@
<string name="provider_lang_settings">Provider languages</string> <string name="provider_lang_settings">Provider languages</string>
<string name="app_layout">App Layout</string> <string name="app_layout">App Layout</string>
<string name="preferred_media_settings">Preferred media</string> <string name="preferred_media_settings">Preferred media</string>
<string name="subtitles_encoding">Subtitle encoding</string>
<string name="category_preferred_media_and_lang">Preferred media and language</string> <string name="category_preferred_media_and_lang">Preferred media and language</string>
<string name="category_ui">User interface</string> <string name="category_ui">User interface</string>