SettingsComponent.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. #include <QDebug>
  2. #include <QFile>
  3. #include <QFileInfo>
  4. #include "SettingsComponent.h"
  5. #include "SettingsSection.h"
  6. #include "Paths.h"
  7. #include "utils/Utils.h"
  8. #include "AudioSettingsController.h"
  9. #include "Names.h"
  10. #include <QSaveFile>
  11. #include <QJsonDocument>
  12. #include <QJsonObject>
  13. #include <QJsonArray>
  14. #include <QList>
  15. #include <QSettings>
  16. #include "input/InputComponent.h"
  17. #include "system/SystemComponent.h"
  18. #include "Version.h"
  19. #define OLDEST_PREVIOUS_VERSION_KEY "oldestPreviousVersion"
  20. ///////////////////////////////////////////////////////////////////////////////////////////////////
  21. SettingsComponent::SettingsComponent(QObject *parent) : ComponentBase(parent), m_settingsVersion(-1)
  22. {
  23. }
  24. /////////////////////////////////////////////////////////////////////////////////////////
  25. void SettingsComponent::componentPostInitialize()
  26. {
  27. InputComponent::Get().registerHostCommand("cycle_setting", this, "cycleSettingCommand");
  28. InputComponent::Get().registerHostCommand("set_setting", this, "setSettingCommand");
  29. }
  30. /////////////////////////////////////////////////////////////////////////////////////////
  31. void SettingsComponent::cycleSettingCommand(const QString& args)
  32. {
  33. QString settingName = args;
  34. QStringList sub = settingName.split(".");
  35. if (sub.size() != 2)
  36. {
  37. qCritical() << "Setting must be in the form section.name but got:" << settingName;
  38. return;
  39. }
  40. QString sectionID = sub[0];
  41. QString valueName = sub[1];
  42. SettingsSection* section = getSection(sectionID);
  43. if (!section)
  44. {
  45. qCritical() << "Section" << sectionID << "is unknown";
  46. return;
  47. }
  48. QVariantList values = section->possibleValues(valueName);
  49. // If no possible values are defined, check the type of the default value.
  50. // In the case it's a boolean simply negate the current value to cycle through.
  51. // Otherwise log an error message, that it's not possible to cycle through the value.
  52. if (values.size() == 0)
  53. {
  54. if (section->defaultValue(valueName).type() == QVariant::Bool)
  55. {
  56. QVariant currentValue = section->value(valueName);
  57. auto nextValue = currentValue.toBool() ? false : true;
  58. setValue(sectionID, valueName, nextValue);
  59. qDebug() << "Setting" << settingName << "to " << (nextValue ? "Enabled" : "Disabled");
  60. emit SystemComponent::Get().settingsMessage(valueName, nextValue ? "Enabled" : "Disabled");
  61. return;
  62. }
  63. else
  64. {
  65. qCritical() << "Setting" << settingName << "is unknown or is not cycleable.";
  66. return;
  67. }
  68. }
  69. QVariant currentValue = section->value(valueName);
  70. int nextValueIndex = 0;
  71. for (int n = 0; n < values.size(); n++)
  72. {
  73. if (currentValue == values[n].toMap()["value"])
  74. {
  75. nextValueIndex = n + 1;
  76. break;
  77. }
  78. }
  79. if (nextValueIndex >= values.size())
  80. nextValueIndex = 0;
  81. auto nextSetting = values[nextValueIndex].toMap();
  82. auto nextValue = nextSetting["value"];
  83. qDebug() << "Setting" << settingName << "to" << nextValue;
  84. setValue(sectionID, valueName, nextValue);
  85. emit SystemComponent::Get().settingsMessage(valueName, nextSetting["title"].toString());
  86. }
  87. ///////////////////////////////////////////////////////////////////////////////////////////////////
  88. void SettingsComponent::setSettingCommand(const QString& args)
  89. {
  90. int spaceIndex = args.indexOf(" ");
  91. if (spaceIndex < 0)
  92. {
  93. qCritical() << "No value provided to settings set command.";
  94. return;
  95. }
  96. QString settingName = args.mid(0, spaceIndex);
  97. QString settingValue = args.mid(spaceIndex + 1);
  98. int subIndex = settingName.indexOf(".");
  99. if (subIndex < 0 || subIndex == args.size() - 1)
  100. {
  101. qCritical() << "Setting must be in the form section.name but got:" << settingName;
  102. return;
  103. }
  104. QString sectionID = settingName.mid(0, subIndex);
  105. QString valueName = settingName.mid(subIndex + 1);
  106. SettingsSection* section = getSection(sectionID);
  107. if (!section)
  108. {
  109. qCritical() << "Section" << sectionID << "is unknown";
  110. return;
  111. }
  112. QString jsonString = "{\"value\": " + settingValue + "}";
  113. QJsonParseError err;
  114. QVariant value = QJsonDocument::fromJson(jsonString.toUtf8(), &err).object()["value"].toVariant();
  115. printf("val: '%s'\n", settingValue.toUtf8().data());
  116. if (!value.isValid())
  117. {
  118. qCritical() << "Invalid settings value:" << settingValue << "(if it's a string, make sure to quote it)";
  119. return;
  120. }
  121. qDebug() << "Setting" << settingName << "to" << value;
  122. setValue(sectionID, valueName, value);
  123. emit SystemComponent::Get().settingsMessage(valueName, value.toString());
  124. }
  125. ///////////////////////////////////////////////////////////////////////////////////////////////////
  126. void SettingsComponent::updatePossibleValues(const QString &sectionID, const QString &key, const QVariantList &possibleValues)
  127. {
  128. SettingsSection* section = getSection(sectionID);
  129. if (!section)
  130. {
  131. qCritical() << "Section" << sectionID << "is unknown";
  132. return;
  133. }
  134. section->updatePossibleValues(key, possibleValues);
  135. qDebug() << "Updated possible values for:" << key << "to" << possibleValues;
  136. }
  137. ///////////////////////////////////////////////////////////////////////////////////////////////////
  138. QVariant SettingsComponent::allValues(const QString& section)
  139. {
  140. if (section.isEmpty())
  141. {
  142. QVariantMap all;
  143. for(const QString& sname : m_sections.keys())
  144. all[sname] = m_sections[sname]->allValues();
  145. return all;
  146. }
  147. else if (m_sections.contains(section))
  148. {
  149. return m_sections[section]->allValues();
  150. }
  151. else
  152. {
  153. // look for the value in the webclient section
  154. return m_sections[SETTINGS_SECTION_WEBCLIENT]->value(section);
  155. }
  156. }
  157. ///////////////////////////////////////////////////////////////////////////////////////////////////
  158. static void writeFile(const QString& filename, const QByteArray& data)
  159. {
  160. QSaveFile file(filename);
  161. file.open(QIODevice::WriteOnly | QIODevice::Text);
  162. file.write(data);
  163. if (!file.commit())
  164. {
  165. qCritical() << "Could not write" << filename;
  166. }
  167. }
  168. ///////////////////////////////////////////////////////////////////////////////////////////////////
  169. static QJsonObject loadJson(const QString& filename)
  170. {
  171. // Checking existence before opening is technically a race condition, but
  172. // it looks like Qt doesn't let us distinguish errors on opening.
  173. if (!QFile(filename).exists())
  174. return QJsonObject();
  175. QJsonParseError err;
  176. QJsonDocument json = Utils::OpenJsonDocument(filename, &err);
  177. if (json.isNull())
  178. {
  179. qCritical() << "Could not open" << filename << "due to" << err.errorString();
  180. }
  181. return json.object();
  182. }
  183. ///////////////////////////////////////////////////////////////////////////////////////////////////
  184. static void writeJson(const QString& filename, const QJsonObject& data, bool pretty = true)
  185. {
  186. QJsonDocument json(data);
  187. writeFile(filename, json.toJson(pretty ? QJsonDocument::Indented : QJsonDocument::Compact));
  188. }
  189. /////////////////////////////////////////////////////////////////////////////////////////
  190. QVariant SettingsComponent::readPreinitValue(const QString& sectionID, const QString& key)
  191. {
  192. QJsonObject json = loadJson(Paths::dataDir("jellyfinmediaplayer.conf"));
  193. return json["sections"].toObject()[sectionID].toObject()[key].toVariant();
  194. }
  195. /////////////////////////////////////////////////////////////////////////////////////////
  196. void SettingsComponent::load()
  197. {
  198. loadConf(Paths::dataDir("jellyfinmediaplayer.conf"), false);
  199. loadConf(Paths::dataDir("storage.json"), true);
  200. }
  201. ///////////////////////////////////////////////////////////////////////////////////////////////////
  202. void SettingsComponent::loadConf(const QString& path, bool storage)
  203. {
  204. bool migrateJmpSettings4 = false;
  205. bool migrateJmpSettings5 = false;
  206. QJsonObject json = loadJson(path);
  207. int version = json["version"].toInt(0);
  208. if (version == 4 && m_settingsVersion == 6)
  209. {
  210. migrateJmpSettings4 = true;
  211. }
  212. else if (version == 5 && m_settingsVersion == 6)
  213. {
  214. migrateJmpSettings5 = true;
  215. }
  216. else if (version != m_settingsVersion)
  217. {
  218. QString backup = path + ".broken";
  219. QFile::remove(backup);
  220. QFile::rename(path, backup);
  221. if (version == 0)
  222. qCritical() << "Could not read config file.";
  223. else
  224. qCritical() << "Config version is" << version << "but" << m_settingsVersion << "expected. Moving old config to" << backup;
  225. // Overwrite/create it with the defaults.
  226. if (storage)
  227. saveStorage();
  228. else
  229. saveSettings();
  230. return;
  231. }
  232. QJsonObject jsonSections = json["sections"].toObject();
  233. for(const QString& section : jsonSections.keys())
  234. {
  235. QJsonObject jsonSection = jsonSections[section].toObject();
  236. SettingsSection* sec = getSection(section);
  237. if (!sec && storage)
  238. {
  239. sec = new SettingsSection(section, PLATFORM_ANY, -1, this);
  240. sec->setHidden(true);
  241. sec->setStorage(true);
  242. m_sections.insert(section, sec);
  243. }
  244. else if (!sec)
  245. {
  246. qCritical() << "Trying to load section:" << section << "from config file, but we don't want that.";
  247. continue;
  248. }
  249. for(const QString& setting : jsonSection.keys())
  250. sec->setValue(setting, jsonSection.value(setting).toVariant());
  251. }
  252. if (migrateJmpSettings4) {
  253. getSection(SETTINGS_SECTION_MAIN)->setValue("webMode", "desktop");
  254. getSection(SETTINGS_SECTION_MAIN)->setValue("layout", "desktop");
  255. if (getSection(SETTINGS_SECTION_VIDEO)->value("hardwareDecoding") == "disabled") {
  256. getSection(SETTINGS_SECTION_VIDEO)->setValue("hardwareDecoding", "copy");
  257. }
  258. } else if (migrateJmpSettings5) {
  259. if (getSection(SETTINGS_SECTION_VIDEO)->value("hardwareDecoding") == "enabled") {
  260. getSection(SETTINGS_SECTION_VIDEO)->setValue("hardwareDecoding", "copy");
  261. }
  262. }
  263. }
  264. ///////////////////////////////////////////////////////////////////////////////////////////////////
  265. void SettingsComponent::saveSettings()
  266. {
  267. if (m_oldestPreviousVersion.isEmpty())
  268. {
  269. qCritical() << "Not writing settings: uninitialized.\n";
  270. return;
  271. }
  272. QVariantMap sections;
  273. for(SettingsSection* section : m_sections.values())
  274. {
  275. if (!section->isStorage())
  276. sections.insert(section->sectionName(), section->allValues());
  277. }
  278. QJsonObject json;
  279. json.insert("sections", QJsonValue::fromVariant(sections));
  280. json.insert("version", m_settingsVersion);
  281. writeJson(Paths::dataDir("jellyfinmediaplayer.conf"), json);
  282. }
  283. ///////////////////////////////////////////////////////////////////////////////////////////////////
  284. void SettingsComponent::saveStorage()
  285. {
  286. QVariantMap storage;
  287. for(SettingsSection* section : m_sections.values())
  288. {
  289. if (section->isStorage())
  290. storage.insert(section->sectionName(), section->allValues());
  291. }
  292. QJsonObject storagejson;
  293. storagejson.insert("sections", QJsonValue::fromVariant(storage));
  294. storagejson.insert("version", m_settingsVersion);
  295. writeJson(Paths::dataDir("storage.json"), storagejson, false);
  296. }
  297. ///////////////////////////////////////////////////////////////////////////////////////////////////
  298. void SettingsComponent::saveSection(SettingsSection* section)
  299. {
  300. if (section && section->isStorage())
  301. saveStorage();
  302. else
  303. saveSettings();
  304. }
  305. ///////////////////////////////////////////////////////////////////////////////////////////////////
  306. QVariant SettingsComponent::value(const QString& sectionID, const QString &key)
  307. {
  308. SettingsSection* section = getSection(sectionID);
  309. if (!section)
  310. {
  311. qCritical() << "Section" << sectionID << "is unknown";
  312. return QVariant();
  313. }
  314. return section->value(key);
  315. }
  316. ///////////////////////////////////////////////////////////////////////////////////////////////////
  317. void SettingsComponent::setValue(const QString& sectionID, const QString &key, const QVariant &value)
  318. {
  319. SettingsSection* section = getSection(sectionID);
  320. if (!section)
  321. {
  322. qCritical() << "Section" << sectionID << "is unknown";
  323. return;
  324. }
  325. section->setValue(key, value);
  326. saveSection(section);
  327. }
  328. ///////////////////////////////////////////////////////////////////////////////////////////////////
  329. void SettingsComponent::setValues(const QVariantMap& options)
  330. {
  331. Q_ASSERT(options.contains("key"));
  332. Q_ASSERT(options.contains("value"));
  333. QString key = options["key"].toString();
  334. QVariant values = options["value"];
  335. if (values.type() == QVariant::Map || values.isNull())
  336. {
  337. SettingsSection* section = getSection(key);
  338. if (!section)
  339. {
  340. // let's create this section since it's most likely created by the webclient
  341. section = new SettingsSection(key, PLATFORM_ANY, -1, this);
  342. section->setHidden(true);
  343. section->setStorage(true);
  344. m_sections.insert(key, section);
  345. }
  346. if (values.isNull())
  347. section->resetValues();
  348. else
  349. section->setValues(values);
  350. saveSection(section);
  351. }
  352. else if (values.type() == QVariant::String)
  353. {
  354. setValue(SETTINGS_SECTION_WEBCLIENT, key, values);
  355. }
  356. else
  357. {
  358. qWarning() << "the values sent was not a map, string or empty value. it will be ignored";
  359. // return so we don't call save()
  360. return;
  361. }
  362. }
  363. ///////////////////////////////////////////////////////////////////////////////////////////////////
  364. void SettingsComponent::removeValue(const QString &sectionOrKey)
  365. {
  366. SettingsSection* section = getSection(sectionOrKey);
  367. if (section)
  368. {
  369. // we want to remove a full section
  370. // dont remove the section, but remove all keys
  371. section->resetValues();
  372. saveSection(section);
  373. }
  374. else
  375. {
  376. // we want to remove a root key from webclient
  377. // which is stored in webclient section
  378. section = m_sections[SETTINGS_SECTION_WEBCLIENT];
  379. section->resetValue(sectionOrKey);
  380. saveSection(section);
  381. }
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////////////////////////
  384. void SettingsComponent::resetToDefault(const QString &sectionID)
  385. {
  386. SettingsSection* section = getSection(sectionID);
  387. if (section)
  388. {
  389. section->resetValues();
  390. saveSection(section);
  391. }
  392. }
  393. ///////////////////////////////////////////////////////////////////////////////////////////////////
  394. void SettingsComponent::resetToDefaultAll()
  395. {
  396. for(SettingsSection *section : m_sections)
  397. {
  398. section->resetValues();
  399. saveSection(section);
  400. }
  401. }
  402. /////////////////////////////////////////////////////////////////////////////////////////
  403. struct SectionOrderIndex
  404. {
  405. inline bool operator ()(SettingsSection* a, SettingsSection* b)
  406. {
  407. return a->orderIndex() < b->orderIndex();
  408. }
  409. };
  410. ///////////////////////////////////////////////////////////////////////////////////////////////////
  411. QVariantList SettingsComponent::settingDescriptions()
  412. {
  413. QJsonArray desc;
  414. QList<SettingsSection*> sectionList = m_sections.values();
  415. std::sort(sectionList.begin(), sectionList.end(), SectionOrderIndex());
  416. for(SettingsSection* section : sectionList)
  417. {
  418. if (!section->isHidden())
  419. desc.push_back(QJsonValue::fromVariant(section->descriptions()));
  420. }
  421. return desc.toVariantList();
  422. }
  423. /////////////////////////////////////////////////////////////////////////////////////////
  424. bool SettingsComponent::loadDescription()
  425. {
  426. QJsonParseError err;
  427. auto doc = Utils::OpenJsonDocument(":/settings/settings_description.json", &err);
  428. if (doc.isNull())
  429. {
  430. qCritical() << "Failed to read settings description:" << err.errorString();
  431. throw FatalException("Failed to read settings description!");
  432. }
  433. if (!doc.isArray())
  434. {
  435. qCritical() << "The object needs to be an array";
  436. return false;
  437. }
  438. m_sectionIndex = 0;
  439. for(auto val : doc.array())
  440. {
  441. if (!val.isObject())
  442. {
  443. qCritical() << "Hoped to find sections in the root array, but they where not JSON objects";
  444. return false;
  445. }
  446. QJsonObject section = val.toObject();
  447. if (!section.contains("section"))
  448. {
  449. qCritical() << "A section needs to contain the section keyword.";
  450. return false;
  451. }
  452. if (section["section"] == "__meta__")
  453. m_settingsVersion = section.value("version").toInt();
  454. else
  455. parseSection(section);
  456. }
  457. return true;
  458. }
  459. /////////////////////////////////////////////////////////////////////////////////////////
  460. void SettingsComponent::parseSection(const QJsonObject& sectionObject)
  461. {
  462. QString sectionName = sectionObject.value("section").toString();
  463. if (!sectionObject.contains("values") || !sectionObject.value("values").isArray())
  464. {
  465. qCritical() << "section object:" << sectionName << "did not contain a values array";
  466. return;
  467. }
  468. int platformMask = platformMaskFromObject(sectionObject);
  469. auto section = new SettingsSection(sectionName, (quint8)platformMask, m_sectionIndex ++, this);
  470. section->setHidden(sectionObject.value("hidden").toBool(false));
  471. section->setStorage(sectionObject.value("storage").toBool(false));
  472. auto values = sectionObject.value("values").toArray();
  473. int order = 0;
  474. for(auto val : values)
  475. {
  476. if (!val.isObject())
  477. continue;
  478. QJsonObject valobj = val.toObject();
  479. if (!valobj.contains("value") || !valobj.contains("default") || valobj.value("value").isNull())
  480. continue;
  481. QJsonValue defaults = valobj.value("default");
  482. QVariant defaultval = defaults.toVariant();
  483. if (defaults.isArray())
  484. {
  485. defaultval = QVariant();
  486. // Whichever default matches the current platform first is used.
  487. for(auto v : defaults.toArray())
  488. {
  489. auto vobj = v.toObject();
  490. int defPlatformMask = platformMaskFromObject(vobj);
  491. if ((defPlatformMask & Utils::CurrentPlatform()) == Utils::CurrentPlatform())
  492. {
  493. defaultval = vobj.value("value").toVariant();
  494. break;
  495. }
  496. }
  497. }
  498. int vPlatformMask = platformMaskFromObject(valobj);
  499. SettingsValue* setting = new SettingsValue(valobj.value("value").toString(), defaultval, (quint8)vPlatformMask, this);
  500. setting->setHasDescription(true);
  501. setting->setHidden(valobj.value("hidden").toBool(false));
  502. setting->setIndexOrder(order ++);
  503. if (valobj.contains("input_type"))
  504. setting->setInputType(valobj.value("input_type").toString());
  505. if (valobj.contains("possible_values") && valobj.value("possible_values").isArray())
  506. {
  507. auto list = valobj.value("possible_values").toArray();
  508. for(auto v : list)
  509. {
  510. int platform = PLATFORM_ANY;
  511. auto vl = v.toArray();
  512. if (vl.size() < 2)
  513. continue;
  514. if (vl.size() == 3 && vl.at(2).isObject())
  515. {
  516. // platform options
  517. QJsonObject options = vl.at(2).toObject();
  518. platform = platformMaskFromObject(options);
  519. }
  520. if ((platform & Utils::CurrentPlatform()) == Utils::CurrentPlatform())
  521. setting->addPossibleValue(vl.at(1).toString(), vl.at(0).toVariant());
  522. }
  523. }
  524. section->registerSetting(setting);
  525. }
  526. m_sections.insert(sectionName, section);
  527. }
  528. /////////////////////////////////////////////////////////////////////////////////////////
  529. int SettingsComponent::platformMaskFromObject(const QJsonObject& object)
  530. {
  531. int platformMask = PLATFORM_ANY;
  532. // only include the listed platforms
  533. if (object.contains("platforms"))
  534. {
  535. // start by resetting it to no platforms
  536. platformMask = PLATFORM_UNKNOWN;
  537. QJsonValue platforms = object.value("platforms");
  538. // platforms can be both array or a single string
  539. if (platforms.isArray())
  540. {
  541. for(auto pl : platforms.toArray())
  542. {
  543. if (!pl.isString())
  544. continue;
  545. platformMask |= platformFromString(pl.toString());
  546. }
  547. }
  548. else
  549. {
  550. platformMask = platformFromString(platforms.toString());
  551. }
  552. }
  553. else if (object.contains("platforms_excluded"))
  554. {
  555. QJsonValue val = object.value("platforms_excluded");
  556. if (val.isArray())
  557. {
  558. for(auto pl : val.toArray())
  559. {
  560. if (!pl.isString())
  561. continue;
  562. platformMask &= ~platformFromString(pl.toString());
  563. }
  564. }
  565. else
  566. {
  567. platformMask &= ~platformFromString(val.toString());
  568. }
  569. }
  570. return platformMask;
  571. }
  572. /////////////////////////////////////////////////////////////////////////////////////////
  573. Platform SettingsComponent::platformFromString(const QString& platformString)
  574. {
  575. if (platformString == "osx")
  576. return PLATFORM_OSX;
  577. else if (platformString == "windows")
  578. return PLATFORM_WINDOWS;
  579. else if (platformString == "linux")
  580. return PLATFORM_LINUX;
  581. else if (platformString == "oe")
  582. return PLATFORM_OE;
  583. else if (platformString == "oe_rpi")
  584. return PLATFORM_OE_RPI;
  585. else if (platformString == "oe_x86")
  586. return PLATFORM_OE_X86;
  587. else if (platformString == "any")
  588. return PLATFORM_ANY;
  589. return PLATFORM_UNKNOWN;
  590. }
  591. /////////////////////////////////////////////////////////////////////////////////////////
  592. bool SettingsComponent::componentInitialize()
  593. {
  594. if (!loadDescription())
  595. return false;
  596. // Must be called before we possibly write the config file.
  597. setupVersion();
  598. load();
  599. // add our AudioSettingsController that will inspect audio settings and react.
  600. // then run the signal the first time to make sure that we set the proper visibility
  601. // on the items from the start.
  602. //
  603. auto ctrl = new AudioSettingsController(this);
  604. QVariantMap val;
  605. val.insert("devicetype", value(SETTINGS_SECTION_AUDIO, "devicetype"));
  606. ctrl->valuesUpdated(val);
  607. connect(ctrl, &AudioSettingsController::settingsUpdated, this, &SettingsComponent::groupUpdate);
  608. return true;
  609. }
  610. /////////////////////////////////////////////////////////////////////////////////////////
  611. void SettingsComponent::setupVersion()
  612. {
  613. QSettings settings;
  614. m_oldestPreviousVersion = settings.value(OLDEST_PREVIOUS_VERSION_KEY).toString();
  615. if (m_oldestPreviousVersion.isEmpty())
  616. {
  617. // Version key was not present. It could still be a pre-1.1 PMP install,
  618. // so here we try to find out whether this is the very first install, or
  619. // if an older one exists.
  620. QFile configFile(Paths::dataDir("jellyfinmediaplayer.conf"));
  621. if (configFile.exists())
  622. m_oldestPreviousVersion = "legacy";
  623. else
  624. m_oldestPreviousVersion = Version::GetVersionString();
  625. settings.setValue(OLDEST_PREVIOUS_VERSION_KEY, m_oldestPreviousVersion);
  626. }
  627. }
  628. /////////////////////////////////////////////////////////////////////////////////////////
  629. bool SettingsComponent::resetAndSaveOldConfiguration()
  630. {
  631. QFile settingsFile(Paths::dataDir("jellyfinmediaplayer.conf"));
  632. return settingsFile.rename(Paths::dataDir("jellyfinmediaplayer.conf.old"));
  633. }
  634. /////////////////////////////////////////////////////////////////////////////////////////
  635. bool SettingsComponent::isUsingExternalWebClient()
  636. {
  637. QString url;
  638. url = SettingsComponent::Get().value(SETTINGS_SECTION_PATH, "startupurl_desktop").toString();
  639. if (url == "bundled")
  640. {
  641. auto path = Paths::webClientPath("desktop");
  642. QFileInfo check_file(path);
  643. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "forceExternalWebclient").toBool() ||
  644. !(check_file.exists() && check_file.isFile())) {
  645. // use built-in fallback
  646. return true;
  647. }
  648. }
  649. return false;
  650. }
  651. /////////////////////////////////////////////////////////////////////////////////////////
  652. QString SettingsComponent::getWebClientUrl(bool desktop)
  653. {
  654. QString url;
  655. url = SettingsComponent::Get().value(SETTINGS_SECTION_PATH, "startupurl_desktop").toString();
  656. if (url == "bundled")
  657. {
  658. auto path = Paths::webClientPath("desktop");
  659. QFileInfo check_file(path);
  660. if (SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "forceExternalWebclient").toBool() ||
  661. !(check_file.exists() && check_file.isFile())) {
  662. // use built-in fallback
  663. path = Paths::webExtensionPath() + "find-webclient.html";
  664. }
  665. url = "file:///" + path;
  666. }
  667. qDebug() << "Using web-client URL: " << url;
  668. return url;
  669. }
  670. /////////////////////////////////////////////////////////////////////////////////////////
  671. QString SettingsComponent::getExtensionPath()
  672. {
  673. QString url;
  674. url = SettingsComponent::Get().value(SETTINGS_SECTION_PATH, "startupurl_extension").toString();
  675. if (url == "bundled")
  676. {
  677. auto path = Paths::webExtensionPath("extension");
  678. url = path;
  679. }
  680. return url;
  681. }
  682. /////////////////////////////////////////////////////////////////////////////////////////
  683. QString SettingsComponent::getClientName()
  684. {
  685. QString name;
  686. name = SettingsComponent::Get().value(SETTINGS_SECTION_SYSTEM, "systemname").toString();
  687. if (name.compare("JellyfinMediaPlayer") == 0) {
  688. name = Utils::ComputerName();
  689. }
  690. return name;
  691. }
  692. /////////////////////////////////////////////////////////////////////////////////////////
  693. bool SettingsComponent::ignoreSSLErrors()
  694. {
  695. return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "ignoreSSLErrors").toBool();
  696. }
  697. /////////////////////////////////////////////////////////////////////////////////////////
  698. bool SettingsComponent::autodetectCertBundle()
  699. {
  700. return SettingsComponent::Get().value(SETTINGS_SECTION_MAIN, "autodetectCertBundle").toBool();
  701. }
  702. /////////////////////////////////////////////////////////////////////////////////////////
  703. void SettingsComponent::setCommandLineValues(const QStringList& values)
  704. {
  705. qDebug() << values;
  706. for (const QString& value : values)
  707. {
  708. if (value == "fullscreen")
  709. setValue(SETTINGS_SECTION_MAIN, "fullscreen", true);
  710. else if (value == "windowed")
  711. setValue(SETTINGS_SECTION_MAIN, "fullscreen", false);
  712. else if (value == "desktop")
  713. setValue(SETTINGS_SECTION_MAIN, "layout", "desktop");
  714. else if (value == "tv")
  715. setValue(SETTINGS_SECTION_MAIN, "layout", "tv");
  716. else if (value == "force-external-webclient")
  717. setValue(SETTINGS_SECTION_MAIN, "forceExternalWebclient", true);
  718. }
  719. }