mirror of
https://github.com/SoPat712/videospeed.git
synced 2026-04-26 22:23:09 -04:00
feat: add import/export settings with modular architecture
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
// Import/Export functionality for Video Speed Controller settings
|
||||
|
||||
function generateBackupFilename() {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(now.getDate()).padStart(2, "0");
|
||||
const hours = String(now.getHours()).padStart(2, "0");
|
||||
const minutes = String(now.getMinutes()).padStart(2, "0");
|
||||
const seconds = String(now.getSeconds()).padStart(2, "0");
|
||||
return `speeder-backup_${year}-${month}-${day}_${hours}.${minutes}.${seconds}.json`;
|
||||
}
|
||||
|
||||
function exportSettings() {
|
||||
chrome.storage.sync.get(null, function (storage) {
|
||||
const backup = {
|
||||
version: "1.0",
|
||||
exportDate: new Date().toISOString(),
|
||||
settings: storage
|
||||
};
|
||||
|
||||
const dataStr = JSON.stringify(backup, null, 2);
|
||||
const blob = new Blob([dataStr], { type: "application/json" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = generateBackupFilename();
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
showStatus("Settings exported successfully");
|
||||
});
|
||||
}
|
||||
|
||||
function importSettings() {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".json";
|
||||
|
||||
input.onchange = function (event) {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
try {
|
||||
const backup = JSON.parse(e.target.result);
|
||||
|
||||
if (!backup.settings) {
|
||||
showStatus("Error: Invalid backup file format", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Import all settings
|
||||
chrome.storage.sync.clear(function () {
|
||||
chrome.storage.sync.set(backup.settings, function () {
|
||||
showStatus("Settings imported successfully. Reloading...");
|
||||
setTimeout(function () {
|
||||
if (typeof restore_options === "function") {
|
||||
restore_options();
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
showStatus("Error: Failed to parse backup file - " + err.message, true);
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = function () {
|
||||
showStatus("Error: Failed to read file", true);
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
input.click();
|
||||
}
|
||||
|
||||
function showStatus(message, isError = false) {
|
||||
const status = document.getElementById("status");
|
||||
if (status) {
|
||||
status.textContent = message;
|
||||
status.style.color = isError ? "#d32f2f" : "";
|
||||
setTimeout(function () {
|
||||
status.textContent = "";
|
||||
status.style.color = "";
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize import/export buttons when DOM is ready
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", initImportExport);
|
||||
} else {
|
||||
initImportExport();
|
||||
}
|
||||
|
||||
function initImportExport() {
|
||||
const exportBtn = document.getElementById("exportSettings");
|
||||
const importBtn = document.getElementById("importSettings");
|
||||
|
||||
if (exportBtn) {
|
||||
exportBtn.addEventListener("click", exportSettings);
|
||||
}
|
||||
|
||||
if (importBtn) {
|
||||
importBtn.addEventListener("click", importSettings);
|
||||
}
|
||||
}
|
||||
+14
@@ -81,6 +81,13 @@ button {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#exportSettings,
|
||||
#importSettings {
|
||||
background-image: linear-gradient(#e3f2fd, #e3f2fd 38%, #bbdefb);
|
||||
border-color: rgba(33, 150, 243, 0.4);
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 75px;
|
||||
text-align: center;
|
||||
@@ -211,6 +218,13 @@ select {
|
||||
background-image: linear-gradient(#4a4a4a, #4a4a4a 38%, #3f3f3f);
|
||||
}
|
||||
|
||||
#exportSettings,
|
||||
#importSettings {
|
||||
background-image: linear-gradient(#1e3a5f, #1e3a5f 38%, #152d47);
|
||||
border-color: rgba(100, 181, 246, 0.4);
|
||||
color: #90caf9;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="checkbox"],
|
||||
select,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<title>Video Speed Controller: Options</title>
|
||||
<link rel="stylesheet" href="options.css" />
|
||||
<script src="options.js"></script>
|
||||
<script src="importExport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@@ -282,6 +283,8 @@
|
||||
|
||||
<button id="save">Save</button>
|
||||
<button id="restore">Restore Defaults</button>
|
||||
<button id="exportSettings">Export Settings</button>
|
||||
<button id="importSettings">Import Settings</button>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
|
||||
+3
-2
@@ -351,7 +351,7 @@ function normalizeStoredBinding(binding, fallbackKeyCode) {
|
||||
|
||||
function getBindingLabel(binding) {
|
||||
if (!binding) return "";
|
||||
if (binding.disabled) return "null";
|
||||
if (binding.disabled) return "";
|
||||
if (binding.key) {
|
||||
return displayKeyAliases[binding.key] || binding.key;
|
||||
}
|
||||
@@ -556,7 +556,8 @@ function save_options() {
|
||||
var status = document.getElementById("status");
|
||||
var saveError = null;
|
||||
|
||||
Array.from(document.querySelectorAll(".customs")).forEach((item) => {
|
||||
// Only collect shortcuts from the main shortcuts section, NOT from site rules
|
||||
Array.from(document.querySelectorAll("#customs .customs")).forEach((item) => {
|
||||
if (saveError) return;
|
||||
var result = createKeyBindings(item);
|
||||
if (!result.valid) saveError = result.message;
|
||||
|
||||
Reference in New Issue
Block a user