Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c084d1f
Add import/export settings buttons
Shadorc May 10, 2026
0580386
Add exported settings exclusion list
Shadorc May 11, 2026
3cb4a0b
Add documentation
Shadorc May 11, 2026
d9eaa2e
Add tooltip to the data settings header
Shadorc May 11, 2026
565fa56
Consider settingsPassword to be exportable, could be discussed
Shadorc May 11, 2026
d942100
Take into account settings that need restart
Shadorc May 12, 2026
59d2592
Simplify const access across store and vue script
Shadorc May 12, 2026
f4be70c
Add documentation
Shadorc May 14, 2026
45d2d4b
Improve tooltip text
Shadorc May 15, 2026
3c067e0
Fix tooltip on mobile screen
Shadorc May 15, 2026
8eaa007
Add more items to settingsNotExportable set
Shadorc May 15, 2026
79883d9
Add backendPreference key to settingsNotExportable set because it is …
Shadorc May 15, 2026
1458985
Set 'showRestartPrompt.value' once after for loop
Shadorc May 18, 2026
dba0b34
Replace 'exportable' occurences by 'transferrable'
Shadorc May 18, 2026
e29d898
Add more settings to 'settingsNotTransferrable' array
Shadorc May 18, 2026
166ef5f
Remove pending restart and update 'settingsNotTransferrable'
Shadorc May 18, 2026
ad621af
Fix typo
Shadorc May 19, 2026
33f5997
Differentiate between non-transferable and unknown settings
Shadorc May 19, 2026
2987336
Fix localization key
Shadorc May 19, 2026
173d9c9
Use same format as db file for import/export
Shadorc Jul 11, 2026
0e27838
Apply code suggestions
Shadorc Jul 11, 2026
ffdb4c3
Apply code suggestions
Shadorc Jul 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/renderer/components/DataSettings/DataSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@
@click="showExportSearchHistoryPrompt = true"
/>
</FtFlexBox>
<h4 class="groupTitle">
{{ t('Settings.Settings') }}
<FtTooltip
class="selectTooltip"
position="top"
:tooltip="t('Settings.Data Settings.Settings Tooltip')"
/>
</h4>
<FtFlexBox class="box">
<FtButton
:label="t('Settings.Data Settings.Import Settings')"
@click="importSettings"
/>
<FtButton
:label="t('Settings.Data Settings.Export Settings')"
@click="exportSettings"
/>
</FtFlexBox>
<FtPrompt
v-if="showExportSubscriptionsPrompt"
:label="$t('Settings.Data Settings.Select Export Type')"
Expand Down Expand Up @@ -98,8 +116,10 @@ import FtButton from '../FtButton/FtButton.vue'
import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtPrompt from '../FtPrompt/FtPrompt.vue'
import FtSettingsSection from '../FtSettingsSection/FtSettingsSection.vue'
import FtTooltip from '../FtTooltip/FtTooltip.vue'

import store from '../../store/index'
import { defaultUpdaterId, NON_TRANSFERABLE_SETTINGS } from '../../store/modules/settings'

import { MAIN_PROFILE_ID } from '../../../constants'
import { calculateColorLuminance, getRandomColor } from '../../helpers/colors'
Expand Down Expand Up @@ -1465,6 +1485,81 @@ async function exportYouTubeSearchHistory() {
}

// #endregion search history

// #region settings

async function importSettings() {
let response
try {
response = await readFileWithPicker(
t('Settings.Data Settings.Settings File'),
{
'application/x-freetube-db': '.db',
'application/json': '.json'
},
IMPORT_DIRECTORY_ID,
START_IN_DIRECTORY
)
} catch (err) {
const message = t('Settings.Data Settings.Unable to read file')
showToast(`${message}: ${err}`)
return
}

if (response === null) {
return
}

const textDecode = response.content.split('\n')
textDecode.pop()

const currentSettings = store.state.settings

textDecode.forEach((rawEntry) => {
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

</script>

<style scoped src="./DataSettings.css" />
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
39 changes: 38 additions & 1 deletion src/renderer/store/modules/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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'
/*****/
Expand Down Expand Up @@ -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 */
Comment thread
Shadorc marked this conversation as resolved.
'backendFallback',
'backendPreference',
'proxyVideos',
])

const customState = {
}

Expand Down
11 changes: 11 additions & 0 deletions static/locales/en-US.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand Down