mirror of
https://github.com/jellyfin/jellyfin-desktop.git
synced 2026-10-26 16:45:24 +00:00
Add extra transcoder settings and make global reactive settings object.
This commit is contained in:
1 parent
7a0f6f903f
commit
43e068c5f7
7 files changed
+244
-99
No files matched your search
+14
-15
@@ -2,13 +2,7 @@
|
||||
|
||||
## Detection
|
||||
|
||||
You can detect JMP by querying the injected utilities script:
|
||||
|
||||
```js
|
||||
window?.NativeShell?.AppHost?.appName() === "Jellyfin Media Player"
|
||||
```
|
||||
|
||||
This is not async. It can be queried immediately.
|
||||
You can detect JMP by looking for the object `window.jmpInfo`, which is defined immediately.
|
||||
|
||||
## Device Profile
|
||||
|
||||
@@ -28,7 +22,7 @@ The following functions are available immediately:
|
||||
- `window.NativeShell.AppHost.appVersion()` - Returns version number in `x.y.z` format.
|
||||
- `window.NativeShell.AppHost.deviceName()` - Returns computer hostname.
|
||||
|
||||
The following functions require calling `window.NativeShell.AppHost.init()` before using them:
|
||||
The following functions are available after less than a second:
|
||||
|
||||
- `window.NativeShell.openUrl(url)` - Opens a URL in the user's browser.
|
||||
- `window.NativeShell.openClientSettings()` - Opens prebuilt settings modal.
|
||||
@@ -210,7 +204,17 @@ Current Settings:
|
||||
- `size: int`: Controls subtitle size. Default is `32`.
|
||||
- Provided options: [see enum](https://github.com/jellyfin/jellyfin-media-player/blob/7d5943becc1ca672d599887cac9107836c38d337/resources/settings/settings_description.json#L376-L382)
|
||||
|
||||
API methods:
|
||||
The global `window.jmpInfo` object contains settings for the application in the form of `window.jmpInfo.settings.[section][key] = value`.
|
||||
Settings descriptions are stored in the form `window.jmpInfo.settingsDescriptions.[section] = [{ key, options }]`
|
||||
|
||||
- These are kept up-to-date in response to user changes.
|
||||
- If you want to change any settings, `await window.initCompleted` first.
|
||||
- To change a setting, simply set the setting to a new value. Don't overwrite an entire section.
|
||||
- You can subscribe to settings changes by adding a `callback(section, changes)` to `window.jmpInfo.settingsUpdate`.
|
||||
- Similarly, you can subscribe to settings description updates by adding a `callback(section, changes)` to `window.jmpInfo.settingsDescriptionsUpdate`.
|
||||
- This is useful for reactive settings, for instance when you change the audio device type.
|
||||
|
||||
You can also use the API to set and query settings. This is largely not needed unless you want to be sure a setting change is complete before you do something:
|
||||
|
||||
- `api.settings.settingDescriptions(callback)`: Get list of all setting descriptions and options.
|
||||
- You may need to re-query this if you update certain settings.
|
||||
@@ -218,13 +222,8 @@ API methods:
|
||||
- `api.settings.value(section, key, callback)`: Get a specific setting.
|
||||
- `api.settings.setValue(section, key, value, callback)`: Set a specific setting.
|
||||
|
||||
You can also listen for setting and setting group option updates. I don't currently use this, but you can see
|
||||
details of how to do that [from the signals here](https://github.com/jellyfin/jellyfin-media-player/blob/7d5943becc1ca672d599887cac9107836c38d337/src/settings/SettingsComponent.h#L76-L83).
|
||||
|
||||
## Checking for Updates
|
||||
|
||||
You need to call `window.NativeShell.AppHost.init()` before you can use this.
|
||||
|
||||
```js
|
||||
const updatePlugin = await window.jmpUpdatePlugin();
|
||||
updatePlugin({
|
||||
@@ -261,7 +260,7 @@ async function createApi() {
|
||||
```
|
||||
|
||||
The injected utilities script makes `window.apiPromise` available immediately and `window.api` available
|
||||
after less than a second available if you call `window.NativeShell.AppHost.init()`.
|
||||
after less than a second.
|
||||
|
||||
### Client API Usage
|
||||
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
function loadScript(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const s = document.createElement('script');
|
||||
s.src = src;
|
||||
s.onload = resolve;
|
||||
s.onerror = reject;
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
}
|
||||
|
||||
async function createApi() {
|
||||
await loadScript('qrc:///qtwebchannel/qwebchannel.js');
|
||||
const channel = await new Promise((resolve) => {
|
||||
/*global QWebChannel */
|
||||
new QWebChannel(window.qt.webChannelTransport, resolve);
|
||||
});
|
||||
return channel.objects;
|
||||
}
|
||||
|
||||
window.apiPromise = createApi();
|
||||
|
||||
async function tryConnect(server) {
|
||||
document.getElementById('connect-button').disabled = true;
|
||||
|
||||
@@ -70,10 +49,9 @@ async function tryConnect(server) {
|
||||
window.location = server;
|
||||
}
|
||||
|
||||
const api = await window.apiPromise;
|
||||
await new Promise(resolve => {
|
||||
api.settings.setValue('main', 'userWebClient', server, resolve);
|
||||
});
|
||||
await window.initCompleted;
|
||||
window.jmpInfo.settings.main.userWebClient = server;
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -99,10 +77,7 @@ document.getElementById('connect-fail-button').addEventListener('click', () => {
|
||||
|
||||
// load the server if we have one
|
||||
(async() => {
|
||||
const api = await window.apiPromise;
|
||||
const savedServer = await new Promise(resolve => {
|
||||
api.settings.value('main', 'userWebClient', resolve);
|
||||
});
|
||||
const savedServer = window.jmpInfo.settings.main.userWebClient;
|
||||
|
||||
if (!savedServer || !(await tryConnect(savedServer))) {
|
||||
document.getElementById('splash').style.display = 'none';
|
||||
|
||||
@@ -5,10 +5,9 @@ class jellyscrubPlugin {
|
||||
this.id = 'jellyscrubPlugin';
|
||||
|
||||
(async() => {
|
||||
const api = await window.apiPromise;
|
||||
const enabled = await new Promise(resolve => {
|
||||
api.settings.value('plugins', 'jellyscrub', resolve);
|
||||
});
|
||||
await window.initCompleted;
|
||||
const enabled = window.jmpInfo.settings.plugins.jellyscrub;
|
||||
|
||||
console.log("JellyScrub Plugin enabled: " + enabled);
|
||||
if (!enabled) return;
|
||||
|
||||
|
||||
+188
-45
@@ -1,4 +1,5 @@
|
||||
const viewdata = JSON.parse(window.atob("@@data@@"));
|
||||
const jmpInfo = JSON.parse(window.atob("@@data@@"));
|
||||
window.jmpInfo = jmpInfo;
|
||||
|
||||
const features = [
|
||||
"filedownload",
|
||||
@@ -40,7 +41,7 @@ function loadScript(src) {
|
||||
// Add plugin loaders
|
||||
for (const plugin of plugins) {
|
||||
window[plugin] = async () => {
|
||||
await loadScript(`${viewdata.scriptPath}${plugin}.js`);
|
||||
await loadScript(`${jmpInfo.scriptPath}${plugin}.js`);
|
||||
return window["_" + plugin];
|
||||
};
|
||||
}
|
||||
@@ -77,7 +78,7 @@ function getDeviceProfile() {
|
||||
}
|
||||
];
|
||||
|
||||
if (viewdata.force_transcode_hdr) {
|
||||
if (jmpInfo.settings.video.force_transcode_hdr) {
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Conditions': [
|
||||
@@ -90,6 +91,82 @@ function getDeviceProfile() {
|
||||
});
|
||||
}
|
||||
|
||||
if (jmpInfo.settings.video.force_transcode_hi10p) {
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Conditions': [
|
||||
{
|
||||
'Condition': 'LessThanEqual',
|
||||
'Property': 'VideoBitDepth',
|
||||
'Value': '8',
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
if (jmpInfo.settings.video.force_transcode_hevc) {
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Codec': 'hevc',
|
||||
'Conditions': [
|
||||
{
|
||||
'Condition': 'Equals',
|
||||
'Property': 'Width',
|
||||
'Value': '0',
|
||||
}
|
||||
],
|
||||
});
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Codec': 'h265',
|
||||
'Conditions': [
|
||||
{
|
||||
'Condition': 'Equals',
|
||||
'Property': 'Width',
|
||||
'Value': '0',
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (jmpInfo.settings.video.force_transcode_av1) {
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Codec': 'av1',
|
||||
'Conditions': [
|
||||
{
|
||||
'Condition': 'Equals',
|
||||
'Property': 'Width',
|
||||
'Value': '0',
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
if (jmpInfo.settings.video.force_transcode_4k) {
|
||||
CodecProfiles.push({
|
||||
'Type': 'Video',
|
||||
'Conditions': [
|
||||
{
|
||||
'Condition': 'LessThanEqual',
|
||||
'Property': 'Width',
|
||||
'Value': '1920',
|
||||
},
|
||||
{
|
||||
'Condition': 'LessThanEqual',
|
||||
'Property': 'Height',
|
||||
'Value': '1080',
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
const DirectPlayProfiles = [{'Type': 'Audio'}, {'Type': 'Photo'}];
|
||||
|
||||
if (!jmpInfo.settings.video.always_force_transcode) {
|
||||
DirectPlayProfiles.push({'Type': 'Video'});
|
||||
}
|
||||
|
||||
return {
|
||||
'Name': 'Jellyfin Media Player',
|
||||
'MaxStaticBitrate': 1000000000,
|
||||
@@ -102,14 +179,18 @@ function getDeviceProfile() {
|
||||
'Type': 'Video',
|
||||
'Protocol': 'hls',
|
||||
'AudioCodec': 'aac,mp3,ac3,opus,flac,vorbis',
|
||||
'VideoCodec': viewdata.allow_transcode_to_hevc
|
||||
? 'h264,h265,hevc,mpeg4,mpeg2video'
|
||||
'VideoCodec': jmpInfo.settings.video.allow_transcode_to_hevc
|
||||
? (
|
||||
jmpInfo.settings.video.prefer_transcode_to_h265
|
||||
? 'h265,hevc,h264,mpeg4,mpeg2video'
|
||||
: 'h264,h265,hevc,mpeg4,mpeg2video'
|
||||
)
|
||||
: 'h264,mpeg4,mpeg2video',
|
||||
'MaxAudioChannels': '6'
|
||||
},
|
||||
{'Container': 'jpeg', 'Type': 'Photo'}
|
||||
],
|
||||
'DirectPlayProfiles': [{'Type': 'Video'}, {'Type': 'Audio'}, {'Type': 'Photo'}],
|
||||
DirectPlayProfiles,
|
||||
'ResponseProfiles': [],
|
||||
'ContainerProfiles': [],
|
||||
CodecProfiles,
|
||||
@@ -134,6 +215,11 @@ function getDeviceProfile() {
|
||||
|
||||
async function createApi() {
|
||||
try {
|
||||
// Can't append script until document exists
|
||||
await new Promise(resolve => {
|
||||
document.addEventListener('DOMContentLoaded', resolve);
|
||||
});
|
||||
|
||||
await loadScript('qrc:///qtwebchannel/qwebchannel.js');
|
||||
} catch (e) {
|
||||
// try clearing out any cached CSPs
|
||||
@@ -157,15 +243,80 @@ async function createApi() {
|
||||
return channel.objects;
|
||||
}
|
||||
|
||||
let rawSettings = {};
|
||||
Object.assign(rawSettings, jmpInfo.settings);
|
||||
const settingsFromStorage = window.sessionStorage.getItem('settings');
|
||||
if (settingsFromStorage) {
|
||||
rawSettings = JSON.parse(settingsFromStorage);
|
||||
Object.assign(jmpInfo.settings, rawSettings);
|
||||
}
|
||||
|
||||
const settingsDescriptionsFromStorage = window.sessionStorage.getItem('settingsDescriptions');
|
||||
if (settingsDescriptionsFromStorage) {
|
||||
jmpInfo.settingsDescriptions = JSON.parse(settingsDescriptionsFromStorage);
|
||||
}
|
||||
|
||||
jmpInfo.settingsDescriptionsUpdate = [];
|
||||
jmpInfo.settingsUpdate = [];
|
||||
window.apiPromise = createApi();
|
||||
window.initCompleted = new Promise(async (resolve) => {
|
||||
window.api = await window.apiPromise;
|
||||
const settingUpdate = (section, key) => (
|
||||
(data) => new Promise(resolve => {
|
||||
rawSettings[section][key] = data;
|
||||
window.sessionStorage.setItem("settings", JSON.stringify(rawSettings));
|
||||
window.api.settings.setValue(section, key, data, resolve);
|
||||
})
|
||||
);
|
||||
const setSetting = (section, key) => {
|
||||
Object.defineProperty(jmpInfo.settings[section], key, {
|
||||
set: settingUpdate(section, key),
|
||||
get: () => rawSettings[section][key]
|
||||
});
|
||||
};
|
||||
for (const settingGroup of Object.keys(rawSettings)) {
|
||||
jmpInfo.settings[settingGroup] = {};
|
||||
for (const setting of Object.keys(rawSettings[settingGroup])) {
|
||||
setSetting(settingGroup, setting, jmpInfo.settings[settingGroup][setting]);
|
||||
}
|
||||
}
|
||||
window.api.settings.sectionValueUpdate.connect(
|
||||
(section, data) => {
|
||||
Object.assign(rawSettings[section], data);
|
||||
for (const callback of jmpInfo.settingsUpdate) {
|
||||
try {
|
||||
callback(section, data);
|
||||
} catch (e) {
|
||||
console.error("Update handler failed:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Settings will be outdated if page reloads, so save them to session storage
|
||||
window.sessionStorage.setItem("settings", JSON.stringify(rawSettings));
|
||||
}
|
||||
);
|
||||
window.api.settings.groupUpdate.connect(
|
||||
(section, data) => {
|
||||
jmpInfo.settingsDescriptions[section] = data.settings;
|
||||
for (const callback of jmpInfo.settingsDescriptionsUpdate) {
|
||||
try {
|
||||
callback(section, data);
|
||||
} catch (e) {
|
||||
console.error("Description update handler failed:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Settings will be outdated if page reloads, so save them to session storage
|
||||
window.sessionStorage.setItem("settingsDescriptions", JSON.stringify(jmpInfo.settingsDescriptions));
|
||||
}
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
|
||||
window.NativeShell.AppHost = {
|
||||
init() {
|
||||
window.apiPromise = createApi();
|
||||
(async () => {
|
||||
window.api = await window.apiPromise;
|
||||
})();
|
||||
},
|
||||
init() {},
|
||||
getDefaultLayout() {
|
||||
return viewdata.mode;
|
||||
return jmpInfo.mode;
|
||||
},
|
||||
supports(command) {
|
||||
return features.includes(command.toLowerCase());
|
||||
@@ -179,7 +330,7 @@ window.NativeShell.AppHost = {
|
||||
return navigator.userAgent.split(" ")[1];
|
||||
},
|
||||
deviceName() {
|
||||
return viewdata.deviceName;
|
||||
return jmpInfo.deviceName;
|
||||
},
|
||||
exit() {
|
||||
window.api.system.exit();
|
||||
@@ -187,9 +338,7 @@ window.NativeShell.AppHost = {
|
||||
};
|
||||
|
||||
async function showSettingsModal() {
|
||||
let settings = await new Promise(resolve => {
|
||||
window.api.settings.settingDescriptions(resolve);
|
||||
});
|
||||
await initCompleted;
|
||||
|
||||
const modalContainer = document.createElement("div");
|
||||
modalContainer.className = "dialogContainer";
|
||||
@@ -220,7 +369,8 @@ async function showSettingsModal() {
|
||||
modalContents.style.marginBottom = "6.2em";
|
||||
modalContainer2.appendChild(modalContents);
|
||||
|
||||
for (let section of settings) {
|
||||
const settingUpdateHandlers = {};
|
||||
for (const section of Object.keys(jmpInfo.settingsDescriptions)) {
|
||||
const group = document.createElement("fieldset");
|
||||
group.className = "editItemMetadataForm editMetadataForm dialog-content-centered";
|
||||
group.style.border = 0;
|
||||
@@ -232,23 +382,22 @@ async function showSettingsModal() {
|
||||
group.innerHTML = "";
|
||||
}
|
||||
|
||||
const values = await new Promise(resolve => {
|
||||
window.api.settings.allValues(section.key, resolve);
|
||||
});
|
||||
const values = jmpInfo.settings[section];
|
||||
const settings = jmpInfo.settingsDescriptions[section];
|
||||
|
||||
const legend = document.createElement("legend");
|
||||
const legendHeader = document.createElement("h2");
|
||||
legendHeader.textContent = section.key;
|
||||
legendHeader.textContent = section;
|
||||
legendHeader.style.textTransform = "capitalize";
|
||||
legend.appendChild(legendHeader);
|
||||
if (section.key == "plugins") {
|
||||
if (section == "plugins") {
|
||||
const legendSubHeader = document.createElement("h4");
|
||||
legendSubHeader.textContent = "Plugins are UNOFFICIAL and require a restart to take effect.";
|
||||
legend.appendChild(legendSubHeader);
|
||||
}
|
||||
group.appendChild(legend);
|
||||
|
||||
for (const setting of section.settings) {
|
||||
for (const setting of settings) {
|
||||
const label = document.createElement("label");
|
||||
label.className = "inputContainer";
|
||||
label.style.marginBottom = "1.8em";
|
||||
@@ -264,8 +413,8 @@ async function showSettingsModal() {
|
||||
opt.value = option.value;
|
||||
opt.selected = option.value == values[setting.key];
|
||||
let optionName = option.title;
|
||||
const swTest = `${section.key}.${setting.key}.`;
|
||||
const swTest2 = `${section.key}.`;
|
||||
const swTest = `${section}.${setting.key}.`;
|
||||
const swTest2 = `${section}.`;
|
||||
if (optionName.startsWith(swTest)) {
|
||||
optionName = optionName.substring(swTest.length);
|
||||
} else if (optionName.startsWith(swTest2)) {
|
||||
@@ -275,16 +424,7 @@ async function showSettingsModal() {
|
||||
control.appendChild(opt);
|
||||
}
|
||||
control.addEventListener("change", async (e) => {
|
||||
await new Promise(resolve => {
|
||||
window.api.settings.setValue(section.key, setting.key, safeValues[e.target.value], resolve);
|
||||
});
|
||||
|
||||
if (setting.key == "devicetype") {
|
||||
section = (await new Promise(resolve => {
|
||||
window.api.settings.settingDescriptions(resolve);
|
||||
})).filter(x => x.key == section.key)[0];
|
||||
createSection(true);
|
||||
}
|
||||
jmpInfo.settings[section][setting.key] = safeValues[e.target.value];
|
||||
});
|
||||
const labelText = document.createElement('label');
|
||||
labelText.className = "inputLabel";
|
||||
@@ -296,7 +436,7 @@ async function showSettingsModal() {
|
||||
control.type = "checkbox";
|
||||
control.checked = values[setting.key];
|
||||
control.addEventListener("change", e => {
|
||||
window.api.settings.setValue(section.key, setting.key, e.target.checked);
|
||||
jmpInfo.settings[section][setting.key] = e.target.checked;
|
||||
});
|
||||
label.appendChild(control);
|
||||
label.appendChild(document.createTextNode(" " + setting.key));
|
||||
@@ -304,14 +444,19 @@ async function showSettingsModal() {
|
||||
group.appendChild(label);
|
||||
}
|
||||
};
|
||||
settingUpdateHandlers[section] = () => createSection(true);
|
||||
createSection();
|
||||
}
|
||||
|
||||
const savedServer = await new Promise(resolve => {
|
||||
window.api.settings.value('main', 'userWebClient', resolve);
|
||||
});
|
||||
const onSectionUpdate = (section) => {
|
||||
if (section in settingUpdateHandlers) {
|
||||
settingUpdateHandlers[section]();
|
||||
}
|
||||
};
|
||||
jmpInfo.settingsDescriptionsUpdate.push(onSectionUpdate);
|
||||
jmpInfo.settingsUpdate.push(onSectionUpdate);
|
||||
|
||||
if (savedServer) {
|
||||
if (jmpInfo.settings.main.userWebClient) {
|
||||
const group = document.createElement("fieldset");
|
||||
group.className = "editItemMetadataForm editMetadataForm dialog-content-centered";
|
||||
group.style.border = 0;
|
||||
@@ -337,10 +482,8 @@ async function showSettingsModal() {
|
||||
resetSavedServer.style.marginRight = "auto";
|
||||
resetSavedServer.style.maxWidth = "50%";
|
||||
resetSavedServer.addEventListener("click", async () => {
|
||||
await new Promise(resolve => {
|
||||
window.api.settings.setValue('main', 'userWebClient', '', resolve);
|
||||
});
|
||||
window.location.href = viewdata.scriptPath + "/find-webclient.html";
|
||||
window.jmpInfo.settings.main.userWebClient = '';
|
||||
window.location.href = jmpInfo.scriptPath + "/find-webclient.html";
|
||||
});
|
||||
group.appendChild(resetSavedServer);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@ class skipIntroPlugin {
|
||||
this.id = 'skipIntroPlugin';
|
||||
|
||||
(async() => {
|
||||
const api = await window.apiPromise;
|
||||
const enabled = await new Promise(resolve => {
|
||||
api.settings.value('plugins', 'skipintro', resolve);
|
||||
});
|
||||
await window.initCompleted;
|
||||
const enabled = window.jmpInfo.settings.plugins.skipintro;
|
||||
|
||||
console.log("Skip Intro Plugin enabled: " + enabled);
|
||||
if (!enabled) return;
|
||||
|
||||
|
||||
@@ -268,6 +268,30 @@
|
||||
"value": "force_transcode_hdr",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "prefer_transcode_to_h265",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "force_transcode_hi10p",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "force_transcode_hevc",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "force_transcode_av1",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "force_transcode_4k",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "always_force_transcode",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"value": "sync_mode",
|
||||
"default": "audio",
|
||||
|
||||
@@ -331,8 +331,14 @@ QString SystemComponent::getNativeShellScript()
|
||||
clientData.insert("deviceName", QJsonValue::fromVariant(SettingsComponent::Get().getClientName()));
|
||||
clientData.insert("scriptPath", QJsonValue::fromVariant("file:///" + path));
|
||||
clientData.insert("mode", QJsonValue::fromVariant(SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "layout").toString()));
|
||||
clientData.insert("allow_transcode_to_hevc", QJsonValue::fromVariant(SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "allow_transcode_to_hevc").toBool()));
|
||||
clientData.insert("force_transcode_hdr", QJsonValue::fromVariant(SettingsComponent::Get().value(SETTINGS_SECTION_VIDEO, "force_transcode_hdr").toBool()));
|
||||
QVariantList settingsDescriptionsList = SettingsComponent::Get().settingDescriptions();
|
||||
QVariantMap settingsDescriptions = QVariantMap();
|
||||
for (auto setting : settingsDescriptionsList) {
|
||||
QVariantMap settingMap = setting.toMap();
|
||||
settingsDescriptions.insert(settingMap["key"].toString(), settingMap["settings"]);
|
||||
}
|
||||
clientData.insert("settingsDescriptions", QJsonValue::fromVariant(settingsDescriptions));
|
||||
clientData.insert("settings", QJsonValue::fromVariant(SettingsComponent::Get().allValues()));
|
||||
nativeshellString.replace("@@data@@", QJsonDocument(clientData).toJson(QJsonDocument::Compact).toBase64());
|
||||
return nativeshellString;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user