config.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { ConfigManager, compress, type ConfigMigrationsDict, decompress } from "@sv443-network/userutils";
  2. import { featInfo } from "./features/index";
  3. import { compressionSupported, info, log } from "./utils";
  4. import { emitSiteEvent } from "./siteEvents";
  5. import { compressionFormat } from "./constants";
  6. import type { FeatureConfig } from "./types";
  7. /** If this number is incremented, the features object data will be migrated to the new format */
  8. export const formatVersion = 4;
  9. /** Config data format migration dictionary */
  10. export const migrations: ConfigMigrationsDict = {
  11. // 1 -> 2
  12. 2: (oldData: Record<string, unknown>) => {
  13. const queueBtnsEnabled = Boolean(oldData.queueButtons);
  14. delete oldData.queueButtons;
  15. return {
  16. ...oldData,
  17. deleteFromQueueButton: queueBtnsEnabled,
  18. lyricsQueueButton: queueBtnsEnabled,
  19. };
  20. },
  21. // 2 -> 3
  22. 3: (oldData: Record<string, unknown>) => ({
  23. ...oldData,
  24. removeShareTrackingParam: getFeatureDefault("removeShareTrackingParam"),
  25. numKeysSkipToTime: getFeatureDefault("numKeysSkipToTime"),
  26. fixSpacing: getFeatureDefault("fixSpacing"),
  27. scrollToActiveSongBtn: getFeatureDefault("scrollToActiveSongBtn"),
  28. logLevel: getFeatureDefault("logLevel"),
  29. }),
  30. // 3 -> 4
  31. 4: (oldData: Record<string, unknown>) => {
  32. const oldSwitchSitesHotkey = oldData.switchSitesHotkey as Record<string, unknown>;
  33. return {
  34. ...oldData,
  35. rememberSongTime: getFeatureDefault("rememberSongTime"),
  36. rememberSongTimeSites: getFeatureDefault("rememberSongTimeSites"),
  37. arrowKeySkipBy: 10,
  38. switchSitesHotkey: {
  39. code: oldSwitchSitesHotkey.key ?? "F9",
  40. shift: Boolean(oldSwitchSitesHotkey.shift ?? false),
  41. ctrl: Boolean(oldSwitchSitesHotkey.ctrl ?? false),
  42. alt: Boolean(oldSwitchSitesHotkey.meta ?? false),
  43. },
  44. listButtonsPlacement: "queueOnly",
  45. volumeSliderScrollStep: getFeatureDefault("volumeSliderScrollStep"),
  46. locale: getFeatureDefault("locale"),
  47. versionCheck: getFeatureDefault("versionCheck"),
  48. };
  49. },
  50. };
  51. function getFeatureDefault<TKey extends keyof typeof featInfo>(key: TKey): typeof featInfo[TKey]["default"] {
  52. return featInfo[key].default;
  53. }
  54. export const defaultConfig = (Object.keys(featInfo) as (keyof typeof featInfo)[])
  55. .reduce<Partial<FeatureConfig>>((acc, key) => {
  56. acc[key] = featInfo[key].default as unknown as undefined;
  57. return acc;
  58. }, {}) as FeatureConfig;
  59. let canCompress = true;
  60. const cfgMgr = new ConfigManager({
  61. id: "bytm-config",
  62. formatVersion,
  63. defaultConfig,
  64. migrations,
  65. encodeData: (data) => canCompress ? compress(data, compressionFormat, "string") : data,
  66. decodeData: (data) => canCompress ? decompress(data, compressionFormat, "string") : data,
  67. });
  68. /** Initializes the ConfigManager instance and loads persistent data into memory */
  69. export async function initConfig() {
  70. canCompress = await compressionSupported();
  71. const oldFmtVer = Number(await GM.getValue(`_uucfgver-${cfgMgr.id}`, NaN));
  72. const data = await cfgMgr.loadData();
  73. log(`Initialized ConfigManager (format version = ${cfgMgr.formatVersion})`);
  74. if(isNaN(oldFmtVer))
  75. info("Config data initialized with default values");
  76. else if(oldFmtVer !== cfgMgr.formatVersion)
  77. info(`Config data migrated from version ${oldFmtVer} to ${cfgMgr.formatVersion}`);
  78. return data;
  79. }
  80. /** Returns the current feature config from the in-memory cache */
  81. export function getFeatures() {
  82. return cfgMgr.getData();
  83. }
  84. /** Saves the feature config synchronously to the in-memory cache and asynchronously to the persistent storage */
  85. export function saveFeatures(featureConf: FeatureConfig) {
  86. const res = cfgMgr.setData(featureConf);
  87. emitSiteEvent("configChanged", cfgMgr.getData());
  88. info("Saved new feature config:", featureConf);
  89. return res;
  90. }
  91. /** Saves the default feature config synchronously to the in-memory cache and asynchronously to persistent storage */
  92. export function setDefaultFeatures() {
  93. const res = cfgMgr.saveDefaultData();
  94. emitSiteEvent("configChanged", cfgMgr.getData());
  95. info("Reset feature config to its default values");
  96. return res;
  97. }
  98. /** Clears the feature config from the persistent storage - since the cache will be out of whack, this should only be run before a site re-/unload */
  99. export async function clearConfig() {
  100. await cfgMgr.deleteConfig();
  101. info("Deleted config from persistent storage");
  102. }