-
-
-
+ navMenu={true}
+ />
+
+
+
+
+
+
+
App Version: {process.env.VERSION}
+ {
+ streaminServer.type === 'Ready' ?
+
Server Version: {streaminServer.settings.serverVersion}
+ :
+ null
+ }
+
+
+
+
General
+
+
+
+
+
+ {profile.auth === null ? 'Anonymous user' : profile.auth.user.email}
+
+
+ {
+ profile.auth !== null ?
+
+ :
+ null
+ }
+
+
+ {
+ profile.auth === null ?
+
+ :
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
Player
+
+
+
+
+
+
Subtitles background color
+
+
+
+
+
+
Subtitles outline color
+
+
+
+
+
+
Auto-play next episode
+
+
+
+
+
+
+
Play in external player
+
+
+
+
+
+
Hardware-accelerated decoding
+
+
+
+
+
+
Streaming Server
+
+
+
+
+
+ {
+ streaminServer.type === 'Ready' ?
+ 'Online'
+ :
+ streaminServer.type === 'Error' ?
+ `Error: (${streaminServer.error})`
+ :
+ streaminServer.type
+ }
+
+
+
+ {
+ streaminServer.type === 'Ready' ?
+
+
+
+
+ {streaminServer.base_url}
+
+
+
+ :
+ null
+ }
+ {
+ cacheSizeSelect !== null ?
+
+ :
+ null
+ }
+ {
+ torrentProfileSelect !== null ?
+
+ :
+ null
+ }
+
+
);
diff --git a/src/routes/Settings/constants.js b/src/routes/Settings/constants.js
deleted file mode 100644
index 9a3a95b9d..000000000
--- a/src/routes/Settings/constants.js
+++ /dev/null
@@ -1,28 +0,0 @@
-const languageOptions = [ { "label": "аҧсуа бызшәа", "value": "abk" }, { "label": "Afaraf", "value": "aar" }, { "label": "Afrikaans", "value": "afr" }, { "label": "Akan", "value": "aka" }, { "label": "gjuha shqipe", "value": "sqi" }, { "label": "አማርኛ", "value": "amh" }, { "label": "العربية", "value": "ara" }, { "label": "aragonés", "value": "arg" }, { "label": "Հայերեն", "value": "hye" }, { "label": "অসমীয়া", "value": "asm" }, { "label": "авар мацӀ", "value": "ava" }, { "label": "avesta", "value": "ave" }, { "label": "aymar aru", "value": "aym" }, { "label": "azərbaycan dili", "value": "aze" }, { "label": "bamanankan", "value": "bam" }, { "label": "башҡорт теле", "value": "bak" }, { "label": "euskara", "value": "eus" }, { "label": "беларуская мова", "value": "bel" }, { "label": "বাংলা", "value": "ben" }, { "label": "भोजपुरी", "value": "bih" }, { "label": "Bislama", "value": "bis" }, { "label": "bosanski jezik", "value": "bos" }, { "label": "brezhoneg", "value": "bre" }, { "label": "български език", "value": "bul" }, { "label": "ဗမာစာ", "value": "mya" }, { "label": "català", "value": "cat" }, { "label": "Chamoru", "value": "cha" }, { "label": "нохчийн мотт", "value": "che" }, { "label": "chiCheŵa", "value": "nya" }, { "label": "中文 (Zhōngwén)", "value": "zho" }, { "label": "чӑваш чӗлхи", "value": "chv" }, { "label": "Kernewek", "value": "cor" }, { "label": "corsu", "value": "cos" }, { "label": "ᓀᐦᐃᔭᐍᐏᐣ", "value": "cre" }, { "label": "hrvatski jezik", "value": "hrv" }, { "label": "čeština", "value": "ces" }, { "label": "dansk", "value": "dan" }, { "label": "ދިވެހި", "value": "div" }, { "label": "Nederlands", "value": "nld" }, { "label": "རྫོང་ཁ", "value": "dzo" }, { "label": "English", "value": "eng" }, { "label": "Esperanto", "value": "epo" }, { "label": "eesti", "value": "est" }, { "label": "Eʋegbe", "value": "ewe" }, { "label": "føroyskt", "value": "fao" }, { "label": "vosa Vakaviti", "value": "fij" }, { "label": "suomi", "value": "fin" }, { "label": "français", "value": "fre" }, { "label": "Fulfulde", "value": "ful" }, { "label": "galego", "value": "glg" }, { "label": "ქართული", "value": "kat" }, { "label": "Deutsch", "value": "ger" }, { "label": "ελληνικά", "value": "ell" }, { "label": "Avañe'ẽ", "value": "grn" }, { "label": "ગુજરાતી", "value": "guj" }, { "label": "Kreyòl ayisyen", "value": "hat" }, { "label": "Hausa", "value": "hau" }, { "label": "עברית", "value": "heb" }, { "label": "Otjiherero", "value": "her" }, { "label": "हिन्दी", "value": "hin" }, { "label": "Hiri Motu", "value": "hmo" }, { "label": "magyar", "value": "hun" }, { "label": "Interlingua", "value": "ina" }, { "label": "Bahasa Indonesia", "value": "ind" }, { "label": "Interlingue", "value": "ile" }, { "label": "Gaeilge", "value": "gle" }, { "label": "Asụsụ Igbo", "value": "ibo" }, { "label": "Iñupiaq", "value": "ipk" }, { "label": "Ido", "value": "ido" }, { "label": "Íslenska", "value": "isl" }, { "label": "italiano", "value": "ita" }, { "label": "ᐃᓄᒃᑎᑐᑦ", "value": "iku" }, { "label": "日本語 (にほんご)", "value": "jpn" }, { "label": "basa Jawa", "value": "jav" }, { "label": "kalaallisut", "value": "kal" }, { "label": "ಕನ್ನಡ", "value": "kan" }, { "label": "Kanuri", "value": "kau" }, { "label": "कश्मीरी", "value": "kas" }, { "label": "қазақ тілі", "value": "kaz" }, { "label": "ខ្មែរ", "value": "khm" }, { "label": "Gĩkũyũ", "value": "kik" }, { "label": "Ikinyarwanda", "value": "kin" }, { "label": "Кыргызча", "value": "kir" }, { "label": "коми кыв", "value": "kom" }, { "label": "KiKongo", "value": "kon" }, { "label": "한국어 (韓國語)", "value": "kor" }, { "label": "Kurdî", "value": "kur" }, { "label": "Kuanyama", "value": "kua" }, { "label": "latine", "value": "lat" }, { "label": "Lëtzebuergesch", "value": "ltz" }, { "label": "Luganda", "value": "lug" }, { "label": "Limburgs", "value": "lim" }, { "label": "Lingála", "value": "lin" }, { "label": "ພາສາລາວ", "value": "lao" }, { "label": "lietuvių kalba", "value": "lit" }, { "label": "Tshiluba", "value": "lub" }, { "label": "latviešu valoda", "value": "lav" }, { "label": "Gaelg", "value": "glv" }, { "label": "македонски јазик", "value": "mkd" }, { "label": "fiteny malagasy", "value": "mlg" }, { "label": "bahasa Melayu", "value": "msa" }, { "label": "മലയാളം", "value": "mal" }, { "label": "Malti", "value": "mlt" }, { "label": "te reo Māori", "value": "mri" }, { "label": "मराठी", "value": "mar" }, { "label": "Kajin M̧ajeļ", "value": "mah" }, { "label": "монгол", "value": "mon" }, { "label": "Ekakairũ Naoero", "value": "nau" }, { "label": "Diné bizaad", "value": "nav" }, { "label": "Norsk bokmål", "value": "nob" }, { "label": "isiNdebele", "value": "nde" }, { "label": "नेपाली", "value": "nep" }, { "label": "Owambo", "value": "ndo" }, { "label": "Norsk nynorsk", "value": "nno" }, { "label": "Norsk", "value": "nor" }, { "label": "ꆈꌠ꒿ Nuosuhxop", "value": "iii" }, { "label": "isiNdebele", "value": "nbl" }, { "label": "occitan", "value": "oci" }, { "label": "ᐊᓂᔑᓈᐯᒧᐎᓐ", "value": "oji" }, { "label": "ѩзыкъ словѣньскъ", "value": "chu" }, { "label": "Afaan Oromoo", "value": "orm" }, { "label": "ଓଡ଼ିଆ", "value": "ori" }, { "label": "ирон æвзаг", "value": "oss" }, { "label": "ਪੰਜਾਬੀ", "value": "pan" }, { "label": "पाऴि", "value": "pli" }, { "label": "فارسی", "value": "fas" }, { "label": "język polski", "value": "pol" }, { "label": "پښتو", "value": "pus" }, { "label": "português", "value": "por" }, { "label": "português Brazil", "value": "pob" }, { "label": "Runa Simi", "value": "que" }, { "label": "rumantsch grischun", "value": "roh" }, { "label": "Ikirundi", "value": "run" }, { "label": "limba română", "value": "ron" }, { "label": "русский язык", "value": "rus" }, { "label": "संस्कृतम्", "value": "san" }, { "label": "sardu", "value": "srd" }, { "label": "सिन्धी", "value": "snd" }, { "label": "Davvisámegiella", "value": "sme" }, { "label": "gagana fa'a Samoa", "value": "smo" }, { "label": "yângâ tî sängö", "value": "sag" }, { "label": "српски језик", "value": "srp" }, { "label": "Gàidhlig", "value": "gla" }, { "label": "chiShona", "value": "sna" }, { "label": "සිංහල", "value": "sin" }, { "label": "slovenčina", "value": "slk" }, { "label": "slovenski jezik", "value": "slv" }, { "label": "Soomaaliga", "value": "som" }, { "label": "Sesotho", "value": "sot" }, { "label": "español", "value": "spa" }, { "label": "Basa Sunda", "value": "sun" }, { "label": "Kiswahili", "value": "swa" }, { "label": "SiSwati", "value": "ssw" }, { "label": "Svenska", "value": "swe" }, { "label": "தமிழ்", "value": "tam" }, { "label": "తెలుగు", "value": "tel" }, { "label": "тоҷикӣ", "value": "tgk" }, { "label": "ไทย", "value": "tha" }, { "label": "ትግርኛ", "value": "tir" }, { "label": "བོད་ཡིག", "value": "bod" }, { "label": "Türkmen", "value": "tuk" }, { "label": "Wikang Tagalog", "value": "tgl" }, { "label": "Setswana", "value": "tsn" }, { "label": "faka Tonga", "value": "ton" }, { "label": "Türkçe", "value": "tur" }, { "label": "Xitsonga", "value": "tso" }, { "label": "татар теле", "value": "tat" }, { "label": "Twi", "value": "twi" }, { "label": "Reo Tahiti", "value": "tah" }, { "label": "Uyƣurqə", "value": "uig" }, { "label": "українська мова", "value": "ukr" }, { "label": "اردو", "value": "urd" }, { "label": "O'zbek", "value": "uzb" }, { "label": "Tshivenḓa", "value": "ven" }, { "label": "Tiếng Việt", "value": "vie" }, { "label": "Volapük", "value": "vol" }, { "label": "walon", "value": "wln" }, { "label": "Cymraeg", "value": "cym" }, { "label": "Wollof", "value": "wol" }, { "label": "Frysk", "value": "fry" }, { "label": "isiXhosa", "value": "xho" }, { "label": "ייִדיש", "value": "yid" }, { "label": "Yorùbá", "value": "yor" }, { "label": "Saɯ cueŋƅ", "value": "zha" }, { "label": "isiZulu", "value": "zul" } ];
-
-const settingsSections = {
- 'General': [
- { 'id': 'user', 'type': 'user' },
- { 'id': 'language', 'header': 'UI Language', 'label': 'UI Language', 'type': 'select', 'options': [{ 'label': 'Български език', 'value': 'bul' }, { 'label': 'English', 'value': 'eng' }, { 'label': 'Deutsch', 'value': 'ger' }, { 'label': 'Español', 'value': 'esp' }, { 'label': 'Italiano', 'value': 'ita' }] },
- { 'id': 'show_vid_overview', 'label': 'Show videos overview', 'type': 'checkbox' },
- ],
- 'Player': [
- { 'id': 'add-ons', 'label': 'ADD-ONS', 'type': 'button', 'icon': 'ic_addons', 'href': '#/addons' },
- { 'id': 'subtitles_language', 'header': 'Default Subtitles Language', 'label': 'Default Subtitles Language', 'type': 'select', 'options': languageOptions },
- { 'id': 'subtitles_size', 'header': 'Default Subtitles Size', 'label': 'Default Subtitles Size', 'type': 'select', 'options': [{ 'label': '72%', 'value': '72%' }, { 'label': '80%', 'value': '80%' }, { 'label': '100%', 'value': '100%' }, { 'label': '120%', 'value': '120%' }, { 'label': '140%', 'value': '140%' }, { 'label': '160%', 'value': '160%' }, { 'label': '180%', 'value': '180%' }] },
- { 'id': 'subtitles_background', 'header': 'Subtitles Background', 'label': 'Subtitles background', 'type': 'select', 'options': [{ 'label': 'None', 'value': '' }, { 'label': 'Solid', 'value': 'solid' }, { 'label': 'Transparent', 'value': 'transparent' }] },
- { 'id': 'subtitles_color', 'header': 'Subtitles color', 'label': 'Subtitles color', 'type': 'color' },
- { 'id': 'subtitles_outline_color', 'header': 'Subtitles outline color', 'label': 'Subtitles outline color', 'type': 'color' },
- { 'id': 'autoplay_next_vid', 'label': 'Auto-play next episode', 'type': 'checkbox' },
- { 'id': 'pause_on_lost_focus', 'label': 'Pause playback when not in focus', 'type': 'checkbox' },
- { 'id': 'use_external_player', 'label': 'Launch player in a separate window (advanced)', 'type': 'checkbox' },
- ],
- 'Streaming': [
- { 'id': 'streaming', 'type': 'streaming' },
- ]
-};
-
-
-module.exports = {
- settingsSections,
-};
diff --git a/src/routes/Settings/languages.json b/src/routes/Settings/languages.json
new file mode 100644
index 000000000..ad4abb3ec
--- /dev/null
+++ b/src/routes/Settings/languages.json
@@ -0,0 +1,187 @@
+{
+ "abk": "аҧсуа бызшәа",
+ "aar": "Afaraf",
+ "afr": "Afrikaans",
+ "aka": "Akan",
+ "sqi": "gjuha shqipe",
+ "amh": "አማርኛ",
+ "ara": "العربية",
+ "arg": "aragonés",
+ "hye": "Հայերեն",
+ "asm": "অসমীয়া",
+ "ava": "авар мацӀ",
+ "ave": "avesta",
+ "aym": "aymar aru",
+ "aze": "azərbaycan dili",
+ "bam": "bamanankan",
+ "bak": "башҡорт теле",
+ "eus": "euskara",
+ "bel": "беларуская мова",
+ "ben": "বাংলা",
+ "bih": "भोजपुरी",
+ "bis": "Bislama",
+ "bos": "bosanski jezik",
+ "bre": "brezhoneg",
+ "bul": "български език",
+ "mya": "ဗမာစာ",
+ "cat": "català",
+ "cha": "Chamoru",
+ "che": "нохчийн мотт",
+ "nya": "chiCheŵa",
+ "zho": "中文 (Zhōngwén)",
+ "chv": "чӑваш чӗлхи",
+ "cor": "Kernewek",
+ "cos": "corsu",
+ "cre": "ᓀᐦᐃᔭᐍᐏᐣ",
+ "hrv": "hrvatski jezik",
+ "ces": "čeština",
+ "dan": "dansk",
+ "div": "ދިވެހި",
+ "nld": "Nederlands",
+ "dzo": "རྫོང་ཁ",
+ "eng": "English",
+ "epo": "Esperanto",
+ "est": "eesti",
+ "ewe": "Eʋegbe",
+ "fao": "føroyskt",
+ "fij": "vosa Vakaviti",
+ "fin": "suomi",
+ "fre": "français",
+ "ful": "Fulfulde",
+ "glg": "galego",
+ "kat": "ქართული",
+ "ger": "Deutsch",
+ "ell": "ελληνικά",
+ "grn": "Avañe'ẽ",
+ "guj": "ગુજરાતી",
+ "hat": "Kreyòl ayisyen",
+ "hau": "Hausa",
+ "heb": "עברית",
+ "her": "Otjiherero",
+ "hin": "हिन्दी",
+ "hmo": "Hiri Motu",
+ "hun": "magyar",
+ "ina": "Interlingua",
+ "ind": "Bahasa Indonesia",
+ "ile": "Interlingue",
+ "gle": "Gaeilge",
+ "ibo": "Asụsụ Igbo",
+ "ipk": "Iñupiaq",
+ "ido": "Ido",
+ "isl": "Íslenska",
+ "ita": "italiano",
+ "iku": "ᐃᓄᒃᑎᑐᑦ",
+ "jpn": "日本語 (にほんご)",
+ "jav": "basa Jawa",
+ "kal": "kalaallisut",
+ "kan": "ಕನ್ನಡ",
+ "kau": "Kanuri",
+ "kas": "कश्मीरी",
+ "kaz": "қазақ тілі",
+ "khm": "ខ្មែរ",
+ "kik": "Gĩkũyũ",
+ "kin": "Ikinyarwanda",
+ "kir": "Кыргызча",
+ "kom": "коми кыв",
+ "kon": "KiKongo",
+ "kor": "한국어 (韓國語)",
+ "kur": "Kurdî",
+ "kua": "Kuanyama",
+ "lat": "latine",
+ "ltz": "Lëtzebuergesch",
+ "lug": "Luganda",
+ "lim": "Limburgs",
+ "lin": "Lingála",
+ "lao": "ພາສາລາວ",
+ "lit": "lietuvių kalba",
+ "lub": "Tshiluba",
+ "lav": "latviešu valoda",
+ "glv": "Gaelg",
+ "mkd": "македонски јазик",
+ "mlg": "fiteny malagasy",
+ "msa": "bahasa Melayu",
+ "mal": "മലയാളം",
+ "mlt": "Malti",
+ "mri": "te reo Māori",
+ "mar": "मराठी",
+ "mah": "Kajin M̧ajeļ",
+ "mon": "монгол",
+ "nau": "Ekakairũ Naoero",
+ "nav": "Diné bizaad",
+ "nob": "Norsk bokmål",
+ "nde": "isiNdebele",
+ "nep": "नेपाली",
+ "ndo": "Owambo",
+ "nno": "Norsk nynorsk",
+ "nor": "Norsk",
+ "iii": "ꆈꌠ꒿ Nuosuhxop",
+ "nbl": "isiNdebele",
+ "oci": "occitan",
+ "oji": "ᐊᓂᔑᓈᐯᒧᐎᓐ",
+ "chu": "ѩзыкъ словѣньскъ",
+ "orm": "Afaan Oromoo",
+ "ori": "ଓଡ଼ିଆ",
+ "oss": "ирон æвзаг",
+ "pan": "ਪੰਜਾਬੀ",
+ "pli": "पाऴि",
+ "fas": "فارسی",
+ "pol": "język polski",
+ "pus": "پښتو",
+ "por": "português",
+ "pob": "português Brazil",
+ "que": "Runa Simi",
+ "roh": "rumantsch grischun",
+ "run": "Ikirundi",
+ "ron": "limba română",
+ "rus": "русский язык",
+ "san": "संस्कृतम्",
+ "srd": "sardu",
+ "snd": "सिन्धी",
+ "sme": "Davvisámegiella",
+ "smo": "gagana fa'a Samoa",
+ "sag": "yângâ tî sängö",
+ "srp": "српски језик",
+ "gla": "Gàidhlig",
+ "sna": "chiShona",
+ "sin": "සිංහල",
+ "slk": "slovenčina",
+ "slv": "slovenski jezik",
+ "som": "Soomaaliga",
+ "sot": "Sesotho",
+ "spa": "español",
+ "sun": "Basa Sunda",
+ "swa": "Kiswahili",
+ "ssw": "SiSwati",
+ "swe": "Svenska",
+ "tam": "தமிழ்",
+ "tel": "తెలుగు",
+ "tgk": "тоҷикӣ",
+ "tha": "ไทย",
+ "tir": "ትግርኛ",
+ "bod": "བོད་ཡིག",
+ "tuk": "Türkmen",
+ "tgl": "Wikang Tagalog",
+ "tsn": "Setswana",
+ "ton": "faka Tonga",
+ "tur": "Türkçe",
+ "tso": "Xitsonga",
+ "tat": "татар теле",
+ "twi": "Twi",
+ "tah": "Reo Tahiti",
+ "uig": "Uyƣurqə",
+ "ukr": "українська мова",
+ "urd": "اردو",
+ "uzb": "O'zbek",
+ "ven": "Tshivenḓa",
+ "vie": "Tiếng Việt",
+ "vol": "Volapük",
+ "wln": "walon",
+ "cym": "Cymraeg",
+ "wol": "Wollof",
+ "fry": "Frysk",
+ "xho": "isiXhosa",
+ "yid": "ייִדיש",
+ "yor": "Yorùbá",
+ "zha": "Saɯ cueŋƅ",
+ "zul": "isiZulu"
+}
\ No newline at end of file
diff --git a/src/routes/Settings/styles.less b/src/routes/Settings/styles.less
index df5281628..9757d578e 100644
--- a/src/routes/Settings/styles.less
+++ b/src/routes/Settings/styles.less
@@ -1,4 +1,13 @@
-.settings-parent-container {
+:import('~stremio/common/Checkbox/styles.less') {
+ checkbox-icon: icon;
+}
+
+:import('~stremio/common/Multiselect/styles.less') {
+ multiselect-menu-container: menu-container;
+ multiselect-label: label;
+}
+
+.settings-container {
display: flex;
flex-direction: column;
width: 100%;
@@ -10,32 +19,242 @@
align-self: stretch;
}
- .settings-container {
- width: 100%;
- height: 100%;
+ .settings-content {
+ flex: 1;
+ align-self: stretch;
display: flex;
flex-direction: row;
- background-color: var(--color-backgroundlight);
- --input-width: 35rem;
-
- .side-menu {
- padding: 1rem;
- width: 17rem;
+
+ .side-menu-container {
+ flex: none;
+ align-self: stretch;
display: flex;
flex-direction: column;
+ width: 17rem;
+ padding: 1rem;
background-color: var(--color-backgroundlighter);
+ .side-menu-button {
+ flex: none;
+ align-self: stretch;
+ padding: 1rem;
+ font-size: 1.1rem;
+ color: var(--color-surfacelighter);
+ &.selected {
+ background-color: var(--color-background);
+ }
+
+ &:hover {
+ background-color: var(--color-surface20);
+ }
+ }
+
+ .spacing {
+ flex: 1;
+ }
+
+ .version-info-label {
+ margin: 0.5rem 0;
+ color: var(--color-surfacelight);
+ }
}
- .scroll-container {
- padding: 0 2rem;
+ .sections-container {
flex: 1;
+ align-self: stretch;
+ padding: 0 2rem;
overflow-y: auto;
- >:not(:last-child) {
- border-bottom: calc(var(--focusable-border-size) * 0.5) solid var(--color-primary);
+ .section-container {
+ display: flex;
+ flex-direction: column;
+ padding: 2rem 0;
+ overflow: visible;
+
+ &:first-child {
+ padding-top: 1rem;
+ }
+
+ &:not(:last-child) {
+ border-bottom: thin solid var(--color-primary40);
+ }
+
+ .section-title {
+ flex: none;
+ align-self: stretch;
+ font-size: 1.8rem;
+ line-height: 3.4rem;
+ margin-bottom: 1rem;
+ color: var(--color-surfacelighter);
+ }
+
+ .option-container {
+ flex: none;
+ align-self: stretch;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ max-width: 35rem;
+ margin-bottom: 2rem;
+ overflow: visible;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &.user-info-option-container {
+ height: 7rem;
+
+ .avatar-container {
+ flex: none;
+ align-self: stretch;
+ width: 7rem;
+ margin-right: 1rem;
+ border-radius: 50%;
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-origin: content-box;
+ background-clip: content-box;
+ }
+
+ .email-logout-container {
+ flex: 1;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ padding: 1rem 0;
+
+ .email-label-container, .logout-button-container {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ }
+
+ .email-label-container {
+ flex: 1 0 auto;
+
+ .email-label {
+ flex: 1;
+ font-size: 1.4rem;
+ max-height: 2.4em;
+ color: var(--color-surfacelighter);
+ }
+ }
+
+ .logout-button-container {
+ flex: 0 1 50%;
+
+ &:hover, &:focus {
+ outline: none;
+
+ .logout-label {
+ color: var(--color-surfacelighter);
+ text-decoration: underline;
+ }
+ }
+
+ .logout-label {
+ flex: 1;
+ max-height: 1.2em;
+ color: var(--color-surface);
+ }
+ }
+ }
+ }
+
+ .option-name-container, .option-input-container {
+ flex: 1 1 50%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ .icon {
+ flex: none;
+ width: 1.5rem;
+ height: 1.5rem;
+ margin-right: 0.5rem;
+ fill: var(--color-surfacelighter);
+ }
+
+ .label {
+ flex-grow: 0;
+ flex-shrink: 1;
+ flex-basis: auto;
+ line-height: 1.5rem;
+ color: var(--color-surfacelighter);
+ }
+ }
+
+ .option-name-container {
+ justify-content: flex-start;
+ padding: 1rem 1rem 1rem 0;
+ margin-right: 2rem;
+ }
+
+ .option-input-container {
+ padding: 1rem;
+
+ &.button-container {
+ justify-content: center;
+ background-color: var(--color-primary);
+
+ &:hover {
+ background-color: var(--color-primarylight);
+ }
+ }
+
+ &.multiselect-container {
+ >.multiselect-label {
+ line-height: 1.5rem;
+ max-height: 1.5rem;
+ }
+
+ .multiselect-menu-container {
+ max-height: calc(3.2rem * 7);
+ overflow: auto;
+ }
+ }
+
+ &.link-container {
+ flex: 0 1 auto;
+ padding: 1rem 0;
+
+ &:hover {
+ .label {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ &.checkbox-container {
+ justify-content: center;
+
+ .checkbox-icon {
+ width: 1.5rem;
+ height: 1.5rem;
+ }
+ }
+
+ &.color-input-container {
+ padding: 1.75rem 1rem;
+ }
+
+ &.info-container {
+ justify-content: center;
+
+ &.selectable {
+ user-select: text;
+
+ .label {
+ user-select: text;
+ }
+ }
+ }
+ }
+ }
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/routes/Settings/useProfileSettingsInputs.js b/src/routes/Settings/useProfileSettingsInputs.js
new file mode 100644
index 000000000..42e06cd50
--- /dev/null
+++ b/src/routes/Settings/useProfileSettingsInputs.js
@@ -0,0 +1,202 @@
+const React = require('react');
+const { useServices } = require('stremio/services');
+const { useProfile } = require('stremio/common');
+const languages = require('./languages');
+
+const SUBTITLES_SIZES = [75, 100, 125, 150, 175, 200, 250];
+
+const useProfileSettingsInputs = () => {
+ const { core } = useServices();
+ const profile = useProfile();
+ const interfaceLanguageSelect = React.useMemo(() => ({
+ options: Object.keys(languages).map((code) => ({
+ value: code,
+ label: languages[code]
+ })),
+ selected: [profile.settings.interface_language],
+ renderLabelText: () => {
+ return typeof languages[profile.settings.interface_language] === 'string' ?
+ languages[profile.settings.interface_language]
+ :
+ profile.settings.interface_language;
+ },
+ onSelect: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ interface_language: event.value
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const subtitlesLanguageSelect = React.useMemo(() => ({
+ options: Object.keys(languages).map((code) => ({
+ value: code,
+ label: languages[code]
+ })),
+ selected: [profile.settings.subtitles_language],
+ renderLabelText: () => {
+ return typeof languages[profile.settings.subtitles_language] === 'string' ?
+ languages[profile.settings.subtitles_language]
+ :
+ profile.settings.subtitles_language;
+ },
+ onSelect: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ subtitles_language: event.value
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const subtitlesSizeSelect = React.useMemo(() => ({
+ options: SUBTITLES_SIZES.map((size) => ({
+ value: `${size}`,
+ label: `${size}%`
+ })),
+ selected: [`${profile.settings.subtitles_size}`],
+ renderLabelText: () => {
+ return `${profile.settings.subtitles_size}%`;
+ },
+ onSelect: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ subtitles_size: parseInt(event.value)
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const subtitlesTextColorInput = React.useMemo(() => ({
+ value: profile.settings.subtitles_text_color,
+ onChange: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ subtitles_text_color: event.value
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const subtitlesBackgroundColorInput = React.useMemo(() => ({
+ value: profile.settings.subtitles_background_color,
+ onChange: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ subtitles_background_color: event.value
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const subtitlesOutlineColorInput = React.useMemo(() => ({
+ value: profile.settings.subtitles_outline_color,
+ onChange: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ subtitles_outline_color: event.value
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const bingeWatchingCheckbox = React.useMemo(() => ({
+ checked: profile.settings.binge_watching,
+ onClick: () => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ binge_watching: !profile.settings.binge_watching
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const playInBackgroundCheckbox = React.useMemo(() => ({
+ checked: profile.settings.play_in_background,
+ onClick: () => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ play_in_background: !profile.settings.play_in_background
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const playInExternalPlayerCheckbox = React.useMemo(() => ({
+ checked: profile.settings.play_in_external_player,
+ onClick: () => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ play_in_external_player: !profile.settings.play_in_external_player
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ const hardwareDecodingCheckbox = React.useMemo(() => ({
+ checked: profile.settings.hardware_decoding,
+ onClick: () => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...profile.settings,
+ hardware_decoding: !profile.settings.hardware_decoding
+ }
+ }
+ });
+ }
+ }), [profile.settings]);
+ return {
+ interfaceLanguageSelect,
+ subtitlesLanguageSelect,
+ subtitlesSizeSelect,
+ subtitlesTextColorInput,
+ subtitlesBackgroundColorInput,
+ subtitlesOutlineColorInput,
+ bingeWatchingCheckbox,
+ playInBackgroundCheckbox,
+ playInExternalPlayerCheckbox,
+ hardwareDecodingCheckbox
+ };
+};
+
+module.exports = useProfileSettingsInputs;
diff --git a/src/routes/Settings/useSettings.js b/src/routes/Settings/useSettings.js
deleted file mode 100644
index 4f71eba0e..000000000
--- a/src/routes/Settings/useSettings.js
+++ /dev/null
@@ -1,68 +0,0 @@
-const React = require('react');
-const { useServices } = require('stremio/services');
-
-const IGNORED_SETTINGS = Object.freeze(['user', 'streaming']);
-
-module.exports = () => {
- const { core } = useServices();
-
- const [settings, setSettings] = React.useState({
- user: null,
- streaming: {},
- streaming_loaded: false,
- streaming_error: ""
- });
-
- React.useEffect(() => {
- const onNewState = () => {
- const { ctx, streaming_server_settings } = core.getState()
- try {
- const newSettings = {
- ...settings,
- ...ctx.content.settings,
- user: ctx.content.auth ? ctx.content.auth.user : null,
- streaming: streaming_server_settings && streaming_server_settings.ready || {},
- streaming_loaded: streaming_server_settings && !!(streaming_server_settings.error || streaming_server_settings.ready),
- streaming_error: streaming_server_settings && streaming_server_settings.error || "",
- };
- setSettings(newSettings);
- } catch (e) {
- console.log('Cannot update settings state', e);
- }
- };
- const onStoreError = ({ event, args }) => {
- if (event !== "SettingsStoreError") return;
- // TODO: Notify with maybe a toast?
- console.log(args)
- }
-
- core.on('NewModel', onNewState);
- core.on('Event', onStoreError);
-
- onNewState();
-
- return () => {
- // Destructor function
- core.off('NewModel', onNewState);
- core.off('Event', onStoreError);
- };
- }, []);
-
- const setTheSettings = React.useCallback(newSettings => {
- const event = { action: 'Settings', args: { args: {} } };
- // This can be done with React.useEffect and newSettings.streaming as dependency
- const streamingServerSettingChanged = settings.streaming && Object.keys(newSettings.streaming)
- .some(prop => settings.streaming[prop] !== newSettings.streaming[prop]);
- if (streamingServerSettingChanged) {
- event.args = { settings: 'StoreStreamingServer', args: newSettings.streaming };
- } else {
- event.args.settings = 'Store';
- Object.keys(newSettings)
- .filter(prop => !IGNORED_SETTINGS.includes(prop))
- .forEach(key => event.args.args[key] = newSettings[key].toString());
- }
- core.dispatch(event);
- }, [settings])
-
- return [settings, setTheSettings];
-};
diff --git a/src/routes/Settings/useStreamingServerSettingsInputs.js b/src/routes/Settings/useStreamingServerSettingsInputs.js
new file mode 100644
index 000000000..fc247c902
--- /dev/null
+++ b/src/routes/Settings/useStreamingServerSettingsInputs.js
@@ -0,0 +1,134 @@
+const React = require('react');
+const isEqual = require('lodash.isequal');
+const { useServices } = require('stremio/services');
+const { useStreamingServer } = require('stremio/common');
+
+const CACHE_SIZES = [0, 2147483648, 5368709120, 10737418240, null];
+
+const cacheSizeToString = (size) => {
+ return size === null ?
+ 'Infinite'
+ :
+ size === 0 ?
+ 'No caching'
+ :
+ `${size / 1024 / 1024 / 1024}GiB`;
+};
+
+const TORRENT_PROFILES = {
+ default: {
+ btDownloadSpeedHardLimit: 2621440,
+ btDownloadSpeedSoftLimit: 1677721.6,
+ btHandshakeTimeout: 20000,
+ btMaxConnections: 35,
+ btMinPeersForStable: 5,
+ btRequestTimeout: 4000
+ },
+ soft: {
+ btDownloadSpeedHardLimit: 1677721.6,
+ btDownloadSpeedSoftLimit: 1677721.6,
+ btHandshakeTimeout: 20000,
+ btMaxConnections: 35,
+ btMinPeersForStable: 5,
+ btRequestTimeout: 4000
+ },
+ fast: {
+ btDownloadSpeedHardLimit: 39321600,
+ btDownloadSpeedSoftLimit: 4194304,
+ btHandshakeTimeout: 20000,
+ btMaxConnections: 200,
+ btMinPeersForStable: 10,
+ btRequestTimeout: 4000
+ }
+};
+
+const useStreaminServerSettingsInputs = () => {
+ const { core } = useServices();
+ const streaminServer = useStreamingServer();
+ const cacheSizeSelect = React.useMemo(() => {
+ if (streaminServer.type !== 'Ready') {
+ return null;
+ }
+
+ return {
+ options: CACHE_SIZES.map((size) => ({
+ label: cacheSizeToString(size),
+ value: JSON.stringify(size)
+ })),
+ selected: [JSON.stringify(streaminServer.settings.cacheSize)],
+ renderLabelText: () => {
+ return cacheSizeToString(streaminServer.settings.cacheSize);
+ },
+ onSelect: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...streaminServer.settings,
+ cacheSize: JSON.parse(event.value)
+ }
+ }
+ });
+ }
+ };
+ }, [streaminServer.type, streaminServer.settings]);
+ const torrentProfileSelect = React.useMemo(() => {
+ if (streaminServer.type !== 'Ready') {
+ return null;
+ }
+
+ const selectedTorrentProfile = {
+ btDownloadSpeedHardLimit: streaminServer.settings.btDownloadSpeedHardLimit,
+ btDownloadSpeedSoftLimit: streaminServer.settings.btDownloadSpeedSoftLimit,
+ btHandshakeTimeout: streaminServer.settings.btHandshakeTimeout,
+ btMaxConnections: streaminServer.settings.btMaxConnections,
+ btMinPeersForStable: streaminServer.settings.btMinPeersForStable,
+ btRequestTimeout: streaminServer.settings.btRequestTimeout
+ };
+ const isCustomTorrentProfileSelected = Object.values(TORRENT_PROFILES).every((torrentProfile) => {
+ return !isEqual(torrentProfile, selectedTorrentProfile);
+ });
+ return {
+ options: Object.keys(TORRENT_PROFILES)
+ .map((profileName) => ({
+ label: profileName,
+ value: JSON.stringify(TORRENT_PROFILES[profileName])
+ }))
+ .concat(
+ isCustomTorrentProfileSelected ?
+ [{
+ label: 'custom',
+ value: JSON.stringify(selectedTorrentProfile)
+ }]
+ :
+ []
+ ),
+ selected: [JSON.stringify(selectedTorrentProfile)],
+ renderLabelText: () => {
+ return Object.keys(TORRENT_PROFILES).reduce((result, profileName) => {
+ if (isEqual(TORRENT_PROFILES[profileName], selectedTorrentProfile)) {
+ return profileName;
+ }
+
+ return result;
+ }, 'custom');
+ },
+ onSelect: (event) => {
+ core.dispatch({
+ action: 'Ctx',
+ args: {
+ action: 'UpdateSettings',
+ args: {
+ ...streaminServer.settings,
+ ...JSON.parse(event.value)
+ }
+ }
+ });
+ }
+ };
+ }, [streaminServer.type, streaminServer.settings]);
+ return { cacheSizeSelect, torrentProfileSelect };
+};
+
+module.exports = useStreaminServerSettingsInputs;
diff --git a/src/services/KeyboardNavigation/KeyboardNavigation.js b/src/services/KeyboardNavigation/KeyboardNavigation.js
index bd46169d2..c3ad4a65b 100644
--- a/src/services/KeyboardNavigation/KeyboardNavigation.js
+++ b/src/services/KeyboardNavigation/KeyboardNavigation.js
@@ -12,6 +12,14 @@ function KeyboardNavigation() {
if (tab) {
event.preventDefault();
window.location = tab.href;
+ return;
+ }
+
+ if (event.target.tagName !== 'INPUT') {
+ if (event.key === 'Backspace') {
+ window.history.back();
+ return;
+ }
}
}
function start() {
diff --git a/webpack.config.js b/webpack.config.js
index cf679670e..9676769b4 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,10 +1,12 @@
const path = require('path');
+const child_process = require('child_process');
const webpack = require('webpack');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
+const pachageJson = require('./package.json');
module.exports = (env, argv) => ({
entry: './src/index.js',
@@ -130,6 +132,8 @@ module.exports = (env, argv) => ({
plugins: [
new webpack.EnvironmentPlugin({
DEBUG: argv.mode !== 'production',
+ VERSION: pachageJson.version,
+ COMMIT_HASH: child_process.execSync('git rev-parse HEAD').toString(),
...env
}),
new webpack.ProgressPlugin(),