diff --git a/src/renderer/components/DataSettings/DataSettings.vue b/src/renderer/components/DataSettings/DataSettings.vue
index f4b1dd004d4d1..97b284e7ed5cd 100644
--- a/src/renderer/components/DataSettings/DataSettings.vue
+++ b/src/renderer/components/DataSettings/DataSettings.vue
@@ -65,6 +65,24 @@
@click="showExportSearchHistoryPrompt = true"
/>
+
+ {{ t('Settings.Settings') }}
+
+
+
+
+
+
{
+ const entry = JSON.parse(rawEntry)
+ if (typeof entry._id !== 'string' || !Object.hasOwn(entry, 'value')) {
+ showToast(t('Settings.Data Settings.Setting object has insufficient data, skipping item'))
+ console.error('Missing keys:', entry)
+ } else if (!Object.hasOwn(currentSettings, entry._id)) {
+ const message = t('Settings.Data Settings.Unknown setting key', { key: entry._id })
+ showToast(message)
+ } else if (NON_TRANSFERABLE_SETTINGS.has(entry._id)) {
+ const message = t('Settings.Data Settings.Non-transferable setting key', { key: entry._id })
+ showToast(message)
+ } else {
+ const currentValue = currentSettings[entry._id]
+ const areValuesEqual = currentValue === entry.value ||
+ (typeof entry.value === 'object' && JSON.stringify(currentValue) === JSON.stringify(entry.value))
+ if (!areValuesEqual) {
+ const updaterId = defaultUpdaterId(entry._id)
+ store.dispatch(updaterId, entry.value)
+ }
+ }
+ })
+
+ showToast(t('Settings.Data Settings.All settings have been successfully imported'))
+}
+
+async function exportSettings() {
+ const settingDb = Object.entries(store.state.settings)
+ .filter(([_id]) => !NON_TRANSFERABLE_SETTINGS.has(_id))
+ .map(([_id, value]) => JSON.stringify({ _id, value }))
+ .join('\n') + '\n'
+ const dateStr = getTodayDateStrLocalTimezone()
+ const exportFileName = 'freetube-settings-' + dateStr + '.db'
+
+ await promptAndWriteToFile(
+ exportFileName,
+ settingDb,
+ t('Settings.Data Settings.Settings File'),
+ 'application/x-freetube-db',
+ '.db',
+ t('Settings.Data Settings.All settings have been successfully exported')
+ )
+}
+
+// #endregion settings
+
diff --git a/src/renderer/components/FtSettingsSection/FtSettingsSection.scss b/src/renderer/components/FtSettingsSection/FtSettingsSection.scss
index 9d9fad66dba36..c723cdcbceb5d 100644
--- a/src/renderer/components/FtSettingsSection/FtSettingsSection.scss
+++ b/src/renderer/components/FtSettingsSection/FtSettingsSection.scss
@@ -97,7 +97,7 @@
padding-inline: 10px;
}
- :deep(:not(.select, .selectLabel) > .tooltip) {
+ :deep(:not(.select, .selectLabel, .groupTitle) > .tooltip) {
display: inline-block;
position: absolute;
inset-inline-end: -25px;
diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js
index 8e221365114f2..694be8ff0be32 100644
--- a/src/renderer/store/modules/settings.js
+++ b/src/renderer/store/modules/settings.js
@@ -121,6 +121,15 @@ import { getSystemLocale, showToast } from '../../helpers/utils'
* to evaluate if it is truly necessary
* and to ensure that the implementation works as intended.
*
+ ***
+ * `NON_TRANSFERABLE_SETTINGS`
+ * This set contains setting keys
+ * that should not be exported when a user chooses to "Export settings".
+ *
+ * When adding a new setting, it should be considered
+ * whether this setting can be exported or not. For example, settings
+ * that are OS or user specific like paths should not be exported.
+ *
****
* ENDING NOTES
*
@@ -143,7 +152,7 @@ import { getSystemLocale, showToast } from '../../helpers/utils'
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
const defaultGetterId = settingId => 'get' + capitalize(settingId)
const defaultMutationId = settingId => 'set' + capitalize(settingId)
-const defaultUpdaterId = settingId => 'update' + capitalize(settingId)
+export const defaultUpdaterId = settingId => 'update' + capitalize(settingId)
const defaultSideEffectsTriggerId = settingId =>
'trigger' + capitalize(settingId) + 'SideEffects'
/*****/
@@ -415,6 +424,34 @@ const sideEffectHandlers = {
const settingsWithSideEffects = Object.keys(sideEffectHandlers)
+export const NON_TRANSFERABLE_SETTINGS = new Set([
+ /* Depends on process.env.IS_ELECTRON */
+ // ProxySettings
+ 'useProxy',
+ 'proxyProtocol',
+ 'proxyHostname',
+ 'proxyPort',
+ 'proxyUsername',
+ 'proxyPassword',
+ // ExternalPlayerSettings
+ 'externalPlayer',
+ 'externalPlayerExecutable',
+ 'externalPlayerIgnoreWarnings',
+ 'externalPlayerIgnoreDefaultArgs',
+ 'externalPlayerCustomArgs',
+ 'showAddedExternalPlayerCustomArgs',
+ // Others
+ 'disableSmoothScrolling',
+ 'hideToTrayOnMinimize',
+ 'screenshotAskPath',
+ 'screenshotFolderPath',
+
+ /* Depends on process.env.SUPPORTS_LOCAL_API */
+ 'backendFallback',
+ 'backendPreference',
+ 'proxyVideos',
+])
+
const customState = {
}
diff --git a/static/locales/en-US.yaml b/static/locales/en-US.yaml
index f7384a4400b14..8e332b02dc8f6 100644
--- a/static/locales/en-US.yaml
+++ b/static/locales/en-US.yaml
@@ -593,6 +593,7 @@ Settings:
History File: History File
Playlist File: Playlist File
Search history file: Search history file
+ Settings File: Settings File
Export Subscriptions: Export Subscriptions
Export FreeTube: Export FreeTube
Export YouTube: Export YouTube
@@ -604,6 +605,9 @@ Settings:
Search history: Search history
Import search history: Import search history
Export search history: Export search history
+ Import Settings: Import Settings
+ Export Settings: Export Settings
+ Settings Tooltip: Settings that are OS/user-specific or experimental cannot be exported or imported (e.g., proxy, external player, screenshot folder...)
Profile object has insufficient data, skipping item: Profile object has insufficient
data, skipping item
All subscriptions and profiles have been successfully imported: All subscriptions
@@ -629,9 +633,16 @@ Settings:
successfully imported
All search history has been successfully exported: All search history has been
successfully exported
+ All settings have been successfully imported: All settings have been
+ successfully imported
+ All settings have been successfully exported: All settings have been
+ successfully exported
Unable to read file: Unable to read file
Unable to write file: Unable to write file
Unknown data key: Unknown data key
+ Setting object has insufficient data, skipping item: Setting object has insufficient data, skipping item
+ Unknown setting key: 'Unknown setting key: {key}'
+ Non-transferable setting key: 'Non-transferable setting key: {key}'
How do I import my subscriptions?: How do I import my subscriptions?
Manage Subscriptions: Manage Subscriptions
Proxy Settings: