Merge pull request #564 from halibiram/feature/i18n-web-config
feature/i18n web config
This commit is contained in:
commit
baba142275
8 changed files with 175 additions and 64 deletions
|
|
@ -1,5 +1,6 @@
|
|||
package com.nuvio.tv.core.server
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
|
@ -8,6 +9,7 @@ import java.util.UUID
|
|||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class AddonConfigServer(
|
||||
private val context: Context,
|
||||
private val currentPageStateProvider: () -> PageState,
|
||||
private val onChangeProposed: (PendingAddonChange) -> Unit,
|
||||
private val logoProvider: (() -> ByteArray?)? = null,
|
||||
|
|
@ -71,7 +73,7 @@ class AddonConfigServer(
|
|||
}
|
||||
|
||||
private fun serveWebPage(): Response {
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/html", AddonWebPage.getHtml())
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/html", AddonWebPage.getHtml(context))
|
||||
}
|
||||
|
||||
private fun serveLogo(): Response {
|
||||
|
|
@ -156,6 +158,7 @@ class AddonConfigServer(
|
|||
|
||||
companion object {
|
||||
fun startOnAvailablePort(
|
||||
context: Context,
|
||||
currentPageStateProvider: () -> PageState,
|
||||
onChangeProposed: (PendingAddonChange) -> Unit,
|
||||
logoProvider: (() -> ByteArray?)? = null,
|
||||
|
|
@ -164,7 +167,7 @@ class AddonConfigServer(
|
|||
): AddonConfigServer? {
|
||||
for (port in startPort until startPort + maxAttempts) {
|
||||
try {
|
||||
val server = AddonConfigServer(currentPageStateProvider, onChangeProposed, logoProvider, port)
|
||||
val server = AddonConfigServer(context, currentPageStateProvider, onChangeProposed, logoProvider, port)
|
||||
server.start(SOCKET_READ_TIMEOUT, false)
|
||||
return server
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,27 @@
|
|||
package com.nuvio.tv.core.server
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import com.nuvio.tv.R
|
||||
import java.util.Locale
|
||||
|
||||
object AddonWebPage {
|
||||
|
||||
fun getHtml(): String = """
|
||||
fun getHtml(baseContext: Context): String {
|
||||
val tag = baseContext.getSharedPreferences("app_locale", Context.MODE_PRIVATE)
|
||||
.getString("locale_tag", null)
|
||||
val context = if (!tag.isNullOrEmpty()) {
|
||||
val config = Configuration(baseContext.resources.configuration)
|
||||
config.setLocale(Locale.forLanguageTag(tag))
|
||||
baseContext.createConfigurationContext(config)
|
||||
} else baseContext
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>NuvioTV - Manage Addons</title>
|
||||
<title>${context.getString(R.string.app_name)} - ${context.getString(R.string.web_manage_addons_title)}</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
|
|
@ -399,36 +412,36 @@ object AddonWebPage {
|
|||
<div class="page">
|
||||
<div class="header">
|
||||
<img src="/logo.png" alt="NuvioTV" class="header-logo">
|
||||
<p>Manage addons and home catalogs</p>
|
||||
<p>${context.getString(R.string.web_manage_addons_subtitle)}</p>
|
||||
</div>
|
||||
|
||||
<div class="add-section">
|
||||
<label>Add addon by URL</label>
|
||||
<label>${context.getString(R.string.web_add_addon_url)}</label>
|
||||
<div class="add-row">
|
||||
<input type="url" id="addonUrl" placeholder="https://example.com/manifest.json" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
<button class="btn" id="addBtn" onclick="addAddon()">Add</button>
|
||||
<input type="url" id="addonUrl" placeholder="${context.getString(R.string.web_placeholder_url)}" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
<button class="btn" id="addBtn" onclick="addAddon()">${context.getString(R.string.web_btn_add)}</button>
|
||||
</div>
|
||||
<div class="add-error" id="addError"></div>
|
||||
</div>
|
||||
|
||||
<div class="section-label">Installed Addons</div>
|
||||
<div class="section-label">${context.getString(R.string.web_installed_addons)}</div>
|
||||
<ul class="addon-list" id="addonList"></ul>
|
||||
<div class="empty-state" id="emptyState">No addons installed</div>
|
||||
<div class="empty-state" id="emptyState">${context.getString(R.string.web_no_addons)}</div>
|
||||
|
||||
<div class="section-block">
|
||||
<div class="section-label">Home Catalogs</div>
|
||||
<div class="section-label">${context.getString(R.string.web_home_catalogs)}</div>
|
||||
<ul class="addon-list" id="catalogList"></ul>
|
||||
<div class="empty-state" id="catalogEmptyState">No home catalogs available</div>
|
||||
<div class="empty-state" id="catalogEmptyState">${context.getString(R.string.web_no_catalogs)}</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-save" id="saveBtn" onclick="saveChanges()">Save Changes</button>
|
||||
<button class="btn btn-save" id="saveBtn" onclick="saveChanges()">${context.getString(R.string.web_btn_save)}</button>
|
||||
</div>
|
||||
|
||||
<div class="status-overlay" id="statusOverlay">
|
||||
<div class="status-content" id="statusContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="connection-bar" id="connectionBar">Connection to TV lost</div>
|
||||
<div class="connection-bar" id="connectionBar">${context.getString(R.string.web_connection_lost)}</div>
|
||||
|
||||
<script>
|
||||
var addons = [];
|
||||
|
|
@ -502,13 +515,13 @@ function renderAddons() {
|
|||
'</div>' +
|
||||
'<div class="addon-info">' +
|
||||
'<div class="addon-name">' + escapeHtml(addon.name || addon.url) +
|
||||
(addon.isNew ? '<span class="badge-new">New</span>' : '') +
|
||||
(addon.isNew ? '<span class="badge-new">${context.getString(R.string.web_badge_new).replace("'", "\\'")}</span>' : '') +
|
||||
'</div>' +
|
||||
(addon.description ? '<div class="addon-desc">' + escapeHtml(addon.description) + '</div>' : '') +
|
||||
'<div class="addon-url">' + escapeHtml(addon.url) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="addon-actions">' +
|
||||
'<button class="btn btn-remove" onclick="removeAddon(' + i + ')">Remove</button>' +
|
||||
'<button class="btn btn-remove" onclick="removeAddon(' + i + ')">${context.getString(R.string.web_btn_remove).replace("'", "\\'")}</button>' +
|
||||
'</div>';
|
||||
|
||||
list.appendChild(li);
|
||||
|
|
@ -545,13 +558,13 @@ function renderCatalogs() {
|
|||
'</div>' +
|
||||
'<div class="catalog-info">' +
|
||||
'<div class="catalog-name">' + escapeHtml(formatCatalogTitle(catalog.catalogName, catalog.type)) +
|
||||
(catalog.isDisabled ? '<span class="badge-disabled">Disabled</span>' : '') +
|
||||
(catalog.isDisabled ? '<span class="badge-disabled">${context.getString(R.string.web_badge_disabled).replace("'", "\\'")}</span>' : '') +
|
||||
'</div>' +
|
||||
'<div class="catalog-meta">' + escapeHtml(catalog.addonName) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="addon-actions">' +
|
||||
'<button class="' + toggleClass + '" onclick="toggleCatalog(' + i + ')">' +
|
||||
(catalog.isDisabled ? 'Enable' : 'Disable') +
|
||||
(catalog.isDisabled ? '${context.getString(R.string.web_btn_enable).replace("'", "\\'")}' : '${context.getString(R.string.web_btn_disable).replace("'", "\\'")}') +
|
||||
'</button>' +
|
||||
'</div>';
|
||||
|
||||
|
|
@ -600,7 +613,7 @@ async function addAddon() {
|
|||
url = url.replace(/\/+$/, '');
|
||||
|
||||
if (addons.some(function(a) { return a.url === url; })) {
|
||||
errorEl.textContent = 'This addon is already in the list';
|
||||
errorEl.textContent = '${context.getString(R.string.web_error_addon_exists).replace("'", "\\'")}';
|
||||
errorEl.style.display = 'block';
|
||||
setTimeout(function() { errorEl.style.display = 'none'; }, 3000);
|
||||
return;
|
||||
|
|
@ -646,7 +659,7 @@ async function saveChanges() {
|
|||
saveBtn.disabled = false;
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorStatus('Failed to save. Check your connection to the TV.');
|
||||
showErrorStatus('${context.getString(R.string.web_error_failed_save).replace("'", "\\'")}');
|
||||
saveBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -656,8 +669,8 @@ function showPendingStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="spinner"></div></div>' +
|
||||
'<div class="status-title">Waiting for TV</div>' +
|
||||
'<div class="status-message">Please confirm the changes on your TV to apply them.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_waiting_tv).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_waiting_tv).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content';
|
||||
overlay.classList.add('visible');
|
||||
}
|
||||
|
|
@ -666,8 +679,8 @@ function showSuccessStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></div></div>' +
|
||||
'<div class="status-title">Changes Applied</div>' +
|
||||
'<div class="status-message">Your addon configuration has been updated on the TV.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_changes_applied).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_addon_updated).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content status-success';
|
||||
setTimeout(dismissStatus, 2500);
|
||||
}
|
||||
|
|
@ -676,8 +689,8 @@ function showRejectedStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg></div></div>' +
|
||||
'<div class="status-title">Changes Rejected</div>' +
|
||||
'<div class="status-message">The changes were declined on the TV. Your list has been reverted.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_changes_rejected).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_changes_rejected).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content status-rejected';
|
||||
setTimeout(function() {
|
||||
addons = JSON.parse(JSON.stringify(originalAddons));
|
||||
|
|
@ -693,9 +706,9 @@ function showErrorStatus(msg) {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg></div></div>' +
|
||||
'<div class="status-title">Something Went Wrong</div>' +
|
||||
'<div class="status-title">${context.getString(R.string.web_status_error).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">' + escapeHtml(msg) + '</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
overlay.classList.add('visible');
|
||||
}
|
||||
|
|
@ -704,9 +717,9 @@ function showTimeoutStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div></div>' +
|
||||
'<div class="status-title">Timed Out</div>' +
|
||||
'<div class="status-message">No response from the TV. Please try again.</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_timeout).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_timeout).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
}
|
||||
|
||||
|
|
@ -714,9 +727,9 @@ function showDisconnectedStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 1l22 22M16.72 11.06A10.94 10.94 0 0 1 19 12.55M5 12.55a10.94 10.94 0 0 1 5.17-2.39M10.71 5.05A16 16 0 0 1 22.56 9M1.42 9a15.91 15.91 0 0 1 4.7-2.88M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/></svg></div></div>' +
|
||||
'<div class="status-title">Connection Lost</div>' +
|
||||
'<div class="status-message">The TV server is no longer reachable. The changes may have been applied.</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_connection_lost).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_connection_lost).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
}
|
||||
|
||||
|
|
@ -800,4 +813,5 @@ loadState();
|
|||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.nuvio.tv.core.server
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import fi.iki.elonen.NanoHTTPD
|
||||
|
|
@ -8,6 +9,7 @@ import java.util.UUID
|
|||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class RepositoryConfigServer(
|
||||
private val context: Context,
|
||||
private val currentRepositoriesProvider: () -> List<RepositoryInfo>,
|
||||
private val onChangeProposed: (PendingRepoChange) -> Unit,
|
||||
private val logoProvider: (() -> ByteArray?)? = null,
|
||||
|
|
@ -54,7 +56,7 @@ class RepositoryConfigServer(
|
|||
}
|
||||
|
||||
private fun serveWebPage(): Response {
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/html", RepositoryWebPage.getHtml())
|
||||
return newFixedLengthResponse(Response.Status.OK, "text/html", RepositoryWebPage.getHtml(context))
|
||||
}
|
||||
|
||||
private fun serveLogo(): Response {
|
||||
|
|
@ -118,6 +120,7 @@ class RepositoryConfigServer(
|
|||
|
||||
companion object {
|
||||
fun startOnAvailablePort(
|
||||
context: Context,
|
||||
currentRepositoriesProvider: () -> List<RepositoryInfo>,
|
||||
onChangeProposed: (PendingRepoChange) -> Unit,
|
||||
logoProvider: (() -> ByteArray?)? = null,
|
||||
|
|
@ -126,7 +129,7 @@ class RepositoryConfigServer(
|
|||
): RepositoryConfigServer? {
|
||||
for (port in startPort until startPort + maxAttempts) {
|
||||
try {
|
||||
val server = RepositoryConfigServer(currentRepositoriesProvider, onChangeProposed, logoProvider, port)
|
||||
val server = RepositoryConfigServer(context, currentRepositoriesProvider, onChangeProposed, logoProvider, port)
|
||||
server.start(SOCKET_READ_TIMEOUT, false)
|
||||
return server
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,27 @@
|
|||
package com.nuvio.tv.core.server
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import com.nuvio.tv.R
|
||||
import java.util.Locale
|
||||
|
||||
object RepositoryWebPage {
|
||||
|
||||
fun getHtml(): String = """
|
||||
fun getHtml(baseContext: Context): String {
|
||||
val tag = baseContext.getSharedPreferences("app_locale", Context.MODE_PRIVATE)
|
||||
.getString("locale_tag", null)
|
||||
val context = if (!tag.isNullOrEmpty()) {
|
||||
val config = Configuration(baseContext.resources.configuration)
|
||||
config.setLocale(Locale.forLanguageTag(tag))
|
||||
baseContext.createConfigurationContext(config)
|
||||
} else baseContext
|
||||
return """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>NuvioTV - Manage Repositories</title>
|
||||
<title>${context.getString(R.string.app_name)} - ${context.getString(R.string.web_manage_repos_title)}</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
|
|
@ -305,30 +318,30 @@ object RepositoryWebPage {
|
|||
<div class="page">
|
||||
<div class="header">
|
||||
<img src="/logo.png" alt="NuvioTV" class="header-logo">
|
||||
<p>Manage your repositories</p>
|
||||
<p>${context.getString(R.string.web_manage_repos_subtitle)}</p>
|
||||
</div>
|
||||
|
||||
<div class="add-section">
|
||||
<label>Add repository by URL</label>
|
||||
<label>${context.getString(R.string.web_add_repo_url)}</label>
|
||||
<div class="add-row">
|
||||
<input type="url" id="repoUrl" placeholder="https://example.com/manifest.json" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
<button class="btn" id="addBtn" onclick="addRepo()">Add</button>
|
||||
<input type="url" id="repoUrl" placeholder="${context.getString(R.string.web_placeholder_url)}" autocomplete="off" autocapitalize="off" spellcheck="false">
|
||||
<button class="btn" id="addBtn" onclick="addRepo()">${context.getString(R.string.web_btn_add)}</button>
|
||||
</div>
|
||||
<div class="add-error" id="addError"></div>
|
||||
</div>
|
||||
|
||||
<div class="section-label">Installed</div>
|
||||
<div class="section-label">${context.getString(R.string.web_installed)}</div>
|
||||
<ul class="repo-list" id="repoList"></ul>
|
||||
<div class="empty-state" id="emptyState">No repositories installed</div>
|
||||
<div class="empty-state" id="emptyState">${context.getString(R.string.web_no_repos)}</div>
|
||||
|
||||
<button class="btn btn-save" id="saveBtn" onclick="saveChanges()">Save Changes</button>
|
||||
<button class="btn btn-save" id="saveBtn" onclick="saveChanges()">${context.getString(R.string.web_btn_save)}</button>
|
||||
</div>
|
||||
|
||||
<div class="status-overlay" id="statusOverlay">
|
||||
<div class="status-content" id="statusContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="connection-bar" id="connectionBar">Connection to TV lost</div>
|
||||
<div class="connection-bar" id="connectionBar">${context.getString(R.string.web_connection_lost)}</div>
|
||||
|
||||
<script>
|
||||
var repos = [];
|
||||
|
|
@ -385,13 +398,13 @@ function renderList() {
|
|||
li.innerHTML =
|
||||
'<div class="repo-info">' +
|
||||
'<div class="repo-name">' + escapeHtml(repo.name || repo.url) +
|
||||
(repo.isNew ? '<span class="badge-new">New</span>' : '') +
|
||||
(repo.isNew ? '<span class="badge-new">${context.getString(R.string.web_badge_new).replace("'", "\\'")}</span>' : '') +
|
||||
'</div>' +
|
||||
(repo.description ? '<div class="repo-desc">' + escapeHtml(repo.description) + '</div>' : '') +
|
||||
'<div class="repo-url">' + escapeHtml(repo.url) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="repo-actions">' +
|
||||
'<button class="btn btn-remove" onclick="removeRepo(' + i + ')">Remove</button>' +
|
||||
'<button class="btn btn-remove" onclick="removeRepo(' + i + ')">${context.getString(R.string.web_btn_remove).replace("'", "\\'")}</button>' +
|
||||
'</div>';
|
||||
|
||||
list.appendChild(li);
|
||||
|
|
@ -413,7 +426,7 @@ async function addRepo() {
|
|||
url = url.replace(/\/+$/, '');
|
||||
|
||||
if (repos.some(function(r) { return r.url === url; })) {
|
||||
errorEl.textContent = 'This repository is already in the list';
|
||||
errorEl.textContent = '${context.getString(R.string.web_error_repo_exists).replace("'", "\\'")}';
|
||||
errorEl.style.display = 'block';
|
||||
setTimeout(function() { errorEl.style.display = 'none'; }, 3000);
|
||||
return;
|
||||
|
|
@ -451,7 +464,7 @@ async function saveChanges() {
|
|||
saveBtn.disabled = false;
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorStatus('Failed to save. Check your connection to the TV.');
|
||||
showErrorStatus('${context.getString(R.string.web_error_failed_save).replace("'", "\\'")}');
|
||||
saveBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -461,8 +474,8 @@ function showPendingStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="spinner"></div></div>' +
|
||||
'<div class="status-title">Waiting for TV</div>' +
|
||||
'<div class="status-message">Please confirm the changes on your TV to apply them.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_waiting_tv).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_waiting_tv).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content';
|
||||
overlay.classList.add('visible');
|
||||
}
|
||||
|
|
@ -471,8 +484,8 @@ function showSuccessStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg></div></div>' +
|
||||
'<div class="status-title">Changes Applied</div>' +
|
||||
'<div class="status-message">Your repository configuration has been updated on the TV.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_changes_applied).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_repo_updated).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content status-success';
|
||||
setTimeout(dismissStatus, 2500);
|
||||
}
|
||||
|
|
@ -481,8 +494,8 @@ function showRejectedStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18M6 6l12 12"/></svg></div></div>' +
|
||||
'<div class="status-title">Changes Rejected</div>' +
|
||||
'<div class="status-message">The changes were declined on the TV. Your list has been reverted.</div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_changes_rejected).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_changes_rejected).replace("'", "\\'")}</div>';
|
||||
content.className = 'status-content status-rejected';
|
||||
setTimeout(function() {
|
||||
repos = JSON.parse(JSON.stringify(originalRepos));
|
||||
|
|
@ -496,9 +509,9 @@ function showErrorStatus(msg) {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg></div></div>' +
|
||||
'<div class="status-title">Something Went Wrong</div>' +
|
||||
'<div class="status-title">${context.getString(R.string.web_status_error).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">' + escapeHtml(msg) + '</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
overlay.classList.add('visible');
|
||||
}
|
||||
|
|
@ -507,9 +520,9 @@ function showTimeoutStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div></div>' +
|
||||
'<div class="status-title">Timed Out</div>' +
|
||||
'<div class="status-message">No response from the TV. Please try again.</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_status_timeout).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_timeout).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
}
|
||||
|
||||
|
|
@ -517,9 +530,9 @@ function showDisconnectedStatus() {
|
|||
var content = document.getElementById('statusContent');
|
||||
content.innerHTML =
|
||||
'<div class="status-icon"><div class="status-svg"><svg viewBox="0 0 24 24" fill="none" stroke="rgba(207,102,121,0.9)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 1l22 22M16.72 11.06A10.94 10.94 0 0 1 19 12.55M5 12.55a10.94 10.94 0 0 1 5.17-2.39M10.71 5.05A16 16 0 0 1 22.56 9M1.42 9a15.91 15.91 0 0 1 4.7-2.88M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01"/></svg></div></div>' +
|
||||
'<div class="status-title">Connection Lost</div>' +
|
||||
'<div class="status-message">The TV server is no longer reachable. The changes may have been applied.</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">Dismiss</button></div>';
|
||||
'<div class="status-title">${context.getString(R.string.web_connection_lost).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-message">${context.getString(R.string.web_status_msg_connection_lost).replace("'", "\\'")}</div>' +
|
||||
'<div class="status-dismiss"><button class="btn" onclick="dismissStatus()">${context.getString(R.string.web_btn_dismiss).replace("'", "\\'")}</button></div>';
|
||||
content.className = 'status-content status-error';
|
||||
}
|
||||
|
||||
|
|
@ -591,4 +604,5 @@ loadRepos();
|
|||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ class AddonManagerViewModel @Inject constructor(
|
|||
stopServerInternal()
|
||||
|
||||
server = AddonConfigServer.startOnAvailablePort(
|
||||
context = context,
|
||||
currentPageStateProvider = {
|
||||
val addons = _uiState.value.installedAddons
|
||||
val orderedCatalogs = buildOrderedCatalogEntries(
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ class PluginViewModel @Inject constructor(
|
|||
stopRepoServerInternal()
|
||||
|
||||
repoServer = RepositoryConfigServer.startOnAvailablePort(
|
||||
context = context,
|
||||
currentRepositoriesProvider = {
|
||||
_uiState.value.repositories.map { repo ->
|
||||
RepositoryConfigServer.RepositoryInfo(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,43 @@
|
|||
<string name="type_series">Dizi</string>
|
||||
<string name="type_unknown">Bilinmiyor</string>
|
||||
|
||||
<!-- WebConfigServers -->
|
||||
<string name="web_manage_addons_title">Eklentileri Yönet</string>
|
||||
<string name="web_manage_addons_subtitle">Eklentileri ve ana sayfa kataloglarını yönetin</string>
|
||||
<string name="web_add_addon_url">URL ile eklenti ekle</string>
|
||||
<string name="web_placeholder_url">https://example.com/manifest.json</string>
|
||||
<string name="web_btn_add">Ekle</string>
|
||||
<string name="web_installed_addons">Yüklü Eklentiler</string>
|
||||
<string name="web_no_addons">Yüklü eklenti yok</string>
|
||||
<string name="web_home_catalogs">Ana Sayfa Katalogları</string>
|
||||
<string name="web_no_catalogs">Ana sayfa kataloğu mevcut değil</string>
|
||||
<string name="web_btn_save">Değişiklikleri Kaydet</string>
|
||||
<string name="web_connection_lost">TV bağlantısı koptu</string>
|
||||
<string name="web_btn_remove">Kaldır</string>
|
||||
<string name="web_badge_new">Yeni</string>
|
||||
<string name="web_badge_disabled">Devre Dışı</string>
|
||||
<string name="web_btn_enable">Etkinleştir</string>
|
||||
<string name="web_btn_disable">Devre Dışı</string>
|
||||
<string name="web_error_addon_exists">Bu eklenti zaten listede var</string>
|
||||
<string name="web_status_waiting_tv">TV Bekleniyor</string>
|
||||
<string name="web_status_msg_waiting_tv">Değişiklikleri uygulamak için lütfen TV\'nizden onaylayın.</string>
|
||||
<string name="web_status_changes_applied">Değişiklikler Uygulandı</string>
|
||||
<string name="web_status_msg_addon_updated">Eklenti yapılandırmanız TV\'de güncellendi.</string>
|
||||
<string name="web_status_msg_repo_updated">Uzantı deposu yapılandırmanız TV\'de güncellendi.</string>
|
||||
<string name="web_status_changes_rejected">Değişiklikler Reddedildi</string>
|
||||
<string name="web_status_msg_changes_rejected">Değişiklikler TV\'de reddedildi. Listeniz eski haline döndürüldü.</string>
|
||||
<string name="web_status_error">Bir Şeyler Ters Gitti</string>
|
||||
<string name="web_error_failed_save">Kaydedilemedi. TV ile bağlantınızı kontrol edin.</string>
|
||||
<string name="web_btn_dismiss">Kapat</string>
|
||||
<string name="web_status_timeout">Zaman Aşımı</string>
|
||||
<string name="web_status_msg_timeout">TV\'den yanıt alınamadı. Lütfen tekrar deneyin.</string>
|
||||
<string name="web_status_msg_connection_lost">TV sunucusuna artık ulaşılamıyor. Değişiklikler uygulanmış olabilir.</string>
|
||||
<string name="web_manage_repos_title">Uzantı Depolarını Yönet</string>
|
||||
<string name="web_manage_repos_subtitle">Uzantı depolarınızı yönetin</string>
|
||||
<string name="web_add_repo_url">URL ile uzantı deposu ekle</string>
|
||||
<string name="web_installed">Yüklü</string>
|
||||
<string name="web_no_repos">Yüklü uzantı deposu yok</string>
|
||||
<string name="web_error_repo_exists">Bu uzantı deposu zaten listede var</string>
|
||||
<!-- HomeScreen -->
|
||||
<string name="home_no_addons">Yüklü eklenti yok. Başlamak için bir eklenti ekleyin.</string>
|
||||
<string name="home_no_catalog_addons">Katalog eklentisi yok. İçerikleri görmek için bir katalog eklentisi yükleyin.</string>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,44 @@
|
|||
<string name="type_series">Series</string>
|
||||
<string name="type_unknown">Unknown</string>
|
||||
|
||||
<!-- WebConfigServers -->
|
||||
<string name="web_manage_addons_title">Manage Addons</string>
|
||||
<string name="web_manage_addons_subtitle">Manage addons and home catalogs</string>
|
||||
<string name="web_add_addon_url">Add addon by URL</string>
|
||||
<string name="web_placeholder_url">https://example.com/manifest.json</string>
|
||||
<string name="web_btn_add">Add</string>
|
||||
<string name="web_installed_addons">Installed Addons</string>
|
||||
<string name="web_no_addons">No addons installed</string>
|
||||
<string name="web_home_catalogs">Home Catalogs</string>
|
||||
<string name="web_no_catalogs">No home catalogs available</string>
|
||||
<string name="web_btn_save">Save Changes</string>
|
||||
<string name="web_connection_lost">Connection to TV lost</string>
|
||||
<string name="web_btn_remove">Remove</string>
|
||||
<string name="web_badge_new">New</string>
|
||||
<string name="web_badge_disabled">Disabled</string>
|
||||
<string name="web_btn_enable">Enable</string>
|
||||
<string name="web_btn_disable">Disable</string>
|
||||
<string name="web_error_addon_exists">This addon is already in the list</string>
|
||||
<string name="web_status_waiting_tv">Waiting for TV</string>
|
||||
<string name="web_status_msg_waiting_tv">Please confirm the changes on your TV to apply them.</string>
|
||||
<string name="web_status_changes_applied">Changes Applied</string>
|
||||
<string name="web_status_msg_addon_updated">Your addon configuration has been updated on the TV.</string>
|
||||
<string name="web_status_msg_repo_updated">Your repository configuration has been updated on the TV.</string>
|
||||
<string name="web_status_changes_rejected">Changes Rejected</string>
|
||||
<string name="web_status_msg_changes_rejected">The changes were declined on the TV. Your list has been reverted.</string>
|
||||
<string name="web_status_error">Something Went Wrong</string>
|
||||
<string name="web_error_failed_save">Failed to save. Check your connection to the TV.</string>
|
||||
<string name="web_btn_dismiss">Dismiss</string>
|
||||
<string name="web_status_timeout">Timed Out</string>
|
||||
<string name="web_status_msg_timeout">No response from the TV. Please try again.</string>
|
||||
<string name="web_status_msg_connection_lost">The TV server is no longer reachable. The changes may have been applied.</string>
|
||||
<string name="web_manage_repos_title">Manage Repositories</string>
|
||||
<string name="web_manage_repos_subtitle">Manage your repositories</string>
|
||||
<string name="web_add_repo_url">Add repository by URL</string>
|
||||
<string name="web_installed">Installed</string>
|
||||
<string name="web_no_repos">No repositories installed</string>
|
||||
<string name="web_error_repo_exists">This repository is already in the list</string>
|
||||
|
||||
<!-- HomeScreen -->
|
||||
<string name="home_no_addons">No addons installed. Add one to get started.</string>
|
||||
<string name="home_no_catalog_addons">No catalog addons installed. Install a catalog addon to see content.</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue