Add backup export and local artwork storage
This commit is contained in:
@@ -31,12 +31,37 @@
|
||||
<div id="ml-action-matrix" class="ml-action-matrix"></div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="ml-section">
|
||||
<legend>Import and export</legend>
|
||||
<div class="ml-actions">
|
||||
<button id="ml-user-export" is="emby-button" type="button" class="raised"><span>Export my settings</span></button>
|
||||
<button id="ml-user-import-open" is="emby-button" type="button" class="raised"><span>Import settings...</span></button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<button id="ml-save-user" is="emby-button" type="submit" class="raised button-submit"><span>Save</span></button>
|
||||
<button id="ml-reset-user" is="emby-button" type="button" class="raised"><span>Reload</span></button>
|
||||
</form>
|
||||
|
||||
<div id="ml-user-status" class="ml-status"></div>
|
||||
|
||||
<div id="ml-user-import-modal" class="ml-modal" hidden>
|
||||
<div class="ml-modal-panel">
|
||||
<h2>Import user settings</h2>
|
||||
<div class="inputContainer">
|
||||
<input id="ml-user-import-file" type="file" accept=".zip,application/zip">
|
||||
</div>
|
||||
<div id="ml-user-import-choice" class="inputContainer" hidden>
|
||||
<label class="inputLabel inputLabelUnfocused" for="ml-user-import-source">Import settings from user ID</label>
|
||||
<select id="ml-user-import-source" class="emby-select ml-select"></select>
|
||||
</div>
|
||||
<div class="ml-actions">
|
||||
<button id="ml-user-import-run" is="emby-button" type="button" class="raised button-submit"><span>Import</span></button>
|
||||
<button id="ml-user-import-cancel" is="emby-button" type="button" class="raised"><span>Cancel</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/Multilang/shared.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
@@ -1131,6 +1156,103 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadUserExport() {
|
||||
M.setStatus(status, "Creating export...");
|
||||
var response = await fetch(M.basePath() + "/Multilang/UserRules/self/export", {
|
||||
cache: "no-store",
|
||||
headers: M.authHeaders()
|
||||
});
|
||||
if (!response.ok) throw new Error("HTTP " + response.status);
|
||||
var blob = await response.blob();
|
||||
var url = URL.createObjectURL(blob);
|
||||
var a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "multilang-user-settings.zip";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
M.setStatus(status, "Export created.");
|
||||
}
|
||||
|
||||
function userImportModal(show) {
|
||||
$("ml-user-import-modal").hidden = !show;
|
||||
if (!show) {
|
||||
$("ml-user-import-choice").hidden = true;
|
||||
$("ml-user-import-source").replaceChildren();
|
||||
}
|
||||
}
|
||||
|
||||
function userImportFormData(sourceUserId) {
|
||||
var file = $("ml-user-import-file").files[0];
|
||||
if (!file) throw new Error("Choose an export zip first.");
|
||||
var formData = new FormData();
|
||||
formData.append("file", file);
|
||||
if (sourceUserId) formData.append("sourceUserId", sourceUserId);
|
||||
return formData;
|
||||
}
|
||||
|
||||
async function inspectUserImport() {
|
||||
var response = await fetch(M.basePath() + "/Multilang/UserRules/self/import/inspect", {
|
||||
method: "POST",
|
||||
cache: "no-store",
|
||||
headers: M.authHeaders(),
|
||||
body: userImportFormData("")
|
||||
});
|
||||
if (!response.ok) throw new Error("HTTP " + response.status);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function postUserImport(sourceUserId) {
|
||||
var response = await fetch(M.basePath() + "/Multilang/UserRules/self/import", {
|
||||
method: "POST",
|
||||
cache: "no-store",
|
||||
headers: M.authHeaders(),
|
||||
body: userImportFormData(sourceUserId)
|
||||
});
|
||||
if (!response.ok) throw new Error("HTTP " + response.status);
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async function runUserImport() {
|
||||
M.setStatus(status, "Inspecting import...");
|
||||
var choice = $("ml-user-import-choice");
|
||||
if (!choice.hidden) {
|
||||
var selected = $("ml-user-import-source").value;
|
||||
if (!selected) throw new Error("Choose a source user ID.");
|
||||
var chosen = await postUserImport(selected);
|
||||
if ((chosen.UserSettingsImported ?? chosen.userSettingsImported ?? 0) <= 0) throw new Error("No user settings were imported.");
|
||||
userImportModal(false);
|
||||
await load();
|
||||
M.setStatus(status, "Imported user settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
var inspect = await inspectUserImport();
|
||||
var count = inspect.UserSettingsCount ?? inspect.userSettingsCount ?? 0;
|
||||
var containsCurrent = inspect.ContainsCurrentUser ?? inspect.containsCurrentUser;
|
||||
var ids = inspect.UserIds || inspect.userIds || [];
|
||||
if (count <= 0) {
|
||||
M.setStatus(status, "No user settings were found in this export.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (containsCurrent) {
|
||||
var result = await postUserImport("");
|
||||
if ((result.UserSettingsImported ?? result.userSettingsImported ?? 0) <= 0) throw new Error("No user settings were imported.");
|
||||
userImportModal(false);
|
||||
await load();
|
||||
M.setStatus(status, "Imported user settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
var select = $("ml-user-import-source");
|
||||
select.replaceChildren();
|
||||
ids.forEach(function (id) { select.appendChild(option(id, id, false)); });
|
||||
choice.hidden = false;
|
||||
M.setStatus(status, "No matching user ID was found. Choose which exported user settings to import.", true);
|
||||
}
|
||||
|
||||
function bindEvents() {
|
||||
if (page.dataset.bound === "1") return;
|
||||
page.dataset.bound = "1";
|
||||
@@ -1147,6 +1269,17 @@
|
||||
});
|
||||
$("ml-reset-user").addEventListener("click", load);
|
||||
$("ml-sort-locale").addEventListener("change", renderSortLocaleAutoText);
|
||||
$("ml-user-export").addEventListener("click", function () {
|
||||
downloadUserExport().catch(function (err) { M.setStatus(status, String(err), true); });
|
||||
});
|
||||
$("ml-user-import-open").addEventListener("click", function () {
|
||||
$("ml-user-import-file").value = "";
|
||||
userImportModal(true);
|
||||
});
|
||||
$("ml-user-import-cancel").addEventListener("click", function () { userImportModal(false); });
|
||||
$("ml-user-import-run").addEventListener("click", function () {
|
||||
runUserImport().catch(function (err) { M.setStatus(status, String(err), true); });
|
||||
});
|
||||
document.addEventListener("click", function () { closeValuePanels(null); });
|
||||
$("ml-add-category").addEventListener("click", function () {
|
||||
syncCategoriesFromDom();
|
||||
|
||||
Reference in New Issue
Block a user